fix: backport all features from v2 that came in after merge

This commit is contained in:
zk
2026-05-17 12:53:26 +00:00
parent 426bb250d9
commit fe76dad32d
34 changed files with 602 additions and 5219 deletions
@@ -325,6 +325,13 @@ function ImportInfoDisplayer({ game }: { game: GameGroup }) {
);
} else if (game === "maimaidx") {
Content.unshift(
<ImportInfoCard
desc="Automatically import scores, whenever you get them."
href="rizu"
key="Rizu"
moreInfo="This is the recommended way to import maimai DX scores; data is submitted at the end of each play."
name="Rizu"
/>,
<ImportInfoCard
desc="Use your data from maimai DX NET."
href="kt-maimaidx-site-importer"
@@ -0,0 +1,47 @@
import useSetSubheader from "#components/layout/header/useSetSubheader";
import Divider from "#components/util/Divider";
import ExternalLink from "#components/util/ExternalLink";
import { TachiConfig } from "#lib/config";
import React from "react";
import Alert from "react-bootstrap/Alert";
export default function RizuPage() {
useSetSubheader(["Import Scores", "Rizu"]);
return (
<div>
<h1 className="text-center mb-4">What is Rizu?</h1>
<div>
Rizu automatically sends maimai DX scores to a server. {TachiConfig.NAME} is
compatible with what Rizu sends, so you can use it to submit scores!
</div>
<Divider />
<h1 className="text-center my-4">Setup Instructions</h1>
<ol className="instructions-list">
<li>
Download the latest version of <code>Rizu-MelonLoader-vVERSION.zip</code>{" "}
<ExternalLink href="https://gitea.tendokyu.moe/beerpsi/Rizu/releases/latest">
here
</ExternalLink>
.
</li>
<li>
Download your <code>Rizu.cfg</code> config file{" "}
<ExternalLink href="/client-file-flow/CXRizu">here</ExternalLink>
. <br />
<Alert className="mt-2" variant="warning">
This file contains an API Key, which is meant to be kept secret!
</Alert>
</li>
<li>
Follow the installation instructions on{" "}
<ExternalLink href="https://gitea.tendokyu.moe/beerpsi/Rizu#melonloader">
Tendokyu
</ExternalLink>
.
</li>
<li>Your scores are now automatically uploaded to {TachiConfig.NAME}!</li>
</ol>
</div>
);
}
@@ -0,0 +1,205 @@
import useSetSubheader from "#components/layout/header/useSetSubheader";
import Divider from "#components/util/Divider";
import { UserContext } from "#context/UserContext";
import { TachiConfig } from "#lib/config";
import { Contributors } from "#util/constants/contributors";
import { RFA } from "#util/misc";
import React, { useContext, useState } from "react";
import { Link } from "react-router-dom";
export default function CreditsPage() {
useSetSubheader("Credits");
const [alt, setAlt] = useState(false);
const [whatTheHell, setWhatTheHell] = useState(0);
const { user } = useContext(UserContext);
function a(normal: string | JSX.Element, ...altStr: (string | JSX.Element)[]) {
if (!alt) {
return normal;
} else if (whatTheHell < 0) {
return normal;
}
return RFA(altStr);
}
return (
<div style={{ fontSize: "1.15rem" }}>
<p>
{a(
<>
{TachiConfig.NAME} has been the work of many people, and many more
contributors. None of this would be possible without these people.
</>,
"Funding for this program was made possible by by by by by-",
`${TachiConfig.NAME} actually just appeared here one day, and we're not sure how.`,
`Actually, we just ran npm install ${TachiConfig.NAME}.`,
"To be honest, we had a surplus of monkeys and typewriters.",
)}
</p>
<Divider />
<p>
If you want to support {TachiConfig.NAME} development, see{" "}
<Link to="/support">Support</Link>.
</p>
<Divider />
<div className="mt-4">
<h1 className="user-select-none">
De
<span
className="credits-easter-egg"
onClick={() => {
if (alt) {
// Force a different RFA
// by incrementing a random
// variable
setWhatTheHell(whatTheHell + 1);
} else {
setAlt(true);
}
}}
>
v
</span>{" "}
Team
</h1>
<ul>
<li>
{a(
"Lead Dev",
"Rules Of Hooks Violator",
"ABBA Enthusiast",
"Nutcase",
"IIDX 'player'",
)}
: <strong>zk</strong>
</li>
<li>
{a(
"Infrastructure & Dev/Ops",
"Professional YAML Editor",
"Professional nginx.conf Editor",
"'What do you mean you rm -rf'd that folder?'",
"WintOr",
"Frog Lover",
)}
: <strong>winter</strong>
<br />
<small>Also the reason I even started learning how to program.</small>
</li>
</ul>
</div>
<div className="mt-6">
<h1>Significant Contributors</h1>
<p>
These people have contributed a significant amount of their time to{" "}
{TachiConfig.NAME}.
</p>
<ul>
<li>
{a(
"Tachi Discord Bot Work + Homepage",
"oooOoooo you want to use redux oooo",
"Java Skirt Boy",
"Urbit Enjoyer",
)}
: <strong>pfych</strong>
</li>
<li>
{a("Mikado", "SDVX Pocky")}: <strong>adamaq01</strong>
</li>
<li>
{a("Fervidex", "IIDX Black Magic")}: <strong>aixxe</strong>
</li>
<li>
{a("Barbatos", "SDVX Black Magic")}: <strong>Arm1stice</strong>
</li>
<li>
{a(
"Graphic Design",
"Adobe Suite Purchaser",
"Squiggly Line Drawer",
"Straight Line Drawer",
"Ratboy Genius",
"Large Headded Individual",
"The Ace Of Craigs",
)}
: <strong>Craig</strong>
</li>
<li>
{a(
"Database Management, Various features, generally just a hell of a lot of work",
"This guy put emojis in my codebase",
"Emacs Evangelist",
)}
: <strong>cg505</strong>
</li>
<li>
{a("KsHook", "Konaste Black Magic")}: <strong>Emma</strong>
</li>
<li>
{a("APIs, Databasing", "All Black Magic")}: <strong>Felix</strong>
</li>
<li>
{a("APIs, Massive Dev Support", "Have you tried Elixir?")}:{" "}
<strong>haste</strong>
</li>
<li>
{a("Chunitachi", "CHUNITHM Black Magic")}: <strong>tomatosoup</strong>
</li>
<li>
{a(
"Dev, Maths Consultation",
"Functional Programming Evangelist",
"Born On The Cob",
"Crunchy Nut Enjoyer",
"Has An Afro",
)}
: <strong>Percyqaz</strong>
</li>
<li>
{a("Server", "Ethereum Mining Solutions")}: <strong>viddy</strong>
<br />
<small>Yes, the entire server, networking costs, everything.</small>
</li>
</ul>
</div>
<div className="mt-6">
<h1>Contributors</h1>
<p>These people have contributed to {TachiConfig.NAME}.</p>
<ul>
{Contributors.slice(0)
.sort((a, b) => a.localeCompare(b))
.map((e) => (
<li key={e}>{e}</li>
))}
</ul>
</div>
<div className="mt-6">
<h1>And...</h1>
<p>
Everyone who was part of the beta.
{user?.badges.some((x) => x === "beta" || x === "alpha")
? " (That's you!)"
: ""}
</p>
<p>
Everyone in the <code>#dev</code> chat for the constant advice and comedy gold.
</p>
</div>
<span className="text-body-secondary" style={{ fontSize: "0.2rem" }}>
Click the V in Dev Team.
</span>
</div>
);
}
@@ -1,18 +1,8 @@
import { TachiConfig } from "#lib/config";
import React, { useEffect, useState } from "react";
import React from "react";
import { type UserDocument } from "tachi-common";
export default function SupportBanner({ user }: { user: UserDocument }) {
// const { data, error } = useApiQuery<{ scores: integer; sessions: integer }>(
// `/users/${user.id}/stats`,
// );
const [show, _setShow] = useState(localStorage.getItem("SHOW_SUPPORT_TACHI") === "true");
useEffect(() => {
localStorage.setItem("SHOW_SUPPORT_TACHI", `${show}`);
}, [show]);
// thank you
if (user.isSupporter) {
return (
@@ -23,69 +13,4 @@ export default function SupportBanner({ user }: { user: UserDocument }) {
}
return <></>;
// if (!data) {
// return <></>;
// }
// if (error) {
// return <></>;
// }
// if (data.scores < 1_000) {
// return <></>;
// }
// // too soon to bother
// if (Date.now() - user.joinDate < ONE_DAY * 7) {
// return <></>;
// }
// if (!show) {
// return (
// <div
// className="d-flex w-100 justify-content-center flex-column align-items-center"
// onClick={() => setShow(true)}
// >
// <div>Support us? ❤️</div>
// <div>
// <Icon type="chevron-down" />
// </div>
// </div>
// );
// }
// return (
// <Card
// className="mb-8"
// footer={
// <div className="d-flex w-100 justify-content-center" onClick={() => setShow(false)}>
// <Icon type="chevron-up" />
// </div>
// }
// header={
// <span style={{ fontSize: "1.55rem" }}>
// You've set {data.scores} scores and had {data.sessions} sessions, wew!
// </span>
// }
// >
// <span style={{ fontSize: "1.15rem" }}>
// {TachiConfig.NAME} is a passion project, and is primarily developed by one person.
// <br />
// Since you're enjoying the site so much, maybe you'd want to support me? :3
// <br />
// <br />
// If you want to support development, you can donate to my{" "}
// <ExternalLink href="https://ko-fi.com/zkrising">Ko-Fi</ExternalLink>, if you
// indicate your account name in the donation, you'll get a shiny name on the site!
// <br />
// I'm working on some new projects in secret. Maybe I'll post some updates on there :P
// <br />
// <br />
// Alternatively, you can star or contribute to the fully-open-source{" "}
// <ExternalLink href="https://github.com/zkldi/Tachi">GitHub Repo</ExternalLink>. This
// makes me look cool to employers!
// </span>
// </Card>
// );
}
@@ -5,7 +5,7 @@ import React from "react";
import { Link } from "react-router-dom";
export default function SupportMePage() {
useSetSubheader("Support / Ko-fi");
useSetSubheader("Support");
return (
<div style={{ fontSize: "1.15rem" }}>
@@ -14,11 +14,7 @@ export default function SupportMePage() {
<Link to="/credits">people like you</Link>.
</p>
<p>
If you want to support {TachiConfig.NAME} development, you can donate to my{" "}
<ExternalLink href="https://ko-fi.com/zkrising">Ko-fi</ExternalLink>.
</p>
<p>
Alternatively, you can star the{" "}
You can star the{" "}
<ExternalLink href="https://github.com/zkldi/Tachi">GitHub Repo</ExternalLink>. This
makes me look cool to employers!
</p>
@@ -1,3 +1,4 @@
import CreditsPage from "#app/pages/dashboard/misc/CreditsPage";
import MyProposalsPage from "#app/pages/dashboard/proposals/MyProposalsPage";
import ProposalsPage from "#app/pages/dashboard/proposals/ProposalsPage";
import SearchPage from "#app/pages/dashboard/search/SearchPage";
@@ -95,6 +96,10 @@ export default function DashboardRoutes() {
<PrivacyPolicyPage />
</Route>
<Route exact path="/credits">
<CreditsPage />
</Route>
{/* <Route exact path="/support">
<SupportMePage />
</Route> */}
@@ -19,6 +19,7 @@ import MikadoPage from "#app/pages/dashboard/import/MikadoPage";
import OngekiArtemisExportPage from "#app/pages/dashboard/import/OngekiArtemisExportPage";
import OngekiInoharaPage from "#app/pages/dashboard/import/OngekiInoharaPage";
import OngekiSiteImportPage from "#app/pages/dashboard/import/OngekiSiteImportPage";
import RizuPage from "#app/pages/dashboard/import/RizuPage";
import SaekawaPage from "#app/pages/dashboard/import/SaekawaPage";
import SDVXEamCSVPage from "#app/pages/dashboard/import/SDVXEamCSVPage";
import SilentHookPage from "#app/pages/dashboard/import/SilentHookPage";
@@ -255,6 +256,10 @@ export default function ImportRoutes() {
<WACCAMyPageScraperPage />
</Route>
<Route exact path="/import/rizu">
<RizuPage />
</Route>
<Route exact path="/import/kt-maimaidx-site-importer">
<MaimaiDXSiteImportPage />
</Route>
@@ -45,8 +45,10 @@ export default function ClassBadge<GPT extends V3Game = V3Game>({
</Badge>
);
} else {
const styleWithMaybeShine = classStyle as { shine?: boolean } & React.CSSProperties;
const { shine, ...badgeStyle } = styleWithMaybeShine;
badgeComponent = (
<Badge bg={""} className="mx-2" style={classStyle}>
<Badge bg={""} className={`mx-2${shine ? " shine" : ""}`} style={badgeStyle}>
{data.display}
</Badge>
);
@@ -185,7 +185,9 @@ function ChartInfoMiddle({
const diff = FormatDifficultySearch(chart);
const gameName =
game === "ongeki" ? "オンゲキ" : game === "maimaidx" ? "maimai" : gameGroupConfig.name;
const formattedTitle = song.title.replace(/-/gu, " ");
const formattedTitle = song.title
.replace(/([!?,]){4,}/gu, (m) => m[0].repeat(3))
.replace(/-/gu, " ");
let search = `${gameName} ${formattedTitle}`;
@@ -16,9 +16,14 @@ export default function IIDXStyleSongChartInfoFormat({
game: V3Game;
song: SongDocument<"bms" | "chunithm" | "iidx" | "maimaidx" | "ongeki" | "pms" | "popn">;
}) {
const genre =
game === "ongeki"
? ((song as SongDocument<"ongeki">).data.flavorGenre ?? song.data.genre)
: song.data.genre;
return (
<>
<h4>{song.data.genre}</h4>
<h4>{genre}</h4>
<h4 style={{ fontSize: "2.5rem", fontWeight: "bold" }}>{song.title}</h4>
<h4>{song.artist}</h4>
{chart && <h5>({LevelText(chart)})</h5>}
@@ -63,6 +63,14 @@ export function Footer() {
>
GDPR
</Nav.Link>
<Nav.Link
as={Link}
className={linkClassNames}
onClick={() => window.scrollTo(0, 0)}
to="/credits"
>
Credits
</Nav.Link>
{import.meta.env.VITE_DISCORD && (
<Nav.Link
as={ExternalLink}
@@ -149,7 +149,7 @@ function GraphComponent({
},
]}
difficulty={chart.difficulty}
duration={song.data.duration}
duration={song.data.duration ?? 240}
game="ongeki"
height="360px"
maximumAbsoluteValue={
@@ -25,7 +25,7 @@ import { NumericSOV } from "#util/sorts";
import React from "react";
import { COLOUR_SET, type PBScoreDocument, type ScoreDocument, type V3Game } from "tachi-common";
import { bgc, RAINBOW_EX_GRADIENT, RAINBOW_GRADIENT, RAINBOW_SHINY_GRADIENT } from "./games/_util";
import { bgc, RAINBOW_EX_GRADIENT, RAINBOW_GRADIENT } from "./games/_util";
import { ARCAEA_TOUCH_IMPL } from "./games/arcaea";
import { BMS_7K_IMPL, BMS_14K_IMPL, PMS_IMPL } from "./games/bms-pms";
import { DDR_DP_IMPL, DDR_SP_IMPL } from "./games/ddr";
@@ -785,9 +785,22 @@ export const GPT_CLIENT_IMPLEMENTATIONS: GPTClientImplementations = {
SILVER: bgc("gray", "var(--bs-light)"),
GOLD: bgc("var(--bs-warning)", "var(--bs-dark)"),
PLATINUM: bgc("silver", "var(--bs-dark)"),
RAINBOW: RAINBOW_GRADIENT,
RAINBOW_SHINY: RAINBOW_SHINY_GRADIENT,
RAINBOW_EX: RAINBOW_EX_GRADIENT,
RAINBOW: {
background: "linear-gradient(-45deg, #ff73d6 20%, #00ffff 50%, #ebfb2a 80%)",
color: "var(--bs-dark)",
},
RAINBOW_SHINY: {
background: "linear-gradient(-45deg, #ff73d6 20%, #00ffff 50%, #ebfb2a 80%)",
color: "var(--bs-dark)",
shine: true,
},
RAINBOW_EX: {
background:
"linear-gradient(-45deg, #df7e85 20%, #efef51 40%, #4bee83 60%, #70bdd7 80%)",
color: "var(--bs-dark)",
shine: true,
},
RAINBOW_EX_TRUE: { ...RAINBOW_EX_GRADIENT, shine: true },
},
},
enumColours: {
@@ -845,7 +858,14 @@ export const GPT_CLIENT_IMPLEMENTATIONS: GPTClientImplementations = {
[
"Platinum Score",
"P-Score",
NumericSOV((x) => x.scoreData.platinumStars * 100000 + x.scoreData.platinumScore),
// __related is attached when rendering table rows with chart context
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- NumericSOV row shape
NumericSOV((x: any) => {
if (x.__related?.chart?.data !== undefined) {
return x.scoreData.platinumScore / x.__related.chart.data.maxPlatScore;
}
return x.scoreData.platinumStars * 100000 + x.scoreData.platinumScore;
}),
],
["Judgements", "Notes", NumericSOV((x) => x.scoreData.judgements.cbreak ?? 0)],
[
-6
View File
@@ -36,12 +36,6 @@ export const RAINBOW_GRADIENT = {
color: "var(--bs-dark)",
} as const;
export const RAINBOW_SHINY_GRADIENT = {
background:
"linear-gradient(-45deg, #ff8697, #ff9dbf, #9e81d0, #87caff, #7dae80, #ffffa7, #ffd88a, #ffab8d, #ff8697)",
color: "var(--bs-dark)",
} as const;
export const RAINBOW_EX_GRADIENT = {
background: "linear-gradient(-45deg, #0fa091, #0f98d5, #67087f, #d9007e, #f56e06)",
color: "var(--bs-light)",
+1 -1
View File
@@ -65,13 +65,13 @@ export type GPTClassColours<GPT extends V3Game> = {
// css properties => css
// null -> no styling
[V in ClassConfigs[GPT][C]["values"][number]["id"]]:
| ({ shine?: boolean } & CSSProperties)
| "danger"
| "info"
| "primary"
| "secondary"
| "success"
| "warning"
| CSSProperties
| null;
};
};
@@ -6,3 +6,35 @@
opacity: 1;
}
}
@keyframes shine {
0% {
top: -150%;
left: -150%;
}
100% {
top: 150%;
left: 150%;
}
}
.shine {
position: relative;
overflow: hidden;
}
.shine::before {
background: linear-gradient(
transparent,
rgba(255, 255, 255, 0.5) 50%,
transparent
);
content: "";
display: block;
width: 150%;
height: 2rem;
position: absolute;
transform: rotate(-45deg);
animation: shine 4s infinite ease-in-out;
z-index: 100;
}
@@ -23,7 +23,8 @@ export const GAME_GROUP_ONGEKI_CONF = {
"LUNATIC",
"ボーナストラック",
]),
duration: z.number(),
duration: z.number().nullable(),
flavorGenre: z.string().optional(),
}),
} as const satisfies INTERNAL_GAME_GROUP_CONFIG;
@@ -39,7 +40,8 @@ export const OngekiColours = [
ClassValue("PLATINUM", "鉑", "Platinum: 18.000~18.999 Rating"),
ClassValue("RAINBOW", "虹", "Rainbow: 19.000~19.999 Rating"),
ClassValue("RAINBOW_SHINY", "虹(光)", "Rainbow Shiny: 20.000~20.999 Rating"),
ClassValue("RAINBOW_EX", "虹(極)", "Rainbow Extreme: 21.000~ Rating"),
ClassValue("RAINBOW_EX", "虹(極)", "Rainbow Extreme: 21.000~21.999 Rating"),
ClassValue("RAINBOW_EX_TRUE", "虹(極)・真", "Rainbow Extreme (True): 22.000~ Rating"),
];
export const GAME_ONGEKI_CONF = {
@@ -228,11 +230,11 @@ export const GAME_ONGEKI_CONF = {
"オンゲキ bright MEMORY Act.3",
"オンゲキ Re:Fresh",
]),
isBonusTrack: z.boolean(),
isReMaster: z.boolean().optional(),
maxPlatScore: z.number().int(),
inGameID: z.number().int(),
maxPlatScore: z.number().int().nonnegative(),
inGameID: z.number().int().nonnegative().nullable(),
chartViewURL: z.string().optional(),
isBonusTrack: z.boolean().optional(),
}),
preferences: z.strictObject({}),
+1
View File
@@ -612,6 +612,7 @@ export enum ONGEKI_COLOURS {
PURPLE = 4,
RAINBOW = 9,
RAINBOW_EX = 11,
RAINBOW_EX_TRUE = 12,
RAINBOW_SHINY = 10,
RED = 3,
SILVER = 6,
@@ -106,7 +106,7 @@ export async function GetBestJubilityOnSongs(
return Promise.all(rows.rows.map((row) => ToPbScoreDocument(row)));
}
const CURRENT_JUBEAT_HOT_VERSION: Versions["jubeat"] = "ave";
const CURRENT_JUBEAT_HOT_VERSION: Versions["jubeat"] = "beyond";
export async function GetPBsForJubility(userID: integer) {
const hotSongRows = await DB.selectFrom("song")
@@ -293,6 +293,8 @@ describe("ONGEKI_IMPL", () => {
c(19, "RAINBOW");
c(20, "RAINBOW_SHINY");
c(21, "RAINBOW_EX");
c(22, "RAINBOW_EX_TRUE");
c(23, "RAINBOW_EX_TRUE");
});
});
@@ -16,8 +16,13 @@ import {
import { GoalFmtScore, GoalOutOfFmtScore, GradeGoalFormatter } from "./_common";
const isUnranked = (chart: ChartDocument<"ongeki">) =>
(chart.data.inGameID >= 7000 && chart.data.inGameID < 8000) || chart.levelNum === 0.0;
const isUnranked = (chart: ChartDocument<"ongeki">) => {
const ig = chart.data.inGameID;
return (
(ig !== null && ig >= 7000 && ig < 8000) ||
chart.levelNum === 0.0
);
};
const starCount = (platinumScore: number, maxPlatinumScore: number) => {
const pct = Math.floor((platinumScore / maxPlatinumScore) * 100);
@@ -113,7 +118,9 @@ export const ONGEKI_IMPL: GameImplementation<"ongeki"> = {
return { colour: null };
}
if (rating >= 21) {
if (rating >= 22) {
return { colour: "RAINBOW_EX_TRUE" };
} else if (rating >= 21) {
return { colour: "RAINBOW_EX" };
} else if (rating >= 20) {
return { colour: "RAINBOW_SHINY" };
@@ -230,9 +237,11 @@ export const ONGEKI_IMPL: GameImplementation<"ongeki"> = {
}
}
if (s.scoreData.bellLamp === "FULL BELL" && s.scoreData.noteLamp === "LOSS") {
return "Cannot have a LOSS with a FULL BELL.";
}
// LMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO
// if (s.scoreData.bellLamp === "FULL BELL" && s.scoreData.noteLamp === "LOSS") {
// return "Cannot have a LOSS with a FULL BELL.";
// }
// LMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO
if (s.scoreData.platinumScore > chart.data.maxPlatScore) {
return `Cannot have ${s.scoreData.platinumScore}/${chart.data.maxPlatScore} Platinum Score.`;
@@ -240,6 +240,47 @@ EnableGraphs = true
#
# If present, an * matches everyone else`,
},
{
clientID: "CXRizu",
requestedPermissions: ["submit_score"],
name: "Rizu",
redirectUri: null,
webhookUri: null,
apiKeyFilename: "Rizu.cfg",
apiKeyTemplate: `[General]
## Whether to enable score submissions
# Setting type: Boolean
# Default value: true
Enable = true
## Timeout for score submission in seconds
# Setting type: Int32
# Default value: 30
NetworkTimeout = 30
## Folder for storing imports that failed due to network errors.
## Leave empty to disable
# Setting type: String
# Default value: UserData/Rizu/FailedImports
FailedImportsFolder = UserData/Rizu/FailedImports
[Keys]
## Kamaitachi API keys to use for score submissions, in the format
## of \`<access-code> = <api-key>\`. The \`default\` key is used as fallback
## if an access code does not have an API key set, and can be removed.
# Setting type: String
# Default value:
default = %%TACHI_KEY%%
[Tachi]
## Tachi instance base URL
# Setting type: String
# Default value: https://kamai.tachi.ac
BaseUrl = ${ServerConfig.OUR_URL}`,
},
];
const BokuDefaultClients: DefaultClients = [
@@ -119,6 +119,8 @@ function ConvertVersion(ver: number): Versions["jubeat"] {
return "festo";
case 11:
return "ave";
case 12:
return "beyond";
}
throw new InvalidScoreFailure(`Unknown Game Version ${ver}.`);
@@ -9,6 +9,7 @@ import {
type SEEDS_QuestlineDocument,
type SEEDS_SongDocument,
type SEEDS_TableDocument,
type V3Game,
} from "tachi-common";
/* eslint-disable no-await-in-loop */
import type {
@@ -719,8 +719,9 @@ export const CHUNITHMBBKKSong: SongDocument<"chunithm"> = {
altTitles: [],
artist: "nora2r",
data: {
duration: 128.541,
genre: "VARIETY",
duration: 128.541,
displayVersion: "CHUNITHM",
},
id: "S19d35e0b935765ded25",
searchTerms: [],
@@ -736,7 +737,6 @@ export const CHUNITHMBBKKChart: ChartDocument<"chunithm"> = {
level: "3",
data: {
inGameID: 3,
displayVersion: "CHUNITHM",
},
isPrimary: true,
versions: ["paradiselost"],
@@ -1485,6 +1485,7 @@ export const TestingOngekiChart: ChartDocument<"ongeki"> = {
displayVersion: "オンゲキ bright MEMORY Act.3",
inGameID: 2137,
maxPlatScore: 1000,
isBonusTrack: false,
},
difficulty: "MASTER",
isPrimary: true,
@@ -1537,6 +1538,7 @@ export const TestingOngekiChartConverter: ChartDocument<"ongeki"> = {
displayVersion: "オンゲキ bright",
inGameID: 678,
maxPlatScore: 2768,
isBonusTrack: false,
},
difficulty: "MASTER",
isPrimary: true,
@@ -1552,6 +1554,7 @@ export const TestingChunithmSongConverter: SongDocument<"chunithm"> = {
data: {
genre: "POPS & ANIME",
duration: 120,
displayVersion: "CHUNITHM CRYSTAL PLUS",
},
id: "s956",
searchTerms: [],
@@ -1562,7 +1565,6 @@ export const TestingChunithmChartConverter: ChartDocument<"chunithm"> = {
game: "chunithm",
chartID: "fbba26a6a223b1dd0bf8ef139cfee83114b91086",
data: {
displayVersion: "CHUNITHM CRYSTAL PLUS",
inGameID: 956,
},
difficulty: "MASTER",
@@ -1579,6 +1581,7 @@ export const TestingChunithmWESongConverter: SongDocument<"chunithm"> = {
data: {
duration: 135.275,
genre: "VARIETY",
displayVersion: "CHUNITHM NEW PLUS",
},
id: "S19d35e0c127c7e441ce",
searchTerms: ["xl techno more dance"],
@@ -1588,7 +1591,6 @@ export const TestingChunithmWESongConverter: SongDocument<"chunithm"> = {
export const TestingChunithmWEChartConverter: ChartDocument<"chunithm"> = {
game: "chunithm",
data: {
displayVersion: "CHUNITHM NEW PLUS",
inGameID: 8235,
},
difficulty: "割☆4",