mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-07 19:55:46 -05:00
Special weapon damages for analyzers + Trizooka
This commit is contained in:
@@ -14,12 +14,17 @@ import {
|
||||
subWeaponIds,
|
||||
weaponCategories,
|
||||
} from "~/modules/in-game-lists";
|
||||
import { nonBombSubWeaponIds } from "~/modules/in-game-lists/weapon-ids";
|
||||
import {
|
||||
nonBombSubWeaponIds,
|
||||
nonDamagingSpecialWeaponIds,
|
||||
specialWeaponIds,
|
||||
} from "~/modules/in-game-lists/weapon-ids";
|
||||
import { type SerializedMapPoolEvent } from "~/routes/calendar/map-pool-events";
|
||||
import type { Unpacked } from "~/utils/types";
|
||||
import {
|
||||
gearImageUrl,
|
||||
mainWeaponImageUrl,
|
||||
specialWeaponImageUrl,
|
||||
subWeaponImageUrl,
|
||||
} from "~/utils/urls";
|
||||
import { Image } from "./Image";
|
||||
@@ -307,6 +312,16 @@ export function AllWeaponCombobox({
|
||||
});
|
||||
}
|
||||
|
||||
for (const specialWeaponId of specialWeaponIds) {
|
||||
if (nonDamagingSpecialWeaponIds.includes(specialWeaponId)) continue;
|
||||
|
||||
result.push({
|
||||
value: `SPECIAL_${specialWeaponId}`,
|
||||
label: t(`SPECIAL_${specialWeaponId}`),
|
||||
imgPath: specialWeaponImageUrl(specialWeaponId),
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ export const DAMAGE_TYPE = [
|
||||
"ROLL_OVER",
|
||||
] as const;
|
||||
|
||||
// a bit weird there is no SPECIAL here listed but it is used
|
||||
// only to determine if the damage type is for main weapon or not
|
||||
export const damageTypeToWeaponType: Record<
|
||||
DamageType,
|
||||
"MAIN" | "SUB" | "SPECIAL"
|
||||
|
||||
@@ -206,6 +206,7 @@ export interface AnalyzedBuild {
|
||||
subWeaponInkConsumptionPercentage: Stat;
|
||||
fullInkTankOptions: Array<FullInkTankOption & { id: string }>;
|
||||
damages: Array<Damage & { id: string }>;
|
||||
specialWeaponDamages: Array<Damage & { id: string }>;
|
||||
subWeaponDefenseDamages: Array<SubWeaponDamage & { id: string }>;
|
||||
squidFormInkRecoverySeconds: Stat;
|
||||
humanoidFormInkRecoverySeconds: Stat;
|
||||
@@ -263,4 +264,5 @@ export type AbilityValuesKeys = keyof typeof abilityValues;
|
||||
|
||||
export type AnyWeapon =
|
||||
| { type: "MAIN"; id: MainWeaponId }
|
||||
| { type: "SUB"; id: SubWeaponId };
|
||||
| { type: "SUB"; id: SubWeaponId }
|
||||
| { type: "SPECIAL"; id: SpecialWeaponId };
|
||||
|
||||
@@ -21,6 +21,7 @@ import type {
|
||||
DamageType,
|
||||
InkConsumeType,
|
||||
MainWeaponParams,
|
||||
SpecialWeaponParams,
|
||||
StatFunctionInput,
|
||||
SubWeaponParams,
|
||||
} from "../analyzer-types";
|
||||
@@ -107,6 +108,7 @@ export function buildStats({
|
||||
specialLostSplattedByRP: specialLost(input, true),
|
||||
fullInkTankOptions: fullInkTankOptions(input),
|
||||
damages: damages(input),
|
||||
specialWeaponDamages: specialWeaponDamages(input),
|
||||
subWeaponDefenseDamages: subWeaponDefenseDamages(input),
|
||||
mainWeaponWhiteInkSeconds:
|
||||
typeof mainWeaponParams.InkRecoverStop === "number"
|
||||
@@ -386,7 +388,9 @@ const damageTypeToParamsKey: Record<
|
||||
DamageType,
|
||||
| keyof MainWeaponParams
|
||||
| keyof SubWeaponParams
|
||||
| Array<keyof MainWeaponParams | keyof SubWeaponParams>
|
||||
| Array<
|
||||
keyof MainWeaponParams | keyof SubWeaponParams | keyof SpecialWeaponParams
|
||||
>
|
||||
> = {
|
||||
NORMAL_MIN: "DamageParam_ValueMin",
|
||||
NORMAL_MAX: "DamageParam_ValueMax",
|
||||
@@ -458,6 +462,49 @@ function damages(args: StatFunctionInput): AnalyzedBuild["stats"]["damages"] {
|
||||
return result;
|
||||
}
|
||||
|
||||
// xxx: TODO: handle damage distance increasing e.g. inkzooka, small special power up icon next to the distance
|
||||
function specialWeaponDamages(
|
||||
args: StatFunctionInput
|
||||
): AnalyzedBuild["stats"]["specialWeaponDamages"] {
|
||||
const result: AnalyzedBuild["stats"]["specialWeaponDamages"] = [];
|
||||
|
||||
for (const type of DAMAGE_TYPE) {
|
||||
for (const key of [damageTypeToParamsKey[type]].flat()) {
|
||||
const value = args.specialWeaponParams[key as keyof SpecialWeaponParams];
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
for (const subValue of value.flat()) {
|
||||
result.push({
|
||||
type,
|
||||
value: subValue.Damage / 10,
|
||||
distance: subValue.Distance,
|
||||
id: semiRandomId(),
|
||||
multiShots: multiShot[args.weaponSplId],
|
||||
});
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof value !== "number") continue;
|
||||
|
||||
result.push({
|
||||
id: semiRandomId(),
|
||||
type,
|
||||
value: value / 10,
|
||||
shotsToSplat: shotsToSplat({
|
||||
value,
|
||||
type,
|
||||
multiShots: multiShot[args.weaponSplId],
|
||||
}),
|
||||
multiShots: multiShot[args.weaponSplId],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function shotsToSplat({
|
||||
value,
|
||||
type,
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
import type { AbilityType, SubWeaponId } from "~/modules/in-game-lists";
|
||||
import { subWeaponIds } from "~/modules/in-game-lists";
|
||||
import type {
|
||||
AbilityType,
|
||||
SpecialWeaponId,
|
||||
SubWeaponId,
|
||||
} from "~/modules/in-game-lists";
|
||||
import {
|
||||
subWeaponIds,
|
||||
nonBombSubWeaponIds,
|
||||
nonDamagingSpecialWeaponIds,
|
||||
specialWeaponIds,
|
||||
} from "~/modules/in-game-lists";
|
||||
import {
|
||||
abilities,
|
||||
mainWeaponIds,
|
||||
@@ -25,7 +34,6 @@ import invariant from "tiny-invariant";
|
||||
import { EMPTY_BUILD } from "~/constants";
|
||||
import { UNKNOWN_SHORT } from "../analyzer-constants";
|
||||
import type { Unpacked } from "~/utils/types";
|
||||
import { nonBombSubWeaponIds } from "~/modules/in-game-lists/weapon-ids";
|
||||
|
||||
export function weaponParams(): ParamsJson {
|
||||
return weaponParamsJson as ParamsJson;
|
||||
@@ -190,6 +198,20 @@ export function validatedAnyWeaponFromSearchParams(
|
||||
return { type: "SUB", id: id as SubWeaponId };
|
||||
}
|
||||
|
||||
if (rawWeapon?.startsWith("SPECIAL_")) {
|
||||
const id = Number(rawWeapon.replace("SPECIAL_", ""));
|
||||
|
||||
if (
|
||||
!specialWeaponIds
|
||||
.filter((id) => !nonDamagingSpecialWeaponIds.includes(id))
|
||||
.includes(id as any)
|
||||
) {
|
||||
return DEFAULT_ANY_WEAPON;
|
||||
}
|
||||
|
||||
return { type: "SPECIAL", id: id as SpecialWeaponId };
|
||||
}
|
||||
|
||||
if (rawWeapon?.startsWith("MAIN_")) {
|
||||
const id = Number(rawWeapon.replace("MAIN_", ""));
|
||||
|
||||
|
||||
@@ -1688,7 +1688,14 @@
|
||||
"Low": 360,
|
||||
"Mid": 420
|
||||
}
|
||||
}
|
||||
},
|
||||
"DistanceDamage": [
|
||||
{
|
||||
"Damage": 400,
|
||||
"Distance": 4
|
||||
}
|
||||
],
|
||||
"DirectDamage": 2200
|
||||
},
|
||||
"2": {
|
||||
"overwrites": {
|
||||
@@ -1814,7 +1821,17 @@
|
||||
"Low": 0.63,
|
||||
"Mid": 0.665
|
||||
}
|
||||
}
|
||||
},
|
||||
"DistanceDamage": [
|
||||
{
|
||||
"Damage": 500,
|
||||
"Distance": 3
|
||||
},
|
||||
{
|
||||
"Damage": 300,
|
||||
"Distance": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
"11": {
|
||||
"overwrites": {
|
||||
|
||||
@@ -703,6 +703,19 @@ export default function BuildAnalyzerPage() {
|
||||
</StatCategory>
|
||||
)}
|
||||
|
||||
{analyzed.stats.specialWeaponDamages.length > 0 && (
|
||||
<StatCategory
|
||||
title={t("analyzer:stat.category.special.damage", {
|
||||
specialWeapon: t(
|
||||
`weapons:SPECIAL_${analyzed.weapon.specialWeaponSplId}`
|
||||
),
|
||||
})}
|
||||
containerClassName="analyzer__table-container"
|
||||
>
|
||||
<DamageTable values={analyzed.stats.specialWeaponDamages} />
|
||||
</StatCategory>
|
||||
)}
|
||||
|
||||
{analyzed.stats.fullInkTankOptions.length > 0 && (
|
||||
<StatCategory
|
||||
title={t("analyzer:stat.category.actionsPerInkTank")}
|
||||
@@ -1249,7 +1262,7 @@ function DamageTable({
|
||||
values:
|
||||
| AnalyzedBuild["stats"]["damages"]
|
||||
| AnalyzedBuild["stats"]["subWeaponDefenseDamages"];
|
||||
multiShots: AnalyzedBuild["weapon"]["multiShots"];
|
||||
multiShots?: AnalyzedBuild["weapon"]["multiShots"];
|
||||
}) {
|
||||
const { t } = useTranslation(["weapons", "analyzer"]);
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ export const objectDamageJsonKeyPriority: Record<
|
||||
SuperHook: null,
|
||||
SuperLanding: null,
|
||||
TripleTornado: null,
|
||||
UltraShot: null,
|
||||
UltraShot: ["BOMB_NORMAL", "BOMB_DIRECT"],
|
||||
UltraStamp_Swing: null,
|
||||
UltraStamp_Throw_BombCore: null,
|
||||
UltraStamp_Throw: null,
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
resolveAllUniqueDamageTypes,
|
||||
} from "./core/objectDamage";
|
||||
import type { AnyWeapon } from "../build-analyzer/analyzer-types";
|
||||
import { exampleMainWeaponIdWithSpecialWeaponId } from "~/modules/in-game-lists";
|
||||
|
||||
const ABILITY_POINTS_SP_KEY = "ap";
|
||||
const DAMAGE_TYPE_SP_KEY = "dmg";
|
||||
@@ -25,13 +26,19 @@ export function useObjectDamage() {
|
||||
const abilityPoints = validatedAbilityPointsFromSearchParams(searchParams);
|
||||
const isMultiShot = validatedMultiShotFromSearchParams(searchParams);
|
||||
const analyzed = buildStats({
|
||||
weaponSplId: anyWeapon.type === "MAIN" ? anyWeapon.id : 0,
|
||||
weaponSplId:
|
||||
anyWeapon.type === "MAIN"
|
||||
? anyWeapon.id
|
||||
: anyWeapon.type === "SPECIAL"
|
||||
? exampleMainWeaponIdWithSpecialWeaponId(anyWeapon.id)
|
||||
: 0,
|
||||
hasTacticooler: false,
|
||||
});
|
||||
|
||||
const damageType = validatedDamageTypeFromSearchParams({
|
||||
searchParams,
|
||||
analyzed,
|
||||
anyWeapon,
|
||||
});
|
||||
|
||||
const handleChange = ({
|
||||
@@ -126,18 +133,24 @@ assertType<
|
||||
function validatedDamageTypeFromSearchParams({
|
||||
searchParams,
|
||||
analyzed,
|
||||
anyWeapon,
|
||||
}: {
|
||||
searchParams: URLSearchParams;
|
||||
analyzed: AnalyzedBuild;
|
||||
anyWeapon: AnyWeapon;
|
||||
}) {
|
||||
const damages =
|
||||
anyWeapon.type === "SPECIAL"
|
||||
? analyzed.stats.specialWeaponDamages
|
||||
: analyzed.stats.damages;
|
||||
const damageType = searchParams.get(DAMAGE_TYPE_SP_KEY);
|
||||
|
||||
const found = analyzed.stats.damages.find((d) => d.type === damageType);
|
||||
const found = damages.find((d) => d.type === damageType);
|
||||
|
||||
if (found) return found.type;
|
||||
|
||||
const fallbackFound = damageTypePriorityList.find((type) =>
|
||||
analyzed.stats.damages.some((d) => d.type === type)
|
||||
damages.some((d) => d.type === type)
|
||||
);
|
||||
|
||||
return fallbackFound;
|
||||
|
||||
@@ -114,6 +114,8 @@ export function resolveAllUniqueDamageTypes({
|
||||
? analyzed.stats.subWeaponDefenseDamages
|
||||
.filter((damage) => damage.subWeaponId === anyWeapon.id)
|
||||
.map((d) => d.type)
|
||||
: anyWeapon.type === "SPECIAL"
|
||||
? analyzed.stats.specialWeaponDamages.map((d) => d.type)
|
||||
: analyzed.stats.damages.map((d) => d.type);
|
||||
|
||||
return removeDuplicates(damageTypes);
|
||||
@@ -138,7 +140,12 @@ function resolveFilteredDamages({
|
||||
);
|
||||
}
|
||||
|
||||
return analyzed.stats.damages
|
||||
const damages =
|
||||
anyWeapon.type === "SPECIAL"
|
||||
? analyzed.stats.specialWeaponDamages
|
||||
: analyzed.stats.damages;
|
||||
|
||||
return damages
|
||||
.filter((d) => d.type === damageType || toCombine?.combineWith === d.type)
|
||||
.map((damage) => {
|
||||
if (!isMultiShot || !damage.multiShots) return damage;
|
||||
|
||||
@@ -284,7 +284,7 @@ function DamageReceiversGrid({
|
||||
variant="build"
|
||||
className="object-damage__weapon-image"
|
||||
/>
|
||||
) : (
|
||||
) : weapon.type === "SUB" ? (
|
||||
<Image
|
||||
alt=""
|
||||
path={subWeaponImageUrl(weapon.id)}
|
||||
@@ -292,6 +292,14 @@ function DamageReceiversGrid({
|
||||
height={24}
|
||||
className="object-damage__weapon-image"
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
alt=""
|
||||
path={specialWeaponImageUrl(weapon.id)}
|
||||
width={24}
|
||||
height={24}
|
||||
className="object-damage__weapon-image"
|
||||
/>
|
||||
)}
|
||||
{t(`weapons:${weapon.type}_${weapon.id}` as any)}
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,9 @@ export {
|
||||
weaponIdIsNotAlt,
|
||||
subWeaponIds,
|
||||
specialWeaponIds,
|
||||
exampleMainWeaponIdWithSpecialWeaponId,
|
||||
nonBombSubWeaponIds,
|
||||
nonDamagingSpecialWeaponIds,
|
||||
SPLAT_BOMB_ID,
|
||||
SUCTION_BOMB_ID,
|
||||
BURST_BOMB_ID,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { MainWeaponId, SubWeaponId } from "./types";
|
||||
import { assertUnreachable } from "~/utils/types";
|
||||
import type { MainWeaponId, SpecialWeaponId, SubWeaponId } from "./types";
|
||||
|
||||
export const weaponCategories = [
|
||||
{
|
||||
@@ -137,3 +138,49 @@ export const specialWeaponIds = [
|
||||
SUPER_CHUMP_ID,
|
||||
KRAKEN_ROYALE_ID,
|
||||
] as const;
|
||||
|
||||
export const nonDamagingSpecialWeaponIds = [BIG_BUBBLER_ID, TACTICOOLER_ID];
|
||||
|
||||
export const exampleMainWeaponIdWithSpecialWeaponId = (
|
||||
specialWeaponId: SpecialWeaponId
|
||||
): MainWeaponId => {
|
||||
switch (specialWeaponId) {
|
||||
case TRIZOOKA_ID:
|
||||
return 40;
|
||||
case BIG_BUBBLER_ID:
|
||||
return 10;
|
||||
case ZIPCASTER_ID:
|
||||
return 8000;
|
||||
case TENTA_MISSILES_ID:
|
||||
return 1030;
|
||||
case INK_STORM_ID:
|
||||
return 3040;
|
||||
case BOOYAH_BOMB_ID:
|
||||
return 3020;
|
||||
case WAVE_BREAKER_ID:
|
||||
return 220;
|
||||
case INK_VAC_ID:
|
||||
return 2010;
|
||||
case KILLER_WAIL_ID:
|
||||
return 50;
|
||||
case INKJET_ID:
|
||||
return 3010;
|
||||
case ULTRA_STAMP_ID:
|
||||
return 4000;
|
||||
case CRAB_TANK_ID:
|
||||
return 20;
|
||||
case REEF_SLIDER_ID:
|
||||
return 5040;
|
||||
case TRIPLE_INKSTRIKE_ID:
|
||||
return 41;
|
||||
case TACTICOOLER_ID:
|
||||
return 60;
|
||||
case SUPER_CHUMP_ID:
|
||||
return 61;
|
||||
case KRAKEN_ROYALE_ID:
|
||||
return 4011;
|
||||
default: {
|
||||
assertUnreachable(specialWeaponId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"stat.category.subDef": "Sub weapon effect defense",
|
||||
"stat.category.actionsPerInkTank": "Actions per ink tank",
|
||||
"stat.category.damage": "Main weapon damage",
|
||||
"stat.category.special.damage": "{{specialWeapon}} damage",
|
||||
"stat.category.subWeaponDefenseDamages": "Sub weapon damage defense",
|
||||
"stat.category.movement": "Movement",
|
||||
"stat.category.misc": "Miscellaneous",
|
||||
|
||||
@@ -461,6 +461,8 @@ function parametersToSpecialWeaponResult(params: any) {
|
||||
return {
|
||||
ArmorHP: params["WeaponSpChariotParam"]?.["ArmorHP"],
|
||||
overwrites: resultUnwrapped,
|
||||
DistanceDamage: params["BlastParam"]?.["DistanceDamage"],
|
||||
DirectDamage: params["DamageParam"]?.["DirectHitDamage"],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user