mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-09 04:36:02 -05:00
Object damage calculator page initial
This commit is contained in:
@@ -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;
|
||||
|
||||
74
app/modules/analyzer/damageMultipliers.ts
Normal file
74
app/modules/analyzer/damageMultipliers.ts
Normal 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,
|
||||
}));
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
5579
app/modules/analyzer/object-dmg.json
Normal file
5579
app/modules/analyzer/object-dmg.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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`);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
48
app/modules/analyzer/useObjectDamage.ts
Normal file
48
app/modules/analyzer/useObjectDamage.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
34
app/routes/object-damage.tsx
Normal file
34
app/routes/object-damage.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
55
scripts/create-object-dmg-json.ts
Normal file
55
scripts/create-object-dmg-json.ts
Normal 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)
|
||||
);
|
||||
Reference in New Issue
Block a user