Merge branch 'Sendouc:rewrite' into rewrite
@@ -99,7 +99,7 @@ npm run rename-badge -- sundae "New 4v4 Sundaes"
|
||||
While in the folder with the images:
|
||||
|
||||
```bash
|
||||
for i in *; do npx @squoosh/cli --avif '{"cqLevel":33,"cqAlphaLevel":-1,"denoiseLevel":0,"tileColsLog2":0,"tileRowsLog2":0,"speed":6,"subsample":1,"chromaDeltaQ":false,"sharpness":0,"tune":0}' $i; done
|
||||
for i in *.png; do npx @squoosh/cli --avif '{"cqLevel":33,"cqAlphaLevel":-1,"denoiseLevel":0,"tileColsLog2":0,"tileRowsLog2":0,"speed":6,"subsample":1,"chromaDeltaQ":false,"sharpness":0,"tune":0}' $i; done
|
||||
```
|
||||
|
||||
## How to...
|
||||
|
||||
57
app/components/Placement.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import { getEnglishOrdinalSuffix } from "~/utils/strings";
|
||||
|
||||
export type PlacementProps = {
|
||||
placement: number;
|
||||
iconClassName?: string;
|
||||
textClassName?: string;
|
||||
};
|
||||
|
||||
const getSpecialPlacementIconPath = (placement: number): string | null => {
|
||||
switch (placement) {
|
||||
case 3:
|
||||
return "/svg/placements/third.svg";
|
||||
case 2:
|
||||
return "/svg/placements/second.svg";
|
||||
case 1:
|
||||
return "/svg/placements/first.svg";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export function Placement({
|
||||
placement,
|
||||
iconClassName,
|
||||
textClassName,
|
||||
}: PlacementProps) {
|
||||
/*
|
||||
Placements are using english ordinal syntax only.
|
||||
If wished for, we could look into properly adding translations here, but
|
||||
english-style ordinals are commonly used internationally as well.
|
||||
*/
|
||||
const ordinalSuffix = getEnglishOrdinalSuffix(placement);
|
||||
const iconPath = getSpecialPlacementIconPath(placement);
|
||||
|
||||
if (!iconPath) {
|
||||
return (
|
||||
<span className={textClassName} lang="en-us">
|
||||
{placement}
|
||||
<sup>{ordinalSuffix}</sup>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
const placementString = `${placement}${ordinalSuffix}`;
|
||||
|
||||
return (
|
||||
<img
|
||||
lang="en-us"
|
||||
alt={placementString}
|
||||
title={placementString}
|
||||
src={iconPath}
|
||||
className={iconClassName}
|
||||
height={20}
|
||||
width={20}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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`);
|
||||
@@ -515,6 +515,7 @@ function swimSpeed(
|
||||
|
||||
const RESPAWN_CHASE_FRAME = 150;
|
||||
const OWN_RESPAWN_PUNISHER_EXTRA_RESPAWN_FRAMES = 68;
|
||||
const SPLATOON_3_FASTER_RESPAWN = 60;
|
||||
function quickRespawnTime(
|
||||
args: StatFunctionInput
|
||||
): AnalyzedBuild["stats"]["quickRespawnTime"] {
|
||||
@@ -546,10 +547,17 @@ function quickRespawnTime(
|
||||
|
||||
return {
|
||||
baseValue: framesToSeconds(
|
||||
RESPAWN_CHASE_FRAME + chase.baseEffect + around.baseEffect
|
||||
RESPAWN_CHASE_FRAME +
|
||||
chase.baseEffect +
|
||||
around.baseEffect -
|
||||
SPLATOON_3_FASTER_RESPAWN
|
||||
),
|
||||
value: framesToSeconds(
|
||||
RESPAWN_CHASE_FRAME + chase.effect + around.effect + extraFrames
|
||||
RESPAWN_CHASE_FRAME +
|
||||
chase.effect +
|
||||
around.effect +
|
||||
extraFrames -
|
||||
SPLATOON_3_FASTER_RESPAWN
|
||||
),
|
||||
modifiedBy: [QUICK_RESPAWN_TIME_ABILITY, "RP"],
|
||||
};
|
||||
|
||||
@@ -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
@@ -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];
|
||||
}
|
||||
|
||||
@@ -52,4 +52,7 @@ export const config = {
|
||||
fallbackLng: DEFAULT_LANGUAGE,
|
||||
defaultNS: "common",
|
||||
react: { useSuspense: false },
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -59,7 +59,9 @@ export function generateMapList(
|
||||
}
|
||||
|
||||
function isValid(stageId: StageId, mapHistory: StageId[]) {
|
||||
return !mapHistory.slice(-BACKLOG, mapHistory.length).includes(stageId);
|
||||
// [1,2,3,4,5,6,7,8,9,10].slice(-2)
|
||||
// > (2) [9, 10]
|
||||
return !mapHistory.slice(-BACKLOG).includes(stageId);
|
||||
}
|
||||
|
||||
function addAndReturnMap(
|
||||
@@ -154,8 +156,28 @@ function getMap(
|
||||
for (let bucketNum = 0; bucketNum < buckets.size; bucketNum++) {
|
||||
const item = buckets.get(bucketNum);
|
||||
shuffle(item![mode]);
|
||||
for (const stageId of item![mode]) {
|
||||
if (isValid(stageId, mapHistory)) {
|
||||
|
||||
for (const [i, stageId] of item![mode].entries()) {
|
||||
// fallback solution, might happen if map pool is small
|
||||
const isLast = () => {
|
||||
// is actually last
|
||||
if (bucketNum === buckets.size - 1 && i === item![mode].length - 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// is last in bucket and next is empty
|
||||
const nextBucket = buckets.get(bucketNum + 1);
|
||||
if (
|
||||
i === item![mode].length - 1 &&
|
||||
nextBucket &&
|
||||
nextBucket[mode].length === 0
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
if (isLast() || isValid(stageId, mapHistory)) {
|
||||
return addAndReturnMap(stageId, mode, buckets, bucketNum);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ export default function EditBadgePage() {
|
||||
const { badgeName } = useOutletContext<BadgeDetailsContext>();
|
||||
|
||||
return (
|
||||
<Dialog isOpen className="stack md">
|
||||
<Form method="post">
|
||||
<Dialog isOpen>
|
||||
<Form method="post" className="stack md">
|
||||
<div>
|
||||
<h2 className="badges-edit__big-header">
|
||||
Editing winners of {badgeName}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { Avatar } from "~/components/Avatar";
|
||||
import { LinkButton } from "~/components/Button";
|
||||
import { Image } from "~/components/Image";
|
||||
import { Main } from "~/components/Main";
|
||||
import { Placement } from "~/components/Placement";
|
||||
import { Section } from "~/components/Section";
|
||||
import { db } from "~/db";
|
||||
import { useIsMounted } from "~/hooks/useIsMounted";
|
||||
@@ -28,7 +29,7 @@ import calendarStyles from "~/styles/calendar-event.css";
|
||||
import mapsStyles from "~/styles/maps.css";
|
||||
import { databaseTimestampToDate } from "~/utils/dates";
|
||||
import { notFoundIfFalsy } from "~/utils/remix";
|
||||
import { discordFullName, makeTitle, placementString } from "~/utils/strings";
|
||||
import { discordFullName, makeTitle } from "~/utils/strings";
|
||||
import {
|
||||
calendarEditPage,
|
||||
calendarReportWinnersPage,
|
||||
@@ -200,7 +201,9 @@ function Results() {
|
||||
<tbody>
|
||||
{data.results.map((result, i) => (
|
||||
<tr key={i}>
|
||||
<td className="pl-4">{placementString(result.placement)}</td>
|
||||
<td className="pl-4">
|
||||
<Placement placement={result.placement} />
|
||||
</td>
|
||||
<td>{result.teamName}</td>
|
||||
<td>
|
||||
<ul className="event__results-players">
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import type { LinksFunction, LoaderArgs } from "@remix-run/node";
|
||||
import type {
|
||||
LinksFunction,
|
||||
LoaderArgs,
|
||||
MetaFunction,
|
||||
SerializeFrom,
|
||||
} from "@remix-run/node";
|
||||
import type { ShouldReloadFunction } from "@remix-run/react";
|
||||
import { Link } from "@remix-run/react";
|
||||
import { useLoaderData, useSearchParams } from "@remix-run/react";
|
||||
@@ -13,6 +18,7 @@ import { Label } from "~/components/Label";
|
||||
import { Main } from "~/components/Main";
|
||||
import { Toggle } from "~/components/Toggle";
|
||||
import { db } from "~/db";
|
||||
import { i18next } from "~/modules/i18n";
|
||||
import {
|
||||
modes,
|
||||
stageIds,
|
||||
@@ -31,6 +37,7 @@ import {
|
||||
} from "~/modules/map-pool-serializer";
|
||||
import type { MapPool } from "~/modules/map-pool-serializer/types";
|
||||
import styles from "~/styles/maps.css";
|
||||
import { makeTitle } from "~/utils/strings";
|
||||
import {
|
||||
calendarEventPage,
|
||||
ipLabsMaps,
|
||||
@@ -46,13 +53,24 @@ export const links: LinksFunction = () => {
|
||||
return [{ rel: "stylesheet", href: styles }];
|
||||
};
|
||||
|
||||
export const meta: MetaFunction = (args) => {
|
||||
const data = args.data as SerializeFrom<typeof loader> | null;
|
||||
|
||||
if (!data) return {};
|
||||
|
||||
return {
|
||||
title: data.title,
|
||||
};
|
||||
};
|
||||
|
||||
export const handle = {
|
||||
i18n: "game-misc",
|
||||
};
|
||||
|
||||
export const loader = ({ request }: LoaderArgs) => {
|
||||
export const loader = async ({ request }: LoaderArgs) => {
|
||||
const url = new URL(request.url);
|
||||
const calendarEventId = url.searchParams.get("eventId");
|
||||
const t = await i18next.getFixedT(request);
|
||||
|
||||
const event = calendarEventId
|
||||
? db.calendarEvents.findById(Number(calendarEventId))
|
||||
@@ -68,6 +86,7 @@ export const loader = ({ request }: LoaderArgs) => {
|
||||
mapPool: event
|
||||
? db.calendarEvents.findMapPoolByEventId(event.eventId)
|
||||
: null,
|
||||
title: makeTitle([t("pages.maps")]),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -216,7 +235,6 @@ function MapPoolSelector({
|
||||
);
|
||||
}
|
||||
|
||||
// xxx: crashes if only one map in mode
|
||||
function MapListCreator({ mapPool }: { mapPool: MapPool }) {
|
||||
const { t } = useTranslation(["game-misc", "common"]);
|
||||
const [mapList, setMapList] = React.useState<ModeWithStage[]>();
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -2,9 +2,10 @@ import { Link, useMatches } from "@remix-run/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import invariant from "tiny-invariant";
|
||||
import { Avatar } from "~/components/Avatar";
|
||||
import { Placement } from "~/components/Placement";
|
||||
import { Section } from "~/components/Section";
|
||||
import { databaseTimestampToDate } from "~/utils/dates";
|
||||
import { discordFullName, placementString } from "~/utils/strings";
|
||||
import { discordFullName } from "~/utils/strings";
|
||||
import { calendarEventPage, userPage } from "~/utils/urls";
|
||||
import type { UserPageLoaderData } from "../u.$identifier";
|
||||
|
||||
@@ -15,7 +16,7 @@ export default function UserResultsPage() {
|
||||
const data = parentRoute.data as UserPageLoaderData;
|
||||
|
||||
return (
|
||||
<main className="u__results-main layout__main">
|
||||
<main className="main layout__main">
|
||||
<Section className="u__results-section">
|
||||
<table>
|
||||
<thead>
|
||||
@@ -23,6 +24,7 @@ export default function UserResultsPage() {
|
||||
<th>{t("results.placing")}</th>
|
||||
<th>{t("results.team")}</th>
|
||||
<th>{t("results.tournament")}</th>
|
||||
<th>{t("results.participants")}</th>
|
||||
<th>{t("results.date")}</th>
|
||||
<th>{t("results.mates")}</th>
|
||||
</tr>
|
||||
@@ -30,13 +32,16 @@ export default function UserResultsPage() {
|
||||
<tbody>
|
||||
{data.results.map((result) => (
|
||||
<tr key={result.eventId}>
|
||||
<td className="pl-4">{placementString(result.placement)}</td>
|
||||
<td className="pl-4">
|
||||
<Placement placement={result.placement} />
|
||||
</td>
|
||||
<td>{result.teamName}</td>
|
||||
<td>
|
||||
<Link to={calendarEventPage(result.eventId)}>
|
||||
{result.eventName}
|
||||
</Link>
|
||||
</td>
|
||||
<td>{result.participantCount}</td>
|
||||
<td>
|
||||
{databaseTimestampToDate(result.startTime).toLocaleDateString(
|
||||
i18n.language,
|
||||
|
||||
@@ -61,6 +61,8 @@
|
||||
|
||||
.plus__comment {
|
||||
white-space: pre-wrap;
|
||||
min-width: auto;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.plus__comment-time {
|
||||
|
||||
@@ -147,11 +147,6 @@
|
||||
font-weight: var(--bold);
|
||||
}
|
||||
|
||||
.u__results-main {
|
||||
max-width: 52rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.u__results-section {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
@@ -10,12 +10,18 @@ export function makeTitle(title: string | string[]) {
|
||||
return `${Array.isArray(title) ? title.join(" | ") : title} | sendou.ink`;
|
||||
}
|
||||
|
||||
export function placementString(placement: number) {
|
||||
if (placement === 1) return "🥇";
|
||||
if (placement === 2) return "🥈";
|
||||
if (placement === 3) return "🥉";
|
||||
export function getEnglishOrdinalSuffix(num: number) {
|
||||
const lastDigit = num % 10;
|
||||
const last2Digits = num % 100;
|
||||
|
||||
return `${placement}th`;
|
||||
if (lastDigit === 1 && last2Digits !== 11) {
|
||||
return "st";
|
||||
} else if (lastDigit === 2 && last2Digits !== 12) {
|
||||
return "nd";
|
||||
} else if (lastDigit === 3 && last2Digits !== 13) {
|
||||
return "rd";
|
||||
}
|
||||
return "th";
|
||||
}
|
||||
|
||||
export function semiRandomId() {
|
||||
|
||||
@@ -15,10 +15,12 @@
|
||||
"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",
|
||||
"replace-img-names": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/replace-img-names.ts",
|
||||
"remove-bad-custom-urls": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/remove-bad-custom-urls.ts",
|
||||
"lint:ts": "eslint . --ext .ts,.tsx",
|
||||
"lint:styles": "stylelint \"app/styles/**/*.css\"",
|
||||
"prettier:check": "prettier --check . --loglevel warn",
|
||||
|
||||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 749 B |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 867 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 938 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 885 B |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 753 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 726 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 783 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 726 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 793 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 763 B |
|
Before Width: | Height: | Size: 1007 B After Width: | Height: | Size: 732 B |
|
Before Width: | Height: | Size: 971 B After Width: | Height: | Size: 652 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 745 B |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 815 B After Width: | Height: | Size: 596 B |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
@@ -13,6 +13,7 @@
|
||||
"results.placing": "Platzierung",
|
||||
"results.team": "Team",
|
||||
"results.tournament": "Turnier",
|
||||
"results.participants": "Teilnehmer",
|
||||
"results.date": "Datum",
|
||||
"results.mates": "Mitspieler",
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"results.placing": "Placing",
|
||||
"results.team": "Team",
|
||||
"results.tournament": "Tournament",
|
||||
"results.participants": "Participants",
|
||||
"results.date": "Date",
|
||||
"results.mates": "Mates",
|
||||
|
||||
|
||||
1
public/svg/placements/first.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 128 128" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="a" x1="13.101" x2="114.9" y1="-64" y2="-64" gradientUnits="userSpaceOnUse"><stop stop-color="#f3e64a" offset="0"/><stop stop-color="#e2b405" offset="1"/></linearGradient><linearGradient id="e" x1="70.984" x2="88.639" y1="-23.39" y2="-97.925" gradientUnits="userSpaceOnUse"><stop stop-color="#e16a16" offset="0"/><stop stop-color="#eb8014" stop-opacity=".415" offset="1"/></linearGradient><linearGradient id="b" x1="52.493" x2="80.947" y1="33.175" y2="99.585" gradientTransform="translate(-1.513)" gradientUnits="userSpaceOnUse"><stop stop-color="#c98413" offset="0"/><stop stop-color="#b74e00" offset="1"/></linearGradient><radialGradient id="c" cx="64" cy="-64" r="48.304" gradientTransform="matrix(1.0682 0 0 1.0634 -4.367 4.058)" gradientUnits="userSpaceOnUse"><stop stop-color="#ccc" stop-opacity="0" offset=".892"/><stop stop-color="#d4d4d4" offset="1"/></radialGradient><radialGradient id="d" cx="64" cy="-64" r="48.304" gradientTransform="matrix(1.0749 0 0 1.0568 -4.795 3.638)" gradientUnits="userSpaceOnUse"><stop stop-color="#fff" stop-opacity="0" offset=".954"/><stop stop-color="#fff" stop-opacity=".432" offset="1"/></radialGradient><filter id="f" x="-.059" y="-.025" width="1.119" height="1.051" color-interpolation-filters="sRGB"><feGaussianBlur result="blur" stdDeviation="1.004"/></filter></defs><g transform="translate(-3.288 -3.288) scale(1.0514)"><circle transform="rotate(90)" cx="64" cy="-64" r="50.899" fill="url(#a)"/><path d="M44.995 28.584s-1.022 16.073-.372 28.336c0 0 3.81.465 6.41.279 0 0 .094 24.713.558 41.807 0 0 14.68.65 28.894.464 0 0 .186-34.189 0-70.7 0 0-16.816-.465-35.49-.187z" fill="url(#b)" style="shape-inside:url(#rect77399);white-space:pre"/><ellipse transform="rotate(90)" cx="64" cy="-64" rx="51.6" ry="51.366" fill="url(#c)" style="mix-blend-mode:overlay"/><ellipse transform="rotate(90)" cx="64" cy="-64" rx="51.923" ry="51.05" fill="url(#d)" style="mix-blend-mode:luminosity"/><path transform="rotate(89.024 -1.49 -.09)" d="M71.494-16.465A48.126 48.126 0 0 0 112.127-64a48.126 48.126 0 0 0-40.633-47.535z" fill="url(#e)" filter="url(#f)" opacity=".434"/><path d="M64 3.127C30.428 3.127 3.127 30.428 3.127 64S30.428 124.873 64 124.873 124.873 97.572 124.873 64 97.572 3.127 64 3.127zm0 8c29.248 0 52.873 23.625 52.873 52.873S93.248 116.873 64 116.873 11.127 93.248 11.127 64 34.752 11.127 64 11.127z" color="#000" fill="#fff" style="-inkscape-stroke:none;paint-order:stroke fill markers"/><path d="M64 7.127A56.873 56.873 0 0 0 7.127 64 56.873 56.873 0 0 0 64 120.873 56.873 56.873 0 0 0 120.873 64 56.873 56.873 0 0 0 64 7.127zm0 5.975A50.899 50.899 0 0 1 114.898 64 50.899 50.899 0 0 1 64 114.898 50.899 50.899 0 0 1 13.102 64 50.899 50.899 0 0 1 64 13.102z" color="#000" fill="#9d3801"/></g></svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
1
public/svg/placements/second.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 128 128" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="a" x1="13.101" x2="114.9" y1="-64" y2="-64" gradientUnits="userSpaceOnUse"><stop stop-color="#a2b5ca" offset="0"/><stop stop-color="#7c94b3" offset="1"/></linearGradient><linearGradient id="e" x1="79.345" x2="83.332" y1="-18.74" y2="-106.86" gradientUnits="userSpaceOnUse"><stop stop-color="#1f3e64" offset="0"/><stop stop-color="#14407b" stop-opacity=".56" offset="1"/></linearGradient><linearGradient id="b" x1="44.567" x2="97.969" y1="39.61" y2="111.55" gradientTransform="translate(2.082 -.139)" gradientUnits="userSpaceOnUse"><stop stop-color="#4e6284" offset="0"/><stop stop-color="#35435d" offset="1"/></linearGradient><radialGradient id="c" cx="64" cy="-64" r="48.304" gradientTransform="matrix(1.0684 0 0 1.0635 -4.383 4.06)" gradientUnits="userSpaceOnUse"><stop stop-color="#ccc" stop-opacity="0" offset=".892"/><stop stop-color="#1985aa" offset="1"/></radialGradient><radialGradient id="d" cx="64" cy="-64" r="48.304" gradientTransform="matrix(1.0749 0 0 1.0568 -4.795 3.638)" gradientUnits="userSpaceOnUse"><stop stop-color="#fff" stop-opacity="0" offset=".939"/><stop stop-color="#fff" stop-opacity=".71" offset="1"/></radialGradient><filter id="f" x="-.059" y="-.025" width="1.119" height="1.051" color-interpolation-filters="sRGB"><feGaussianBlur result="blur" stdDeviation="1.004"/></filter></defs><g transform="translate(-3.288 -3.288) scale(1.0514)"><circle transform="rotate(90)" cx="64" cy="-64" r="50.899" fill="url(#a)"/><path d="M69.628 81.133c25.401-5.268 25.495-26.06 25.495-26.06 0-22.485-20.697-25.966-30.199-26.53-.188 0-19.662-1.129-27.659 2.164-.376 8.655-.188 23.896-.094 28.223 19.286-.47 20.603 8.09 20.697 8.843.658 7.432-10.443 13.077-20.697 15.335-.094 7.526-.094 13.735 0 15.993 22.579.47 48.168.188 53.436.094.376-6.962.188-13.453 0-15.993-7.056-.188-20.697.094-20.697.094z" fill="url(#b)"/><ellipse transform="rotate(90)" cx="63.993" cy="-64.007" rx="51.607" ry="51.373" fill="url(#c)" opacity=".346" style="mix-blend-mode:overlay"/><ellipse transform="rotate(90)" cx="64" cy="-64" rx="51.923" ry="51.05" fill="url(#d)" style="mix-blend-mode:luminosity"/><path transform="rotate(89.024 -1.49 -.09)" d="M71.494-16.465A48.126 48.126 0 0 0 112.127-64a48.126 48.126 0 0 0-40.633-47.535z" fill="url(#e)" filter="url(#f)" opacity=".291"/><path d="M64 3.127C30.428 3.127 3.127 30.428 3.127 64S30.428 124.873 64 124.873 124.873 97.572 124.873 64 97.572 3.127 64 3.127zm0 8c29.248 0 52.873 23.625 52.873 52.873S93.248 116.873 64 116.873 11.127 93.248 11.127 64 34.752 11.127 64 11.127z" color="#000" fill="#fff" style="-inkscape-stroke:none;paint-order:stroke fill markers"/><path d="M64 7.127A56.873 56.873 0 0 0 7.127 64 56.873 56.873 0 0 0 64 120.873 56.873 56.873 0 0 0 120.873 64 56.873 56.873 0 0 0 64 7.127zm0 5.975A50.899 50.899 0 0 1 114.898 64 50.899 50.899 0 0 1 64 114.898 50.899 50.899 0 0 1 13.102 64 50.899 50.899 0 0 1 64 13.102z" color="#000" fill="#4b536e" /></g></svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
1
public/svg/placements/third.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 128 128" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="a" x1="13.101" x2="114.9" y1="-64" y2="-64" gradientUnits="userSpaceOnUse"><stop stop-color="#eb7258" offset="0"/><stop stop-color="#ac5341" offset="1"/></linearGradient><linearGradient id="e" x1="73.629" x2="93.549" y1="-25.502" y2="-103.49" gradientUnits="userSpaceOnUse"><stop stop-color="#3f1e18" offset="0"/><stop stop-color="#51271f" stop-opacity=".421" offset="1"/></linearGradient><linearGradient id="b" x1="43.134" x2="49.212" y1="36.009" y2="45.558" gradientTransform="matrix(8.5638 0 0 7.8638 -325.88 -248.07)" gradientUnits="userSpaceOnUse"><stop stop-color="#6d342a" offset="0"/><stop stop-color="#4b231c" offset="1"/></linearGradient><radialGradient id="c" cx="64" cy="-64" r="48.304" gradientTransform="matrix(1.0682 0 0 1.0634 -4.367 4.058)" gradientUnits="userSpaceOnUse"><stop stop-color="#ccc" stop-opacity="0" offset=".892"/><stop stop-color="#d4d4d4" offset="1"/></radialGradient><radialGradient id="d" cx="64" cy="-64" r="48.304" gradientTransform="matrix(1.0749 0 0 1.0568 -4.795 3.638)" gradientUnits="userSpaceOnUse"><stop stop-color="#fff" stop-opacity="0" offset=".954"/><stop stop-color="#fff" stop-opacity=".432" offset="1"/></radialGradient><filter id="f" x="-.059" y="-.025" width="1.119" height="1.051" color-interpolation-filters="sRGB"><feGaussianBlur result="blur" stdDeviation="1.004"/></filter></defs><g transform="translate(-3.288 -3.288) scale(1.0514)"><circle transform="rotate(90)" cx="64" cy="-64" r="50.899" fill="url(#a)"/><path d="M87.978 66.718c9.763-6.04 8.221-17.835 8.118-18.213-1.85-12.268-11.613-17.08-20.759-18.779-11.407-2.076-29.699-1.038-38.126-.377-.308 10.003 0 22.553.103 26.894 15.826-5.38 17.573 4.246 17.676 4.529 1.336 6.7-8.015 9.53-16.956 8.021l.103 6.511c7.296-1.604 16.442-.188 16.751 5.568.411 7.55-7.913 8.304-16.751 6.417-.103 4.152 0 8.21 0 9.908 8.735 1.321 27.13 2.076 27.13 2.076 14.49 1.227 24.561-2.359 29.494-11.7 5.036-9.343 1.233-17.742-6.782-20.856z" fill="url(#b)" style="shape-inside:url(#rect244);white-space:pre"/><ellipse transform="rotate(90)" cx="64" cy="-64" rx="51.6" ry="51.366" fill="url(#c)" style="mix-blend-mode:overlay"/><ellipse transform="rotate(90)" cx="64" cy="-64" rx="51.923" ry="51.05" fill="url(#d)" style="mix-blend-mode:luminosity"/><path transform="rotate(89.024 -1.49 -.09)" d="M71.494-16.465A48.126 48.126 0 0 0 112.127-64a48.126 48.126 0 0 0-40.633-47.535z" fill="url(#e)" filter="url(#f)" opacity=".326"/><path d="M64 3.127C30.428 3.127 3.127 30.428 3.127 64S30.428 124.873 64 124.873 124.873 97.572 124.873 64 97.572 3.127 64 3.127zm0 8c29.248 0 52.873 23.625 52.873 52.873S93.248 116.873 64 116.873 11.127 93.248 11.127 64 34.752 11.127 64 11.127z" color="#000" fill="#fff" style="-inkscape-stroke:none;paint-order:stroke fill markers"/><path d="M64 7.127A56.873 56.873 0 0 0 7.127 64 56.873 56.873 0 0 0 64 120.873 56.873 56.873 0 0 0 120.873 64 56.873 56.873 0 0 0 64 7.127zm0 5.975A50.899 50.899 0 0 1 114.898 64 50.899 50.899 0 0 1 64 114.898 50.899 50.899 0 0 1 13.102 64 50.899 50.899 0 0 1 64 13.102z" color="#000" fill="#692913" /></g></svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
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)
|
||||
);
|
||||
9
scripts/remove-bad-custom-urls.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/* eslint-disable no-console */
|
||||
import "dotenv/config";
|
||||
import { sql } from "~/db/sql";
|
||||
|
||||
sql
|
||||
.prepare(`update "User" set "customUrl" = NULL where "customUrl" like '%/%'`)
|
||||
.run();
|
||||
|
||||
console.log("Done");
|
||||
@@ -78,7 +78,7 @@
|
||||
|
||||
### 🟡 user.json
|
||||
|
||||
**7/19**
|
||||
**7/20**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
@@ -91,6 +91,7 @@
|
||||
- motion
|
||||
- stick
|
||||
- sens
|
||||
- results.participants
|
||||
- forms.errors.invalidCustomUrl.numbers
|
||||
- forms.errors.invalidCustomUrl.strangeCharacter
|
||||
- forms.errors.invalidCustomUrl.duplicate
|
||||
@@ -178,7 +179,7 @@
|
||||
|
||||
### 🟢 user.json
|
||||
|
||||
**19/19**
|
||||
**20/20**
|
||||
|
||||
---
|
||||
|
||||
@@ -270,7 +271,7 @@
|
||||
|
||||
### 🟡 user.json
|
||||
|
||||
**7/19**
|
||||
**7/20**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
@@ -283,6 +284,7 @@
|
||||
- motion
|
||||
- stick
|
||||
- sens
|
||||
- results.participants
|
||||
- forms.errors.invalidCustomUrl.numbers
|
||||
- forms.errors.invalidCustomUrl.strangeCharacter
|
||||
- forms.errors.invalidCustomUrl.duplicate
|
||||
@@ -392,7 +394,7 @@
|
||||
|
||||
### 🟡 user.json
|
||||
|
||||
**7/19**
|
||||
**7/20**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
@@ -405,6 +407,7 @@
|
||||
- motion
|
||||
- stick
|
||||
- sens
|
||||
- results.participants
|
||||
- forms.errors.invalidCustomUrl.numbers
|
||||
- forms.errors.invalidCustomUrl.strangeCharacter
|
||||
- forms.errors.invalidCustomUrl.duplicate
|
||||
@@ -465,7 +468,7 @@
|
||||
|
||||
### 🔴 user.json
|
||||
|
||||
**0/19**
|
||||
**0/20**
|
||||
|
||||
---
|
||||
|
||||
@@ -647,7 +650,7 @@
|
||||
|
||||
### 🟡 user.json
|
||||
|
||||
**7/19**
|
||||
**7/20**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
@@ -660,6 +663,7 @@
|
||||
- motion
|
||||
- stick
|
||||
- sens
|
||||
- results.participants
|
||||
- forms.errors.invalidCustomUrl.numbers
|
||||
- forms.errors.invalidCustomUrl.strangeCharacter
|
||||
- forms.errors.invalidCustomUrl.duplicate
|
||||
@@ -770,7 +774,7 @@
|
||||
|
||||
### 🟡 user.json
|
||||
|
||||
**7/19**
|
||||
**7/20**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
@@ -783,6 +787,7 @@
|
||||
- motion
|
||||
- stick
|
||||
- sens
|
||||
- results.participants
|
||||
- forms.errors.invalidCustomUrl.numbers
|
||||
- forms.errors.invalidCustomUrl.strangeCharacter
|
||||
- forms.errors.invalidCustomUrl.duplicate
|
||||
@@ -869,9 +874,16 @@
|
||||
|
||||
</details>
|
||||
|
||||
### 🟢 user.json
|
||||
### 🟡 user.json
|
||||
|
||||
**19/19**
|
||||
**19/20**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- results.participants
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
@@ -976,7 +988,7 @@
|
||||
|
||||
### 🟡 user.json
|
||||
|
||||
**7/19**
|
||||
**7/20**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
@@ -989,6 +1001,7 @@
|
||||
- motion
|
||||
- stick
|
||||
- sens
|
||||
- results.participants
|
||||
- forms.errors.invalidCustomUrl.numbers
|
||||
- forms.errors.invalidCustomUrl.strangeCharacter
|
||||
- forms.errors.invalidCustomUrl.duplicate
|
||||
@@ -1099,7 +1112,7 @@
|
||||
|
||||
### 🟡 user.json
|
||||
|
||||
**7/19**
|
||||
**7/20**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
@@ -1112,6 +1125,7 @@
|
||||
- motion
|
||||
- stick
|
||||
- sens
|
||||
- results.participants
|
||||
- forms.errors.invalidCustomUrl.numbers
|
||||
- forms.errors.invalidCustomUrl.strangeCharacter
|
||||
- forms.errors.invalidCustomUrl.duplicate
|
||||
|
||||