mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-25 20:50:14 -05:00
Object Damage Calc: Separate subs in the combobox Closes #1220
Also due to a bug the subs were not accessible at all before.
This was a bug introduced in c014ba5e18
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
import { Combobox as HeadlessCombobox } from "@headlessui/react";
|
||||
import * as React from "react";
|
||||
import Fuse from "fuse.js";
|
||||
import clsx from "clsx";
|
||||
import type { Unpacked } from "~/utils/types";
|
||||
import Fuse from "fuse.js";
|
||||
import * as React from "react";
|
||||
import type { GearType, UserWithPlusTier } from "~/db/types";
|
||||
import { useAllEventsWithMapPools, useUsers } from "~/hooks/swr";
|
||||
import { useTranslation } from "~/hooks/useTranslation";
|
||||
@@ -10,13 +9,20 @@ import type { MainWeaponId } from "~/modules/in-game-lists";
|
||||
import {
|
||||
clothesGearIds,
|
||||
headGearIds,
|
||||
shoesGearIds,
|
||||
mainWeaponIds,
|
||||
shoesGearIds,
|
||||
subWeaponIds,
|
||||
weaponCategories,
|
||||
} from "~/modules/in-game-lists";
|
||||
import { gearImageUrl, mainWeaponImageUrl } from "~/utils/urls";
|
||||
import { Image } from "./Image";
|
||||
import { nonBombSubWeaponIds } 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,
|
||||
subWeaponImageUrl,
|
||||
} from "~/utils/urls";
|
||||
import { Image } from "./Image";
|
||||
|
||||
const MAX_RESULTS_SHOWN = 6;
|
||||
|
||||
@@ -266,6 +272,56 @@ export function WeaponCombobox({
|
||||
);
|
||||
}
|
||||
|
||||
export function AllWeaponCombobox({
|
||||
id,
|
||||
inputName,
|
||||
onChange,
|
||||
fullWidth,
|
||||
}: Pick<
|
||||
ComboboxProps<ComboboxBaseOption>,
|
||||
"inputName" | "onChange" | "id" | "fullWidth"
|
||||
>) {
|
||||
const { t } = useTranslation("weapons");
|
||||
|
||||
const options = () => {
|
||||
const result: ComboboxProps<
|
||||
Record<string, string | null | number>
|
||||
>["options"] = [];
|
||||
|
||||
for (const mainWeaponId of mainWeaponIds) {
|
||||
result.push({
|
||||
value: `MAIN_${mainWeaponId}`,
|
||||
label: t(`MAIN_${mainWeaponId}`),
|
||||
imgPath: mainWeaponImageUrl(mainWeaponId),
|
||||
});
|
||||
}
|
||||
|
||||
for (const subWeaponId of subWeaponIds) {
|
||||
if (nonBombSubWeaponIds.includes(subWeaponId)) continue;
|
||||
|
||||
result.push({
|
||||
value: `SUB_${subWeaponId}`,
|
||||
label: t(`SUB_${subWeaponId}`),
|
||||
imgPath: subWeaponImageUrl(subWeaponId),
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
return (
|
||||
<Combobox
|
||||
inputName={inputName}
|
||||
options={options()}
|
||||
initialValue={null}
|
||||
placeholder={t(`MAIN_${weaponCategories[0].weaponIds[0]}`)}
|
||||
onChange={onChange}
|
||||
id={id}
|
||||
fullWidth={fullWidth}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function GearCombobox({
|
||||
id,
|
||||
required,
|
||||
|
||||
@@ -253,3 +253,7 @@ export interface AnalyzedBuild {
|
||||
export type SpecialEffectType = (typeof SPECIAL_EFFECTS)[number]["type"];
|
||||
|
||||
export type AbilityValuesKeys = keyof typeof abilityValues;
|
||||
|
||||
export type AnyWeapon =
|
||||
| { type: "MAIN"; id: MainWeaponId }
|
||||
| { type: "SUB"; id: SubWeaponId };
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { AbilityType } from "~/modules/in-game-lists";
|
||||
import type { AbilityType, SubWeaponId } from "~/modules/in-game-lists";
|
||||
import { subWeaponIds } from "~/modules/in-game-lists";
|
||||
import {
|
||||
abilities,
|
||||
mainWeaponIds,
|
||||
@@ -13,6 +14,7 @@ import abilityValuesJson from "./ability-values.json";
|
||||
import type {
|
||||
AbilityPoints,
|
||||
AnalyzedBuild,
|
||||
AnyWeapon,
|
||||
MainWeaponParams,
|
||||
ParamsJson,
|
||||
SpecialWeaponParams,
|
||||
@@ -23,6 +25,7 @@ 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;
|
||||
@@ -163,6 +166,43 @@ export function hasEffect({
|
||||
return high !== mid || mid !== low;
|
||||
}
|
||||
|
||||
const DEFAULT_ANY_WEAPON = {
|
||||
type: "MAIN",
|
||||
id: weaponCategories[0].weaponIds[0],
|
||||
} as const;
|
||||
export function validatedAnyWeaponFromSearchParams(
|
||||
searchParams: URLSearchParams
|
||||
): AnyWeapon {
|
||||
const rawWeapon = searchParams.get("weapon");
|
||||
if (!rawWeapon) return DEFAULT_ANY_WEAPON;
|
||||
|
||||
if (rawWeapon?.startsWith("SUB_")) {
|
||||
const id = Number(rawWeapon.replace("SUB_", ""));
|
||||
|
||||
if (
|
||||
!subWeaponIds
|
||||
.filter((id) => !nonBombSubWeaponIds.includes(id))
|
||||
.includes(id as any)
|
||||
) {
|
||||
return DEFAULT_ANY_WEAPON;
|
||||
}
|
||||
|
||||
return { type: "SUB", id: id as SubWeaponId };
|
||||
}
|
||||
|
||||
if (rawWeapon?.startsWith("MAIN_")) {
|
||||
const id = Number(rawWeapon.replace("MAIN_", ""));
|
||||
|
||||
if (!mainWeaponIds.includes(id as any)) {
|
||||
return DEFAULT_ANY_WEAPON;
|
||||
}
|
||||
|
||||
return { type: "MAIN", id: id as MainWeaponId };
|
||||
}
|
||||
|
||||
return { type: "MAIN", id: validatedWeaponIdFromSearchParams(searchParams) };
|
||||
}
|
||||
|
||||
export function validatedWeaponIdFromSearchParams(
|
||||
searchParams: URLSearchParams
|
||||
): MainWeaponId {
|
||||
|
||||
@@ -4,6 +4,7 @@ export {
|
||||
validatedBuildFromSearchParams,
|
||||
serializeBuild,
|
||||
hpDivided,
|
||||
validatedAnyWeaponFromSearchParams,
|
||||
} from "./core/utils";
|
||||
export type {
|
||||
DamageType,
|
||||
@@ -11,6 +12,7 @@ export type {
|
||||
AnalyzedBuild,
|
||||
SpecialWeaponParams,
|
||||
SubWeaponParams,
|
||||
AnyWeapon,
|
||||
} from "./analyzer-types";
|
||||
export {
|
||||
buildStats,
|
||||
|
||||
@@ -1281,7 +1281,7 @@ function DamageTable({
|
||||
: val.value;
|
||||
|
||||
const typeRowName = damageIsSubWeaponDamage(val)
|
||||
? t(`weapons:SUB_${val.subWeaponId}`)
|
||||
? (`weapons:SUB_${val.subWeaponId}` as const)
|
||||
: damageTypeTranslationString({
|
||||
damageType: val.type,
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import { type MainWeaponId, mainWeaponIds } from "~/modules/in-game-lists";
|
||||
import type { DamageType } from "~/features/build-analyzer";
|
||||
import type objectDamages from "./core/object-dmg.json";
|
||||
import invariant from "tiny-invariant";
|
||||
import type { CombineWith } from "./calculator-types";
|
||||
|
||||
export const DAMAGE_RECEIVERS = [
|
||||
"Chariot", // Crab Tank
|
||||
@@ -136,15 +137,7 @@ export const objectDamageJsonKeyPriority: Record<
|
||||
};
|
||||
|
||||
export const damageTypesToCombine: Partial<
|
||||
Record<
|
||||
MainWeaponId,
|
||||
Array<{
|
||||
when: DamageType;
|
||||
combineWith: DamageType;
|
||||
/** for this weapon "when" damage already includes "combineWith" damage, so calculating multiplier only */
|
||||
multiplierOnly?: boolean;
|
||||
}>
|
||||
>
|
||||
Record<MainWeaponId, Array<CombineWith>>
|
||||
> = {
|
||||
// Explosher
|
||||
3040: [{ when: "DIRECT", combineWith: "DISTANCE" }],
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { useSearchParams } from "@remix-run/react";
|
||||
import { assertType } from "~/utils/types";
|
||||
import { type MainWeaponId } from "~/modules/in-game-lists";
|
||||
import {
|
||||
type DAMAGE_TYPE,
|
||||
buildStats,
|
||||
possibleApValues,
|
||||
validatedWeaponIdFromSearchParams,
|
||||
validatedAnyWeaponFromSearchParams,
|
||||
type AnalyzedBuild,
|
||||
type DamageType,
|
||||
} from "~/features/build-analyzer";
|
||||
import { calculateDamage } from "./core/objectDamage";
|
||||
import {
|
||||
calculateDamage,
|
||||
resolveAllUniqueDamageTypes,
|
||||
} from "./core/objectDamage";
|
||||
import type { AnyWeapon } from "../build-analyzer/analyzer-types";
|
||||
|
||||
const ABILITY_POINTS_SP_KEY = "ap";
|
||||
const DAMAGE_TYPE_SP_KEY = "dmg";
|
||||
@@ -18,11 +21,11 @@ const MULTI_SHOT_SP_KEY = "multi";
|
||||
export function useObjectDamage() {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
|
||||
const mainWeaponId = validatedWeaponIdFromSearchParams(searchParams);
|
||||
const anyWeapon = validatedAnyWeaponFromSearchParams(searchParams);
|
||||
const abilityPoints = validatedAbilityPointsFromSearchParams(searchParams);
|
||||
const isMultiShot = validatedMultiShotFromSearchParams(searchParams);
|
||||
const analyzed = buildStats({
|
||||
weaponSplId: mainWeaponId,
|
||||
weaponSplId: anyWeapon.type === "MAIN" ? anyWeapon.id : 0,
|
||||
hasTacticooler: false,
|
||||
});
|
||||
|
||||
@@ -32,19 +35,19 @@ export function useObjectDamage() {
|
||||
});
|
||||
|
||||
const handleChange = ({
|
||||
newMainWeaponId = mainWeaponId,
|
||||
newAnyWeapon = anyWeapon,
|
||||
newAbilityPoints = abilityPoints,
|
||||
newDamageType = damageType,
|
||||
newIsMultiShot = isMultiShot,
|
||||
}: {
|
||||
newMainWeaponId?: MainWeaponId;
|
||||
newAnyWeapon?: AnyWeapon;
|
||||
newAbilityPoints?: number;
|
||||
newDamageType?: DamageType;
|
||||
newIsMultiShot?: boolean;
|
||||
}) => {
|
||||
setSearchParams(
|
||||
{
|
||||
weapon: String(newMainWeaponId),
|
||||
weapon: `${newAnyWeapon.type}_${newAnyWeapon.id}`,
|
||||
[ABILITY_POINTS_SP_KEY]: String(newAbilityPoints),
|
||||
[DAMAGE_TYPE_SP_KEY]: newDamageType ?? "",
|
||||
[MULTI_SHOT_SP_KEY]: String(newIsMultiShot),
|
||||
@@ -54,10 +57,7 @@ export function useObjectDamage() {
|
||||
};
|
||||
|
||||
return {
|
||||
weapon: {
|
||||
type: "MAIN" as const,
|
||||
id: mainWeaponId,
|
||||
},
|
||||
weapon: anyWeapon,
|
||||
isMultiShot,
|
||||
multiShotCount: analyzed.stats.damages.find((d) => d.type === damageType)
|
||||
?.multiShots,
|
||||
@@ -69,16 +69,14 @@ export function useObjectDamage() {
|
||||
["SPU", abilityPoints],
|
||||
]),
|
||||
analyzed,
|
||||
mainWeaponId,
|
||||
anyWeapon,
|
||||
damageType,
|
||||
isMultiShot,
|
||||
})
|
||||
: null,
|
||||
abilityPoints: String(abilityPoints),
|
||||
damageType,
|
||||
allDamageTypes: Array.from(
|
||||
new Set(analyzed.stats.damages.map((d) => d.type))
|
||||
),
|
||||
allDamageTypes: resolveAllUniqueDamageTypes({ analyzed, anyWeapon }),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import type { DamageType } from "../build-analyzer";
|
||||
import type { DAMAGE_RECEIVERS } from "./calculator-constants";
|
||||
|
||||
export type DamageReceiver = (typeof DAMAGE_RECEIVERS)[number];
|
||||
|
||||
export type HitPoints = Record<DamageReceiver, number>;
|
||||
|
||||
export interface CombineWith {
|
||||
when: DamageType;
|
||||
combineWith: DamageType;
|
||||
/** for this weapon "when" damage already includes "combineWith" damage, so calculating multiplier only */
|
||||
multiplierOnly?: boolean;
|
||||
}
|
||||
|
||||
@@ -47,9 +47,12 @@
|
||||
font-size: var(--fonts-xs);
|
||||
font-weight: var(--semi-bold);
|
||||
padding-block: var(--s-2);
|
||||
padding-inline: var(--s-2);
|
||||
padding-inline: var(--s-4);
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--s-1-5);
|
||||
}
|
||||
|
||||
.object-damage__weapon-image {
|
||||
|
||||
@@ -25,7 +25,7 @@ function calculate({
|
||||
return calculateDamage({
|
||||
abilityPoints,
|
||||
analyzed,
|
||||
mainWeaponId,
|
||||
anyWeapon: { type: "MAIN", id: mainWeaponId },
|
||||
damageType,
|
||||
isMultiShot: true,
|
||||
});
|
||||
|
||||
@@ -3,7 +3,6 @@ import type {
|
||||
AnalyzedBuild,
|
||||
DamageType,
|
||||
} from "~/features/build-analyzer";
|
||||
import { damageTypeToWeaponType } from "~/features/build-analyzer";
|
||||
import objectDamages from "./object-dmg.json";
|
||||
import type {
|
||||
MainWeaponId,
|
||||
@@ -18,7 +17,9 @@ import {
|
||||
DAMAGE_RECEIVERS,
|
||||
objectDamageJsonKeyPriority,
|
||||
} from "../calculator-constants";
|
||||
import type { DamageReceiver } from "../calculator-types";
|
||||
import type { CombineWith, DamageReceiver } from "../calculator-types";
|
||||
import type { AnyWeapon } from "~/features/build-analyzer";
|
||||
import { removeDuplicates } from "~/utils/arrays";
|
||||
|
||||
export function damageTypeToMultipliers({
|
||||
type,
|
||||
@@ -101,25 +102,43 @@ export function multipliersToRecordWithFallbacks(
|
||||
) as Record<DamageReceiver, number>;
|
||||
}
|
||||
|
||||
const objectShredderMultipliers = objectDamages.ObjectEffect_Up.rates;
|
||||
export function calculateDamage({
|
||||
export function resolveAllUniqueDamageTypes({
|
||||
analyzed,
|
||||
mainWeaponId,
|
||||
abilityPoints,
|
||||
damageType,
|
||||
isMultiShot,
|
||||
anyWeapon,
|
||||
}: {
|
||||
analyzed: AnalyzedBuild;
|
||||
anyWeapon: AnyWeapon;
|
||||
}) {
|
||||
const damageTypes =
|
||||
anyWeapon.type === "SUB"
|
||||
? analyzed.stats.subWeaponDefenseDamages
|
||||
.filter((damage) => damage.subWeaponId === anyWeapon.id)
|
||||
.map((d) => d.type)
|
||||
: analyzed.stats.damages.map((d) => d.type);
|
||||
|
||||
return removeDuplicates(damageTypes);
|
||||
}
|
||||
|
||||
function resolveFilteredDamages({
|
||||
analyzed,
|
||||
damageType,
|
||||
isMultiShot,
|
||||
toCombine,
|
||||
anyWeapon,
|
||||
}: {
|
||||
analyzed: AnalyzedBuild;
|
||||
mainWeaponId: MainWeaponId;
|
||||
abilityPoints: AbilityPoints;
|
||||
damageType: DamageType;
|
||||
isMultiShot: boolean;
|
||||
toCombine?: CombineWith;
|
||||
anyWeapon: AnyWeapon;
|
||||
}) {
|
||||
const toCombine = (damageTypesToCombine[mainWeaponId] ?? []).find(
|
||||
(c) => c.when === damageType
|
||||
);
|
||||
if (anyWeapon.type === "SUB") {
|
||||
return analyzed.stats.subWeaponDefenseDamages.filter(
|
||||
(damage) => damage.subWeaponId === anyWeapon.id
|
||||
);
|
||||
}
|
||||
|
||||
const filteredDamages = analyzed.stats.damages
|
||||
return analyzed.stats.damages
|
||||
.filter((d) => d.type === damageType || toCombine?.combineWith === d.type)
|
||||
.map((damage) => {
|
||||
if (!isMultiShot || !damage.multiShots) return damage;
|
||||
@@ -129,24 +148,46 @@ export function calculateDamage({
|
||||
value: damage.value * damage.multiShots,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const objectShredderMultipliers = objectDamages.ObjectEffect_Up.rates;
|
||||
export function calculateDamage({
|
||||
analyzed,
|
||||
anyWeapon,
|
||||
abilityPoints,
|
||||
damageType,
|
||||
isMultiShot,
|
||||
}: {
|
||||
analyzed: AnalyzedBuild;
|
||||
anyWeapon: AnyWeapon;
|
||||
abilityPoints: AbilityPoints;
|
||||
damageType: DamageType;
|
||||
isMultiShot: boolean;
|
||||
}) {
|
||||
const toCombine =
|
||||
anyWeapon.type == "MAIN"
|
||||
? (damageTypesToCombine[anyWeapon.id] ?? []).find(
|
||||
(c) => c.when === damageType
|
||||
)
|
||||
: undefined;
|
||||
|
||||
const filteredDamages = resolveFilteredDamages({
|
||||
analyzed,
|
||||
damageType,
|
||||
isMultiShot,
|
||||
toCombine,
|
||||
anyWeapon,
|
||||
});
|
||||
|
||||
const hitPoints = objectHitPoints(abilityPoints);
|
||||
const multipliers = Object.fromEntries(
|
||||
filteredDamages.map((damage) => {
|
||||
const weaponType = damageTypeToWeaponType[damage.type];
|
||||
const weaponId: any =
|
||||
weaponType === "MAIN"
|
||||
? mainWeaponId
|
||||
: weaponType === "SUB"
|
||||
? analyzed.weapon.subWeaponSplId
|
||||
: analyzed.weapon.specialWeaponSplId;
|
||||
|
||||
return [
|
||||
damage.type,
|
||||
multipliersToRecordWithFallbacks(
|
||||
damageTypeToMultipliers({
|
||||
type: damage.type,
|
||||
weapon: { type: weaponType, id: weaponId },
|
||||
weapon: anyWeapon,
|
||||
})
|
||||
),
|
||||
];
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
import { WeaponCombobox } from "~/components/Combobox";
|
||||
import type { LinksFunction } from "@remix-run/node";
|
||||
import type { ShouldRevalidateFunction } from "@remix-run/react";
|
||||
import clsx from "clsx";
|
||||
import React from "react";
|
||||
import { Ability } from "~/components/Ability";
|
||||
import { AllWeaponCombobox } from "~/components/Combobox";
|
||||
import { Image, WeaponImage } from "~/components/Image";
|
||||
import { Label } from "~/components/Label";
|
||||
import { Main } from "~/components/Main";
|
||||
import { Toggle } from "~/components/Toggle";
|
||||
import type { AnyWeapon } from "~/features/build-analyzer";
|
||||
import { possibleApValues, type DamageType } from "~/features/build-analyzer";
|
||||
import { useSetTitle } from "~/hooks/useSetTitle";
|
||||
import { useTranslation } from "~/hooks/useTranslation";
|
||||
import {
|
||||
type MainWeaponId,
|
||||
BIG_BUBBLER_ID,
|
||||
BOOYAH_BOMB_ID,
|
||||
CRAB_TANK_ID,
|
||||
SPLASH_WALL_ID,
|
||||
SPRINKLER_ID,
|
||||
SQUID_BEAKON_ID,
|
||||
TORPEDO_ID,
|
||||
WAVE_BREAKER_ID,
|
||||
SPRINKLER_ID,
|
||||
} from "~/modules/in-game-lists";
|
||||
import { damageTypeTranslationString } from "~/utils/i18next";
|
||||
import type { SendouRouteHandle } from "~/utils/remix";
|
||||
import {
|
||||
OBJECT_DAMAGE_CALCULATOR_URL,
|
||||
mainWeaponImageUrl,
|
||||
modeImageUrl,
|
||||
navIconUrl,
|
||||
OBJECT_DAMAGE_CALCULATOR_URL,
|
||||
specialWeaponImageUrl,
|
||||
subWeaponImageUrl,
|
||||
} from "~/utils/urls";
|
||||
import styles from "../calculator.css";
|
||||
import type { LinksFunction } from "@remix-run/node";
|
||||
import type { SendouRouteHandle } from "~/utils/remix";
|
||||
import React from "react";
|
||||
import { useTranslation } from "~/hooks/useTranslation";
|
||||
import clsx from "clsx";
|
||||
import { Label } from "~/components/Label";
|
||||
import { Ability } from "~/components/Ability";
|
||||
import { damageTypeTranslationString } from "~/utils/i18next";
|
||||
import { useSetTitle } from "~/hooks/useSetTitle";
|
||||
import type { ShouldRevalidateFunction } from "@remix-run/react";
|
||||
import { Toggle } from "~/components/Toggle";
|
||||
import { useObjectDamage } from "../calculator-hooks";
|
||||
import { type DamageType, possibleApValues } from "~/features/build-analyzer";
|
||||
import type { DamageReceiver } from "../calculator-types";
|
||||
import styles from "../calculator.css";
|
||||
|
||||
export const CURRENT_PATCH = "3.1";
|
||||
|
||||
@@ -72,19 +72,29 @@ export default function ObjectDamagePage() {
|
||||
<div className="object-damage__selects">
|
||||
<div className="object-damage__selects__weapon">
|
||||
<Label htmlFor="weapon">{t("analyzer:labels.weapon")}</Label>
|
||||
<WeaponCombobox
|
||||
<AllWeaponCombobox
|
||||
id="weapon"
|
||||
inputName="weapon"
|
||||
onChange={(opt) =>
|
||||
opt &&
|
||||
onChange={(opt) => {
|
||||
if (!opt) return;
|
||||
|
||||
const [type, id] = opt.value.split("_");
|
||||
|
||||
handleChange({
|
||||
newMainWeaponId: Number(opt.value) as MainWeaponId,
|
||||
})
|
||||
}
|
||||
newAnyWeapon: {
|
||||
id: Number(id),
|
||||
type,
|
||||
} as AnyWeapon,
|
||||
});
|
||||
}}
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
<div className={clsx({ invisible: !damagesToReceivers })}>
|
||||
<div
|
||||
className={clsx({
|
||||
invisible: !damagesToReceivers || allDamageTypes.length === 1,
|
||||
})}
|
||||
>
|
||||
<Label htmlFor="damage">{t("analyzer:labels.damageType")}</Label>
|
||||
<DamageTypesSelect
|
||||
handleChange={handleChange}
|
||||
@@ -259,17 +269,31 @@ function DamageReceiversGrid({
|
||||
invisible: !damage.distance,
|
||||
})}
|
||||
>
|
||||
{t("analyzer:distanceInline", { value: damage.distance })}
|
||||
{t("analyzer:distanceInline", {
|
||||
value: Array.isArray(damage.distance)
|
||||
? damage.distance.join("-")
|
||||
: damage.distance,
|
||||
})}
|
||||
</div>
|
||||
<div className="text-lighter stack horizontal sm justify-center items-center">
|
||||
<WeaponImage
|
||||
weaponSplId={weapon.id}
|
||||
width={24}
|
||||
height={24}
|
||||
variant="build"
|
||||
className="object-damage__weapon-image"
|
||||
/>
|
||||
{t(`weapons:MAIN_${weapon.id}`)}
|
||||
{weapon.type === "MAIN" ? (
|
||||
<WeaponImage
|
||||
weaponSplId={weapon.id}
|
||||
width={24}
|
||||
height={24}
|
||||
variant="build"
|
||||
className="object-damage__weapon-image"
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
alt=""
|
||||
path={subWeaponImageUrl(weapon.id)}
|
||||
width={24}
|
||||
height={24}
|
||||
className="object-damage__weapon-image"
|
||||
/>
|
||||
)}
|
||||
{t(`weapons:${weapon.type}_${weapon.id}` as any)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { MainWeaponId } from "./types";
|
||||
import type { MainWeaponId, SubWeaponId } from "./types";
|
||||
|
||||
export const weaponCategories = [
|
||||
{
|
||||
@@ -92,6 +92,14 @@ export const subWeaponIds = [
|
||||
TORPEDO_ID,
|
||||
] as const;
|
||||
|
||||
export const nonBombSubWeaponIds = [
|
||||
SPRINKLER_ID,
|
||||
SPLASH_WALL_ID,
|
||||
SQUID_BEAKON_ID,
|
||||
POINT_SENSOR_ID,
|
||||
TOXIC_MIST_ID,
|
||||
] as SubWeaponId[];
|
||||
|
||||
export const TRIZOOKA_ID = 1;
|
||||
export const BIG_BUBBLER_ID = 2;
|
||||
export const ZIPCASTER_ID = 3;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import type { DamageType } from "~/features/build-analyzer";
|
||||
|
||||
// TODO: type this correctly
|
||||
export const damageTypeTranslationString = ({
|
||||
damageType,
|
||||
}: {
|
||||
damageType: DamageType;
|
||||
}): any => `analyzer:damage.${damageType as "NORMAL_MIN"}`;
|
||||
}) => `analyzer:damage.${damageType}` as const;
|
||||
|
||||
@@ -100,6 +100,8 @@
|
||||
"damage.SPLATANA_VERTICAL": "Vertical",
|
||||
"damage.SPLATANA_HORIZONTAL_DIRECT": "Horizontal direct",
|
||||
"damage.SPLATANA_HORIZONTAL": "Horizontal",
|
||||
"damage.BOMB_DIRECT": "Direct",
|
||||
"damage.BOMB_NORMAL": "Splash",
|
||||
"suffix.seconds": "s",
|
||||
"suffix.hp": "hp",
|
||||
"suffix.specialPointsShort": "p",
|
||||
|
||||
Reference in New Issue
Block a user