mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-10 16:28:17 +03:00
feat: barebones itg support + srpg6
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
import { ChangeOpacity } from "util/color-opacity";
|
||||
import { ONE_MINUTE } from "util/constants/time";
|
||||
import QuickTooltip from "components/layout/misc/QuickTooltip";
|
||||
import Muted from "components/util/Muted";
|
||||
import React from "react";
|
||||
import { ChartDocument, COLOUR_SET } from "tachi-common";
|
||||
import Divider from "components/util/Divider";
|
||||
import Icon from "components/util/Icon";
|
||||
import MiniTable from "../components/MiniTable";
|
||||
import RatingSystemPart from "./RatingSystemPart";
|
||||
|
||||
@@ -16,6 +19,24 @@ const COLOUR_LOOKUP = {
|
||||
};
|
||||
|
||||
export default function ITGDifficultyCell({ chart }: { chart: ChartDocument<"itg:Stamina"> }) {
|
||||
const levelStr = chart.data.rankedLevel ?? `${chart.data.chartLevel}?`;
|
||||
|
||||
let breakdown = "No Streams!";
|
||||
|
||||
if (chart.data.breakdown) {
|
||||
if (chart.data.breakdown.detailed.length < 32) {
|
||||
breakdown = chart.data.breakdown.detailed;
|
||||
} else if (chart.data.breakdown.partiallySimplified.length < 32) {
|
||||
breakdown = chart.data.breakdown.partiallySimplified;
|
||||
} else if (chart.data.breakdown.simplified.length < 32) {
|
||||
breakdown = chart.data.breakdown.simplified;
|
||||
} else {
|
||||
breakdown = `${
|
||||
chart.data.breakdown.total
|
||||
} Total (${chart.data.breakdown.density.toFixed(0)}% Density)`;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<td
|
||||
style={{
|
||||
@@ -29,30 +50,60 @@ export default function ITGDifficultyCell({ chart }: { chart: ChartDocument<"itg
|
||||
<QuickTooltip
|
||||
wide
|
||||
tooltipContent={
|
||||
<MiniTable headers={["Breakdown"]} colSpan={2}>
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td>{chart.data.breakdown.total}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Detailed</td>
|
||||
<td>{chart.data.breakdown.detailed}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Simplified</td>
|
||||
<td>{chart.data.breakdown.simplified}</td>
|
||||
</tr>
|
||||
</MiniTable>
|
||||
<>
|
||||
{chart.data.breakdown ? (
|
||||
<MiniTable headers={["Breakdown"]} colSpan={2}>
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td>
|
||||
{chart.data.breakdown.total} Total (
|
||||
{chart.data.breakdown.density.toFixed(0)}% Density)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Detailed</td>
|
||||
<td>{chart.data.breakdown.detailed}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Simplified</td>
|
||||
<td>{chart.data.breakdown.simplified}</td>
|
||||
</tr>
|
||||
</MiniTable>
|
||||
) : (
|
||||
<b>This chart has no streams.</b>
|
||||
)}
|
||||
{chart.data.rankedLevel === null && (
|
||||
<>
|
||||
<Divider />
|
||||
<b>
|
||||
This chart is not ranked. Take the level with a pinch of salt.
|
||||
</b>
|
||||
</>
|
||||
)}
|
||||
{chart.data.length > 60 * 16 && (
|
||||
<>
|
||||
<Divider />
|
||||
<b>This chart is a marathon!</b>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<span>
|
||||
{chart.data.difficultyTag}: {chart.level} [
|
||||
{chart.data.primaryBPM?.toFixed() ?? "???"}]
|
||||
<span style={{ fontSize: "1.05rem" }}>
|
||||
{chart.data.difficultyTag} {levelStr} [
|
||||
{chart.data.streamBPM?.toFixed() ?? "???"}]
|
||||
</span>
|
||||
<br />
|
||||
<Muted>{chart.data.charter}</Muted>
|
||||
<RatingSystemPart chart={chart} game="itg" />
|
||||
<br />
|
||||
<span>{breakdown}</span>
|
||||
{chart.data.length > 60 * 16 && (
|
||||
<>
|
||||
<br />
|
||||
<Icon type="clock" />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</QuickTooltip>
|
||||
</td>
|
||||
|
||||
@@ -7,6 +7,10 @@ export default function ObjCell({ data }: { data: unknown }) {
|
||||
{/* this kinda sucks. have we got a better way to do this? */}
|
||||
{FlattenValue(data)
|
||||
.filter((e) => e.value !== null)
|
||||
.filter(
|
||||
// hack to hide some annoying itg properties
|
||||
(e) => e.keychain[0] !== "npsPerMeasure" && e.keychain[0] !== "notesPerMeasure"
|
||||
)
|
||||
.map((e) => (
|
||||
<>
|
||||
<code>{StringifyKeyChain(e.keychain)}</code>: {JSON.stringify(e.value)}
|
||||
|
||||
@@ -31,8 +31,9 @@ export default function TitleCell({
|
||||
backgroundImage = `url(${ToCDNURL(
|
||||
`/misc/popn/banners/${(chart as any).data.inGameID}.png`
|
||||
)})`;
|
||||
} else if (game === "itg") {
|
||||
const banner = (song as SongDocument<"itg">).data.originalPack;
|
||||
} else if (game === "itg" && chart) {
|
||||
const itgChart = chart as ChartDocument<"itg:Stamina">;
|
||||
const banner = itgChart.data.bannerLocationOverride ?? itgChart.data.originalPack;
|
||||
|
||||
if (banner) {
|
||||
backgroundImage = `url(${ToCDNURL(
|
||||
|
||||
@@ -10,7 +10,6 @@ export const ITG_CONF = {
|
||||
playtypes: ["Stamina"],
|
||||
songData: z.strictObject({
|
||||
subtitle: z.string(),
|
||||
originalPack: z.string(),
|
||||
}),
|
||||
} as const satisfies INTERNAL_GAME_CONFIG;
|
||||
|
||||
@@ -26,7 +25,7 @@ export const ITG_STAMINA_CONF = {
|
||||
|
||||
lamp: {
|
||||
type: "ENUM",
|
||||
values: ["FAILED", "CLEAR", "FULL COMBO", "FULL EXCELLENT COMBO", "QUAD"],
|
||||
values: ["FAILED", "CLEAR", "FULL COMBO", "FULL EXCELLENT COMBO", "QUAD", "QUINT"],
|
||||
minimumRelevantValue: "FAILED", // lol, maybe this is stupid.
|
||||
},
|
||||
},
|
||||
@@ -123,27 +122,44 @@ export const ITG_STAMINA_CONF = {
|
||||
versions: {},
|
||||
|
||||
chartData: z.strictObject({
|
||||
// if this chart is "ranked" (i.e. worth any rating) what rating is it
|
||||
// ranked at?
|
||||
// For now, a chart is given this value if and only if it is in an ECS/SRPG.
|
||||
rankedLevel: z.number().nullable(),
|
||||
|
||||
// what level does the chart say it is?
|
||||
chartLevel: z.number(),
|
||||
|
||||
hashGSv3: z.string(),
|
||||
difficultyTag: z.enum(["Beginner", "Easy", "Medium", "Hard", "Expert", "Edit"] as const),
|
||||
|
||||
// Chart length in seconds.
|
||||
length: z.number().positive(),
|
||||
charter: z.string(),
|
||||
|
||||
// Pick one BPM to represent this chart. If that doesn't make any sense
|
||||
// Pick one BPM to represent this chart's stream speed.
|
||||
// If that doesn't make any sense
|
||||
// (i.e. chart has significant bpm changes)
|
||||
// this should be null.
|
||||
// Note that for some charts with BPM changes, Archi typically picks a common
|
||||
// BPM to place it at. Not sure if that's automatable.
|
||||
primaryBPM: z.number().nullable(),
|
||||
streamBPM: z.number().nullable(),
|
||||
|
||||
breakdown: z.strictObject({
|
||||
detailed: z.string(),
|
||||
partiallySimplified: z.string(),
|
||||
simplified: z.string(),
|
||||
total: z.string(),
|
||||
}),
|
||||
breakdown: z
|
||||
.strictObject({
|
||||
detailed: z.string(),
|
||||
partiallySimplified: z.string(),
|
||||
simplified: z.string(),
|
||||
total: z.string(),
|
||||
density: z.number(),
|
||||
})
|
||||
.nullable(),
|
||||
|
||||
npsPerMeasure: z.array(zodNonNegativeInt),
|
||||
notesPerMeasure: z.array(zodNonNegativeInt),
|
||||
|
||||
bannerLocationOverride: z.string().nullable(),
|
||||
originalPack: z.string(),
|
||||
}),
|
||||
|
||||
preferences: z.strictObject({}),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -2292,6 +2292,206 @@
|
||||
"tableID": "itg-Stamina-levels",
|
||||
"title": "Block Levels"
|
||||
},
|
||||
{
|
||||
"default": false,
|
||||
"description": "All charts for ITG. These may not be accurately rated.",
|
||||
"folders": [
|
||||
"F1cc19c926d15b24e17b41cf6a7fc2a856793637e32edda7c9397a9df322e530d",
|
||||
"F4af15b8cbfbe1de73e8d47c7a6435b9d55ec1c8306a00f5995599170554b0bd8",
|
||||
"F35a1b69e1b5cc21dd37e5bb46d1120e61d54a19fd7f89267db7ddf441c1bdb51",
|
||||
"Fd9360b32a3e0f387daf3912a81e255234d2b89d549c358c36be1d0ece5a35f44",
|
||||
"F825aac6f64ae4f01a1928e95a0f3bd4d1cbc54f70bf651f11de4528e44bfe942",
|
||||
"F7996ebffa651e077d578cfcd00586a8c160e7f73a840e545fde6489439aa094f",
|
||||
"F56b22e5167015603a8e73073571f77487553e3b184961bfc58752110d6a24d43",
|
||||
"F0c02918cb77907528015200cbe9903b1392543ea3472ebb6ff7d68f8b4abddbb",
|
||||
"F35dacb73d6601b4eb1f0ebb50f69238c93aeb8f4ce2910fa4f029bd5a288370b",
|
||||
"F0ca9002af213d560a02ae63760209bdd8c6f0bb17ac97570a27db33e9c76a911",
|
||||
"Faa313715508d3b6771ec29dd5de4ce7d60e51c32c6f2138c73083fe67178912a",
|
||||
"F808ce861396fb29262b792d795b3f5cd92b8f2f95fc6fe18d21c69f8d6baa7d4",
|
||||
"F19d9a3f4f3a4265e30ea81d546536705c0386fc63e2f93bc40cbe4fa9c4ec711",
|
||||
"F2df7455d454651166f694d5aabcfdbb5956d6f007111599e5d0121f4dd37f763",
|
||||
"F5d96c1922fe514f5ecf7f2e40436fca8d89a8ebbed09f594f7c7c3dae66f5780",
|
||||
"Fcf3d33d06c34ddedac295385863a6adadbe281b64b0f783b8d7ef8aa7961dfd2",
|
||||
"F756224315c713cbc2c2cbb997b99b3bbb0fa39d0a5a887ca11915619c36d6628",
|
||||
"Fd2c37cc512c696c5550225e2abb7338f5f3b429baa51b06a9df3f9f302284d52",
|
||||
"Fddf4796fdbc79020b364939b57448d4cb846501e7b866ab958dca1d125c644b9",
|
||||
"Fd7b582e94b4470f6e2f5a16e340444fcaf2ea3b10a09be96bfc2c704eeb273fd",
|
||||
"Ff912e618d9ae91cb586cb50037a5e6bf27b209ef002a6940b5e1f532eb42dcfe",
|
||||
"Fbace16f2f3700e09f49f69de7c68cb74a8d4a1189cabbc11eb98e869193ee6eb",
|
||||
"F549497570821f8633b7b2b3dcf346ad03391f2551491800ce85f0fd11011590e",
|
||||
"Fd74a62791da3fefcebf20d7d0b90f37badf8e8f2c54ebdc8378df888168c9ad4",
|
||||
"Fe63ab76510f4bc00160538d57b07db4c49164be9e2c6877ca23dcb3d2520b029",
|
||||
"F02f46a1825d48a41c6d6bd8a57987bab6d13b805424608bb6b25b5ec5cb870f0",
|
||||
"F7e9311cab5ff5f2792155ca7c0bff802175d88cdfd5007eb1ad474ef97fc7388",
|
||||
"F6034733463d5a43cd3160b41c861f2e110ccb64eea9b38243a81585e0f105204",
|
||||
"Ff974f5280458cb258b1cd3f4c18dfdf17e8bd64b0297380580a88a1860472df2",
|
||||
"F30ab09b39d4f80347153a68ac125a22163457579a6f98762c4ece1088f59fb30",
|
||||
"Ff11bfb413a6f056ce801f7e12ebfcf260242e3a515822f0f8fb4171f88036f71",
|
||||
"Fe35e4400fa0dc602583c8a655af96d979fc36d3b565e61f1e56fc6cc55001d27",
|
||||
"F323e2cc78ef8495b8ab015f36efe7ef966dfa43119252cf687caf138e911ab9b",
|
||||
"Fa5c70004cc81240aba5f2887cd29d5427287d76b9a5db1f04908d97f95464fff",
|
||||
"Ffe3d09126883ecfbf2662531edd1a39a8b69fa05d2ac97463570de7358c9d0cc",
|
||||
"Fb30be3122ab76c5ac7623551414989b13a3c4abb9db7dd2d5742f09087ac5b26",
|
||||
"F577ddc536b77b4be6ea8889e438b95359f7a5e4ecd0740dfe03c93943553aa52",
|
||||
"F8905e896d69b8c6f11bedbcb85f51e7ba5103e48314512b910b6f3f0fe614cce",
|
||||
"F8c3b61962cf9b45085e6b0a30c7b4c1f06d1f933b3c6b9ca5a8879e548cc439e"
|
||||
],
|
||||
"game": "itg",
|
||||
"inactive": false,
|
||||
"playtype": "Stamina",
|
||||
"tableID": "itg-Stamina-any",
|
||||
"title": "ITG Stamina (w/ Unranked)"
|
||||
},
|
||||
{
|
||||
"default": false,
|
||||
"description": "All marathons for ITG. These may not be accurately rated.",
|
||||
"folders": [
|
||||
"Fcd795d50e9ad9d03d8a54be29f020d0c3c8440af7a0e7fe383d5bcd0b2584b6a",
|
||||
"F1df21eb99d760f3c68bccbe8f64ff7ab8fe8e1c2fe9fbec43d04eecd35a61716",
|
||||
"F6f8f324098d941f89ac51bc819a99080b94f65c4e9cd18696556da3558511e57",
|
||||
"F32a36f1af2c124e67e0c2fd94573246444ea7a69904dc95c313a17489fe1efec",
|
||||
"F120e3e1afd9c17ba1b96ad89c92ca4370a2a367463dce9f33cdb10b3e3d409ac",
|
||||
"Fbfa7455653154ce50fc39e9fcd389697ca6cebb8f11724a2f9b4e6bae70be2eb",
|
||||
"Feaf36245f9421b233cdbcae690c73e1a6e61356917273b997007e470044afa92",
|
||||
"Fa7ffd6a050f12705ff08d94bda924a523ef4fda548a7538d67c29a33c2f7ef69",
|
||||
"Fd4d34c0a02d7ce6eb3fa94f8be0b08f5e83909da123c48732914638603869952",
|
||||
"F17a8325e58ad4689dc418bfbc5b814d08834a1ab441c665376520939e5446872",
|
||||
"F483b069cfebd7024294e44d192c95504f4edf8cc9333a5f5482598c000333f05",
|
||||
"F8bc0442131053a09de1ea7ccd0cee4be9bf4b4175acc3a8922e377688c6713fa",
|
||||
"F4161b5d19d647dbf7bbbf52e856f422a2c363ceb6e63c81dc8f2fd16a837dbc7",
|
||||
"Ff6b9ea477abfd9a3261e60100bac68400e083424fc37d1975a7ca4d057ff3f77",
|
||||
"Fb251a42328901c6db8ac79b189dd2291da554f3128f6b5274bd9b34f7257d08d",
|
||||
"Fe883ade51a4dadf7f4798a8a98a557df23d166f537e35d3072191f24bde58b2b",
|
||||
"Fcacd8e3d7d4b6167e07b021459cab3ba0981643d76e44bb832c617429a23fde1",
|
||||
"F467a48d6380f641dd2973e8cd3ed2c56e66e26787d08f604cba79b52f7887ea1",
|
||||
"F83d8933ac16c0f147220ff91666cdeea7720243dbe767da225d6ae457266832e",
|
||||
"F732b4679ccbffe10a02315fd2a3b2ae7d17f0c727eb194747be71fc19a344283",
|
||||
"Fa01d231ffb51f53d93e6cba26447b365060fd13001d138f0ae07d8e81f1259ba",
|
||||
"F7b25f4dfc3537fea34b02579e4e8d2dc986bef69269ab17605e3877d3abff8c1",
|
||||
"Fffd6de4bc62b2a3f40b4c173f797664d2dbc626a9a5dd3dd0111990b195f7ac3",
|
||||
"Fd118336ebbca763a7049cbd4832b7fb882656c6e294462600fef9702f4651406",
|
||||
"F185192f30466c02c4acd0f56c4cd9638a22f055a4c51809e322ab69d50d9ea1e",
|
||||
"Ff000ccc7ff97b3b4f0c791f45e07867a48b861c086d5cb22906df2cd69d7f6e9",
|
||||
"Fc6b9f121eb69b7e9f72461e26d7f7b4158c392072d92ba34be6fc9ba3e945489",
|
||||
"F2302c23c0e101b75f5c06517496e5693baa0d68299cce57affe32f5ac8f2d136",
|
||||
"F6270f12b0df467cbd1d23af2b40110fc3ec7f0ef076621267efc0fea00011d16",
|
||||
"Fabada1bda596b4c3f92748e99d29d75bab96b30e086861741d716dc1aea0abab",
|
||||
"Fe8e3a87c42104bc7063782f0d58c41c0b4fdd6b57f353b57f809a98a19b24548",
|
||||
"Fbddd5c7c0d515dadee6499a26906e171f6dd5879229e62c2bf339b099bb254d5",
|
||||
"F4c7ed0827cb695e41e783b4f0e648bb379d6b01a97cfbc03da639f74178544e1",
|
||||
"F074b8c7b38cc39b08aa3fffc0085135f8272387af87bf4d1380614646941eba9",
|
||||
"F19c7c3e995deccf7119a20d2bdad0a1623d7dd1449e9753220d96bf8d9637275",
|
||||
"Feb336b1650d6bab2d1aa5bbc1dd03e771383c81ab0f665ebc15f46ccf382fa3f",
|
||||
"F184d9fec5d3f59594b3810605e312135680ed71bfd3bfa8f15a161969eef7936",
|
||||
"F54467af217bb396917ec05b97a94cc0c7b3fe8229ced3e695711c3d22131e8f1",
|
||||
"Ffd18193d67f63a66eb50a7afdb217d659561caba2b924c373a454595dd780130"
|
||||
],
|
||||
"game": "itg",
|
||||
"inactive": false,
|
||||
"playtype": "Stamina",
|
||||
"tableID": "itg-Stamina-marathon-any",
|
||||
"title": "ITG Stamina Marathons (w/ Unranked)"
|
||||
},
|
||||
{
|
||||
"default": true,
|
||||
"description": "Ranked charts for ITG. These are more reliably accurately rated than trusting what the charter thinks.",
|
||||
"folders": [
|
||||
"F825cea2c8d750947071448db8819fc44b320063fa8a9baa0ea71b0da25ac384b",
|
||||
"Fd55a9afa388b73a625f24498d1636dbde35c0958dc1b6c042cd3a873f2646997",
|
||||
"F219461f921e03b47110cc036096433f99dbda9f19798722a0c7e8dd4cb73aa15",
|
||||
"F70babe0344463aa8625a36302a094f4473d511e66d5921b5c7eff27673cda049",
|
||||
"F558bc27f22c0c49ff1c037ccc380e634feb04ad16f96c5c5d3bb1088d9bf0fdd",
|
||||
"Fb7c4a4f50b90ae1903beaf8221df8c9e88b39ca528d3074b45af9c41586aec5f",
|
||||
"F119d87309bcc7e3b7056be6c6ef3896ac0252a3e90e5740b8fa52f6e97a465ba",
|
||||
"F1462ca80de5f028f542ac1f9db031192af6b5d9847121ba1fcc386c4475a10f0",
|
||||
"Fd8866390ffaab87aaa457a8b37cbce062f672bd3912aecbcaf3c25ca629c90a9",
|
||||
"Ff00e2ae47e0c5a205addc100befe59f2a950b6593f8d0795b67ab426c5b08499",
|
||||
"F98c12bf00a343c1dc938eabe74b668db6c7d781a8a459d2fedc689009ee66d03",
|
||||
"Fda849f1d2e3abc9501ed85a273c73d2bc9e9f438892d89944b5bd90aca780599",
|
||||
"F9abf1aafd86d50e367d48c1a154e75304d28d0870fd2a103d53f378828e4a8ae",
|
||||
"Fce88074bb302b66b07486c96e43a56da1105a44aa4538390100999604cac1ec5",
|
||||
"F9c8967ca229cf128e7fcaaec9cbb40b247dd023c4a01e9cabc044139bb63e041",
|
||||
"F223633585bb6ec9ba19cba35019e85828cf31f44fdd651177828957034bc7afc",
|
||||
"Fa49950e6e3bdcfbe684711354315cbec07413dfe61fc97419e693cbae1d129c9",
|
||||
"F6411537239a9edf9ff2144f7f64bc78c78351e73ec5f8ff4f6489bbf8aad8343",
|
||||
"Fffd143efe8ec1ed6314b48bc35c33a5bde6d29adbb067ecb325a3342e4d39933",
|
||||
"F1db91314031ea8b3a0aa32c5a6a4938369a320b3f1cd83dbc980ae33556c83e0",
|
||||
"F2d4ca90ffd3bdaf6742d0a5c2ed657d036fedb65d5ad9214378cbbe033c952d2",
|
||||
"F7fd00914a8fd5781e05c4769835a2c7f40235d03d98f715db335a1308c26496a",
|
||||
"F41215629ad3a3af1130555cb14e798da60222a64c18657145432208c739f6da4",
|
||||
"Fef190a528fc71b09e9a6176f8b569a018cc7f89d5b02328b1e25d5b6f5580921",
|
||||
"F75899db5516e5fd92ad686f844d05f3bf46a4142ddb172a8aeaec79f788053cf",
|
||||
"Ff6e9aaf227263ffb3a44e83aaefbafd7df467e09ff8d85d602c9f8de0dd50e58",
|
||||
"F03d7d83a83b2986c2655a0a80ef17d8e96236c9d601c1fac87dd619c0246230d",
|
||||
"F17a43335d220f1bfef3efea1e84fa5bf077299c458646bd86e5622f642dcf886",
|
||||
"Fe68ea23d1be71ea19167a1f5574b4aea8aefe166b9cc9346062bd5de15a942a0",
|
||||
"F0c13ac6d5ffd5b19fc3092479a9c3e279c18f7b1c639afdafc6133ffff2b58d9",
|
||||
"F6adbbaa802bc62d2bc77116ef0c5448e8cf436619ec855208f8f25a5193cef40",
|
||||
"Fcd7b93ab216d39bfe0cac9799030a4e4b45c38dd6efffd27a30d3c0f1bab2d1f",
|
||||
"F92a23b8828d66e6a336c433f9398656afda1b3e12a4f81d4807702f67839dd21",
|
||||
"F07d105baaa80de0e2ccb6d1a65318e9ecea569c1a444b8cd17591ff223937183",
|
||||
"Ffc2b29fd11285b727b204f380e59da9cc9d29a2d91f375b97409136d54b7277b",
|
||||
"F2e4b5f17d3915f7ecd96ebb7d78d1c290b1494d52354c8cd3bdbd9331ed2e416",
|
||||
"F63dde84d7f818a19951aad75b5c5fe62509375a2250df5829bafeefd4feefd60",
|
||||
"F9b86044d92fde52c063c94d8eb4c6b36eae89bf7de6aa40ade6ff4af5a04e8ae",
|
||||
"Fc9927c172d825af09661e4d7187240ce637f7d5b5a45cce295038a9432cbf29a"
|
||||
],
|
||||
"game": "itg",
|
||||
"inactive": false,
|
||||
"playtype": "Stamina",
|
||||
"tableID": "itg-Stamina-ranked",
|
||||
"title": "ITG Stamina Ranked"
|
||||
},
|
||||
{
|
||||
"default": true,
|
||||
"description": "Ranked marathons for ITG. These are more reliably accurately rated than trusting what the charter thinks.",
|
||||
"folders": [
|
||||
"F39515e4f8aa6fec99dcc477ba81fdb0a4ba0e05defe054497960c58c5de422c9",
|
||||
"F884b209ae543a736e736f5c0411089f9b45d190d77c89ef01002ac19db41a2f0",
|
||||
"Fbc4352f4b44ec1882232cc14ee389bef57a34f2dac424e84e34a21439a1f76e3",
|
||||
"Fe3072824048d081a26c9c1964ca95ff04e1f494f1762f01d787b097dcc59e61f",
|
||||
"Ffe712a8af2e437b8ae7a3315ef98aa022b2b33b6f619a806d725230af483daba",
|
||||
"Fa8742da9a7204f70f61d693813dee985c96b3001113d4c7d7c2349f5c66f171e",
|
||||
"F8fb22a22456bb49cbbcd1a1bdd59e874ab7441982193027929e728b842ba0333",
|
||||
"F31391c035b60ee7ea7e81d30214eb6c0c7819a2b7b255c99dd494f2268f49240",
|
||||
"F342055146c9b024c6c4fd1f42dc610ff3b04575fae0f2df374a8b901f4592296",
|
||||
"F42f76550b85132228c3f9482d45e8e8182a867b299ff9d90b2a63ac6a103b984",
|
||||
"Fa4005ef9bca94544d3de1e9b979ab8fedcc11a443f308af9f69983e5c2aee3e6",
|
||||
"Fe46e6ed132b920295ec550fbb675d8d0ed5c8ebb873e24bc57a678ae1c308d4e",
|
||||
"F6b796f48dc02a06b78ef22381aa9345061f66e0c80477a2c91c54884820f999c",
|
||||
"F002d678237fcefdadd067b73db9e207cb3410d670a36fe210a53d7754670919e",
|
||||
"F7ff3a5d06212384e208095e8a611ce62b4f035df693f1760b17f5d31cdfdea98",
|
||||
"F1fb06cd6a4bec083f87e4779d9a13f3fedfbf77794025ccdda833de669710c75",
|
||||
"Fa2b8f08ec4885dded61623b41672d7fbb9d6aecd2f02e3fcafa4b05f615ed41e",
|
||||
"Fa811e50a7324cf9708effde7f8e3be19f89b106c3de33076d8eeba6e8c21387a",
|
||||
"Fbd406c99fb2b644ab78f9ace136a30e47e066c5c8f81e2ceab78b25ef68d3d02",
|
||||
"Fb01235965106f738dfb211647edc4d403ece309778d23709d100bb31af0549c0",
|
||||
"F9f9bf9174ae721cbd586e7036ad820239dd6fcbc7719894b8bdcb4cd968ac486",
|
||||
"F57e0e688b666c6940ab89121e521b02bef8ff0614924c0c6d2a5dba01c29ecba",
|
||||
"F5021e878e55f079fd4f76e09eb4c3f8e1164a4d8e3a0e1d041a9460469d3a53d",
|
||||
"F03ddae762674af00dd6332250e3a497e9c425cccd1257e88fdc8286c0692bee3",
|
||||
"F8606bd646536f372363de9b2299b1356e7966f257b60b924ea887227d9e2fb31",
|
||||
"F0a3c5f01505aefc38dd7a6f48618bf61b655ef915d312b0ef15a6ebb92f84644",
|
||||
"F52ab02d6fb9fa333eefde4ddf67fae73028b4cb5ddfab92de9c6f93f6a6c8f2d",
|
||||
"F2f7df3ac84be64d2cbf319f7e2d3b93212cbc83e06c04c8f8d60ff01ec0ad11d",
|
||||
"F0ba64d6ea159f59d4439ff9341620489fec78634298e7f59f615d7938d7c3e42",
|
||||
"F15477fb81117c4f55defa20c7909770d85cc223e11059bd6d40ea1d76bb888e6",
|
||||
"Fd643bdd3976f10b86b57b5e202d6850a54d0ac7278806076aee652e15d816436",
|
||||
"F70220a22f99cbd33a0d6eb5888e1f735d0d1a059bd1050afab07c50bf7a7c677",
|
||||
"F30636cf68e63c5b4daabf5242038b20a04eb849f74c1f222310aa7f7b5afc717",
|
||||
"Fd0ff3281b867d47c077c79f465f8fbeab240fd3df32e5eed001a2715bb28aab9",
|
||||
"Fe81c8e2791d7bb2521d3543254d99650ff76cba5ba6bbf10f711bf44b42fe8dc",
|
||||
"Fb605220caeb3d35d0ffbf451bbd3be2e2831899fbdc1410446a6fd633bdd90f6",
|
||||
"Ff17cca18e63d5e44b44cc920b33cced3abcc5d0fea31ca1df7e5062d38f2c763",
|
||||
"F42c830dff8008db646f590b2be59ea0c810c270c50a1c21dd72d1a86aba5f046",
|
||||
"F5f95c6098df9821c021b5f456c64922af21adee6b8e1a05f01b9a0d192d1964c"
|
||||
],
|
||||
"game": "itg",
|
||||
"inactive": false,
|
||||
"playtype": "Stamina",
|
||||
"tableID": "itg-Stamina-marathon-ranked",
|
||||
"title": "ITG Stamina Ranked Marathons"
|
||||
},
|
||||
{
|
||||
"default": false,
|
||||
"description": "Decimal Level Folders for jubeat.",
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
const { CreateFolderID, MutateCollection } = require("../../util");
|
||||
|
||||
const folders = [];
|
||||
|
||||
for (let i = 1; i < 40; i++) {
|
||||
const levelFolders = [
|
||||
{
|
||||
type: "charts",
|
||||
data: {
|
||||
"data.rankedLevel": i,
|
||||
},
|
||||
title: `Level ${i} Ranked`,
|
||||
game: "itg",
|
||||
playtype: "Stamina",
|
||||
inactive: false,
|
||||
searchTerms: [],
|
||||
},
|
||||
{
|
||||
type: "charts",
|
||||
data: {
|
||||
"data.chartLevel": i,
|
||||
},
|
||||
title: `Level ${i} (w/ Unranked)`,
|
||||
game: "itg",
|
||||
playtype: "Stamina",
|
||||
inactive: false,
|
||||
searchTerms: [],
|
||||
},
|
||||
{
|
||||
type: "charts",
|
||||
data: {
|
||||
"data.rankedLevel": i,
|
||||
"data.length": { $gte: 60 * 16 },
|
||||
},
|
||||
title: `Level ${i} (Ranked Marathons)`,
|
||||
game: "itg",
|
||||
playtype: "Stamina",
|
||||
inactive: false,
|
||||
searchTerms: [],
|
||||
},
|
||||
{
|
||||
type: "charts",
|
||||
data: {
|
||||
"data.chartLevel": i,
|
||||
"data.length": { $gte: 60 * 16 },
|
||||
},
|
||||
title: `Level ${i} (Ranked Marathons)`,
|
||||
game: "itg",
|
||||
playtype: "Stamina",
|
||||
inactive: false,
|
||||
searchTerms: [],
|
||||
},
|
||||
];
|
||||
|
||||
for (const fld of levelFolders) {
|
||||
fld.folderID = CreateFolderID(fld.data, fld.game, fld.playtype);
|
||||
}
|
||||
|
||||
folders.push(...levelFolders);
|
||||
}
|
||||
|
||||
MutateCollection("folders.json", (v) => [...v, ...folders]);
|
||||
|
||||
MutateCollection("tables.json", (tbls) => {
|
||||
tbls.push({
|
||||
tableID: "itg-Stamina-ranked",
|
||||
game: "itg",
|
||||
playtype: "Stamina",
|
||||
title: `ITG Stamina Ranked`,
|
||||
description: `Ranked charts for ITG. These are more reliably accurately rated than trusting what the charter thinks.`,
|
||||
folders: folders
|
||||
.filter((e) => "data.rankedLevel" in e.data && !("data.length" in e.data))
|
||||
.map((e) => e.folderID),
|
||||
inactive: false,
|
||||
default: true,
|
||||
});
|
||||
|
||||
tbls.push({
|
||||
tableID: "itg-Stamina-any",
|
||||
game: "itg",
|
||||
playtype: "Stamina",
|
||||
title: `ITG Stamina (w/ Unranked)`,
|
||||
description: `All charts for ITG. These may not be accurately rated.`,
|
||||
folders: folders
|
||||
.filter((e) => "data.chartLevel" in e.data && !("data.length" in e.data))
|
||||
.map((e) => e.folderID),
|
||||
inactive: false,
|
||||
default: false,
|
||||
});
|
||||
|
||||
tbls.push({
|
||||
tableID: "itg-Stamina-marathon-ranked",
|
||||
game: "itg",
|
||||
playtype: "Stamina",
|
||||
title: `ITG Stamina Ranked Marathons`,
|
||||
description: `Ranked marathons for ITG. These are more reliably accurately rated than trusting what the charter thinks.`,
|
||||
folders: folders
|
||||
.filter((e) => "data.rankedLevel" in e.data && "data.length" in e.data)
|
||||
.map((e) => e.folderID),
|
||||
inactive: false,
|
||||
default: true,
|
||||
});
|
||||
|
||||
tbls.push({
|
||||
tableID: "itg-Stamina-marathon-any",
|
||||
game: "itg",
|
||||
playtype: "Stamina",
|
||||
title: `ITG Stamina Marathons (w/ Unranked)`,
|
||||
description: `All marathons for ITG. These may not be accurately rated.`,
|
||||
folders: folders
|
||||
.filter((e) => "data.chartLevel" in e.data && "data.length" in e.data)
|
||||
.map((e) => e.folderID),
|
||||
inactive: false,
|
||||
default: false,
|
||||
});
|
||||
|
||||
return tbls;
|
||||
});
|
||||
@@ -0,0 +1,141 @@
|
||||
const { Command } = require("commander");
|
||||
const { readFileSync } = require("fs");
|
||||
const {
|
||||
ReadCollection,
|
||||
GetFreshSongIDGenerator,
|
||||
CreateChartID,
|
||||
MutateCollection,
|
||||
} = require("../../util");
|
||||
|
||||
const program = new Command();
|
||||
program.requiredOption("-i, --input <file.json>");
|
||||
program.option(
|
||||
"-e, --ecsRule",
|
||||
"Parse this using ECS rules, i.e. turning '[12] [120] Song' into the actual song title."
|
||||
);
|
||||
program.requiredOption("-p, --pack <Pack Name>", "What pack is this from?");
|
||||
|
||||
program.parse(process.argv);
|
||||
const options = program.opts();
|
||||
|
||||
const content = JSON.parse(readFileSync(options.input));
|
||||
|
||||
const existingSongs = ReadCollection("songs-itg.json");
|
||||
const existingCharts = ReadCollection("charts-itg.json");
|
||||
|
||||
const artistTitleMap = new Map();
|
||||
for (const song of existingSongs) {
|
||||
// lets just hope nobody ever has \0 in their artist or title lol
|
||||
artistTitleMap.set(`${song.title}\0${song.artist}\0${song.data.subtitle}`, song);
|
||||
}
|
||||
|
||||
const existingChartIDs = existingCharts.map((e) => e.data.hashGSv3);
|
||||
|
||||
const getFreshID = GetFreshSongIDGenerator("itg");
|
||||
|
||||
const newCharts = [];
|
||||
const newSongs = [];
|
||||
|
||||
for (const d of content) {
|
||||
let title;
|
||||
let difficultyTag;
|
||||
|
||||
if (options.ecsRule) {
|
||||
// parses
|
||||
// [16] [160] Song Title (Hard)
|
||||
// into a real form
|
||||
// but obviously dont parse
|
||||
// [16] [160] Song Title (Restep)
|
||||
// into assuming Restep is a difficulty name
|
||||
// lil b was right
|
||||
const strippedTitle = d.meta.title.match(
|
||||
/^\[\d+\] \[\d+\] (.+?)(?: \((Beginner|Easy|Normal|Hard|Edit)\))?$/u
|
||||
);
|
||||
|
||||
const [_, parsedTitle, weirdDiff] = strippedTitle;
|
||||
|
||||
title = parsedTitle;
|
||||
|
||||
if (weirdDiff) {
|
||||
difficultyTag = weirdDiff;
|
||||
}
|
||||
} else {
|
||||
title = d.meta.title;
|
||||
}
|
||||
|
||||
const song = {
|
||||
title,
|
||||
artist: d.meta.artist,
|
||||
data: {
|
||||
subtitle: d.meta.subtitle,
|
||||
},
|
||||
searchTerms: [],
|
||||
altTitles: [],
|
||||
};
|
||||
|
||||
const songExists = artistTitleMap.get(`${title}\0${song.artist}\0${song.data.subtitle}`);
|
||||
|
||||
song.id = songExists ? songExists.id : getFreshID();
|
||||
|
||||
if (!songExists) {
|
||||
newSongs.push(song);
|
||||
}
|
||||
|
||||
for (const chart of d.charts) {
|
||||
if (existingChartIDs.includes(chart.hashGSv3)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!difficultyTag) {
|
||||
difficultyTag = chart.difficultyTag;
|
||||
}
|
||||
|
||||
const rankedLevel = options.ecsRule ? Number(chart.level) : null;
|
||||
const chartLevel = Number(chart.level);
|
||||
|
||||
// leading space into a number is likely a breakdown. remove it.
|
||||
const author = chart.credit.replace(/ [1-9].*$/u, "");
|
||||
|
||||
let preferredBD = null;
|
||||
|
||||
if (chart.breakdown) {
|
||||
if (chart.breakdown.simplified.length < 16) {
|
||||
preferredBD = chart.breakdown.simplified;
|
||||
} else {
|
||||
preferredBD = `${chart.breakdown.total} Total`;
|
||||
}
|
||||
}
|
||||
|
||||
if (difficultyTag === "Challenge") {
|
||||
difficultyTag = "Expert";
|
||||
}
|
||||
|
||||
newCharts.push({
|
||||
chartID: CreateChartID(),
|
||||
songID: song.id,
|
||||
level: "?",
|
||||
levelNum: 0, // do not use, should b removed
|
||||
isPrimary: true,
|
||||
difficulty: `${author} ${difficultyTag}${preferredBD ? ` ${preferredBD}` : ""}`,
|
||||
playtype: "Stamina",
|
||||
versions: [],
|
||||
data: {
|
||||
rankedLevel,
|
||||
chartLevel,
|
||||
streamBPM: chart.streamBPM,
|
||||
breakdown: chart.breakdown,
|
||||
length: chart.length,
|
||||
difficultyTag,
|
||||
hashGSv3: chart.hashGSv3,
|
||||
notesPerMeasure: chart.notesPerMeasure,
|
||||
npsPerMeasure: chart.npsPerMeasure,
|
||||
originalPack: options.pack,
|
||||
bannerLocationOverride: null,
|
||||
charter: author,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
MutateCollection("charts-itg.json", (charts) => [...charts, ...newCharts]);
|
||||
MutateCollection("songs-itg.json", (songs) => [...songs, ...newSongs]);
|
||||
@@ -1,7 +0,0 @@
|
||||
The .patch file in this directory hijacks a waterfall install to also write a `zkldi.json`
|
||||
file when caching charts. This is the easiest way to parse SM files in 100% certainty that
|
||||
they're treated the way the game treats them, oddities and all.
|
||||
|
||||
It then just leverages simply love helper functions to format up interesting song data
|
||||
and stuff.
|
||||
all good!
|
||||
File diff suppressed because it is too large
Load Diff
@@ -89,7 +89,7 @@ function EfficientInPlaceDeepmerge(ref, apply) {
|
||||
* Not thread safe.
|
||||
*/
|
||||
function GetFreshSongIDGenerator(game) {
|
||||
let maxSongID = ReadCollection(`songs-${game}.json`).sort((a, b) => b.id - a.id)[0].id;
|
||||
let maxSongID = ReadCollection(`songs-${game}.json`).sort((a, b) => b.id - a.id)[0]?.id ?? 0;
|
||||
|
||||
return () => ++maxSongID;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user