Merge branch 'Sendouc:rewrite' into rewrite

This commit is contained in:
Frederik
2022-10-19 07:33:49 +02:00
committed by GitHub
88 changed files with 6037 additions and 73 deletions

View File

@@ -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...

View 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}
/>
);
}

View File

@@ -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;

View 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,
}));
}

View File

@@ -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";

File diff suppressed because it is too large Load Diff

View 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"],
};

View File

@@ -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;

View File

@@ -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 {

View 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,
};
}

View File

@@ -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];
}

View File

@@ -52,4 +52,7 @@ export const config = {
fallbackLng: DEFAULT_LANGUAGE,
defaultNS: "common",
react: { useSuspense: false },
interpolation: {
escapeValue: false,
},
};

View File

@@ -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);
}
}

View File

@@ -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}

View File

@@ -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">

View File

@@ -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[]>();

View 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>
);
}

View File

@@ -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,

View File

@@ -61,6 +61,8 @@
.plus__comment {
white-space: pre-wrap;
min-width: auto;
overflow-wrap: break-word;
}
.plus__comment-time {

View File

@@ -147,11 +147,6 @@
font-weight: var(--bold);
}
.u__results-main {
max-width: 52rem;
margin: 0 auto;
}
.u__results-section {
overflow-x: auto;
}

View File

@@ -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() {

View File

@@ -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.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 867 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 938 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 885 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 753 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 726 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 783 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 726 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 793 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 763 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1007 B

After

Width:  |  Height:  |  Size: 732 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 971 B

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 745 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 815 B

After

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -13,6 +13,7 @@
"results.placing": "Platzierung",
"results.team": "Team",
"results.tournament": "Turnier",
"results.participants": "Teilnehmer",
"results.date": "Datum",
"results.mates": "Mitspieler",

View File

@@ -13,6 +13,7 @@
"results.placing": "Placing",
"results.team": "Team",
"results.tournament": "Tournament",
"results.participants": "Participants",
"results.date": "Date",
"results.mates": "Mates",

View 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

View 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

View 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

View 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)
);

View 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");

View File

@@ -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