mirror of
https://gitea.tendokyu.moe/Hay1tsme/artemis.git
synced 2026-02-12 02:37:28 +08:00
chuni: fix map area/unlock challenge conditions (#237)
- Document all map area/unlock challenge condition IDs - Add conditions for missing secret maps in LUMINOUS PLUS/VERSE Reviewed-on: https://gitea.tendokyu.moe/Hay1tsme/artemis/pulls/237 Reviewed-by: Dniel97 <dniel97@noreply.gitea.tendokyu.moe> Co-authored-by: beerpsi <beerpsi@duck.com> Co-committed-by: beerpsi <beerpsi@duck.com>
This commit is contained in:
@@ -112,30 +112,138 @@ class ChuniConstants:
|
|||||||
return cls.VERSION_LUT.get(str(floor_to_nearest_005(ver)), None)
|
return cls.VERSION_LUT.get(str(floor_to_nearest_005(ver)), None)
|
||||||
|
|
||||||
class MapAreaConditionType(IntEnum):
|
class MapAreaConditionType(IntEnum):
|
||||||
"""Condition types for the GetGameMapAreaConditionApi endpoint. Incomplete.
|
"""
|
||||||
|
Condition IDs for the `GetGameMapAreaConditionApi` and `GetGameUCConditionApi` requests.
|
||||||
|
|
||||||
For the MAP_CLEARED/MAP_AREA_CLEARED/TROPHY_OBTAINED conditions, the conditionId
|
- "Item" or "locked item" refers to the map area, unlock challenge or
|
||||||
is the map/map area/trophy.
|
Linked VERSE locked using this system.
|
||||||
|
- "Chart ID" refers to musicID \\* 100 + difficulty, where difficulty is 0 for BASIC
|
||||||
For the RANK_*/ALL_JUSTICE conditions, the conditionId is songId * 100 + difficultyId.
|
up to 6 for WORLD'S END. For example, Halcyon ULTIMA is 17305.
|
||||||
For example, Halcyon [ULTIMA] would be 173 * 100 + 4 = 17304.
|
"""
|
||||||
|
|
||||||
|
INVALID = 0
|
||||||
|
"""
|
||||||
|
Invalid condition type. Should cause the hidden item to be automatically unlocked,
|
||||||
|
but seemingly only works with map areas.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ALWAYS_UNLOCKED = 0
|
|
||||||
|
|
||||||
MAP_CLEARED = 1
|
MAP_CLEARED = 1
|
||||||
|
"""Finish the map with ID `conditionId`."""
|
||||||
|
|
||||||
MAP_AREA_CLEARED = 2
|
MAP_AREA_CLEARED = 2
|
||||||
|
"""Finish the map area with ID `conditionId`."""
|
||||||
|
|
||||||
TROPHY_OBTAINED = 3
|
TROPHY_OBTAINED = 3
|
||||||
|
"""Unlock the trophy with ID `conditionId`."""
|
||||||
|
|
||||||
|
TROPHY_EQUIPPED = 4
|
||||||
|
"""
|
||||||
|
Equip the trophy with ID `conditionId`. The item is locked again when the trophy is
|
||||||
|
unequipped.
|
||||||
|
"""
|
||||||
|
|
||||||
|
NAMEPLATE_OBTAINED = 5
|
||||||
|
"""Unlock the nameplate with ID `conditionId`."""
|
||||||
|
|
||||||
|
NAMEPLATE_EQUIPPED = 6
|
||||||
|
"""
|
||||||
|
Equip the nameplate with ID `conditionId`. The item is locked again when the nameplate
|
||||||
|
is unequipped.
|
||||||
|
"""
|
||||||
|
|
||||||
|
CHARACTER_OBTAINED = 7
|
||||||
|
"""Unlock the character with ID `conditionId`."""
|
||||||
|
|
||||||
|
CHARACTER_EQUIPPED = 8
|
||||||
|
"""
|
||||||
|
Equip the character with ID `conditionId`. The item is locked again when the character
|
||||||
|
is unequipped.
|
||||||
|
"""
|
||||||
|
|
||||||
|
CHARACTER_TRANSFORM_EQUIPPED = 9
|
||||||
|
"""
|
||||||
|
Equip the character, with the character transform ID `conditionId`. The item is locked again
|
||||||
|
if the incorrect character is equipped, or the correct character is equipped with the wrong
|
||||||
|
transform.
|
||||||
|
"""
|
||||||
|
|
||||||
|
MUSIC_OBTAINED = 10
|
||||||
|
"""Unlock the music with ID `conditionId`."""
|
||||||
|
|
||||||
|
AVATAR_ACCESSORY_OBTAINED = 11
|
||||||
|
"""Unlock the avatar accessory with ID `conditionId`."""
|
||||||
|
|
||||||
|
AVATAR_ACCESSORY_EQUIPPED = 12
|
||||||
|
"""
|
||||||
|
Equip the avatar accessory with ID `conditionId`. The item is locked again when the avatar
|
||||||
|
accessory is unequipped.
|
||||||
|
"""
|
||||||
|
|
||||||
|
MAP_ICON_OBTAINED = 13
|
||||||
|
"""Unlock the map icon with ID `conditionId`."""
|
||||||
|
|
||||||
|
MAP_ICON_EQUIPPED = 14
|
||||||
|
"""
|
||||||
|
Equip the map icon with ID `conditionId`. The item is locked again when the map icon is
|
||||||
|
unequipped.
|
||||||
|
"""
|
||||||
|
|
||||||
|
SYSTEM_VOICE_OBTAINED = 15
|
||||||
|
"""Unlock the system voice with ID `conditionId`."""
|
||||||
|
|
||||||
|
SYSTEM_VOICE_EQUIPPED = 16
|
||||||
|
"""
|
||||||
|
Equip the system voice with ID `conditionId`. The item is locked again when the system voice
|
||||||
|
is unequipped.
|
||||||
|
"""
|
||||||
|
|
||||||
|
ALL_JUSTICE_CRITICAL = 17
|
||||||
|
"""Obtain ALL JUSTICE CRITICAL on the chart given by `conditionId`."""
|
||||||
|
|
||||||
RANK_SSSP = 18
|
RANK_SSSP = 18
|
||||||
|
"""Obtain rank SSS+ on the chart given by `conditionId`."""
|
||||||
|
|
||||||
RANK_SSS = 19
|
RANK_SSS = 19
|
||||||
|
"""Obtain rank SSS on the chart given by `conditionId`."""
|
||||||
|
|
||||||
RANK_SSP = 20
|
RANK_SSP = 20
|
||||||
|
"""Obtain rank SS+ on the chart given by `conditionId`."""
|
||||||
|
|
||||||
RANK_SS = 21
|
RANK_SS = 21
|
||||||
|
"""Obtain rank SS on the chart given by `conditionId`."""
|
||||||
|
|
||||||
RANK_SP = 22
|
RANK_SP = 22
|
||||||
|
"""Obtain rank S+ on the chart given by `conditionId`."""
|
||||||
|
|
||||||
RANK_S = 23
|
RANK_S = 23
|
||||||
|
"""Obtain rank S on the chart given by `conditionId`."""
|
||||||
|
|
||||||
|
RANK_AAA = 24
|
||||||
|
"""Obtain rank AAA on the chart given by `conditionId`."""
|
||||||
|
|
||||||
|
RANK_AA = 25
|
||||||
|
"""Obtain rank AA on the chart given by `conditionId`."""
|
||||||
|
|
||||||
|
RANK_A = 26
|
||||||
|
"""Obtain rank A on the chart given by `conditionId`."""
|
||||||
|
|
||||||
|
MINIMUM_BEST_30_AVERAGE = 27
|
||||||
|
"""Obtain a best 30 average of at least `conditionId / 100`."""
|
||||||
|
|
||||||
ALL_JUSTICE = 28
|
ALL_JUSTICE = 28
|
||||||
|
"""Obtain ALL JUSTICE on the chart given by `conditionId`."""
|
||||||
|
|
||||||
|
FULL_COMBO = 29
|
||||||
|
"""Obtain FULL COMBO on the chart given by `conditionId`."""
|
||||||
|
|
||||||
|
UNLOCK_CHALLENGE_DISCOVERED = 30
|
||||||
|
"""Discover/unlock the unlock challenge with ID `conditionId`."""
|
||||||
|
|
||||||
|
UNLOCK_CHALLENGE_CLEARED = 31
|
||||||
|
"""Clear the unlock challenge with ID `conditionId`."""
|
||||||
|
|
||||||
|
MINIMUM_RATING = 32
|
||||||
|
"""Obtain a rating of at least `conditionId / 100`."""
|
||||||
|
|
||||||
|
|
||||||
class MapAreaConditionLogicalOperator(Enum):
|
class MapAreaConditionLogicalOperator(Enum):
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
|
from sqlalchemy.engine import Row
|
||||||
|
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
from titles.chuni.config import ChuniConfig
|
from titles.chuni.config import ChuniConfig
|
||||||
from titles.chuni.const import (
|
from titles.chuni.const import (
|
||||||
@@ -11,6 +13,73 @@ from titles.chuni.const import (
|
|||||||
from titles.chuni.sunplus import ChuniSunPlus
|
from titles.chuni.sunplus import ChuniSunPlus
|
||||||
|
|
||||||
|
|
||||||
|
class MysticAreaConditions:
|
||||||
|
"""The "Mystic Rainbow of <VERSION>" map is a special reward map for obtaining
|
||||||
|
rainbow statues. There's one gold statue area that's unlocked when at least one
|
||||||
|
original map is finished, and additional rainbow statue areas are added as new
|
||||||
|
original maps are added.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, events_by_id: dict[int, Row], map_area_1_id: int, date_time_format: str
|
||||||
|
):
|
||||||
|
self.events_by_id = events_by_id
|
||||||
|
self.date_time_format = date_time_format
|
||||||
|
|
||||||
|
self._map_area_1_conditions = {
|
||||||
|
"mapAreaId": map_area_1_id,
|
||||||
|
"length": 0,
|
||||||
|
"mapAreaConditionList": [],
|
||||||
|
}
|
||||||
|
self._map_area_1_added = False
|
||||||
|
self._conditions = []
|
||||||
|
|
||||||
|
@property
|
||||||
|
def conditions(self):
|
||||||
|
return self._conditions
|
||||||
|
|
||||||
|
def add_condition(
|
||||||
|
self, map_flag_event_id: int, condition_map_id: int, mystic_map_area_id: int
|
||||||
|
):
|
||||||
|
if (event := self.events_by_id.get(map_flag_event_id)) is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
start_date = event["startDate"].strftime(self.date_time_format)
|
||||||
|
|
||||||
|
self._map_area_1_conditions["mapAreaConditionList"].append(
|
||||||
|
{
|
||||||
|
"type": MapAreaConditionType.MAP_CLEARED.value,
|
||||||
|
"conditionId": condition_map_id,
|
||||||
|
"logicalOpe": MapAreaConditionLogicalOperator.OR.value,
|
||||||
|
"startDate": start_date,
|
||||||
|
"endDate": "2099-12-31 00:00:00",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self._map_area_1_conditions["length"] = len(
|
||||||
|
self._map_area_1_conditions["mapAreaConditionList"]
|
||||||
|
)
|
||||||
|
|
||||||
|
if not self._map_area_1_added:
|
||||||
|
self._conditions.append(self._map_area_1_conditions)
|
||||||
|
self._map_area_1_added = True
|
||||||
|
|
||||||
|
self._conditions.append(
|
||||||
|
{
|
||||||
|
"mapAreaId": mystic_map_area_id,
|
||||||
|
"length": 1,
|
||||||
|
"mapAreaConditionList": [
|
||||||
|
{
|
||||||
|
"type": MapAreaConditionType.MAP_CLEARED.value,
|
||||||
|
"conditionId": condition_map_id,
|
||||||
|
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||||
|
"startDate": start_date,
|
||||||
|
"endDate": "2099-12-31 00:00:00",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ChuniLuminous(ChuniSunPlus):
|
class ChuniLuminous(ChuniSunPlus):
|
||||||
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
|
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
|
||||||
super().__init__(core_cfg, game_cfg)
|
super().__init__(core_cfg, game_cfg)
|
||||||
@@ -77,18 +146,18 @@ class ChuniLuminous(ChuniSunPlus):
|
|||||||
async def handle_get_game_map_area_condition_api_request(self, data: Dict) -> Dict:
|
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.
|
# 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.
|
# However, we can selectively show/hide events as data is imported into the server.
|
||||||
events = await self.data.static.get_enabled_events(self.version)
|
events = await self.data.static.get_enabled_events(self.version) or []
|
||||||
event_by_id = {evt["eventId"]: evt for evt in events}
|
event_by_id = {evt["eventId"]: evt for evt in events}
|
||||||
conditions = []
|
conditions = []
|
||||||
|
|
||||||
# The Mystic Rainbow of LUMINOUS map unlocks when any mainline LUMINOUS area
|
mystic_conditions = MysticAreaConditions(
|
||||||
# (ep. I, ep. II, ep. III) are completed.
|
event_by_id, 3229301, self.date_time_format
|
||||||
mystic_area_1_conditions = {
|
)
|
||||||
"mapAreaId": 3229301, # Mystic Rainbow of LUMINOUS Area 1
|
mystic_conditions.add_condition(14005, 3020701, 3229302)
|
||||||
"length": 0,
|
mystic_conditions.add_condition(14251, 3020702, 3229303)
|
||||||
"mapAreaConditionList": [],
|
mystic_conditions.add_condition(14481, 3020703, 3229304)
|
||||||
}
|
|
||||||
mystic_area_1_added = False
|
conditions += mystic_conditions.conditions
|
||||||
|
|
||||||
# Secret AREA: MUSIC GAME
|
# Secret AREA: MUSIC GAME
|
||||||
if 14029 in event_by_id:
|
if 14029 in event_by_id:
|
||||||
@@ -229,114 +298,6 @@ class ChuniLuminous(ChuniSunPlus):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
# LUMINOUS ep. I
|
|
||||||
if 14005 in event_by_id:
|
|
||||||
start_date = event_by_id[14005]["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": 3020701,
|
|
||||||
"logicalOpe": MapAreaConditionLogicalOperator.OR.value,
|
|
||||||
"startDate": start_date,
|
|
||||||
"endDate": "2099-12-31 00:00:00.0",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
conditions.append(
|
|
||||||
{
|
|
||||||
"mapAreaId": 3229302, # Mystic Rainbow of LUMINOUS Area 2,
|
|
||||||
"length": 1,
|
|
||||||
# Unlocks when LUMINOUS ep. I is completed.
|
|
||||||
"mapAreaConditionList": [
|
|
||||||
{
|
|
||||||
"type": MapAreaConditionType.MAP_CLEARED.value,
|
|
||||||
"conditionId": 3020701,
|
|
||||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
|
||||||
"startDate": start_date,
|
|
||||||
"endDate": "2099-12-31 00:00:00.0",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# 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(
|
|
||||||
{
|
|
||||||
"type": MapAreaConditionType.MAP_CLEARED.value,
|
|
||||||
"conditionId": 3020702,
|
|
||||||
"logicalOpe": MapAreaConditionLogicalOperator.OR.value,
|
|
||||||
"startDate": start_date,
|
|
||||||
"endDate": "2099-12-31 00:00:00.0",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
conditions.append(
|
|
||||||
{
|
|
||||||
"mapAreaId": 3229303, # Mystic Rainbow of LUMINOUS Area 3,
|
|
||||||
"length": 1,
|
|
||||||
# Unlocks when LUMINOUS ep. II is completed.
|
|
||||||
"mapAreaConditionList": [
|
|
||||||
{
|
|
||||||
"type": MapAreaConditionType.MAP_CLEARED.value,
|
|
||||||
"conditionId": 3020702,
|
|
||||||
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
|
||||||
"startDate": start_date,
|
|
||||||
"endDate": "2099-12-31 00:00:00.0",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# 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
|
# 1UM1N0U5 ep. 111
|
||||||
if 14483 in event_by_id:
|
if 14483 in event_by_id:
|
||||||
start_date = event_by_id[14483]["startDate"].replace(
|
start_date = event_by_id[14483]["startDate"].replace(
|
||||||
@@ -381,14 +342,14 @@ class ChuniLuminous(ChuniSunPlus):
|
|||||||
MapAreaConditionType.RANK_SSP.value,
|
MapAreaConditionType.RANK_SSP.value,
|
||||||
MapAreaConditionType.RANK_SP.value,
|
MapAreaConditionType.RANK_SP.value,
|
||||||
MapAreaConditionType.RANK_S.value,
|
MapAreaConditionType.RANK_S.value,
|
||||||
MapAreaConditionType.ALWAYS_UNLOCKED.value,
|
MapAreaConditionType.INVALID.value,
|
||||||
]
|
]
|
||||||
):
|
):
|
||||||
start = (start_date + timedelta(days=14 * (i + 1))).strftime(
|
start = (start_date + timedelta(days=14 * (i + 1))).strftime(
|
||||||
self.date_time_format
|
self.date_time_format
|
||||||
)
|
)
|
||||||
|
|
||||||
if typ != MapAreaConditionType.ALWAYS_UNLOCKED.value:
|
if typ != MapAreaConditionType.INVALID.value:
|
||||||
end = (
|
end = (
|
||||||
start_date + timedelta(days=14 * (i + 2)) - timedelta(seconds=1)
|
start_date + timedelta(days=14 * (i + 2)) - timedelta(seconds=1)
|
||||||
).strftime(self.date_time_format)
|
).strftime(self.date_time_format)
|
||||||
@@ -407,7 +368,7 @@ class ChuniLuminous(ChuniSunPlus):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
end = "2099-12-31 00:00:00"
|
end = "2099-12-31 00:00:00"
|
||||||
|
|
||||||
title_conditions.append(
|
title_conditions.append(
|
||||||
{
|
{
|
||||||
"type": typ,
|
"type": typ,
|
||||||
@@ -431,7 +392,7 @@ class ChuniLuminous(ChuniSunPlus):
|
|||||||
# Ultimate Force
|
# Ultimate Force
|
||||||
# For the first 14 days, the condition is to obtain all 9 "Key of ..." titles
|
# 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
|
# 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
|
# by playing the 6 areas, as well as obtaining specific ranks on
|
||||||
# [CRYSTAL_ACCESS] / Strange Love / βlαnoir
|
# [CRYSTAL_ACCESS] / Strange Love / βlαnoir
|
||||||
ultimate_force_conditions = []
|
ultimate_force_conditions = []
|
||||||
|
|
||||||
@@ -473,7 +434,7 @@ class ChuniLuminous(ChuniSunPlus):
|
|||||||
start = (start_date + timedelta(days=14 * (i + 1))).strftime(
|
start = (start_date + timedelta(days=14 * (i + 1))).strftime(
|
||||||
self.date_time_format
|
self.date_time_format
|
||||||
)
|
)
|
||||||
|
|
||||||
end = (
|
end = (
|
||||||
start_date + timedelta(days=14 * (i + 2)) - timedelta(seconds=1)
|
start_date + timedelta(days=14 * (i + 2)) - timedelta(seconds=1)
|
||||||
).strftime(self.date_time_format)
|
).strftime(self.date_time_format)
|
||||||
@@ -487,7 +448,7 @@ class ChuniLuminous(ChuniSunPlus):
|
|||||||
"startDate": start,
|
"startDate": start,
|
||||||
"endDate": end,
|
"endDate": end,
|
||||||
}
|
}
|
||||||
for condition_id in {109403, 212103, 244203}
|
for condition_id in {109403, 212103, 244203}
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from typing import Dict
|
|||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
from titles.chuni.config import ChuniConfig
|
from titles.chuni.config import ChuniConfig
|
||||||
from titles.chuni.const import ChuniConstants, MapAreaConditionLogicalOperator, MapAreaConditionType
|
from titles.chuni.const import ChuniConstants, MapAreaConditionLogicalOperator, MapAreaConditionType
|
||||||
from titles.chuni.luminous import ChuniLuminous
|
from titles.chuni.luminous import ChuniLuminous, MysticAreaConditions
|
||||||
|
|
||||||
|
|
||||||
class ChuniLuminousPlus(ChuniLuminous):
|
class ChuniLuminousPlus(ChuniLuminous):
|
||||||
@@ -66,6 +66,158 @@ class ChuniLuminousPlus(ChuniLuminous):
|
|||||||
events = await self.data.static.get_enabled_events(self.version)
|
events = await self.data.static.get_enabled_events(self.version)
|
||||||
event_by_id = {evt["eventId"]: evt for evt in events}
|
event_by_id = {evt["eventId"]: evt for evt in events}
|
||||||
conditions = []
|
conditions = []
|
||||||
|
|
||||||
|
mystic_conditions = MysticAreaConditions(
|
||||||
|
event_by_id,
|
||||||
|
3229601,
|
||||||
|
self.date_time_format,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Mystic Rainbow of LUMINOUS PLUS - LUMINOUS ep. IV
|
||||||
|
mystic_conditions.add_condition(15005, 3020704, 3229602)
|
||||||
|
|
||||||
|
# Mystic Rainbow of LUMINOUS PLUS - LUMINOUS ep. V
|
||||||
|
mystic_conditions.add_condition(15306, 3020705, 3229603)
|
||||||
|
|
||||||
|
# Mystic Rainbow of LUMINOUS PLUS - LUMINOUS ep. VI
|
||||||
|
mystic_conditions.add_condition(15451, 3020706, 3229604)
|
||||||
|
|
||||||
|
# Mystic Rainbow of LUMINOUS PLUS - LUMINOUS ep. VII
|
||||||
|
mystic_conditions.add_condition(15506, 3020707, 3229605)
|
||||||
|
|
||||||
|
conditions += mystic_conditions.conditions
|
||||||
|
|
||||||
|
# 1UM1N0U5 ep. 111 continues. The map is automatically unlocked after finishing
|
||||||
|
# LUMINOUS ep. III in LUMINOUS PLUS.
|
||||||
|
if ep_111 := event_by_id.get(15009):
|
||||||
|
start_date = ep_111["startDate"].strftime(self.date_time_format)
|
||||||
|
|
||||||
|
conditions.append({
|
||||||
|
"mapAreaId": 3229207,
|
||||||
|
"length": 1,
|
||||||
|
"mapAreaConditionList": [
|
||||||
|
{
|
||||||
|
"type": MapAreaConditionType.MAP_CLEARED.value,
|
||||||
|
"conditionId": 3020703,
|
||||||
|
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||||
|
"startDate": start_date,
|
||||||
|
"endDate": "2099-12-31 00:00:00",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
# ■・■■■■■■・■
|
||||||
|
# Finish LUMINOUS ep. IV and obtain the title 「ここは…何処なんだ…?」.
|
||||||
|
if re_fiction_o := event_by_id.get(15032):
|
||||||
|
start_date = re_fiction_o["startDate"].strftime(self.date_time_format)
|
||||||
|
|
||||||
|
conditions.append({
|
||||||
|
"mapAreaId": 3229501,
|
||||||
|
"length": 2,
|
||||||
|
"mapAreaConditionList": [
|
||||||
|
{
|
||||||
|
"type": MapAreaConditionType.MAP_CLEARED.value,
|
||||||
|
"conditionId": 3020704,
|
||||||
|
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||||
|
"startDate": start_date,
|
||||||
|
"endDate": "2099-12-31 00:00:00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": MapAreaConditionType.TROPHY_OBTAINED.value,
|
||||||
|
"conditionId": 7105,
|
||||||
|
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||||
|
"startDate": start_date,
|
||||||
|
"endDate": "2099-12-31 00:00:00",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
# The Conductor's Path
|
||||||
|
# ALL JUSTICE CRITICAL 其のエメラルドを見よ MASTER.
|
||||||
|
if the_conductors_path := event_by_id.get(15033):
|
||||||
|
start_date = the_conductors_path["startDate"].strftime(self.date_time_format)
|
||||||
|
|
||||||
|
conditions.append({
|
||||||
|
"mapAreaId": 3229701,
|
||||||
|
"length": 1,
|
||||||
|
"mapAreaConditionList": [
|
||||||
|
{
|
||||||
|
"type": MapAreaConditionType.ALL_JUSTICE_CRITICAL.value,
|
||||||
|
"conditionId": 260003,
|
||||||
|
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||||
|
"startDate": start_date,
|
||||||
|
"endDate": "2099-12-31 00:00:00",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
# Cave of RVESE
|
||||||
|
if episode__x__ := event_by_id.get(15254):
|
||||||
|
start_date = episode__x__["startDate"].strftime(self.date_time_format)
|
||||||
|
|
||||||
|
conditions.extend([
|
||||||
|
# Episode. _ _ X _ _ map area 1
|
||||||
|
# Finish the HARDCORE TANO*C collaboration map.
|
||||||
|
{
|
||||||
|
"mapAreaId": 2208801,
|
||||||
|
"length": 1,
|
||||||
|
"mapAreaConditionList": [
|
||||||
|
{
|
||||||
|
"type": MapAreaConditionType.MAP_CLEARED.value,
|
||||||
|
"conditionId": 2006533,
|
||||||
|
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||||
|
"startDate": start_date,
|
||||||
|
"endDate": "2099-12-31 00:00:00",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
# Episode. _ _ X _ _ map area 2
|
||||||
|
# Equip the title 「第壱の石版【V】」 to access the map area.
|
||||||
|
{
|
||||||
|
"mapAreaId": 2208802,
|
||||||
|
"length": 1,
|
||||||
|
"mapAreaConditionList": [
|
||||||
|
{
|
||||||
|
"type": MapAreaConditionType.TROPHY_EQUIPPED.value,
|
||||||
|
"conditionId": 7107,
|
||||||
|
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||||
|
"startDate": start_date,
|
||||||
|
"endDate": "2099-12-31 00:00:00",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
# Episode. _ _ X _ _ map area 3
|
||||||
|
# Equip the title 「第弐の石版【Λ】」 to access the map area.
|
||||||
|
{
|
||||||
|
"mapAreaId": 2208803,
|
||||||
|
"length": 1,
|
||||||
|
"mapAreaConditionList": [
|
||||||
|
{
|
||||||
|
"type": MapAreaConditionType.TROPHY_EQUIPPED.value,
|
||||||
|
"conditionId": 7104,
|
||||||
|
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||||
|
"startDate": start_date,
|
||||||
|
"endDate": "2099-12-31 00:00:00",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
# Episode. _ _ X _ _ map area 4
|
||||||
|
# Complete the 3 other map areas.
|
||||||
|
{
|
||||||
|
"mapAreaId": 2208804,
|
||||||
|
"length": 3,
|
||||||
|
"mapAreaConditionList": [
|
||||||
|
{
|
||||||
|
"type": MapAreaConditionType.MAP_AREA_CLEARED.value,
|
||||||
|
"conditionId": area_id,
|
||||||
|
"logicalOpe": MapAreaConditionLogicalOperator.AND.value,
|
||||||
|
"startDate": start_date,
|
||||||
|
"endDate": "2099-12-31 00:00:00",
|
||||||
|
}
|
||||||
|
for area_id in range(2208801, 2208804)
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
# LUMINOUS ep. Ascension
|
# LUMINOUS ep. Ascension
|
||||||
if ep_ascension := event_by_id.get(15512):
|
if ep_ascension := event_by_id.get(15512):
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from titles.chuni.const import (
|
|||||||
MapAreaConditionLogicalOperator,
|
MapAreaConditionLogicalOperator,
|
||||||
MapAreaConditionType,
|
MapAreaConditionType,
|
||||||
)
|
)
|
||||||
|
from titles.chuni.luminous import MysticAreaConditions
|
||||||
from titles.chuni.luminousplus import ChuniLuminousPlus
|
from titles.chuni.luminousplus import ChuniLuminousPlus
|
||||||
|
|
||||||
|
|
||||||
@@ -22,6 +23,38 @@ class ChuniVerse(ChuniLuminousPlus):
|
|||||||
# Does CARD MAKER 1.35 work this far up?
|
# Does CARD MAKER 1.35 work this far up?
|
||||||
user_data["lastDataVersion"] = "2.30.00"
|
user_data["lastDataVersion"] = "2.30.00"
|
||||||
return user_data
|
return user_data
|
||||||
|
|
||||||
|
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 = []
|
||||||
|
|
||||||
|
mystic_conditions = MysticAreaConditions(
|
||||||
|
event_by_id,
|
||||||
|
3230401,
|
||||||
|
self.date_time_format,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Mystic Rainbow of VERSE - VERSE ep. I
|
||||||
|
mystic_conditions.add_condition(16006, 3020798, 3230402)
|
||||||
|
|
||||||
|
# Mystic Rainbow of VERSE - VERSE ep. II
|
||||||
|
mystic_conditions.add_condition(16204, 3020799, 3230403)
|
||||||
|
|
||||||
|
# Mystic Rainbow of VERSE - VERSE ep. III
|
||||||
|
mystic_conditions.add_condition(16455, 3020800, 3230404)
|
||||||
|
|
||||||
|
# Mystic Rainbow of VERSE - VERSE ep. IV
|
||||||
|
mystic_conditions.add_condition(16607, 3020802, 3230405)
|
||||||
|
|
||||||
|
conditions += mystic_conditions.conditions
|
||||||
|
|
||||||
|
return {
|
||||||
|
"length": len(conditions),
|
||||||
|
"gameMapAreaConditionList": conditions,
|
||||||
|
}
|
||||||
|
|
||||||
async def handle_get_game_course_level_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_course_level_api_request(self, data: Dict) -> Dict:
|
||||||
unlock_challenges = await self.data.static.get_unlock_challenges(self.version)
|
unlock_challenges = await self.data.static.get_unlock_challenges(self.version)
|
||||||
@@ -81,9 +114,10 @@ class ChuniVerse(ChuniLuminousPlus):
|
|||||||
|
|
||||||
unlock_condition = conditions.get(
|
unlock_condition = conditions.get(
|
||||||
unlock_challenge_id,
|
unlock_challenge_id,
|
||||||
|
# default is to unlock for players above 5.00 rating
|
||||||
{
|
{
|
||||||
"type": MapAreaConditionType.TROPHY_OBTAINED.value, # always unlocked
|
"type": MapAreaConditionType.MINIMUM_RATING.value,
|
||||||
"conditionId": 0,
|
"conditionId": 500,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -173,7 +207,7 @@ class ChuniVerse(ChuniLuminousPlus):
|
|||||||
|
|
||||||
user_rec_music_list = [
|
user_rec_music_list = [
|
||||||
{
|
{
|
||||||
"musicId": 1, # no idea
|
"musicId": 1, # a song the player recently played
|
||||||
# recMusicList is a semi colon-separated list of music IDs and their order comma separated
|
# recMusicList is a semi colon-separated list of music IDs and their order comma separated
|
||||||
# for some reason, not all music ids are shown in game?!
|
# for some reason, not all music ids are shown in game?!
|
||||||
"recMusicList": ";".join(
|
"recMusicList": ";".join(
|
||||||
@@ -193,7 +227,8 @@ class ChuniVerse(ChuniLuminousPlus):
|
|||||||
class UserRecRating:
|
class UserRecRating:
|
||||||
ratingMin: int
|
ratingMin: int
|
||||||
ratingMax: int
|
ratingMax: int
|
||||||
# same as recMusicList in get_user_rec_music_api_request
|
# semicolon-delimited list of (musicId, level, sortingKey, score), in the
|
||||||
|
# same format as GetUserRecMusicApi
|
||||||
recMusicList: str
|
recMusicList: str
|
||||||
|
|
||||||
length: int
|
length: int
|
||||||
|
|||||||
Reference in New Issue
Block a user