mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-05-06 13:19:31 -05:00
Merge branch 'Sendouc:rewrite' into rewrite
This commit is contained in:
commit
e45b2b329a
|
|
@ -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
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
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`);
|
||||
|
|
@ -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
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
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",
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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
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
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
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
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
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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user