feat: barebones itg support + srpg6

This commit is contained in:
zkldi
2023-01-20 20:44:55 +00:00
parent 848b5cf51f
commit 6d5c8a46d1
13 changed files with 229668 additions and 1258 deletions
@@ -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(
+26 -10
View File
@@ -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
+200
View File
@@ -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
+1 -1
View File
@@ -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;
}