mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-08 14:58:12 +03:00
sdvx nabla support (#1599)
* feat: add sdvx nabla support Added VF7 algorithm to sdvx profiles Added nabla folder Extended sdvx seed with new charts and decimal rerates * fix: some comments * refactor: break InsaneCharRebinds out into its own file * fix: removed weird chars on sdvx song seeds * fix: charfix --------- Co-authored-by: zk <zkldi.dev@gmail.com>
This commit is contained in:
+21706
-10817
File diff suppressed because it is too large
Load Diff
+276
-16
@@ -49976,6 +49976,19 @@
|
||||
],
|
||||
"where": "chart.level = '1'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a94105487528",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "F06c91aacb43ee04f91d4177f9d49d29a6dfc2fc1cfeef36b9f3e34f1284ff878",
|
||||
"searchTerms": [],
|
||||
"slug": "1-nabla",
|
||||
"title": "Level 1 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level = '1'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19d35f0cf2ae1f2b212",
|
||||
@@ -50028,6 +50041,19 @@
|
||||
],
|
||||
"where": "chart.level = '10'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a942444b4c56",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "F970863cec6edbbe00b06f52ec90cc460a73e93375f7533b6920ee332caf62bf3",
|
||||
"searchTerms": [],
|
||||
"slug": "10-nabla",
|
||||
"title": "Level 10 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level = '10'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19d35f0cf2a6012f137",
|
||||
@@ -50080,6 +50106,19 @@
|
||||
],
|
||||
"where": "chart.level = '11'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a9425d117c9f",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "F4045668507fd550b4419d7cea09d910dff69344863e76ccaa57d371911993412",
|
||||
"searchTerms": [],
|
||||
"slug": "11-nabla",
|
||||
"title": "Level 11 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level = '11'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19d35f0cf2a79f306a2",
|
||||
@@ -50132,6 +50171,19 @@
|
||||
],
|
||||
"where": "chart.level = '12'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a94235340bef",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "F1e79c7a470afc3951b4f83e4609c55464e70d5f149c584eb613dfaa632f8136b",
|
||||
"searchTerms": [],
|
||||
"slug": "12-nabla",
|
||||
"title": "Level 12 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level = '12'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19d35f0cf2acaf7328c",
|
||||
@@ -50184,6 +50236,19 @@
|
||||
],
|
||||
"where": "chart.level = '13'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a94212d9d5b6",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "F4c37c527e97a10aa71cd49319c54724d95e5659d90ddb794cedc5acd02479f31",
|
||||
"searchTerms": [],
|
||||
"slug": "13-nabla",
|
||||
"title": "Level 13 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level = '13'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19d35f0cf2a8394786a",
|
||||
@@ -50236,6 +50301,19 @@
|
||||
],
|
||||
"where": "chart.level = '14'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a942a93b25dd",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "F5cbddfe1699d57b247a5ab3cd000e7b2b77b5e970fc2bcf5fe7b0d4aeddd8716",
|
||||
"searchTerms": [],
|
||||
"slug": "14-nabla",
|
||||
"title": "Level 14 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level = '14'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19d35f0cf2a25154803",
|
||||
@@ -50288,6 +50366,19 @@
|
||||
],
|
||||
"where": "chart.level = '15'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a942d12d2bcd",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "F9a5bc24a0d80df92beb16a0bd901af37e24a4a82ff934cd210d1fe39580cdfe8",
|
||||
"searchTerms": [],
|
||||
"slug": "15-nabla",
|
||||
"title": "Level 15 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level = '15'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19d35f0cf2a942955fb",
|
||||
@@ -50340,6 +50431,19 @@
|
||||
],
|
||||
"where": "chart.level = '16'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a942225ed289",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "F7cb5d43e93e0a7947433629e21cf0bada5b71468925cfd00fa3a5eb6b63e2764",
|
||||
"searchTerms": [],
|
||||
"slug": "16-nabla",
|
||||
"title": "Level 16 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level = '16'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19d35f0cf2ae339891a",
|
||||
@@ -50364,7 +50468,7 @@
|
||||
"versionFilter": [
|
||||
"exceed"
|
||||
],
|
||||
"where": "chart.level = '17'"
|
||||
"where": "chart.level_num >= 17 AND chart.level_num < 18"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
@@ -50377,7 +50481,7 @@
|
||||
"versionFilter": [
|
||||
"heaven"
|
||||
],
|
||||
"where": "chart.level = '17'"
|
||||
"where": "chart.level_num >= 17 AND chart.level_num < 18"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
@@ -50390,7 +50494,20 @@
|
||||
"versionFilter": [
|
||||
"konaste"
|
||||
],
|
||||
"where": "chart.level = '17'"
|
||||
"where": "chart.level_num >= 17 AND chart.level_num < 18"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a942390eb617",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "Fa7f8a1b2ae78df26c3331df5f540a040bbd86a1db1b05d739ecdfc670aeb9637",
|
||||
"searchTerms": [],
|
||||
"slug": "17-nabla",
|
||||
"title": "Level 17 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level_num >= 17 AND chart.level_num < 18"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
@@ -50403,7 +50520,7 @@
|
||||
"versionFilter": [
|
||||
"vivid"
|
||||
],
|
||||
"where": "chart.level = '17'"
|
||||
"where": "chart.level_num >= 17 AND chart.level_num < 18"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
@@ -50416,7 +50533,7 @@
|
||||
"versionFilter": [
|
||||
"exceed"
|
||||
],
|
||||
"where": "chart.level = '18'"
|
||||
"where": "chart.level_num >= 18 AND chart.level_num < 19"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
@@ -50429,7 +50546,7 @@
|
||||
"versionFilter": [
|
||||
"heaven"
|
||||
],
|
||||
"where": "chart.level = '18'"
|
||||
"where": "chart.level_num >= 18 AND chart.level_num < 19"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
@@ -50442,7 +50559,20 @@
|
||||
"versionFilter": [
|
||||
"konaste"
|
||||
],
|
||||
"where": "chart.level = '18'"
|
||||
"where": "chart.level_num >= 18 AND chart.level_num < 19"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a942f68fcb77",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "F046dcc346fd2040e76fa5c0c3017c5b35efd91a6a8213f0b35c1f192e23d36f9",
|
||||
"searchTerms": [],
|
||||
"slug": "18-nabla",
|
||||
"title": "Level 18 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level_num >= 18 AND chart.level_num < 19"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
@@ -50455,7 +50585,7 @@
|
||||
"versionFilter": [
|
||||
"vivid"
|
||||
],
|
||||
"where": "chart.level = '18'"
|
||||
"where": "chart.level_num >= 18 AND chart.level_num < 19"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
@@ -50468,7 +50598,7 @@
|
||||
"versionFilter": [
|
||||
"exceed"
|
||||
],
|
||||
"where": "chart.level = '19'"
|
||||
"where": "chart.level_num >= 19 AND chart.level_num < 20"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
@@ -50481,7 +50611,7 @@
|
||||
"versionFilter": [
|
||||
"heaven"
|
||||
],
|
||||
"where": "chart.level = '19'"
|
||||
"where": "chart.level_num >= 19 AND chart.level_num < 20"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
@@ -50494,7 +50624,20 @@
|
||||
"versionFilter": [
|
||||
"konaste"
|
||||
],
|
||||
"where": "chart.level = '19'"
|
||||
"where": "chart.level_num >= 19 AND chart.level_num < 20"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a94350f1b2f5",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "F6d7a9b50f1839a5e381eeb9f7b7687db32b7ff85dba48a50993e04332af55b70",
|
||||
"searchTerms": [],
|
||||
"slug": "19-nabla",
|
||||
"title": "Level 19 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level_num >= 19 AND chart.level_num < 20"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
@@ -50507,7 +50650,7 @@
|
||||
"versionFilter": [
|
||||
"vivid"
|
||||
],
|
||||
"where": "chart.level = '19'"
|
||||
"where": "chart.level_num >= 19 AND chart.level_num < 20"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
@@ -50548,6 +50691,19 @@
|
||||
],
|
||||
"where": "chart.level = '2'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a94229f855f6",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "Fc13d7cfa4d3c2e40731a344c92f817ef40b4a24dba9acef269afdcf001d963e9",
|
||||
"searchTerms": [],
|
||||
"slug": "2-nabla",
|
||||
"title": "Level 2 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level = '2'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19d35f0cf2a22330d01",
|
||||
@@ -50572,7 +50728,7 @@
|
||||
"versionFilter": [
|
||||
"exceed"
|
||||
],
|
||||
"where": "chart.level = '20'"
|
||||
"where": "chart.level_num >= 20 AND chart.level_num < 21"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
@@ -50585,7 +50741,7 @@
|
||||
"versionFilter": [
|
||||
"heaven"
|
||||
],
|
||||
"where": "chart.level = '20'"
|
||||
"where": "chart.level_num >= 20 AND chart.level_num < 21"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
@@ -50598,7 +50754,20 @@
|
||||
"versionFilter": [
|
||||
"konaste"
|
||||
],
|
||||
"where": "chart.level = '20'"
|
||||
"where": "chart.level_num >= 20 AND chart.level_num < 21"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a94315059cf3",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "Fc0d1303ba2ab371dc6445463e3fb05000d2bec0b67b9cc677014af06c34ceef3",
|
||||
"searchTerms": [],
|
||||
"slug": "20-nabla",
|
||||
"title": "Level 20 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level_num >= 20 AND chart.level_num < 21"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
@@ -50611,7 +50780,7 @@
|
||||
"versionFilter": [
|
||||
"vivid"
|
||||
],
|
||||
"where": "chart.level = '20'"
|
||||
"where": "chart.level_num >= 20 AND chart.level_num < 21"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
@@ -50652,6 +50821,19 @@
|
||||
],
|
||||
"where": "chart.level = '3'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a942170986fd",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "F85a95f0b1e9d63bbdf3e016262a417f0d2a06250dee0fa504946d166b05ece1e",
|
||||
"searchTerms": [],
|
||||
"slug": "3-nabla",
|
||||
"title": "Level 3 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level = '3'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19d35f0cf2a06fc5680",
|
||||
@@ -50704,6 +50886,19 @@
|
||||
],
|
||||
"where": "chart.level = '4'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a942de718bd5",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "F838aad7f1c94023b53a5db8a4451860f3296ffc6b9f57131c9be18fe7aac0e0e",
|
||||
"searchTerms": [],
|
||||
"slug": "4-nabla",
|
||||
"title": "Level 4 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level = '4'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19d35f0cf2a22e402eb",
|
||||
@@ -50756,6 +50951,19 @@
|
||||
],
|
||||
"where": "chart.level = '5'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a94271aae4a7",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "Fd7dbe2c07e0b9b837269b705b49a36c3da497ab9b9b5a51ecd4427d3d2f89511",
|
||||
"searchTerms": [],
|
||||
"slug": "5-nabla",
|
||||
"title": "Level 5 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level = '5'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19d35f0cf2abaec1504",
|
||||
@@ -50808,6 +51016,19 @@
|
||||
],
|
||||
"where": "chart.level = '6'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a942e0ce038a",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "F3ebcf8d84db180f6ad0ebfe996080e1ff1f57f297668b43d3275757670d4657f",
|
||||
"searchTerms": [],
|
||||
"slug": "6-nabla",
|
||||
"title": "Level 6 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level = '6'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19d35f0cf2a1c419e6a",
|
||||
@@ -50860,6 +51081,19 @@
|
||||
],
|
||||
"where": "chart.level = '7'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a9429db47f49",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "F82e42ab32600c922e03364d9da6ce7fef8c2d16351686231ac71d5b2b4e96cd1",
|
||||
"searchTerms": [],
|
||||
"slug": "7-nabla",
|
||||
"title": "Level 7 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level = '7'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19d35f0cf2a59757ac7",
|
||||
@@ -50912,6 +51146,19 @@
|
||||
],
|
||||
"where": "chart.level = '8'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a942aad71db4",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "Fbd0528ee59b36b3d9cf8e77604e8c4de577e02c8f4c2144b1e30155ee3400b2c",
|
||||
"searchTerms": [],
|
||||
"slug": "8-nabla",
|
||||
"title": "Level 8 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level = '8'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19d35f0cf2ad2b8d882",
|
||||
@@ -50964,6 +51211,19 @@
|
||||
],
|
||||
"where": "chart.level = '9'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19e65f7a94252a65257",
|
||||
"inactive": false,
|
||||
"legacyFolderID": "F7373a1e3b34ab71b72326914721e448441cfad85399f554f951ada07af7e44c3",
|
||||
"searchTerms": [],
|
||||
"slug": "9-nabla",
|
||||
"title": "Level 9 (NABLA)",
|
||||
"versionFilter": [
|
||||
"nabla"
|
||||
],
|
||||
"where": "chart.level = '9'"
|
||||
},
|
||||
{
|
||||
"game": "sdvx",
|
||||
"id": "F19d35f0cf2a6fafd1ff",
|
||||
|
||||
@@ -28796,5 +28796,592 @@
|
||||
"metameta_himawaripunch"
|
||||
],
|
||||
"title": "めた・メタ?ひまわり*パンチ"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "青龍改",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c3636d766c",
|
||||
"legacySongID": 2434,
|
||||
"searchTerms": [
|
||||
"bluediamond_seiryu"
|
||||
],
|
||||
"title": "Blue Diamond"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Hommarju",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c4337999b3",
|
||||
"legacySongID": 2440,
|
||||
"searchTerms": [
|
||||
"blackout_hommarju"
|
||||
],
|
||||
"title": "Black Out"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "苺咲べりぃ",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c4347a4b9e",
|
||||
"legacySongID": 2444,
|
||||
"searchTerms": [
|
||||
"cakecakencake_maisaki"
|
||||
],
|
||||
"title": "CAKE,Cake'n Cake!"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "xi",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c43ddb87d6",
|
||||
"legacySongID": 2438,
|
||||
"searchTerms": [
|
||||
"celestialcounq_xi"
|
||||
],
|
||||
"title": "Celestial Conquest"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "ビートまりお",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c4526b47b7",
|
||||
"legacySongID": 2435,
|
||||
"searchTerms": [
|
||||
"newerinnn_beatmario"
|
||||
],
|
||||
"title": "Help me, ERINNNNNN!!"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "TAKIO feat. 駄々子",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c4778216ef",
|
||||
"legacySongID": 2446,
|
||||
"searchTerms": [
|
||||
"kareinaruichigeki_takio"
|
||||
],
|
||||
"title": "華麗なる一撃"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "天束 vs. siromaru",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c4b3c7dff9",
|
||||
"legacySongID": 2442,
|
||||
"searchTerms": [
|
||||
"zany_amatsukasiromaru"
|
||||
],
|
||||
"title": "Zany Arcadia \"E\""
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "irom",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c4b5b2c8cb",
|
||||
"legacySongID": 2436,
|
||||
"searchTerms": [
|
||||
"deadlineskg_irom"
|
||||
],
|
||||
"title": "デッドライン症候群"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "EYEN(seatrus+kuro)",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c4c23b926d",
|
||||
"legacySongID": 2449,
|
||||
"searchTerms": [
|
||||
"dangerxloze_eyen"
|
||||
],
|
||||
"title": "DANGER XLOZE"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "りりぃろっくfeat.ЄИ1GM4&桃寝ちのい",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c4c31395e7",
|
||||
"legacySongID": 2439,
|
||||
"searchTerms": [
|
||||
"kushinadahimeni_lilyrock"
|
||||
],
|
||||
"title": "稲田姫様に叱られちゃいました# ~ Romantic Shortfall"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Capchii × Uzumaki",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c4c52d4116",
|
||||
"legacySongID": 2448,
|
||||
"searchTerms": [
|
||||
"showdawn_capchii"
|
||||
],
|
||||
"title": "ShowDawn"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "polysha&Ashrount",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c4d48846de",
|
||||
"legacySongID": 2443,
|
||||
"searchTerms": [
|
||||
"nextage_polyshaashrount"
|
||||
],
|
||||
"title": "ИEXTAGE"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "めと(Metomate)",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c4d4db856d",
|
||||
"legacySongID": 2447,
|
||||
"searchTerms": [
|
||||
"venisresonance_meto"
|
||||
],
|
||||
"title": "Veins Resonance"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Riz",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c4d7f4f120",
|
||||
"legacySongID": 2441,
|
||||
"searchTerms": [
|
||||
"massivestrength_riz"
|
||||
],
|
||||
"title": "Massive Strength"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "テヅカ x otoshi.b",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c4e53668db",
|
||||
"legacySongID": 2445,
|
||||
"searchTerms": [
|
||||
"tengeitan_teduka"
|
||||
],
|
||||
"title": "天鯨譚"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "かめりあ",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c4f36a8a18",
|
||||
"legacySongID": 2437,
|
||||
"searchTerms": [
|
||||
"npororoca_kameria"
|
||||
],
|
||||
"title": "ンポロロッカ"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Zekk",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c503b3c8b7",
|
||||
"legacySongID": 2468,
|
||||
"searchTerms": [
|
||||
"9thoutburst_zekk"
|
||||
],
|
||||
"title": "9th Outburst"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "PHQUASE vs. dj TAKA",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c50f3fa655",
|
||||
"legacySongID": 2472,
|
||||
"searchTerms": [
|
||||
"fixer_phquasedjtaka"
|
||||
],
|
||||
"title": "fixer"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "qfeileadh",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c518ca991b",
|
||||
"legacySongID": 2450,
|
||||
"searchTerms": [
|
||||
"xelonophobea_qfeileadh"
|
||||
],
|
||||
"title": "XELENOPHOEBEA"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "cosMo@暴走P feat. 初音ミク",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c51a615844",
|
||||
"legacySongID": 2476,
|
||||
"searchTerms": [
|
||||
"mscrazyspeed_cosmo"
|
||||
],
|
||||
"title": "ミスクレイジースピード"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "原口沙輔",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c5371469fa",
|
||||
"legacySongID": 2454,
|
||||
"searchTerms": [
|
||||
"igaku_haraguchisasuke"
|
||||
],
|
||||
"title": "イガク"
|
||||
},
|
||||
{
|
||||
"altTitles": [
|
||||
"華麗なる!音戯探偵ひなビタ鷸"
|
||||
],
|
||||
"artist": "音戯探偵ひなビタ♫",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c537933992",
|
||||
"legacySongID": 2452,
|
||||
"searchTerms": [
|
||||
"kareinaruhinabita_penoreri"
|
||||
],
|
||||
"title": "華麗なる!音戯探偵ひなビタ♫"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "irucaice feat. 初音ミク",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c5382c1dfd",
|
||||
"legacySongID": 2477,
|
||||
"searchTerms": [
|
||||
"angeltest_irucaice"
|
||||
],
|
||||
"title": "えんじぇる☆てすと!"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Laur",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c53cefab49",
|
||||
"legacySongID": 2475,
|
||||
"searchTerms": [
|
||||
"baphomet_laur"
|
||||
],
|
||||
"title": "Baphomet"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Reku Mochizuki",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c53e900b6f",
|
||||
"legacySongID": 2473,
|
||||
"searchTerms": [
|
||||
"moonlitblue_rekumochizuki"
|
||||
],
|
||||
"title": "Moonlit Blue"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "RoughSketch feat.ななひら",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c545c52283",
|
||||
"legacySongID": 2457,
|
||||
"searchTerms": [
|
||||
"777_roughsketch"
|
||||
],
|
||||
"title": "777 (Vocal ver.)"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "wotaku feat. SHIKI",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c549242e80",
|
||||
"legacySongID": 2455,
|
||||
"searchTerms": [
|
||||
"snooze_wotaku"
|
||||
],
|
||||
"title": "snooze"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "tn-shi",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c5699611b6",
|
||||
"legacySongID": 2456,
|
||||
"searchTerms": [
|
||||
"synthesis_tnshi"
|
||||
],
|
||||
"title": "Synthesis."
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Eagle",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c571157c64",
|
||||
"legacySongID": 2471,
|
||||
"searchTerms": [
|
||||
"glitchnride_eagle"
|
||||
],
|
||||
"title": "Glitch N Ride"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "muyu",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c57a7d4a82",
|
||||
"legacySongID": 2474,
|
||||
"searchTerms": [
|
||||
"signalize_muyu"
|
||||
],
|
||||
"title": "シグナライズ"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "なきそ",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c580ffd353",
|
||||
"legacySongID": 2453,
|
||||
"searchTerms": [
|
||||
"imasugurinne_nakiso"
|
||||
],
|
||||
"title": "いますぐ輪廻"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "吉本おじさん",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c5847ae150",
|
||||
"legacySongID": 2463,
|
||||
"searchTerms": [
|
||||
"ohenjimadakana_ojisan"
|
||||
],
|
||||
"title": "お返事まだカナ?おじさん構文!"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "暁Records",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c58883b77b",
|
||||
"legacySongID": 2461,
|
||||
"searchTerms": [
|
||||
"necomantic_akatsuki"
|
||||
],
|
||||
"title": "Necromantic"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Cream puff",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c589d3089e",
|
||||
"legacySongID": 2470,
|
||||
"searchTerms": [
|
||||
"mermaidgirl_creampuff"
|
||||
],
|
||||
"title": "Mermaid girl"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "森羅万象",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c58f8117d7",
|
||||
"legacySongID": 2460,
|
||||
"searchTerms": [
|
||||
"heartinwonderland_sinrabansyou"
|
||||
],
|
||||
"title": "ハートインワンダーランド"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Juggernaut.",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c5b37fdc14",
|
||||
"legacySongID": 2451,
|
||||
"searchTerms": [
|
||||
"downwithyourlove_juggernaut"
|
||||
],
|
||||
"title": "Down with your Love"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "名取さな",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c5ba191883",
|
||||
"legacySongID": 2464,
|
||||
"searchTerms": [
|
||||
"mondainaitiripper_natorisana"
|
||||
],
|
||||
"title": "モンダイナイトリッパー!"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "iconoclasm",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c5d9e9be8f",
|
||||
"legacySongID": 2466,
|
||||
"searchTerms": [
|
||||
"votumrmx_iconoclasm"
|
||||
],
|
||||
"title": "Votum stellarum -forest #25 RMX-"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "m@sumi vs RoughSketch",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c5dbf7cdc8",
|
||||
"legacySongID": 2465,
|
||||
"searchTerms": [
|
||||
"ztiria_masumiroughsketch"
|
||||
],
|
||||
"title": "Zt!ri△"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "PON",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c5dd4334b7",
|
||||
"legacySongID": 2467,
|
||||
"searchTerms": [
|
||||
"kouen_pon"
|
||||
],
|
||||
"title": "紅焔"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Ice",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c5e34d8c0b",
|
||||
"legacySongID": 2459,
|
||||
"searchTerms": [
|
||||
"l2_ice"
|
||||
],
|
||||
"title": "L2 -Reminiscence- (SDVX ver.)"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Orbit1 & Milo",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c5ec2c92bc",
|
||||
"legacySongID": 2469,
|
||||
"searchTerms": [
|
||||
"antimatter_orbit1milo"
|
||||
],
|
||||
"title": "Anti-Matter"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Capchii feat. 初音ミク",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c5f07f7ce8",
|
||||
"legacySongID": 2478,
|
||||
"searchTerms": [
|
||||
"pickupme_capchii"
|
||||
],
|
||||
"title": "Pick up, Me!"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "Ice",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c5fccbe00b",
|
||||
"legacySongID": 2458,
|
||||
"searchTerms": [
|
||||
"l_ice"
|
||||
],
|
||||
"title": "L (SDVX ver.)"
|
||||
},
|
||||
{
|
||||
"altTitles": [],
|
||||
"artist": "MIMI",
|
||||
"data": {
|
||||
"displayVersion": "nabla"
|
||||
},
|
||||
"id": "S19e706756c5fea133b5",
|
||||
"legacySongID": 2462,
|
||||
"searchTerms": [
|
||||
"science_mimi"
|
||||
],
|
||||
"title": "サイエンス"
|
||||
}
|
||||
]
|
||||
+32
-1
@@ -6649,7 +6649,7 @@
|
||||
"title": "Pop'n Music Unilab Levels"
|
||||
},
|
||||
{
|
||||
"default": true,
|
||||
"default": false,
|
||||
"description": "Levels for SOUND VOLTEX in EXCEED GEAR.",
|
||||
"folders": [
|
||||
"1-exceed",
|
||||
@@ -6741,6 +6741,37 @@
|
||||
"legacyTableID": "sdvx-Single-konaste-levels",
|
||||
"title": "SOUND VOLTEX (Konaste)"
|
||||
},
|
||||
{
|
||||
"default": true,
|
||||
"description": "Levels for SOUND VOLTEX in NABLA.",
|
||||
"folders": [
|
||||
"1-nabla",
|
||||
"2-nabla",
|
||||
"3-nabla",
|
||||
"4-nabla",
|
||||
"5-nabla",
|
||||
"6-nabla",
|
||||
"7-nabla",
|
||||
"8-nabla",
|
||||
"9-nabla",
|
||||
"10-nabla",
|
||||
"11-nabla",
|
||||
"12-nabla",
|
||||
"13-nabla",
|
||||
"14-nabla",
|
||||
"15-nabla",
|
||||
"16-nabla",
|
||||
"17-nabla",
|
||||
"18-nabla",
|
||||
"19-nabla",
|
||||
"20-nabla"
|
||||
],
|
||||
"game": "sdvx",
|
||||
"id": "T19e65f7d66c54197d1a",
|
||||
"inactive": false,
|
||||
"legacyTableID": "sdvx-Single-nabla-levels",
|
||||
"title": "SOUND VOLTEX (NABLA)"
|
||||
},
|
||||
{
|
||||
"default": false,
|
||||
"description": "Levels for SOUND VOLTEX in VIVID WAVE.",
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
import useLUGPTSettings from "#components/util/useLUGPTSettings";
|
||||
import { UserContext } from "#context/UserContext";
|
||||
import { IsNullish } from "#util/misc";
|
||||
import React, { useContext } from "react";
|
||||
import { Volforce } from "rg-stats";
|
||||
import {
|
||||
type ChartDocument,
|
||||
GetSpecificGameConfig,
|
||||
type PBScoreDocument,
|
||||
type ScoreDocument,
|
||||
} from "tachi-common";
|
||||
|
||||
type VF7Game = "sdvx" | "usc-controller" | "usc-keyboard";
|
||||
|
||||
const SHORT_LAMPS = {
|
||||
CLEAR: "CLR",
|
||||
"EXCESSIVE CLEAR": "EXC",
|
||||
"MAXXIVE CLEAR": "MXV",
|
||||
"ULTIMATE CHAIN": "UC",
|
||||
} as const;
|
||||
|
||||
export default function VF7Cell({
|
||||
score,
|
||||
chart,
|
||||
}: {
|
||||
chart: ChartDocument<VF7Game>;
|
||||
score: PBScoreDocument<VF7Game> | ScoreDocument<VF7Game>;
|
||||
}) {
|
||||
const { user } = useContext(UserContext);
|
||||
const { settings } = useLUGPTSettings<VF7Game>();
|
||||
|
||||
if (IsNullish(score.calculatedData.VF7)) {
|
||||
return <td>N/A</td>;
|
||||
}
|
||||
|
||||
const vf6Target = settings?.preferences.gameSpecific.vf6Target;
|
||||
|
||||
const game = score.game as VF7Game;
|
||||
const gameConfig = GetSpecificGameConfig(game);
|
||||
|
||||
const targets: Record<string, number | null> = {};
|
||||
|
||||
if (vf6Target && score.userID === user?.id) {
|
||||
for (const lamp of ["CLEAR", "EXCESSIVE CLEAR", "ULTIMATE CHAIN"] as const) {
|
||||
if (
|
||||
score.scoreData.enumIndexes.lamp <=
|
||||
gameConfig.providedMetrics.lamp.values.indexOf(lamp)
|
||||
) {
|
||||
const expectedScore = InverseVF7(vf6Target, lamp, chart.levelNum);
|
||||
|
||||
if (expectedScore === null) {
|
||||
targets[SHORT_LAMPS[lamp]] = null;
|
||||
} else {
|
||||
targets[SHORT_LAMPS[lamp]] = expectedScore;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const maxVF = Volforce.calculateVF7(10_000_000, "PERFECT ULTIMATE CHAIN", chart.levelNum);
|
||||
|
||||
return (
|
||||
<td>
|
||||
<strong className="underline-on-hover">{score.calculatedData.VF7}</strong>
|
||||
|
||||
{vf6Target !== 0 && vf6Target && user?.id === score.userID && (
|
||||
<>
|
||||
<br />
|
||||
|
||||
<div>
|
||||
{score.calculatedData.VF6! >= vf6Target ? (
|
||||
<small className="text-success">{vf6Target}VF Target Achieved!</small>
|
||||
) : vf6Target > maxVF ? (
|
||||
<small className="text-body-secondary">
|
||||
{vf6Target}VF Not Possible (Max {maxVF})
|
||||
</small>
|
||||
) : (
|
||||
Object.entries(targets).map(([k, v], i) => (
|
||||
<React.Fragment key={k}>
|
||||
<VF7TargetCell
|
||||
clearType={k}
|
||||
showClearType={i !== 0 || score.scoreData.lamp === "FAILED"}
|
||||
targetDelta={v === null ? null : v - score.scoreData.score}
|
||||
targetScore={v}
|
||||
vf6Target={vf6Target!}
|
||||
/>
|
||||
<br />
|
||||
</React.Fragment>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
function InverseVF7(
|
||||
vf7: number,
|
||||
lamp: "CLEAR" | "EXCESSIVE CLEAR" | "MAXXIVE CLEAR" | "ULTIMATE CHAIN",
|
||||
level: number,
|
||||
) {
|
||||
try {
|
||||
return Volforce.inverseVF7(vf7, lamp, level);
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function VF7TargetCell({
|
||||
targetDelta,
|
||||
targetScore,
|
||||
vf6Target,
|
||||
clearType,
|
||||
showClearType,
|
||||
}: {
|
||||
clearType: string;
|
||||
showClearType: boolean;
|
||||
targetDelta: number | null;
|
||||
targetScore: number | null;
|
||||
vf6Target: number;
|
||||
}) {
|
||||
if (targetDelta === null || targetScore === null) {
|
||||
return (
|
||||
<small className="text-body-secondary">
|
||||
{vf6Target}VF w/ {clearType}: Not Possible
|
||||
</small>
|
||||
);
|
||||
}
|
||||
|
||||
if (targetDelta < 0) {
|
||||
return (
|
||||
<small className="text-warning">
|
||||
Score is {vf6Target}VF w/ {clearType}
|
||||
</small>
|
||||
);
|
||||
}
|
||||
|
||||
const div = targetDelta / 1_000_000;
|
||||
|
||||
const fmt = `${div.toFixed(3)}m`;
|
||||
|
||||
return (
|
||||
<small className="text-danger">
|
||||
{vf6Target}VF{showClearType ? ` w/ ${clearType}` : ""}: +{fmt} (
|
||||
{(targetScore / 1_000_000).toFixed(3)}
|
||||
m)
|
||||
</small>
|
||||
);
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import MillionsScoreCell from "#components/tables/cells/MillionsScoreCell";
|
||||
import SDVXJudgementCell from "#components/tables/cells/SDVXJudgementCell";
|
||||
import SDVXLampCell from "#components/tables/cells/SDVXLampCell";
|
||||
import VF6Cell from "#components/tables/cells/VF6Cell";
|
||||
import VF7Cell from "#components/tables/cells/VF7Cell";
|
||||
import { GetEnumColour } from "#lib/game-implementations";
|
||||
import { type GPTClientImplementation } from "#lib/types";
|
||||
import { ChangeOpacity } from "#util/color-opacity";
|
||||
@@ -72,9 +73,8 @@ const SDVXCoreCells: GPTClientImplementation<"sdvx">["scoreCoreCells"] = ({ sc }
|
||||
</>
|
||||
);
|
||||
|
||||
const SDVXRatingCell: GPTClientImplementation<SDVXLikes>["ratingCell"] = ({ sc, chart }) => (
|
||||
<VF6Cell chart={chart} score={sc} />
|
||||
);
|
||||
const SDVXRatingCell: GPTClientImplementation<SDVXLikes>["ratingCell"] = ({ sc, chart, rating }) =>
|
||||
rating === "VF7" ? <VF7Cell chart={chart} score={sc} /> : <VF6Cell chart={chart} score={sc} />;
|
||||
|
||||
export const SDVX_IMPL: GPTClientImplementation<"sdvx"> = {
|
||||
sessionImportantScoreCount: 50,
|
||||
@@ -92,6 +92,7 @@ export const SDVX_IMPL: GPTClientImplementation<"sdvx"> = {
|
||||
HVN: COLOUR_SET.teal,
|
||||
VVD: COLOUR_SET.pink,
|
||||
XCD: COLOUR_SET.blue,
|
||||
NBL: COLOUR_SET.paleGreen,
|
||||
MXM: COLOUR_SET.white,
|
||||
ULT: COLOUR_SET.vibrantOrange,
|
||||
},
|
||||
|
||||
@@ -137,6 +137,10 @@ export const GAME_SDVX_CONF = {
|
||||
description: "VOLFORCE as it is implemented in SDVX6.",
|
||||
formatter: ToDecimalPlaces(3),
|
||||
},
|
||||
VF7: {
|
||||
description: "VOLFORCE as it is implemented in SDVX7.",
|
||||
formatter: ToDecimalPlaces(3),
|
||||
},
|
||||
},
|
||||
sessionRatingAlgs: {
|
||||
ProfileVF6: {
|
||||
@@ -144,6 +148,11 @@ export const GAME_SDVX_CONF = {
|
||||
"The average of your best 10 VF6s this session, multiplied to be on the same scale as profile VOLFORCE.",
|
||||
formatter: ToDecimalPlaces(3),
|
||||
},
|
||||
ProfileVF7: {
|
||||
description:
|
||||
"The average of your best 10 VF7s this session, multiplied to be on the same scale as profile VOLFORCE.",
|
||||
formatter: ToDecimalPlaces(3),
|
||||
},
|
||||
},
|
||||
profileRatingAlgs: {
|
||||
VF6: {
|
||||
@@ -151,15 +160,20 @@ export const GAME_SDVX_CONF = {
|
||||
formatter: ToDecimalPlaces(3),
|
||||
associatedScoreAlgs: ["VF6"],
|
||||
},
|
||||
VF7: {
|
||||
description: "Your best 50 VF7 values added together.",
|
||||
formatter: ToDecimalPlaces(3),
|
||||
associatedScoreAlgs: ["VF7"],
|
||||
},
|
||||
},
|
||||
|
||||
defaultScoreRatingAlg: "VF6",
|
||||
defaultSessionRatingAlg: "ProfileVF6",
|
||||
defaultProfileRatingAlg: "VF6",
|
||||
defaultScoreRatingAlg: "VF7",
|
||||
defaultSessionRatingAlg: "ProfileVF7",
|
||||
defaultProfileRatingAlg: "VF7",
|
||||
|
||||
difficulties: {
|
||||
type: "FIXED",
|
||||
order: ["NOV", "ADV", "EXH", "INF", "GRV", "HVN", "VVD", "XCD", "MXM", "ULT"],
|
||||
order: ["NOV", "ADV", "EXH", "INF", "GRV", "HVN", "VVD", "XCD", "NBL", "MXM", "ULT"],
|
||||
formatShort: {}, // they're all already short enough
|
||||
formatLong: {},
|
||||
default: "EXH",
|
||||
@@ -186,6 +200,7 @@ export const GAME_SDVX_CONF = {
|
||||
heaven: "HEAVENLY HAVEN",
|
||||
vivid: "VIVID WAVE",
|
||||
exceed: "EXCEED GEAR",
|
||||
nabla: "NABLA",
|
||||
konaste: "Konaste",
|
||||
},
|
||||
|
||||
|
||||
@@ -9,3 +9,9 @@ exports[`VF4 Validation Tests > Should throw if score is greater than 10 million
|
||||
exports[`VF4 Validation Tests > Should throw if score is negative. 1`] = `"Invalid input, Score cannot be negative. score=-1."`;
|
||||
|
||||
exports[`VF4 Validation Tests > Should throw if the VF4 requested as an inversion is impossible. 1`] = `"Invalid input, A VF4 of 9000 is not possible on a chart with level 20. vf4=9000, level=20."`;
|
||||
|
||||
exports[`VF7 Validation Tests > Should throw if score is greater than 10 million. 1`] = `"Invalid input, Score cannot be greater than 10million. score=10000001."`;
|
||||
|
||||
exports[`VF7 Validation Tests > Should throw if score is negative. 1`] = `"Invalid input, Score cannot be negative. score=-1."`;
|
||||
|
||||
exports[`VF7 Validation Tests > Should throw if the volforce is impossible to achieve with this lamp/level. 1`] = `"Invalid input, A VF7 of 900 is not possible on a chart with level 1. vf7=900, level=1."`;
|
||||
|
||||
@@ -5,9 +5,11 @@ import {
|
||||
calculateVF4,
|
||||
calculateVF5,
|
||||
calculateVF6,
|
||||
calculateVF7,
|
||||
inverseVF4,
|
||||
inverseVF5,
|
||||
inverseVF6,
|
||||
inverseVF7,
|
||||
SDVXLamps,
|
||||
} from "./volforce";
|
||||
|
||||
@@ -818,3 +820,107 @@ test("InverseVF6 Tests", () => {
|
||||
"Should throw if the volforce is impossible to achieve with this lamp/level.",
|
||||
);
|
||||
});
|
||||
|
||||
test("VF7 Tests", () => {
|
||||
function VF7TestCase(
|
||||
level: number,
|
||||
score: number,
|
||||
lamp: SDVXLamps,
|
||||
expectedVF7: number,
|
||||
): TestCase {
|
||||
return () => {
|
||||
const vf7 = calculateVF7(score, lamp, level);
|
||||
|
||||
expect(
|
||||
vf7,
|
||||
`${score} on a level ${level} chart with lamp ${lamp} should be worth ${expectedVF7} VF7. (Got ${vf7})`,
|
||||
).toBe(expectedVF7);
|
||||
};
|
||||
}
|
||||
|
||||
const VF5ExampleData = [
|
||||
// these should be exactly the same as vf6 as long as integer levels
|
||||
// and no UCs are used
|
||||
VF7TestCase(20, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.462),
|
||||
VF7TestCase(19, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.438),
|
||||
VF7TestCase(18, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.415),
|
||||
VF7TestCase(17, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.392),
|
||||
VF7TestCase(16, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.369),
|
||||
VF7TestCase(15, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.346),
|
||||
VF7TestCase(14, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.323),
|
||||
VF7TestCase(13, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.3),
|
||||
VF7TestCase(12, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.277),
|
||||
VF7TestCase(11, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.254),
|
||||
VF7TestCase(10, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.231),
|
||||
VF7TestCase(9, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.207),
|
||||
VF7TestCase(8, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.184),
|
||||
VF7TestCase(7, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.161),
|
||||
VF7TestCase(6, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.138),
|
||||
VF7TestCase(5, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.115),
|
||||
VF7TestCase(4, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.092),
|
||||
VF7TestCase(3, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.069),
|
||||
VF7TestCase(2, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.046),
|
||||
VF7TestCase(1, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.023),
|
||||
VF7TestCase(19, 9_500_000, "EXCESSIVE CLEAR", 0.357),
|
||||
VF7TestCase(18, 9_923_042, "MAXXIVE CLEAR", 0.39),
|
||||
VF7TestCase(19, 9_500_000, "CLEAR", 0.35),
|
||||
VF7TestCase(19, 9_500_000, "FAILED", 0.175),
|
||||
VF7TestCase(20, 5_000_000, "FAILED", 0.08),
|
||||
VF7TestCase(20, 7_000_000, "FAILED", 0.114),
|
||||
VF7TestCase(20, 8_000_000, "FAILED", 0.136),
|
||||
VF7TestCase(20, 8_700_000, "FAILED", 0.153),
|
||||
|
||||
// decimals and UCs
|
||||
VF7TestCase(19.8, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.457),
|
||||
VF7TestCase(18.5, 9_700_000, "ULTIMATE CHAIN", 0.38),
|
||||
VF7TestCase(19.3, 9_500_000, "MAXXIVE CLEAR", 0.369),
|
||||
VF7TestCase(17.5, 9_700_000, "EXCESSIVE CLEAR", 0.346),
|
||||
VF7TestCase(19.6, 9_900_000, "PERFECT ULTIMATE CHAIN", 0.448),
|
||||
VF7TestCase(18.7, 9_700_000, "ULTIMATE CHAIN", 0.384),
|
||||
VF7TestCase(19.6, 8_000_000, "FAILED", 0.133),
|
||||
];
|
||||
|
||||
for (const testCase of VF5ExampleData) {
|
||||
testCase();
|
||||
}
|
||||
});
|
||||
|
||||
test("InverseVF7 Tests", () => {
|
||||
function InvVF7TestCase(
|
||||
level: number,
|
||||
vf7: number,
|
||||
lamp: Exclude<SDVXLamps, "PERFECT ULTIMATE CHAIN">,
|
||||
expectedScore: number,
|
||||
): TestCase {
|
||||
return () => {
|
||||
const score = inverseVF7(vf7, lamp, level);
|
||||
|
||||
expect(
|
||||
score,
|
||||
`${vf7} VF7 on a level ${level} chart with lamp ${lamp} should be inverted to ${expectedScore}. (Got ${score})`,
|
||||
).toBe(expectedScore);
|
||||
};
|
||||
}
|
||||
|
||||
const testCases = [
|
||||
InvVF7TestCase(20, 0.413, "CLEAR", 9_900_000),
|
||||
InvVF7TestCase(20, 0.232, "MAXXIVE CLEAR", 6_971_154),
|
||||
InvVF7TestCase(20, 0.421, "EXCESSIVE CLEAR", 9_900_000),
|
||||
InvVF7TestCase(13, 0.121, "CLEAR", 5_817_308),
|
||||
InvVF7TestCase(13, 0.121, "FAILED", 9_595_559),
|
||||
|
||||
InvVF7TestCase(17.5, 0.371, "EXCESSIVE CLEAR", 9_900_000),
|
||||
InvVF7TestCase(19.8, 0.384, "CLEAR", 9_700_000),
|
||||
InvVF7TestCase(18.5, 0.354, "MAXXIVE CLEAR", 9_500_000),
|
||||
InvVF7TestCase(17.5, 0.328, "EXCESSIVE CLEAR", 9_500_000),
|
||||
InvVF7TestCase(19.8, 0.39, "MAXXIVE CLEAR", 9_700_000),
|
||||
InvVF7TestCase(18.7, 0.36, "CLEAR", 9_700_000),
|
||||
InvVF7TestCase(17.5, 0.328, "EXCESSIVE CLEAR", 9_500_000),
|
||||
InvVF7TestCase(19.8, 0.39, "MAXXIVE CLEAR", 9_700_000),
|
||||
InvVF7TestCase(18.7, 0.36, "CLEAR", 9_700_000),
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
testCase();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -48,6 +48,12 @@ const VF5LampCoefficients: Record<SDVXLamps, number> = {
|
||||
FAILED: 50,
|
||||
};
|
||||
|
||||
// NABLA buffed UC from 105 to 106, everything else matches VF6
|
||||
const VF7LampCoefficients: Record<SDVXLamps, number> = {
|
||||
...VF5LampCoefficients,
|
||||
"ULTIMATE CHAIN": 106,
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate VOLFORCE as it's defined in SDVX4.
|
||||
*
|
||||
@@ -134,9 +140,13 @@ export function inverseVF5(
|
||||
* @param score - The user's score. Between 0 and 10million.
|
||||
* @param level - The level of the chart. Between 0 and 20, but this is not enforced.
|
||||
*/
|
||||
export function calculateVF6(score: integer, lamp: SDVXLamps, level: integer) {
|
||||
export function calculateVF6(score: integer, lamp: SDVXLamps, level: number) {
|
||||
AssertProvidedScore(score);
|
||||
|
||||
// levels can have decimals starting at nabla,
|
||||
// flooring it to get vf6 algorithm
|
||||
level = Math.floor(level);
|
||||
|
||||
const unroundedVF5 = CalculateWholeVF5(score, lamp, level);
|
||||
|
||||
// VF6 is just unroundedVF5 to 3 decimal places instead of 2.
|
||||
@@ -176,6 +186,53 @@ export function inverseVF6(
|
||||
return score!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate VOLFORCE as it's defined in SDVX7.
|
||||
*
|
||||
* @param score - The user's score. Between 0 and 10million.
|
||||
* @param level - The level of the chart. Between 0 and 20, but this is not enforced.
|
||||
*/
|
||||
export function calculateVF7(score: integer, lamp: SDVXLamps, level: number) {
|
||||
AssertProvidedScore(score);
|
||||
|
||||
const vf7 = CalculateWholeVF7(score, lamp, level);
|
||||
|
||||
// just like VF6, VF7 is unroundedVF5 rounded to 3 decimal places instead of 2.
|
||||
return Math.floor(vf7 / 1000) / 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a VF7 value and a chart level, return what score is needed to get that
|
||||
* VF5.
|
||||
*
|
||||
* If the score needed is greater than 10million, this function will throw.
|
||||
**
|
||||
* @param vf7 - The VF7 to invert.
|
||||
* @param lamp - The lamp for this score. This is necessary to know, as lampCoefficient
|
||||
* plays a part in VF6. Passing "PERFECT ULTIMATE CHAIN" as a lamp is invalid, as inverting
|
||||
* it into a score makes no sense.
|
||||
* @param level - The level of the chart you're inverting for.
|
||||
*/
|
||||
export function inverseVF7(
|
||||
vf7: number,
|
||||
// Exclude PUC as input. It doesn't make sense as input, since the answer would
|
||||
// always be 10million.
|
||||
lamp: Exclude<SDVXLamps, "PERFECT ULTIMATE CHAIN">,
|
||||
level: integer,
|
||||
) {
|
||||
// note: this function is actually identical to inverseVF5, but with the caveat
|
||||
// that the error message is different.
|
||||
|
||||
const score = InvertUnroundedVF7(vf7, lamp, level);
|
||||
|
||||
ThrowIf(score === null, `A VF7 of ${vf7} is not possible on a chart with level ${level}.`, {
|
||||
vf7,
|
||||
level,
|
||||
});
|
||||
// guaranteed to not be null
|
||||
return score!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate VF5 without performing any rounding. This is useful because VF5
|
||||
* is floored to 2 decimal places, wherease VF6 is floored to 3. This lets us
|
||||
@@ -190,6 +247,19 @@ function CalculateWholeVF5(score: integer, lamp: SDVXLamps, level: integer) {
|
||||
return level * 2 * (score / 10_000_000) * gradeCoefficient * lampCoefficient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate VF7 without performing any rounding. This is the same as VF6
|
||||
* with the exception that the level can be a number and UCs have been buffed.
|
||||
*/
|
||||
function CalculateWholeVF7(score: integer, lamp: SDVXLamps, level: number) {
|
||||
const grade = SDVXScoreToGrade(score);
|
||||
|
||||
const gradeCoefficient = VF5GradeCoefficients[grade];
|
||||
const lampCoefficient = VF7LampCoefficients[lamp];
|
||||
|
||||
return level * 2 * (score / 10_000_000) * gradeCoefficient * lampCoefficient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to invert VF5 into a score.
|
||||
*
|
||||
@@ -214,6 +284,30 @@ function InvertUnroundedVF5(vf5: number, lamp: SDVXLamps, level: integer) {
|
||||
return score;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to invert VF7 into a score.
|
||||
*
|
||||
* @returns The score if it was possible to be achieved. Else, it returns null.
|
||||
*/
|
||||
function InvertUnroundedVF7(vf7: number, lamp: SDVXLamps, level: number) {
|
||||
// Note: PERFECT ULTIMATE CHAIN is never passed into this function from typescript
|
||||
// as the calling functions Exclude<T> it from the lamps.
|
||||
// However, a JS caller may call it like this anyway, so we mayaswell throw.
|
||||
ThrowIf(
|
||||
lamp === "PERFECT ULTIMATE CHAIN",
|
||||
"PERFECT ULTIMATE CHAIN as a lampCoefficient does not make sense for an inversion, since the answer would always be 10million.",
|
||||
{ lamp },
|
||||
);
|
||||
|
||||
const lampCoefficient = VF7LampCoefficients[lamp];
|
||||
|
||||
const scoreTimesGradeCoef = (1_000_000 * 10_000_000 * vf7) / (2 * level * lampCoefficient);
|
||||
|
||||
const score = AttemptGradeCoefficientDivide(scoreTimesGradeCoef, VF5GradeCoefficients);
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a SDVX percent to the grade it represents.
|
||||
* @param score - The score to convert - between 0 and 10million.
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
import { Command } from "commander";
|
||||
import { XMLParser } from "fast-xml-parser";
|
||||
import fs from "fs";
|
||||
import { decode } from "iconv-lite";
|
||||
import { CreateChartID, CreateSongID } from "tachi-common";
|
||||
|
||||
import { ReadCollection, WriteCollection } from "../../util.js";
|
||||
import { InsaneCharRebinds } from "./chars.js";
|
||||
|
||||
const program = new Command();
|
||||
program.requiredOption("-i, --input <music_db.xml>");
|
||||
program.parse(process.argv);
|
||||
const options = program.opts();
|
||||
|
||||
const VERSION = "nabla";
|
||||
|
||||
type XMLText<T> = { "#text": T };
|
||||
|
||||
type MDBChart = { difnum: XMLText<number> };
|
||||
|
||||
type MDBEntry = {
|
||||
info: {
|
||||
artist_name: string;
|
||||
ascii: string;
|
||||
title_name: string;
|
||||
title_yomigana: string;
|
||||
version: XMLText<number>;
|
||||
inf_ver?: XMLText<number>;
|
||||
};
|
||||
difficulty: Partial<{
|
||||
novice: MDBChart;
|
||||
advanced: MDBChart;
|
||||
exhaust: MDBChart;
|
||||
infinite: MDBChart;
|
||||
maximum: MDBChart;
|
||||
ultimate: MDBChart;
|
||||
}>;
|
||||
"@_id": string;
|
||||
};
|
||||
|
||||
type SeedSong = {
|
||||
altTitles: string[];
|
||||
artist: string;
|
||||
data: { displayVersion: string };
|
||||
id: string;
|
||||
legacySongID: number;
|
||||
searchTerms: string[];
|
||||
title: string;
|
||||
};
|
||||
|
||||
type SeedChart = {
|
||||
data: { inGameID: number; clearTier?: unknown; pucTier?: unknown; sTier?: unknown };
|
||||
difficulty: string;
|
||||
id: string;
|
||||
isPrimary: boolean;
|
||||
legacyChartID: string;
|
||||
level: string;
|
||||
levelNum: number;
|
||||
songID: string;
|
||||
versions: string[];
|
||||
};
|
||||
|
||||
const blacklist = new Set([1259, 1491, 1438, 1490]);
|
||||
|
||||
function fixString(s: string): string {
|
||||
return s
|
||||
.split("")
|
||||
.map((c) => InsaneCharRebinds[c] ?? c)
|
||||
.join("");
|
||||
}
|
||||
|
||||
function randomHex(bytes: number): string {
|
||||
const buf = new Uint8Array(bytes);
|
||||
globalThis.crypto.getRandomValues(buf);
|
||||
return Array.from(buf, (b) => b.toString(16).padStart(2, "0")).join("");
|
||||
}
|
||||
|
||||
function convertVersion(input: number): string {
|
||||
const table: Record<number, string> = {
|
||||
1: "booth",
|
||||
2: "inf",
|
||||
3: "gw",
|
||||
4: "heaven",
|
||||
5: "vivid",
|
||||
6: "exceed",
|
||||
7: "nabla",
|
||||
};
|
||||
const v = table[input];
|
||||
if (!v) throw new Error(`Unknown version field ${input} in mdb.`);
|
||||
return v;
|
||||
}
|
||||
|
||||
function convertDiff(diff: keyof MDBEntry["difficulty"], infVer: number): string {
|
||||
switch (diff) {
|
||||
case "novice":
|
||||
return "NOV";
|
||||
case "advanced":
|
||||
return "ADV";
|
||||
case "exhaust":
|
||||
return "EXH";
|
||||
case "maximum":
|
||||
return "MXM";
|
||||
case "ultimate":
|
||||
return "ULT";
|
||||
case "infinite":
|
||||
switch (infVer) {
|
||||
case 2:
|
||||
return "INF";
|
||||
case 3:
|
||||
return "GRV";
|
||||
case 4:
|
||||
return "HVN";
|
||||
case 5:
|
||||
return "VVD";
|
||||
case 6:
|
||||
return "XCD";
|
||||
case 7:
|
||||
return "NBL";
|
||||
default:
|
||||
throw new Error(`Unknown inf_ver ${infVer}.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const parser = new XMLParser({ ignoreAttributes: false });
|
||||
const utf8 = decode(fs.readFileSync(options.input), "shift-jis");
|
||||
const xml = parser.parse(utf8) as { mdb: { music: MDBEntry[] } };
|
||||
|
||||
const songs: SeedSong[] = ReadCollection("songs-sdvx.json");
|
||||
const charts: SeedChart[] = ReadCollection("charts-sdvx.json");
|
||||
|
||||
const inGameIDToSongID = new Map<number, string>();
|
||||
const chartByKey = new Map<string, SeedChart>();
|
||||
for (const c of charts) {
|
||||
inGameIDToSongID.set(c.data.inGameID, c.songID);
|
||||
chartByKey.set(`${c.data.inGameID}-${c.difficulty}`, c);
|
||||
}
|
||||
|
||||
let nextLegacySongID = songs.reduce((m, s) => Math.max(m, s.legacySongID), 0) + 1;
|
||||
|
||||
let newSongCount = 0;
|
||||
let newChartCount = 0;
|
||||
let versionTagCount = 0;
|
||||
let levelRefreshCount = 0;
|
||||
|
||||
for (const entry of xml.mdb.music) {
|
||||
const inGameID = Number(entry["@_id"]);
|
||||
if (blacklist.has(inGameID)) continue;
|
||||
|
||||
let songID = inGameIDToSongID.get(inGameID);
|
||||
if (songID === undefined) {
|
||||
const fixedTitle = fixString(entry.info.title_name);
|
||||
const altTitles = fixedTitle !== entry.info.title_name ? [entry.info.title_name] : [];
|
||||
|
||||
const song: SeedSong = {
|
||||
altTitles,
|
||||
artist: fixString(entry.info.artist_name),
|
||||
data: { displayVersion: convertVersion(entry.info.version["#text"]) },
|
||||
id: CreateSongID(),
|
||||
legacySongID: nextLegacySongID++,
|
||||
searchTerms: [entry.info.ascii],
|
||||
title: fixedTitle,
|
||||
};
|
||||
songs.push(song);
|
||||
inGameIDToSongID.set(inGameID, song.id);
|
||||
songID = song.id;
|
||||
newSongCount++;
|
||||
}
|
||||
|
||||
for (const diff of [
|
||||
"novice",
|
||||
"advanced",
|
||||
"exhaust",
|
||||
"infinite",
|
||||
"maximum",
|
||||
"ultimate",
|
||||
] as const) {
|
||||
const md = entry.difficulty[diff];
|
||||
if (!md) continue;
|
||||
|
||||
const infVer = entry.info.inf_ver?.["#text"];
|
||||
if (diff === "infinite" && infVer === 0) continue;
|
||||
if (md.difnum["#text"] === 0) continue;
|
||||
|
||||
const difficulty = convertDiff(diff, infVer ?? 2);
|
||||
|
||||
const levelNum = md.difnum["#text"] / 10;
|
||||
const level = Number.isInteger(levelNum) ? levelNum.toString() : levelNum.toFixed(1);
|
||||
|
||||
const key = `${inGameID}-${difficulty}`;
|
||||
const existing = chartByKey.get(key);
|
||||
if (existing) {
|
||||
if (!existing.versions.includes(VERSION)) {
|
||||
existing.versions.push(VERSION);
|
||||
versionTagCount++;
|
||||
}
|
||||
if (existing.levelNum !== levelNum) {
|
||||
existing.level = level;
|
||||
existing.levelNum = levelNum;
|
||||
levelRefreshCount++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const chart: SeedChart = {
|
||||
data: { inGameID },
|
||||
difficulty,
|
||||
id: CreateChartID(),
|
||||
isPrimary: true,
|
||||
legacyChartID: randomHex(20),
|
||||
level,
|
||||
levelNum,
|
||||
songID,
|
||||
versions: [VERSION],
|
||||
};
|
||||
charts.push(chart);
|
||||
chartByKey.set(key, chart);
|
||||
newChartCount++;
|
||||
}
|
||||
}
|
||||
|
||||
WriteCollection("songs-sdvx.json", songs);
|
||||
WriteCollection("charts-sdvx.json", charts);
|
||||
|
||||
console.log(`new songs: ${newSongCount}`);
|
||||
console.log(`new charts: ${newChartCount}`);
|
||||
console.log(`version tag added: ${versionTagCount}`);
|
||||
console.log(`level refreshed: ${levelRefreshCount}`);
|
||||
@@ -0,0 +1,56 @@
|
||||
// these chars are nonsensically remapped in the xml
|
||||
// go figure
|
||||
export const InsaneCharRebinds = {
|
||||
齶: "♡",
|
||||
齲: "♥",
|
||||
齷: "é",
|
||||
齪: "♣",
|
||||
曦: "à",
|
||||
曩: "è",
|
||||
霻: "♠",
|
||||
隯: "©",
|
||||
鑈: "♦",
|
||||
詹: "Ú",
|
||||
罇: "ê",
|
||||
彜: "ū",
|
||||
鬯: "ī",
|
||||
瑟: "ō",
|
||||
驩: "Ø",
|
||||
驫: "ā",
|
||||
骭: "ü",
|
||||
黷: "ē",
|
||||
騫: "á",
|
||||
躔: "🐾",
|
||||
蹙: "ℱ",
|
||||
趁: "Ǣ",
|
||||
壘: "É",
|
||||
黻: "*",
|
||||
隰: "℗",
|
||||
雋: "Ǜ",
|
||||
鬻: "♃",
|
||||
鬥: "Ã",
|
||||
鬆: "Ý",
|
||||
鬮: "¡",
|
||||
隍: "Ü",
|
||||
頽: "ä",
|
||||
龕: "€",
|
||||
蹇: "₂",
|
||||
釁: "🍄",
|
||||
鑷: "ゔ",
|
||||
盥: "⚙",
|
||||
餮: "Ƶ",
|
||||
蔕: "ῦ",
|
||||
闃: "Ā",
|
||||
饌: "²",
|
||||
煢: "ø",
|
||||
眄: "³",
|
||||
墸: " ͟͞ ",
|
||||
鹹: "Ĥ",
|
||||
瀑: "À",
|
||||
疉: "Ö",
|
||||
讎: "ö",
|
||||
鑒: "₩",
|
||||
壥: "Є",
|
||||
醵: "乀",
|
||||
鷸: "♫",
|
||||
};
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
ReadCollection,
|
||||
WriteCollection,
|
||||
} from "../../util";
|
||||
import { InsaneCharRebinds } from "./chars";
|
||||
|
||||
const program = new Command();
|
||||
program.requiredOption("-i, --input <music_db.xml>");
|
||||
@@ -136,63 +137,6 @@ const newCharts: Array<ChartDocument<"sdvx">> = [];
|
||||
// anything we don't want to include?
|
||||
const blacklist = [1259, 1491, 1438, 1490];
|
||||
|
||||
// these chars are nonsensically remapped in the xml
|
||||
// go figure
|
||||
const InsaneCharRebinds = {
|
||||
齶: "♡",
|
||||
齲: "♥",
|
||||
齷: "é",
|
||||
齪: "♣",
|
||||
曦: "à",
|
||||
曩: "è",
|
||||
霻: "♠",
|
||||
隯: "©",
|
||||
鑈: "♦",
|
||||
詹: "Ú",
|
||||
罇: "ê",
|
||||
彜: "ū",
|
||||
鬯: "ī",
|
||||
瑟: "ō",
|
||||
驩: "Ø",
|
||||
驫: "ā",
|
||||
骭: "ü",
|
||||
黷: "ē",
|
||||
騫: "á",
|
||||
躔: "🐾",
|
||||
蹙: "ℱ",
|
||||
趁: "Ǣ",
|
||||
壘: "É",
|
||||
黻: "*",
|
||||
隰: "℗",
|
||||
雋: "Ǜ",
|
||||
鬻: "♃",
|
||||
鬥: "Ã",
|
||||
鬆: "Ý",
|
||||
鬮: "¡",
|
||||
隍: "Ü",
|
||||
頽: "ä",
|
||||
龕: "€",
|
||||
蹇: "₂",
|
||||
釁: "🍄",
|
||||
鑷: "ゔ",
|
||||
盥: "⚙",
|
||||
餮: "Ƶ",
|
||||
蔕: "ῦ",
|
||||
闃: "Ā",
|
||||
饌: "²",
|
||||
煢: "ø",
|
||||
眄: "³",
|
||||
墸: " ͟͞ ",
|
||||
鹹: "Ĥ",
|
||||
瀑: "À",
|
||||
疉: "Ö",
|
||||
讎: "ö",
|
||||
鑒: "₩",
|
||||
壥: "Є",
|
||||
醵: "乀",
|
||||
鷸: "♫",
|
||||
};
|
||||
|
||||
// apply char rebinds
|
||||
function fixString(string: string): string {
|
||||
return string
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { type SongDocument } from "tachi-common";
|
||||
|
||||
import { MutateCollection } from "../../util";
|
||||
import { InsaneCharRebinds } from "./chars";
|
||||
|
||||
function fixString(string: string): string {
|
||||
return string
|
||||
.split("")
|
||||
.map((e) => InsaneCharRebinds[e] ?? e)
|
||||
.join("");
|
||||
}
|
||||
|
||||
MutateCollection("songs-sdvx.json", (songs) => {
|
||||
const sdvxSongs = songs as Array<SongDocument<"sdvx">>;
|
||||
|
||||
for (const song of sdvxSongs) {
|
||||
const fixedTitle = fixString(song.title);
|
||||
|
||||
if (fixedTitle !== song.title) {
|
||||
if (!song.altTitles.includes(song.title)) {
|
||||
song.altTitles.push(song.title);
|
||||
}
|
||||
|
||||
song.title = fixedTitle;
|
||||
}
|
||||
|
||||
song.artist = fixString(song.artist);
|
||||
}
|
||||
|
||||
return sdvxSongs;
|
||||
});
|
||||
@@ -181,16 +181,22 @@ export function VF6ToClass(vf: number): SpecificUserGameStats<"sdvx">["classes"]
|
||||
|
||||
export const SDVXLIKE_SCORE_CALCS: GPTScoreCalcs<SDVXLikes> = (scoreData, _derivedData, chart) => ({
|
||||
VF6: Volforce.calculateVF6(scoreData.score, scoreData.lamp, chart.levelNum),
|
||||
VF7: Volforce.calculateVF7(scoreData.score, scoreData.lamp, chart.levelNum),
|
||||
});
|
||||
|
||||
export const SDVXLIKE_SESSION_CALCS: GPTSessionCalcs<SDVXLikes> = (arr) => {
|
||||
const v = SessionAvgBest10For("VF6")(arr);
|
||||
const v2 = SessionAvgBest10For("VF7")(arr);
|
||||
|
||||
return { ProfileVF6: v !== null ? v * 50 : null };
|
||||
return {
|
||||
ProfileVF6: v !== null ? v * 50 : null,
|
||||
ProfileVF7: v2 !== null ? v2 * 50 : null,
|
||||
};
|
||||
};
|
||||
|
||||
export const SDVXLIKE_PROFILE_CALCS: GPTProfileCalcs<SDVXLikes> = async (game, userID) => ({
|
||||
VF6: await ProfileSumBestN("VF6", 50)(game, userID),
|
||||
VF7: await ProfileSumBestN("VF7", 50)(game, userID),
|
||||
});
|
||||
|
||||
export const SDVXLIKE_CLASS_DERIVERS: GPTClassDerivers<SDVXLikes> = (ratings) => ({
|
||||
|
||||
Reference in New Issue
Block a user