Merge branch 'Sendouc:rewrite' into rewrite

This commit is contained in:
Frederik 2022-10-19 07:33:49 +02:00 committed by GitHub
commit e45b2b329a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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.

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.

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