Object damage calculator page initial

This commit is contained in:
Kalle
2022-10-18 01:12:52 +03:00
parent e934c4ded7
commit 586c27eaa7
12 changed files with 5852 additions and 36 deletions

View File

@@ -1,2 +1,32 @@
export const MAX_LDE_INTENSITY = 21;
export const MAX_AP = 57;
export const DAMAGE_RECEIVERS = [
"Bomb_TorpedoBullet", // Torpedo
"Chariot", // Crab Tank
"Gachihoko_Barrier", // Rainmaker Shield
"GreatBarrier_Barrier", // Big Bubbler Shield
"GreatBarrier_WeakPoint", // Big Bubbler Weak Point
"InkRail", // InkRail
"NiceBall_Armor", // Booyah Bomb Armor
"ShockSonar", // Wave Breaker
"Sponge_Versus", // Sponge
"Wsb_Flag", // Squid Beakon
"Wsb_Shield", // Splash Wall
"Wsb_Sprinkler", // Sprinkler
"BulletUmbrellaCanopyCompact", // Undercover Brella Canopy
"BulletUmbrellaCanopyNormal", // Splat Brella Canopy
"BulletUmbrellaCanopyWide", // Tenta Brella Canopy
] as const;
export const DAMAGE_TYPE = [
"NORMAL_MIN",
"NORMAL_MAX",
"DIRECT",
"FULL_CHARGE",
"MAX_CHARGE",
"TAP_SHOT",
"DISTANCE",
"BOMB_NORMAL",
"BOMB_DIRECT",
] as const;

View File

@@ -0,0 +1,74 @@
import type { DamageType } from "./types";
import objectDamages from "./object-dmg.json";
import type {
MainWeaponId,
SpecialWeaponId,
SubWeaponId,
} from "../in-game-lists";
import { DAMAGE_RECEIVERS } from "./constants";
const objectDamageJsonKeyPriority: Record<
DamageType,
Array<keyof typeof objectDamages>
> = {
NORMAL_MIN: ["Shooter"],
NORMAL_MAX: ["Shooter"],
DIRECT: [],
FULL_CHARGE: [],
MAX_CHARGE: [],
TAP_SHOT: [],
DISTANCE: [],
BOMB_NORMAL: [],
BOMB_DIRECT: [],
};
export function damageTypeToMultipliers({
type,
weapon,
}: {
type: DamageType;
weapon:
| {
type: "MAIN";
id: MainWeaponId;
}
| {
type: "SUB";
id: SubWeaponId;
}
| {
type: "SPECIAL";
id: SpecialWeaponId;
};
}) {
for (const key of objectDamageJsonKeyPriority[type]) {
const objectDamagesObj = objectDamages[key];
let ok = false;
if (weapon.type === "MAIN") {
ok = (objectDamagesObj.mainWeaponIds as MainWeaponId[]).includes(
weapon.id
);
} else if (weapon.type === "SUB") {
ok = (objectDamagesObj.subWeaponIds as SubWeaponId[]).includes(weapon.id);
} else if (weapon.type === "SPECIAL") {
ok = (objectDamagesObj.specialWeaponIds as SpecialWeaponId[]).includes(
weapon.id
);
}
if (ok) return objectDamagesObj.rates;
}
return null;
}
export function fallbackRates(
multipliers: ReturnType<typeof damageTypeToMultipliers>
) {
return DAMAGE_RECEIVERS.map((receiver) => ({
target: receiver,
rate: multipliers?.find((m) => m.target === receiver)?.rate ?? 1,
}));
}

View File

@@ -9,6 +9,8 @@ export type {
export { useAnalyzeBuild } from "./useAnalyzeBuild";
export { MAX_LDE_INTENSITY } from "./constants";
export { useObjectDamage } from "./useObjectDamage";
export { MAX_LDE_INTENSITY, DAMAGE_RECEIVERS, DAMAGE_TYPE } from "./constants";
export { lastDitchEffortIntensityToAp } from "./specialEffects";

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@ import type {
StatFunctionInput,
SubWeaponParams,
} from "./types";
import { DAMAGE_TYPE } from "./types";
import { DAMAGE_TYPE } from "./constants";
import { INK_CONSUME_TYPES } from "./types";
import invariant from "tiny-invariant";
import {
@@ -24,13 +24,13 @@ import { semiRandomId } from "~/utils/strings";
import { roundToTwoDecimalPlaces } from "~/utils/number";
export function buildStats({
abilityPoints,
weaponSplId,
mainOnlyAbilities,
abilityPoints = new Map(),
mainOnlyAbilities = [],
}: {
abilityPoints: AbilityPoints;
weaponSplId: MainWeaponId;
mainOnlyAbilities: Array<Ability>;
abilityPoints?: AbilityPoints;
mainOnlyAbilities?: Array<Ability>;
}): AnalyzedBuild {
const mainWeaponParams = weaponParams().mainWeapons[weaponSplId];
invariant(mainWeaponParams, `Weapon with splId ${weaponSplId} not found`);

View File

@@ -7,6 +7,7 @@ import type {
import type { SPECIAL_EFFECTS } from "./specialEffects";
import type weaponParams from "./weapon-params.json";
import type abilityValues from "./ability-values.json";
import type { DAMAGE_RECEIVERS, DAMAGE_TYPE } from "./constants";
type Overwrites = Record<
string,
@@ -159,20 +160,10 @@ export interface FullInkTankOption {
type: InkConsumeType;
}
export const DAMAGE_TYPE = [
"NORMAL_MIN",
"NORMAL_MAX",
"DIRECT",
"FULL_CHARGE",
"MAX_CHARGE",
"TAP_SHOT",
"DISTANCE",
"BOMB_NORMAL",
"BOMB_DIRECT",
] as const;
export type DamageType = typeof DAMAGE_TYPE[number];
export type DamageReceiver = typeof DAMAGE_RECEIVERS[number];
export interface Damage {
value: number;
type: DamageType;

View File

@@ -3,10 +3,8 @@ import { EMPTY_BUILD } from "~/constants";
import {
type BuildAbilitiesTupleWithUnknown,
type MainWeaponId,
mainWeaponIds,
abilities,
isAbility,
weaponCategories,
} from "../in-game-lists";
import type {
Ability,
@@ -17,7 +15,10 @@ import { MAX_LDE_INTENSITY } from "./constants";
import { applySpecialEffects, SPECIAL_EFFECTS } from "./specialEffects";
import { buildStats } from "./stats";
import type { SpecialEffectType } from "./types";
import { buildToAbilityPoints } from "./utils";
import {
buildToAbilityPoints,
validatedWeaponIdFromSearchParams,
} from "./utils";
const UNKNOWN_SHORT = "U";
@@ -88,20 +89,6 @@ function serializeBuild(build: BuildAbilitiesTupleWithUnknown) {
.join(",");
}
function validatedWeaponIdFromSearchParams(
searchParams: URLSearchParams
): MainWeaponId {
const weaponId = searchParams.get("weapon")
? Number(searchParams.get("weapon"))
: null;
if (mainWeaponIds.includes(weaponId as any)) {
return weaponId as MainWeaponId;
}
return weaponCategories[0].weaponIds[0];
}
function validatedBuildFromSearchParams(
searchParams: URLSearchParams
): BuildAbilitiesTupleWithUnknown {

View File

@@ -0,0 +1,48 @@
import { useSearchParams } from "@remix-run/react";
import { type MainWeaponId } from "../in-game-lists";
import { damageTypeToMultipliers, fallbackRates } from "./damageMultipliers";
import { buildStats } from "./stats";
import { validatedWeaponIdFromSearchParams } from "./utils";
export function useObjectDamage() {
const [searchParams, setSearchParams] = useSearchParams();
const mainWeaponId = validatedWeaponIdFromSearchParams(searchParams);
const handleChange = ({
newMainWeaponId = mainWeaponId,
}: {
newMainWeaponId?: MainWeaponId;
}) => {
setSearchParams(
{
weapon: String(newMainWeaponId),
},
{ replace: true, state: { scroll: false } }
);
};
const analyzed = buildStats({
weaponSplId: mainWeaponId,
});
const multipliers = Object.fromEntries(
analyzed.stats.damages.map((damage) => {
return [
damage.type,
fallbackRates(
damageTypeToMultipliers({
type: damage.type,
weapon: { type: "MAIN", id: mainWeaponId },
})
),
];
})
);
return {
mainWeaponId,
handleChange,
multipliers,
};
}

View File

@@ -1,4 +1,5 @@
import type { Ability, BuildAbilitiesTupleWithUnknown } from "../in-game-lists";
import { mainWeaponIds, weaponCategories } from "../in-game-lists";
import { abilities } from "../in-game-lists";
import weaponParamsJson from "./weapon-params.json";
import abilityValuesJson from "./ability-values.json";
@@ -10,7 +11,7 @@ import type {
SubWeaponParams,
} from "./types";
import invariant from "tiny-invariant";
import type { AbilityWithUnknown } from "../in-game-lists/types";
import type { AbilityWithUnknown, MainWeaponId } from "../in-game-lists/types";
export function weaponParams(): ParamsJson {
return weaponParamsJson as ParamsJson;
@@ -150,3 +151,17 @@ export function hasEffect({
return high !== mid || mid !== low;
}
export function validatedWeaponIdFromSearchParams(
searchParams: URLSearchParams
): MainWeaponId {
const weaponId = searchParams.get("weapon")
? Number(searchParams.get("weapon"))
: null;
if (mainWeaponIds.includes(weaponId as any)) {
return weaponId as MainWeaponId;
}
return weaponCategories[0].weaponIds[0];
}

View File

@@ -0,0 +1,34 @@
import { WeaponCombobox } from "~/components/Combobox";
import { Main } from "~/components/Main";
import { useObjectDamage } from "~/modules/analyzer";
import type { MainWeaponId } from "~/modules/in-game-lists";
export const handle = {
i18n: ["weapons"],
};
export default function ObjectDamagePage() {
const { mainWeaponId, handleChange, multipliers } = useObjectDamage();
if (process.env.NODE_ENV !== "development") {
return <Main>WIP :)</Main>;
}
return (
<Main>
<WeaponCombobox
inputName="weapon"
initialWeaponId={mainWeaponId}
onChange={(opt) =>
opt &&
handleChange({
newMainWeaponId: Number(opt.value) as MainWeaponId,
})
}
className="w-full-important"
clearsInputOnFocus
/>
<pre>{JSON.stringify(multipliers, null, 2)}</pre>
</Main>
);
}

View File

@@ -15,6 +15,7 @@
"rename-badge": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/rename-badge.ts",
"create-weapon-json": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/create-weapon-json.ts",
"create-gear-json": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/create-gear-json.ts",
"create-object-dmg-json": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/create-object-dmg-json.ts",
"create-misc-json": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/create-misc-json.ts",
"create-analyzer-json": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/create-analyzer-json.ts",
"check-translation-jsons": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/check-translation-jsons.ts && npm run prettier:write",

View File

@@ -0,0 +1,55 @@
/* eslint-disable */
// @ts-nocheck
// 1) WeaponInfoMain.json inside dicts
// 2) WeaponInfoSub.json inside dicts
// 3) WeaponInfoSpecial.json inside dicts
// 4) misc/spl__DamageRateInfoConfig.pp__CombinationDataTableData.json
import params from "./dicts/spl__DamageRateInfoConfig.pp__CombinationDataTableData.json";
import weapons from "./dicts/WeaponInfoMain.json";
import subWeapons from "./dicts/WeaponInfoSub.json";
import specialWeapons from "./dicts/WeaponInfoSpecial.json";
import fs from "node:fs";
import path from "node:path";
const OUTPUT_DIR_PATH = path.join(__dirname, "output");
const weaponParamsToWeaponIds = (
params: typeof weapons | typeof subWeapons | typeof specialWeapons,
key: string
) => {
return params
.filter((param) => {
return (
param.DefaultDamageRateInfoRow === key ||
param.ExtraDamageRateInfoRowSet?.some(
(row) => row.DamageRateInfoRow === key
)
);
})
.map((weapon) => weapon.Id);
};
const result = {};
for (const cell of Object.values(params.CellList)) {
if (!cell.DamageRate) continue;
if (!result[cell.RowKey]) {
result[cell.RowKey] = {
mainWeaponIds: weaponParamsToWeaponIds(weapons, cell.RowKey),
subWeaponIds: weaponParamsToWeaponIds(subWeapons, cell.RowKey),
specialWeaponIds: weaponParamsToWeaponIds(specialWeapons, cell.RowKey),
rates: [],
};
}
result[cell.RowKey].rates.push({
target: cell.ColumnKey,
rate: cell.DamageRate,
});
}
fs.writeFileSync(
path.join(OUTPUT_DIR_PATH, "object-dmg.json"),
JSON.stringify(result, null, 2)
);