Merge pull request '[chunithm] support luminous+' (#193) from beerpsi/artemis:feat/chunithm/luminousplus into develop
Reviewed-on: https://gitea.tendokyu.moe/Hay1tsme/artemis/pulls/193
This commit is contained in:
commit
5d2f0eaae6
@ -60,13 +60,14 @@ Games listed below have been tested and confirmed working.
|
||||
|
||||
### SDHD/SDBT
|
||||
|
||||
| Version ID | Version Name |
|
||||
| ---------- | ------------------- |
|
||||
| 11 | CHUNITHM NEW!! |
|
||||
| 12 | CHUNITHM NEW PLUS!! |
|
||||
| 13 | CHUNITHM SUN |
|
||||
| 14 | CHUNITHM SUN PLUS |
|
||||
| 15 | CHUNITHM LUMINOUS |
|
||||
| Version ID | Version Name |
|
||||
| ---------- | ---------------------- |
|
||||
| 11 | CHUNITHM NEW!! |
|
||||
| 12 | CHUNITHM NEW PLUS!! |
|
||||
| 13 | CHUNITHM SUN |
|
||||
| 14 | CHUNITHM SUN PLUS |
|
||||
| 15 | CHUNITHM LUMINOUS |
|
||||
| 16 | CHUNITHM LUMINOUS PLUS |
|
||||
|
||||
|
||||
### Importer
|
||||
|
@ -40,6 +40,9 @@ version:
|
||||
15:
|
||||
rom: 2.20.00
|
||||
data: 2.20.00
|
||||
16:
|
||||
rom: 2.25.00
|
||||
data: 2.25.00
|
||||
|
||||
crypto:
|
||||
encrypted_only: False
|
||||
|
@ -30,6 +30,7 @@ Games listed below have been tested and confirmed working. Only game versions ol
|
||||
+ SUN
|
||||
+ SUN PLUS
|
||||
+ LUMINOUS
|
||||
+ LUMINOUS PLUS
|
||||
|
||||
+ crossbeats REV.
|
||||
+ Crossbeats REV.
|
||||
|
@ -8,7 +8,7 @@ import pytz
|
||||
|
||||
from core.config import CoreConfig
|
||||
from titles.chuni.config import ChuniConfig
|
||||
from titles.chuni.const import ChuniConstants, ItemKind
|
||||
from titles.chuni.const import ChuniConstants, FavoriteItemKind, ItemKind
|
||||
from titles.chuni.database import ChuniData
|
||||
|
||||
|
||||
@ -1014,6 +1014,28 @@ class ChuniBase:
|
||||
)
|
||||
await self.data.profile.put_net_battle(user_id, net_battle)
|
||||
|
||||
# New in LUMINOUS PLUS
|
||||
if "userFavoriteMusicList" in upsert:
|
||||
# musicId, orderId
|
||||
music_ids = set(int(m["musicId"]) for m in upsert["userFavoriteMusicList"])
|
||||
current_favorites = await self.data.item.get_all_favorites(
|
||||
user_id, self.version, fav_kind=FavoriteItemKind.MUSIC
|
||||
)
|
||||
|
||||
if current_favorites is None:
|
||||
current_favorites = []
|
||||
|
||||
current_favorite_ids = set(x.favId for x in current_favorites)
|
||||
keep_ids = current_favorite_ids.intersection(music_ids)
|
||||
deleted_ids = current_favorite_ids - keep_ids
|
||||
added_ids = music_ids - keep_ids
|
||||
|
||||
for fav_id in deleted_ids:
|
||||
await self.data.item.delete_favorite_music(user_id, self.version, fav_id)
|
||||
|
||||
for fav_id in added_ids:
|
||||
await self.data.item.put_favorite_music(user_id, self.version, fav_id)
|
||||
|
||||
return {"returnCode": "1"}
|
||||
|
||||
async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
|
||||
|
@ -25,6 +25,7 @@ class ChuniConstants:
|
||||
VER_CHUNITHM_SUN = 13
|
||||
VER_CHUNITHM_SUN_PLUS = 14
|
||||
VER_CHUNITHM_LUMINOUS = 15
|
||||
VER_CHUNITHM_LUMINOUS_PLUS = 16
|
||||
|
||||
VERSION_NAMES = [
|
||||
"CHUNITHM",
|
||||
@ -43,6 +44,7 @@ class ChuniConstants:
|
||||
"CHUNITHM SUN",
|
||||
"CHUNITHM SUN PLUS",
|
||||
"CHUNITHM LUMINOUS",
|
||||
"CHUNITHM LUMINOUS PLUS",
|
||||
]
|
||||
|
||||
SCORE_RANK_INTERVALS_OLD = [
|
||||
@ -98,6 +100,7 @@ class MapAreaConditionType(IntEnum):
|
||||
|
||||
TROPHY_OBTAINED = 3
|
||||
|
||||
RANK_SSSP = 18
|
||||
RANK_SSS = 19
|
||||
RANK_SSP = 20
|
||||
RANK_SS = 21
|
||||
@ -127,7 +130,7 @@ class ItemKind(IntEnum):
|
||||
FRAME = 2
|
||||
"""
|
||||
"Frame" is the background for the gauge/score/max combo display
|
||||
shown during gameplay. This item cannot be equipped (as of LUMINOUS)
|
||||
shown during gameplay. This item cannot be equipped (as of LUMINOUS PLUS)
|
||||
and is hardcoded to the current game's version.
|
||||
"""
|
||||
|
||||
@ -146,7 +149,7 @@ class ItemKind(IntEnum):
|
||||
|
||||
ULTIMA_UNLOCK = 12
|
||||
"""This only applies to ULTIMA difficulties that are *not* unlocked by
|
||||
SS-ing EXPERT+MASTER.
|
||||
reaching S rank on EXPERT difficulty or above.
|
||||
"""
|
||||
|
||||
|
||||
|
@ -36,6 +36,7 @@ from .newplus import ChuniNewPlus
|
||||
from .sun import ChuniSun
|
||||
from .sunplus import ChuniSunPlus
|
||||
from .luminous import ChuniLuminous
|
||||
from .luminousplus import ChuniLuminousPlus
|
||||
|
||||
class ChuniServlet(BaseServlet):
|
||||
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
||||
@ -64,6 +65,7 @@ class ChuniServlet(BaseServlet):
|
||||
ChuniSun,
|
||||
ChuniSunPlus,
|
||||
ChuniLuminous,
|
||||
ChuniLuminousPlus,
|
||||
]
|
||||
|
||||
self.logger = logging.getLogger("chuni")
|
||||
@ -107,6 +109,7 @@ class ChuniServlet(BaseServlet):
|
||||
f"{ChuniConstants.VER_CHUNITHM_SUN_PLUS}_int": 36,
|
||||
ChuniConstants.VER_CHUNITHM_LUMINOUS: 8,
|
||||
f"{ChuniConstants.VER_CHUNITHM_LUMINOUS}_int": 8,
|
||||
ChuniConstants.VER_CHUNITHM_LUMINOUS_PLUS: 56,
|
||||
}
|
||||
|
||||
for version, keys in self.game_cfg.crypto.keys.items():
|
||||
@ -235,8 +238,10 @@ class ChuniServlet(BaseServlet):
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_SUN
|
||||
elif version >= 215 and version < 220: # SUN PLUS
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_SUN_PLUS
|
||||
elif version >= 220: # LUMINOUS
|
||||
elif version >= 220 and version < 225: # LUMINOUS
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_LUMINOUS
|
||||
elif version >= 225: # LUMINOUS PLUS
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_LUMINOUS_PLUS
|
||||
elif game_code == "SDGS": # Int
|
||||
if version < 105: # SUPERSTAR
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS
|
||||
@ -250,8 +255,10 @@ class ChuniServlet(BaseServlet):
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_SUN
|
||||
elif version >= 125 and version < 130: # SUN PLUS
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_SUN_PLUS
|
||||
elif version >= 130: # LUMINOUS
|
||||
elif version >= 130 and version < 135: # LUMINOUS
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_LUMINOUS
|
||||
elif version >= 135: # LUMINOUS PLUS
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_LUMINOUS_PLUS
|
||||
|
||||
if all(c in string.hexdigits for c in endpoint) and len(endpoint) == 32:
|
||||
# If we get a 32 character long hex string, it's a hash and we're
|
||||
@ -311,8 +318,10 @@ class ChuniServlet(BaseServlet):
|
||||
return Response(zlib.compress(b'{"stat": "0"}'))
|
||||
|
||||
try:
|
||||
unzip = zlib.decompress(req_raw)
|
||||
|
||||
if request.headers.get("x-debug") is not None:
|
||||
unzip = req_raw
|
||||
else:
|
||||
unzip = zlib.decompress(req_raw)
|
||||
except zlib.error as e:
|
||||
self.logger.error(
|
||||
f"Failed to decompress v{version} {endpoint} request -> {e}"
|
||||
@ -352,6 +361,9 @@ class ChuniServlet(BaseServlet):
|
||||
|
||||
self.logger.debug(f"Response {resp}")
|
||||
|
||||
if request.headers.get("x-debug") is not None:
|
||||
return Response(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
||||
|
||||
zipped = zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
||||
|
||||
if not encrtped:
|
||||
|
@ -2,9 +2,13 @@ from datetime import timedelta
|
||||
from typing import Dict
|
||||
|
||||
from core.config import CoreConfig
|
||||
from titles.chuni.sunplus import ChuniSunPlus
|
||||
from titles.chuni.const import ChuniConstants, MapAreaConditionLogicalOperator, MapAreaConditionType
|
||||
from titles.chuni.config import ChuniConfig
|
||||
from titles.chuni.const import (
|
||||
ChuniConstants,
|
||||
MapAreaConditionLogicalOperator,
|
||||
MapAreaConditionType,
|
||||
)
|
||||
from titles.chuni.sunplus import ChuniSunPlus
|
||||
|
||||
|
||||
class ChuniLuminous(ChuniSunPlus):
|
||||
@ -18,7 +22,7 @@ class ChuniLuminous(ChuniSunPlus):
|
||||
# Does CARD MAKER 1.35 work this far up?
|
||||
user_data["lastDataVersion"] = "2.20.00"
|
||||
return user_data
|
||||
|
||||
|
||||
async def handle_get_user_c_mission_api_request(self, data: Dict) -> Dict:
|
||||
user_id = data["userId"]
|
||||
mission_id = data["missionId"]
|
||||
@ -28,7 +32,7 @@ class ChuniLuminous(ChuniSunPlus):
|
||||
|
||||
mission_data = await self.data.item.get_cmission(user_id, mission_id)
|
||||
progress_data = await self.data.item.get_cmission_progress(user_id, mission_id)
|
||||
|
||||
|
||||
if mission_data and progress_data:
|
||||
point = mission_data["point"]
|
||||
|
||||
@ -48,12 +52,14 @@ class ChuniLuminous(ChuniSunPlus):
|
||||
"userCMissionProgressList": progress_list,
|
||||
}
|
||||
|
||||
async def handle_get_user_net_battle_ranking_info_api_request(self, data: Dict) -> Dict:
|
||||
async def handle_get_user_net_battle_ranking_info_api_request(
|
||||
self, data: Dict
|
||||
) -> Dict:
|
||||
user_id = data["userId"]
|
||||
|
||||
net_battle = {}
|
||||
net_battle_data = await self.data.profile.get_net_battle(user_id)
|
||||
|
||||
|
||||
if net_battle_data:
|
||||
net_battle = {
|
||||
"isRankUpChallengeFailed": net_battle_data["isRankUpChallengeFailed"],
|
||||
@ -94,131 +100,135 @@ class ChuniLuminous(ChuniSunPlus):
|
||||
# (event ID 14214) was imported into ARTEMiS, we disable the requirement
|
||||
# for this trophy.
|
||||
if 14214 in event_by_id:
|
||||
mission_in_progress_end_date = (event_by_id[14214]["startDate"] - timedelta(hours=2)).strftime(self.date_time_format)
|
||||
|
||||
conditions.extend([
|
||||
{
|
||||
"mapAreaId": 2206201, # BlythE ULTIMA
|
||||
"length": 1,
|
||||
# Obtain the trophy "MISSION in progress".
|
||||
"mapAreaConditionList": [
|
||||
{
|
||||
"type": MapAreaConditionType.TROPHY_OBTAINED.value,
|
||||
"conditionId": 6832,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date,
|
||||
"endDate": mission_in_progress_end_date,
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"mapAreaId": 2206202, # PRIVATE SERVICE ULTIMA
|
||||
"length": 1,
|
||||
# Obtain the trophy "MISSION in progress".
|
||||
"mapAreaConditionList": [
|
||||
{
|
||||
"type": MapAreaConditionType.TROPHY_OBTAINED.value,
|
||||
"conditionId": 6832,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date,
|
||||
"endDate": mission_in_progress_end_date,
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"mapAreaId": 2206203, # New York Back Raise
|
||||
"length": 1,
|
||||
# SS NightTheater's EXPERT chart and get the title
|
||||
# "今宵、劇場に映し出される景色とは――――。"
|
||||
"mapAreaConditionList": [
|
||||
{
|
||||
"type": MapAreaConditionType.TROPHY_OBTAINED.value,
|
||||
"conditionId": 6833,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date,
|
||||
"endDate": "2099-12-31 00:00:00.0",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"mapAreaId": 2206204, # Spasmodic
|
||||
"length": 2,
|
||||
# - Get 1 miss on Random (any difficulty) and get the title "当たり待ち"
|
||||
# - Get 1 miss on 花たちに希望を (any difficulty) and get the title "花たちに希望を"
|
||||
"mapAreaConditionList": [
|
||||
{
|
||||
"type": MapAreaConditionType.TROPHY_OBTAINED.value,
|
||||
"conditionId": 6834,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date,
|
||||
"endDate": "2099-12-31 00:00:00.0",
|
||||
},
|
||||
{
|
||||
"type": MapAreaConditionType.TROPHY_OBTAINED.value,
|
||||
"conditionId": 6835,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date,
|
||||
"endDate": "2099-12-31 00:00:00.0",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"mapAreaId": 2206205, # ΩΩPARTS
|
||||
"length": 2,
|
||||
# - S Sage EXPERT to get the title "マターリ進行キボンヌ"
|
||||
# - Equip this title and play cab-to-cab with another person with this title
|
||||
# to get "マターリしようよ". Disabled because it is difficult to play cab2cab
|
||||
# on data setups. A network operator may consider re-enabling it by uncommenting
|
||||
# the second condition.
|
||||
"mapAreaConditionList": [
|
||||
{
|
||||
"type": MapAreaConditionType.TROPHY_OBTAINED.value,
|
||||
"conditionId": 6836,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date,
|
||||
"endDate": "2099-12-31 00:00:00.0",
|
||||
},
|
||||
# {
|
||||
# "type": MapAreaConditionType.TROPHY_OBTAINED.value,
|
||||
# "conditionId": 6837,
|
||||
# "logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
# "startDate": start_date,
|
||||
# "endDate": "2099-12-31 00:00:00.0",
|
||||
# },
|
||||
],
|
||||
},
|
||||
{
|
||||
"mapAreaId": 2206206, # Blow My Mind
|
||||
"length": 1,
|
||||
# SS on CHAOS EXPERT, Hydra EXPERT, Surive EXPERT and Jakarta PROGRESSION EXPERT
|
||||
# to get the title "Can you hear me?"
|
||||
"mapAreaConditionList": [
|
||||
{
|
||||
"type": MapAreaConditionType.TROPHY_OBTAINED.value,
|
||||
"conditionId": 6838,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date,
|
||||
"endDate": "2099-12-31 00:00:00.0",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"mapAreaId": 2206207, # VALLIS-NERIA
|
||||
"length": 6,
|
||||
# Finish the 6 other areas
|
||||
"mapAreaConditionList": [
|
||||
{
|
||||
"type": MapAreaConditionType.MAP_AREA_CLEARED.value,
|
||||
"conditionId": x,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date,
|
||||
"endDate": "2099-12-31 00:00:00.0",
|
||||
}
|
||||
for x in range(2206201, 2206207)
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
mission_in_progress_end_date = (
|
||||
event_by_id[14214]["startDate"] - timedelta(hours=2)
|
||||
).strftime(self.date_time_format)
|
||||
|
||||
conditions.extend(
|
||||
[
|
||||
{
|
||||
"mapAreaId": 2206201, # BlythE ULTIMA
|
||||
"length": 1,
|
||||
# Obtain the trophy "MISSION in progress".
|
||||
"mapAreaConditionList": [
|
||||
{
|
||||
"type": MapAreaConditionType.TROPHY_OBTAINED.value,
|
||||
"conditionId": 6832,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date,
|
||||
"endDate": mission_in_progress_end_date,
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"mapAreaId": 2206202, # PRIVATE SERVICE ULTIMA
|
||||
"length": 1,
|
||||
# Obtain the trophy "MISSION in progress".
|
||||
"mapAreaConditionList": [
|
||||
{
|
||||
"type": MapAreaConditionType.TROPHY_OBTAINED.value,
|
||||
"conditionId": 6832,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date,
|
||||
"endDate": mission_in_progress_end_date,
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"mapAreaId": 2206203, # New York Back Raise
|
||||
"length": 1,
|
||||
# SS NightTheater's EXPERT chart and get the title
|
||||
# "今宵、劇場に映し出される景色とは――――。"
|
||||
"mapAreaConditionList": [
|
||||
{
|
||||
"type": MapAreaConditionType.TROPHY_OBTAINED.value,
|
||||
"conditionId": 6833,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date,
|
||||
"endDate": "2099-12-31 00:00:00.0",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"mapAreaId": 2206204, # Spasmodic
|
||||
"length": 2,
|
||||
# - Get 1 miss on Random (any difficulty) and get the title "当たり待ち"
|
||||
# - Get 1 miss on 花たちに希望を (any difficulty) and get the title "花たちに希望を"
|
||||
"mapAreaConditionList": [
|
||||
{
|
||||
"type": MapAreaConditionType.TROPHY_OBTAINED.value,
|
||||
"conditionId": 6834,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date,
|
||||
"endDate": "2099-12-31 00:00:00.0",
|
||||
},
|
||||
{
|
||||
"type": MapAreaConditionType.TROPHY_OBTAINED.value,
|
||||
"conditionId": 6835,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date,
|
||||
"endDate": "2099-12-31 00:00:00.0",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"mapAreaId": 2206205, # ΩΩPARTS
|
||||
"length": 2,
|
||||
# - S Sage EXPERT to get the title "マターリ進行キボンヌ"
|
||||
# - Equip this title and play cab-to-cab with another person with this title
|
||||
# to get "マターリしようよ". Disabled because it is difficult to play cab2cab
|
||||
# on data setups. A network operator may consider re-enabling it by uncommenting
|
||||
# the second condition.
|
||||
"mapAreaConditionList": [
|
||||
{
|
||||
"type": MapAreaConditionType.TROPHY_OBTAINED.value,
|
||||
"conditionId": 6836,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date,
|
||||
"endDate": "2099-12-31 00:00:00.0",
|
||||
},
|
||||
# {
|
||||
# "type": MapAreaConditionType.TROPHY_OBTAINED.value,
|
||||
# "conditionId": 6837,
|
||||
# "logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
# "startDate": start_date,
|
||||
# "endDate": "2099-12-31 00:00:00.0",
|
||||
# },
|
||||
],
|
||||
},
|
||||
{
|
||||
"mapAreaId": 2206206, # Blow My Mind
|
||||
"length": 1,
|
||||
# SS on CHAOS EXPERT, Hydra EXPERT, Surive EXPERT and Jakarta PROGRESSION EXPERT
|
||||
# to get the title "Can you hear me?"
|
||||
"mapAreaConditionList": [
|
||||
{
|
||||
"type": MapAreaConditionType.TROPHY_OBTAINED.value,
|
||||
"conditionId": 6838,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date,
|
||||
"endDate": "2099-12-31 00:00:00.0",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"mapAreaId": 2206207, # VALLIS-NERIA
|
||||
"length": 6,
|
||||
# Finish the 6 other areas
|
||||
"mapAreaConditionList": [
|
||||
{
|
||||
"type": MapAreaConditionType.MAP_AREA_CLEARED.value,
|
||||
"conditionId": x,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date,
|
||||
"endDate": "2099-12-31 00:00:00.0",
|
||||
}
|
||||
for x in range(2206201, 2206207)
|
||||
],
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
# LUMINOUS ep. I
|
||||
if 14005 in event_by_id:
|
||||
start_date = event_by_id[14005]["startDate"].strftime(self.date_time_format)
|
||||
@ -226,7 +236,7 @@ class ChuniLuminous(ChuniSunPlus):
|
||||
if not mystic_area_1_added:
|
||||
conditions.append(mystic_area_1_conditions)
|
||||
mystic_area_1_added = True
|
||||
|
||||
|
||||
mystic_area_1_conditions["length"] += 1
|
||||
mystic_area_1_conditions["mapAreaConditionList"].append(
|
||||
{
|
||||
@ -254,15 +264,15 @@ class ChuniLuminous(ChuniSunPlus):
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
# LUMINOUS ep. II
|
||||
if 14251 in event_by_id:
|
||||
start_date = event_by_id[14251]["startDate"].strftime(self.date_time_format)
|
||||
|
||||
|
||||
if not mystic_area_1_added:
|
||||
conditions.append(mystic_area_1_conditions)
|
||||
mystic_area_1_added = True
|
||||
|
||||
|
||||
mystic_area_1_conditions["length"] += 1
|
||||
mystic_area_1_conditions["mapAreaConditionList"].append(
|
||||
{
|
||||
@ -291,6 +301,203 @@ class ChuniLuminous(ChuniSunPlus):
|
||||
}
|
||||
)
|
||||
|
||||
# LUMINOUS ep. III
|
||||
if 14481 in event_by_id:
|
||||
start_date = event_by_id[14481]["startDate"].strftime(self.date_time_format)
|
||||
|
||||
if not mystic_area_1_added:
|
||||
conditions.append(mystic_area_1_conditions)
|
||||
mystic_area_1_added = True
|
||||
|
||||
mystic_area_1_conditions["length"] += 1
|
||||
mystic_area_1_conditions["mapAreaConditionList"].append(
|
||||
{
|
||||
"type": MapAreaConditionType.MAP_CLEARED.value,
|
||||
"conditionId": 3020703,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.OR.value,
|
||||
"startDate": start_date,
|
||||
"endDate": "2099-12-31 00:00:00.0",
|
||||
}
|
||||
)
|
||||
|
||||
conditions.append(
|
||||
{
|
||||
"mapAreaId": 3229304, # Mystic Rainbow of LUMINOUS Area 4,
|
||||
"length": 1,
|
||||
# Unlocks when LUMINOUS ep. III is completed.
|
||||
"mapAreaConditionList": [
|
||||
{
|
||||
"type": MapAreaConditionType.MAP_CLEARED.value,
|
||||
"conditionId": 3020703,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date,
|
||||
"endDate": "2099-12-31 00:00:00.0",
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
# 1UM1N0U5 ep. 111
|
||||
if 14483 in event_by_id:
|
||||
start_date = event_by_id[14483]["startDate"].replace(
|
||||
hour=0, minute=0, second=0
|
||||
)
|
||||
|
||||
# conditions to unlock the 6 "Key of ..." area in the map
|
||||
# for the first 14 days: Defandour MASTER AJ, crazy (about you) MASTER AJ, Halcyon ULTIMA SSS
|
||||
title_conditions = [
|
||||
{
|
||||
"type": MapAreaConditionType.ALL_JUSTICE.value,
|
||||
"conditionId": 258103, # Defandour MASTER
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date.strftime(self.date_time_format),
|
||||
"endDate": (
|
||||
start_date + timedelta(days=14) - timedelta(seconds=1)
|
||||
).strftime(self.date_time_format),
|
||||
},
|
||||
{
|
||||
"type": MapAreaConditionType.ALL_JUSTICE.value,
|
||||
"conditionId": 258003, # crazy (about you) MASTER
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date.strftime(self.date_time_format),
|
||||
"endDate": (
|
||||
start_date + timedelta(days=14) - timedelta(seconds=1)
|
||||
).strftime(self.date_time_format),
|
||||
},
|
||||
{
|
||||
"type": MapAreaConditionType.RANK_SSS.value,
|
||||
"conditionId": 17304, # Halcyon ULTIMA
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date.strftime(self.date_time_format),
|
||||
"endDate": (
|
||||
start_date + timedelta(days=14) - timedelta(seconds=1)
|
||||
).strftime(self.date_time_format),
|
||||
},
|
||||
]
|
||||
|
||||
# For each next 14 days, the conditions are lowered to SS+, S+, S, and then always unlocked
|
||||
for i, typ in enumerate(
|
||||
[
|
||||
MapAreaConditionType.RANK_SSP.value,
|
||||
MapAreaConditionType.RANK_SP.value,
|
||||
MapAreaConditionType.RANK_S.value,
|
||||
MapAreaConditionType.ALWAYS_UNLOCKED.value,
|
||||
]
|
||||
):
|
||||
start = (start_date + timedelta(days=14 * (i + 1))).strftime(
|
||||
self.date_time_format
|
||||
)
|
||||
|
||||
if typ != MapAreaConditionType.ALWAYS_UNLOCKED.value:
|
||||
end = (
|
||||
start_date + timedelta(days=14 * (i + 2)) - timedelta(seconds=1)
|
||||
).strftime(self.date_time_format)
|
||||
|
||||
title_conditions.extend(
|
||||
[
|
||||
{
|
||||
"type": typ,
|
||||
"conditionId": condition_id,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start,
|
||||
"endDate": end,
|
||||
}
|
||||
for condition_id in {17304, 258003, 258103}
|
||||
]
|
||||
)
|
||||
else:
|
||||
end = "2099-12-31 00:00:00"
|
||||
|
||||
title_conditions.append(
|
||||
{
|
||||
"type": typ,
|
||||
"conditionId": 0,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start,
|
||||
"endDate": end,
|
||||
}
|
||||
)
|
||||
|
||||
# actually add all the conditions
|
||||
for map_area_id in range(3229201, 3229207):
|
||||
conditions.append(
|
||||
{
|
||||
"mapAreaId": map_area_id,
|
||||
"length": len(title_conditions),
|
||||
"mapAreaConditionList": title_conditions,
|
||||
}
|
||||
)
|
||||
|
||||
# Ultimate Force
|
||||
# For the first 14 days, the condition is to obtain all 9 "Key of ..." titles
|
||||
# Afterwards, the condition is the 6 "Key of ..." titles that you can obtain
|
||||
# by playing the 6 areas, as well as obtaining specific ranks on
|
||||
# [CRYSTAL_ACCESS] / Strange Love / βlαnoir
|
||||
ultimate_force_conditions = []
|
||||
|
||||
# Trophies obtained by playing the 6 areas
|
||||
for trophy_id in {6851, 6853, 6855, 6857, 6858, 6860}:
|
||||
ultimate_force_conditions.append(
|
||||
{
|
||||
"type": MapAreaConditionType.TROPHY_OBTAINED.value,
|
||||
"conditionId": trophy_id,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date.strftime(self.date_time_format),
|
||||
"endDate": "2099-12-31 00:00:00",
|
||||
}
|
||||
)
|
||||
|
||||
# βlαnoir MASTER SSS+ / Strange Love MASTER SSS+ / [CRYSTAL_ACCESS] MASTER SSS+
|
||||
for trophy_id in {6852, 6854, 6856}:
|
||||
ultimate_force_conditions.append(
|
||||
{
|
||||
"type": MapAreaConditionType.TROPHY_OBTAINED.value,
|
||||
"conditionId": trophy_id,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date.strftime(self.date_time_format),
|
||||
"endDate": (
|
||||
start_date + timedelta(days=14) - timedelta(seconds=1)
|
||||
).strftime(self.date_time_format),
|
||||
}
|
||||
)
|
||||
|
||||
# For each next 14 days, the rank conditions for the 3 songs lowers
|
||||
# Finally, the Ultimate Force area is unlocked as soon as you finish the 6 other areas.
|
||||
for i, typ in enumerate(
|
||||
[
|
||||
MapAreaConditionType.RANK_SSS.value,
|
||||
MapAreaConditionType.RANK_SS.value,
|
||||
MapAreaConditionType.RANK_S.value,
|
||||
]
|
||||
):
|
||||
start = (start_date + timedelta(days=14 * (i + 1))).strftime(
|
||||
self.date_time_format
|
||||
)
|
||||
|
||||
end = (
|
||||
start_date + timedelta(days=14 * (i + 2)) - timedelta(seconds=1)
|
||||
).strftime(self.date_time_format)
|
||||
|
||||
ultimate_force_conditions.extend(
|
||||
[
|
||||
{
|
||||
"type": typ,
|
||||
"conditionId": condition_id,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start,
|
||||
"endDate": end,
|
||||
}
|
||||
for condition_id in {109403, 212103, 244203}
|
||||
]
|
||||
)
|
||||
|
||||
conditions.append(
|
||||
{
|
||||
"mapAreaId": 3229207,
|
||||
"length": len(ultimate_force_conditions),
|
||||
"mapAreaConditionList": ultimate_force_conditions,
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"length": len(conditions),
|
||||
|
170
titles/chuni/luminousplus.py
Normal file
170
titles/chuni/luminousplus.py
Normal file
@ -0,0 +1,170 @@
|
||||
from datetime import timedelta
|
||||
from typing import Dict
|
||||
|
||||
from core.config import CoreConfig
|
||||
from titles.chuni.config import ChuniConfig
|
||||
from titles.chuni.const import ChuniConstants, MapAreaConditionLogicalOperator, MapAreaConditionType
|
||||
from titles.chuni.luminous import ChuniLuminous
|
||||
|
||||
|
||||
class ChuniLuminousPlus(ChuniLuminous):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_LUMINOUS_PLUS
|
||||
|
||||
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
user_data = await super().handle_cm_get_user_preview_api_request(data)
|
||||
|
||||
# Does CARD MAKER 1.35 work this far up?
|
||||
user_data["lastDataVersion"] = "2.25.00"
|
||||
return user_data
|
||||
|
||||
async def handle_get_user_c_mission_list_api_request(self, data: Dict) -> Dict:
|
||||
user_id = int(data["userId"])
|
||||
user_mission_list_request = data["userCMissionList"]
|
||||
|
||||
user_mission_list = []
|
||||
|
||||
for request in user_mission_list_request:
|
||||
user_id = int(request["userId"])
|
||||
mission_id = int(request["missionId"])
|
||||
point = int(request["point"])
|
||||
|
||||
mission_data = await self.data.item.get_cmission(user_id, mission_id)
|
||||
progress_data = await self.data.item.get_cmission_progress(user_id, mission_id)
|
||||
|
||||
if mission_data is None or progress_data is None:
|
||||
continue
|
||||
|
||||
point = mission_data.point
|
||||
user_mission_progress_list = [
|
||||
{
|
||||
"order": progress.order,
|
||||
"stage": progress.stage,
|
||||
"progress": progress.progress,
|
||||
}
|
||||
for progress in progress_data
|
||||
]
|
||||
|
||||
user_mission_list.append(
|
||||
{
|
||||
"userId": user_id,
|
||||
"missionId": mission_id,
|
||||
"point": point,
|
||||
"userCMissionProgressList": user_mission_progress_list,
|
||||
},
|
||||
)
|
||||
|
||||
return {
|
||||
"userId": user_id,
|
||||
"userCMissionList": user_mission_list,
|
||||
}
|
||||
|
||||
async def handle_get_game_map_area_condition_api_request(self, data: Dict) -> Dict:
|
||||
# There is no game data for this, everything is server side.
|
||||
# However, we can selectively show/hide events as data is imported into the server.
|
||||
events = await self.data.static.get_enabled_events(self.version)
|
||||
event_by_id = {evt["eventId"]: evt for evt in events}
|
||||
conditions = []
|
||||
|
||||
# LUMINOUS ep. Ascension
|
||||
if ep_ascension := event_by_id.get(15512):
|
||||
start_date = ep_ascension["startDate"].replace(hour=0, minute=0, second=0)
|
||||
|
||||
# Finish LUMINOUS ep. VII to unlock LUMINOUS ep. Ascension.
|
||||
task_track_map_conditions = [
|
||||
{
|
||||
"type": MapAreaConditionType.MAP_CLEARED.value,
|
||||
"conditionId": 3020707,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start_date.strftime(self.date_time_format),
|
||||
"endDate": "2099-12-31 00:00:00",
|
||||
}
|
||||
]
|
||||
|
||||
# You also need to reach a specific rank on Acid God MASTER.
|
||||
# This condition lowers every 7 days.
|
||||
# After the first 4 weeks, you only need to finish ep. VII.
|
||||
for i, typ in enumerate([
|
||||
MapAreaConditionType.RANK_SSSP.value,
|
||||
MapAreaConditionType.RANK_SSS.value,
|
||||
MapAreaConditionType.RANK_SS.value,
|
||||
MapAreaConditionType.RANK_S.value,
|
||||
]):
|
||||
start = start_date + timedelta(days=7 * i)
|
||||
end = start_date + timedelta(days=7 * (i + 1)) - timedelta(seconds=1)
|
||||
|
||||
task_track_map_conditions.append(
|
||||
{
|
||||
"type": typ,
|
||||
"conditionId": 265103,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start.strftime(self.date_time_format),
|
||||
"endDate": end.strftime(self.date_time_format),
|
||||
}
|
||||
)
|
||||
|
||||
conditions.extend(
|
||||
[
|
||||
{
|
||||
"mapAreaId": map_area_id,
|
||||
"length": len(task_track_map_conditions),
|
||||
"mapAreaConditionList": task_track_map_conditions,
|
||||
}
|
||||
for map_area_id in {3220801, 3220802, 3220803, 3220804}
|
||||
]
|
||||
)
|
||||
|
||||
# To unlock the final map area (Forsaken Tale), achieve a specific rank
|
||||
# on the 4 task tracks in the previous map areas. This condition also lowers
|
||||
# every 7 days, similar to Acid God.
|
||||
# After 28 days, you only need to finish the other 4 areas in ep. Ascension.
|
||||
forsaken_tale_conditions = []
|
||||
|
||||
for i, typ in enumerate([
|
||||
MapAreaConditionType.RANK_SSSP.value,
|
||||
MapAreaConditionType.RANK_SSS.value,
|
||||
MapAreaConditionType.RANK_SS.value,
|
||||
MapAreaConditionType.RANK_S.value,
|
||||
]):
|
||||
start = start_date + timedelta(days=7 * i)
|
||||
end = start_date + timedelta(days=7 * (i + 1)) - timedelta(seconds=1)
|
||||
|
||||
forsaken_tale_conditions.extend(
|
||||
[
|
||||
{
|
||||
"type": typ,
|
||||
"conditionId": condition_id,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": start.strftime(self.date_time_format),
|
||||
"endDate": end.strftime(self.date_time_format),
|
||||
}
|
||||
for condition_id in {98203, 108603, 247503, 233903}
|
||||
]
|
||||
)
|
||||
|
||||
forsaken_tale_conditions.extend(
|
||||
[
|
||||
{
|
||||
"type": MapAreaConditionType.MAP_AREA_CLEARED.value,
|
||||
"conditionId": map_area_id,
|
||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||
"startDate": (start_date + timedelta(days=28)).strftime(self.date_time_format),
|
||||
"endDate": "2099-12-31 00:00:00",
|
||||
}
|
||||
for map_area_id in {3220801, 3220802, 3220803, 3220804}
|
||||
]
|
||||
)
|
||||
|
||||
conditions.append(
|
||||
{
|
||||
"mapAreaId": 3220805,
|
||||
"length": len(forsaken_tale_conditions),
|
||||
"mapAreaConditionList": forsaken_tale_conditions,
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"length": len(conditions),
|
||||
"gameMapAreaConditionList": conditions,
|
||||
}
|
@ -36,6 +36,8 @@ class ChuniNew(ChuniBase):
|
||||
return "215"
|
||||
if self.version == ChuniConstants.VER_CHUNITHM_LUMINOUS:
|
||||
return "220"
|
||||
if self.version == ChuniConstants.VER_CHUNITHM_LUMINOUS_PLUS:
|
||||
return "225"
|
||||
|
||||
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
# use UTC time and convert it to JST time by adding +9
|
||||
|
Loading…
x
Reference in New Issue
Block a user