mirror of
https://gitea.tendokyu.moe/Hay1tsme/artemis.git
synced 2026-02-12 18:57:29 +08:00
CHUNITHM X-VERSE support (#238)
Reviewed-on: https://gitea.tendokyu.moe/Hay1tsme/artemis/pulls/238 Co-authored-by: beerpsi <beerpsi@duck.com> Co-committed-by: beerpsi <beerpsi@duck.com>
This commit is contained in:
317
titles/chuni/xverse.py
Normal file
317
titles/chuni/xverse.py
Normal file
@@ -0,0 +1,317 @@
|
||||
import asyncio
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Dict
|
||||
|
||||
from core.config import CoreConfig
|
||||
|
||||
from .config import ChuniConfig
|
||||
from .const import (
|
||||
ChuniConstants,
|
||||
LinkedVerseUnlockConditionType,
|
||||
MapAreaConditionLogicalOperator,
|
||||
MapAreaConditionType,
|
||||
)
|
||||
from .luminous import MysticAreaConditions
|
||||
from .verse import ChuniVerse
|
||||
|
||||
|
||||
class ChuniXVerse(ChuniVerse):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_X_VERSE
|
||||
|
||||
async def handle_c_m_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
user_data = await super().handle_c_m_get_user_preview_api_request(data)
|
||||
|
||||
# Does CARD MAKER 1.35 work this far up?
|
||||
user_data["lastDataVersion"] = "2.40.00"
|
||||
return user_data
|
||||
|
||||
async def handle_get_game_map_area_condition_api_request(self, data: Dict) -> Dict:
|
||||
events = await self.data.static.get_enabled_events(self.version)
|
||||
|
||||
if events is None:
|
||||
return {"length": 0, "gameMapAreaConditionList": []}
|
||||
|
||||
events_by_id = {event["eventId"]: event for event in events}
|
||||
mystic_conditions = MysticAreaConditions(
|
||||
events_by_id, 3239201, self.date_time_format
|
||||
)
|
||||
|
||||
# Mystic Rainbow of X-VERSE Area 2 unlocks when VERSE ep. ORIGIN is finished.
|
||||
mystic_conditions.add_condition(17021, 3020803, 3239202)
|
||||
|
||||
# Mystic Rainbow of X-VERSE Area 3 unlocks when VERSE ep. AIR is finished.
|
||||
mystic_conditions.add_condition(17104, 3020804, 3239203)
|
||||
|
||||
# Mystic Rainbow of X-VERSE Area 4 unlocks when VERSE ep. STAR is finished.
|
||||
mystic_conditions.add_condition(17208, 3020805, 3239204)
|
||||
|
||||
# Mystic Rainbow of X-VERSE Area 5 unlocks when VERSE ep. AMAZON is finished.
|
||||
mystic_conditions.add_condition(17304, 3020806, 3239205)
|
||||
|
||||
# Mystic Rainbow of X-VERSE Area 6 unlocks when VERSE ep. CRYSTAL is finished.
|
||||
mystic_conditions.add_condition(17407, 3020807, 3239206)
|
||||
|
||||
# Mystic Rainbow of X-VERSE Area 7 unlocks when VERSE ep. PARADISE is finished.
|
||||
mystic_conditions.add_condition(17483, 3020808, 3239207)
|
||||
|
||||
return {
|
||||
"length": len(mystic_conditions.conditions),
|
||||
"gameMapAreaConditionList": mystic_conditions.conditions,
|
||||
}
|
||||
|
||||
async def handle_get_game_course_level_api_request(self, data: Dict) -> Dict:
|
||||
uc_likes = [] # includes both UCs and LVs, though the former doesn't show up at all in X-VERSE
|
||||
unlock_challenges, linked_verses = await asyncio.gather(
|
||||
self.data.static.get_unlock_challenges(self.version),
|
||||
self.data.static.get_linked_verses(self.version),
|
||||
)
|
||||
|
||||
if unlock_challenges:
|
||||
uc_likes.extend(unlock_challenges)
|
||||
|
||||
if linked_verses:
|
||||
uc_likes.extend(linked_verses)
|
||||
|
||||
if not uc_likes:
|
||||
return {"length": 0, "gameCourseLevelList": []}
|
||||
|
||||
course_level_list = []
|
||||
current_time = datetime.now(timezone.utc).replace(tzinfo=None)
|
||||
|
||||
for uc_like in uc_likes:
|
||||
course_ids = [
|
||||
uc_like[f"courseId{i}"]
|
||||
for i in range(1, 6)
|
||||
if uc_like[f"courseId{i}"] is not None
|
||||
]
|
||||
event_start_date = uc_like["startDate"].replace(hour=0, minute=0, second=0)
|
||||
|
||||
for i, course_id in enumerate(course_ids):
|
||||
start_date = event_start_date + timedelta(days=7 * i)
|
||||
|
||||
if i == len(course_ids) - 1:
|
||||
end_date = datetime(2099, 12, 31, 23, 59, 59)
|
||||
else:
|
||||
end_date = (
|
||||
event_start_date
|
||||
+ timedelta(days=7 * (i + 1))
|
||||
- timedelta(seconds=1)
|
||||
)
|
||||
|
||||
if start_date <= current_time <= end_date:
|
||||
course_level_list.append(
|
||||
{
|
||||
"courseId": course_id,
|
||||
"startDate": start_date.strftime(self.date_time_format),
|
||||
"endDate": end_date.strftime(self.date_time_format),
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"length": len(course_level_list),
|
||||
"gameCourseLevelList": course_level_list,
|
||||
}
|
||||
|
||||
async def handle_get_game_l_v_condition_open_api_request(self, data: Dict) -> Dict:
|
||||
linked_verses = await self.data.static.get_linked_verses(self.version)
|
||||
|
||||
if not linked_verses:
|
||||
return {"length": 0, "gameLinkedVerseConditionOpenList": []}
|
||||
|
||||
linked_verse_by_id = {r["linkedVerseId"]: r for r in linked_verses}
|
||||
conditions = []
|
||||
|
||||
for lv_id, map_id in [
|
||||
(10001, 3020803), # ORIGIN
|
||||
(10002, 3020804), # AIR
|
||||
(10003, 3020805), # STAR
|
||||
(10004, 3020806), # AMAZON
|
||||
(10005, 3020807), # CRYSTAL
|
||||
(10006, 3020808), # PARADISE
|
||||
]:
|
||||
if (lv := linked_verse_by_id.get(lv_id)) is None:
|
||||
continue
|
||||
|
||||
conditions.append(
|
||||
{
|
||||
"linkedVerseId": lv["linkedVerseId"],
|
||||
"length": 1,
|
||||
"conditionList": [
|
||||
{
|
||||
"type": MapAreaConditionType.MAP_CLEARED.value,
|
||||
"conditionId": map_id,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": lv["startDate"].strftime(
|
||||
self.date_time_format
|
||||
),
|
||||
"endDate": "2099-12-31 23:59:59",
|
||||
}
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"length": len(conditions),
|
||||
"gameLinkedVerseConditionOpenList": conditions,
|
||||
}
|
||||
|
||||
async def handle_get_game_l_v_condition_unlock_api_request(
|
||||
self, data: Dict
|
||||
) -> Dict:
|
||||
linked_verses = await self.data.static.get_linked_verses(self.version)
|
||||
|
||||
if not linked_verses:
|
||||
return {
|
||||
"length": 0,
|
||||
"gameLinkedVerseConditionUnlockList": [],
|
||||
}
|
||||
|
||||
linked_verse_by_id = {r["linkedVerseId"]: r for r in linked_verses}
|
||||
conditions = []
|
||||
|
||||
# For reference on official Linked VERSE conditions:
|
||||
# https://docs.google.com/spreadsheets/d/1j7kmCR0-R5W3uivwkw-6A_eUCXttnJLnkTO0Qf7dya0/edit?usp=sharing
|
||||
|
||||
# Linked GATE ORIGIN - Play 30 ORIGIN Fables songs
|
||||
if gate_origin := linked_verse_by_id.get(10001):
|
||||
conditions.append(
|
||||
{
|
||||
"linkedVerseId": gate_origin["linkedVerseId"],
|
||||
"length": 1,
|
||||
"conditionList": [
|
||||
{
|
||||
"type": LinkedVerseUnlockConditionType.PLAY_SONGS.value,
|
||||
"conditionList": "59;79;148;71;75;140;163;80;51;64;65;74;95;67;53;100;108;107;105;82;76;141;63;147;69;151;70;101;152;180",
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": gate_origin["startDate"].strftime(
|
||||
self.date_time_format
|
||||
),
|
||||
"endDate": "2099-12-31 00:00:00",
|
||||
}
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
# Linked GATE AIR - Obtain class banner
|
||||
if gate_air := linked_verse_by_id.get(10002):
|
||||
conditions.append(
|
||||
{
|
||||
"linkedVerseId": gate_air["linkedVerseId"],
|
||||
"length": 1,
|
||||
"conditionList": [
|
||||
{
|
||||
"type": LinkedVerseUnlockConditionType.COURSE_CLEAR_AND_CLASS_EMBLEM.value,
|
||||
"conditionList": "1_2_3_4_5_6",
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": gate_air["startDate"].strftime(
|
||||
self.date_time_format
|
||||
),
|
||||
"endDate": "2099-12-31 00:00:00",
|
||||
}
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
# Linked GATE STAR - Obtain a trophy by leveling a character to level 15
|
||||
if gate_star := linked_verse_by_id.get(10003):
|
||||
conditions.append(
|
||||
{
|
||||
"linkedVerseId": gate_star["linkedVerseId"],
|
||||
"length": 1,
|
||||
"conditionList": [
|
||||
{
|
||||
"type": LinkedVerseUnlockConditionType.TROPHY_OBTAINED.value,
|
||||
"conditionList": "9718",
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": gate_star["startDate"].strftime(
|
||||
self.date_time_format
|
||||
),
|
||||
"endDate": "2099-12-31 00:00:00",
|
||||
}
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
# Linked GATE AMAZON - Play Killing Rhythm and Climax from the favorites folder
|
||||
if gate_amazon := linked_verse_by_id.get(10004):
|
||||
conditions.append(
|
||||
{
|
||||
"linkedVerseId": gate_amazon["linkedVerseId"],
|
||||
"length": 1,
|
||||
"conditionList": [
|
||||
{
|
||||
"type": LinkedVerseUnlockConditionType.PLAY_SONGS_IN_FAVORITE.value,
|
||||
"conditionList": "712;777",
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": gate_amazon["startDate"].strftime(
|
||||
self.date_time_format
|
||||
),
|
||||
"endDate": "2099-12-31 00:00:00",
|
||||
}
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
# Linked GATE CRYSTAL - Clear team course while equipping a character of minimum rank 26
|
||||
if gate_crystal := linked_verse_by_id.get(10005):
|
||||
conditions.append(
|
||||
{
|
||||
"linkedVerseId": gate_crystal["linkedVerseId"],
|
||||
"length": 1,
|
||||
"conditionList": [
|
||||
{
|
||||
"type": LinkedVerseUnlockConditionType.CLEAR_TEAM_COURSE_WITH_CHARACTER_OF_MINIMUM_RANK.value,
|
||||
"conditionList": "26",
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": gate_crystal["startDate"].strftime(
|
||||
self.date_time_format
|
||||
),
|
||||
"endDate": "2099-12-31 00:00:00",
|
||||
}
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
# Linked GATE PARADISE - Play one solo song by each of the artists in Inori
|
||||
if gate_paradise := linked_verse_by_id.get(10006):
|
||||
conditions.append(
|
||||
{
|
||||
"linkedVerseId": gate_paradise["linkedVerseId"],
|
||||
"length": 1,
|
||||
"conditionList": [
|
||||
{
|
||||
"type": LinkedVerseUnlockConditionType.PLAY_SONGS.value,
|
||||
"conditionList": "180_384_2355;407_2353;788_629_600;2704;2050_2354",
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": gate_paradise["startDate"].strftime(
|
||||
self.date_time_format
|
||||
),
|
||||
"endDate": "2099-12-31 00:00:00",
|
||||
}
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"length": len(conditions),
|
||||
"gameLinkedVerseConditionUnlockList": conditions,
|
||||
}
|
||||
|
||||
async def handle_get_user_l_v_api_request(self, data: Dict) -> Dict:
|
||||
user_id = int(data["userId"])
|
||||
rows = await self.data.item.get_linked_verse(user_id) or []
|
||||
linked_verses = []
|
||||
|
||||
for row in rows:
|
||||
data = row._asdict()
|
||||
data.pop("id")
|
||||
data.pop("user")
|
||||
|
||||
linked_verses.append(data)
|
||||
|
||||
return {
|
||||
"userId": user_id,
|
||||
"userLinkedVerseList": linked_verses,
|
||||
}
|
||||
Reference in New Issue
Block a user