31 Commits

Author SHA1 Message Date
e11db14292 Merge pull request '[mai2] Prism Plus support' (#232) from SoulGateKey/artemis:prism_plus_support into develop
Reviewed-on: Hay1tsme/artemis#232
2025-10-07 17:59:48 +00:00
77152bf25c change down_revision when final merge 2025-10-07 17:59:02 +00:00
f346d8572d chuni: fix typo in upgrade script 2025-10-07 13:26:54 -04:00
21415de775 add lut 2025-09-19 23:13:57 +08:00
d16ebe27d9 Merge pull request 'develop' (#16) from develop into prism_plus_support
Reviewed-on: SoulGateKey/artemis#16
2025-09-18 13:05:46 +00:00
fa18b4c6a2 Merge pull request 'develop' (#15) from develop into prism_plus_support
Reviewed-on: SoulGateKey/artemis#15
2025-09-16 17:54:52 +00:00
46d79d156b Merge branch 'mai2_rival_support' into prism_plus_support 2025-08-01 00:55:12 +08:00
15e8eb535b Merge remote-tracking branch 'origin/prism_plus_support' into prism_plus_support 2025-07-28 20:06:24 +08:00
392fdb3783 Merge branch 'mai2_rival_support' into prism_plus_support 2025-07-28 01:04:00 +08:00
91545bb974 Merge branch 'mai2_rival_support' into prism_plus_support 2025-07-26 18:43:37 +08:00
e52362d87f Merge pull request 'develop' (#14) from develop into prism_plus_support
Reviewed-on: SoulGateKey/artemis#14
2025-07-25 18:16:21 +00:00
e3ec58b238 Merge branch 'mai2_MusicScoreApi_support' into prism_plus_support 2025-07-26 01:38:45 +08:00
0a4dc8dbb0 add Gate 7,8,9,10 support 2025-07-22 19:55:33 +08:00
180c027575 Sync Develop branch's update 2025-07-22 03:37:39 +08:00
611806828a add Gate 5,6 judgement 2025-05-15 08:19:42 +08:00
8b050e89eb fix conflict mark 2025-05-15 03:53:42 +08:00
394ec74fb7 add key unlock condition define 2025-05-15 03:14:24 +08:00
a2a333b13f Merge remote-tracking branch 'origin/prism_plus_support' into prism_plus_support
# Conflicts:
#	titles/mai2/index.py
2025-05-15 02:45:56 +08:00
933d8bea21 add import 2025-05-15 02:44:37 +08:00
f70af35343 Merge branch 'refs/heads/develop' into prism_plus_support
# Conflicts:
#	core/data/alembic/versions/16f34bf7b968_mai2_kaleidx_scope_support.py
#	core/data/alembic/versions/5cf98cfe52ad_mai2_prism_support.py
#	core/data/alembic/versions/5d7b38996e67_mai2_prism_support.py
#	core/data/alembic/versions/bdf710616ba4_mai2_add_prism_playlog_support.py
#	titles/mai2/index.py
#	titles/mai2/prism.py
#	titles/mai2/read.py
#	titles/mai2/schema/static.py
2025-05-15 02:41:55 +08:00
1e7f367d0f Merge pull request 'develop' (#9) from develop into prism_plus_support
Reviewed-on: SoulGateKey/artemis#9
2025-04-08 04:37:17 +00:00
134af15ed7 update readme.md and game_specific_info.md 2025-04-07 09:15:29 +08:00
a4bcca9171 add clientplaytimeapi support 2025-04-07 09:15:29 +08:00
d598c8fba0 add prism+ playlog support 2025-04-07 09:15:29 +08:00
3b7a577ea2 add prism+ consts and cm support 2025-04-07 09:15:29 +08:00
dd10508e68 unused database deleted 2025-04-04 09:12:08 +08:00
c0df7cd084 database support for prism
kaleidxScope Key Condition store
2025-04-04 09:10:41 +08:00
756c7ce951 update readme.md and game_specific_info.md 2025-04-02 13:56:56 +08:00
4ceac7db35 add clientplaytimeapi support 2025-04-02 13:49:46 +08:00
f8888c2392 add prism+ playlog support 2025-04-02 12:42:40 +08:00
9bc18f179d add prism+ consts and cm support 2025-04-02 12:37:43 +08:00
10 changed files with 237 additions and 41 deletions

View File

@ -1,7 +1,7 @@
"""CHUNITHM VERSE support
Revision ID: 49c295e89cd4
Revises: f6007bbf057d
Revises: 7070a6fa8cdc
Create Date: 2025-03-09 14:10:03.067328
"""
@ -13,7 +13,7 @@ from sqlalchemy.sql import func
# revision identifiers, used by Alembic.
revision = "49c295e89cd4"
down_revision = "f6007bbf057d"
down_revision = "7070a6fa8cdc"
branch_labels = None
depends_on = None
@ -60,7 +60,7 @@ def upgrade():
sa.Column("conditionType", sa.Integer()),
sa.Column("score", sa.Integer()),
sa.Column("life", sa.Integer()),
sa.Column("clearDate", sa.TIMESTAMP(), server_defaul=func.now()),
sa.Column("clearDate", sa.TIMESTAMP(), server_default=func.now()),
sa.UniqueConstraint(
"version",
"user",

View File

@ -10,6 +10,7 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '5cf98cfe52ad'
down_revision = '263884e774cc'
branch_labels = None

View File

@ -0,0 +1,29 @@
"""Mai2 add PRiSM+ playlog support
Revision ID: bdf710616ba4
Revises: 16f34bf7b968
Create Date: 2025-04-02 12:42:08.981516
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'bdf710616ba4'
down_revision = '49c295e89cd4'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('mai2_playlog', sa.Column('extBool3', sa.Boolean(), nullable=True,server_default=sa.text("NULL")))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('mai2_playlog', 'extBool3')
# ### end Alembic commands ###

View File

@ -205,32 +205,32 @@ Presents are items given to the user when they login, with a little animation (f
### Versions
| Game Code | Version ID | Version Name |
|-----------|------------|-------------------------|
| SBXL | 0 | maimai |
| SBXL | 1 | maimai PLUS |
| SBZF | 2 | maimai GreeN |
| SBZF | 3 | maimai GreeN PLUS |
| SDBM | 4 | maimai ORANGE |
| SDBM | 5 | maimai ORANGE PLUS |
| SDCQ | 6 | maimai PiNK |
| SDCQ | 7 | maimai PiNK PLUS |
| SDDK | 8 | maimai MURASAKi |
| SDDK | 9 | maimai MURASAKi PLUS |
| SDDZ | 10 | maimai MiLK |
| SDDZ | 11 | maimai MiLK PLUS |
| SDEY | 12 | maimai FiNALE |
| SDEZ | 13 | maimai DX |
| SDEZ | 14 | maimai DX PLUS |
| SDEZ | 15 | maimai DX Splash |
| SDEZ | 16 | maimai DX Splash PLUS |
| SDEZ | 17 | maimai DX UNiVERSE |
| SDEZ | 18 | maimai DX UNiVERSE PLUS |
| SDEZ | 19 | maimai DX FESTiVAL |
| SDEZ | 20 | maimai DX FESTiVAL PLUS |
| SDEZ | 21 | maimai DX BUDDiES |
| SDEZ | 22 | maimai DX BUDDiES PLUS |
| SDEZ | 23 | maimai DX PRiSM |
|----------|------------|-------------------------|
| SBXL | 0 | maimai |
| SBXL | 1 | maimai PLUS |
| SBZF | 2 | maimai GreeN |
| SBZF | 3 | maimai GreeN PLUS |
| SDBM | 4 | maimai ORANGE |
| SDBM | 5 | maimai ORANGE PLUS |
| SDCQ | 6 | maimai PiNK |
| SDCQ | 7 | maimai PiNK PLUS |
| SDDK | 8 | maimai MURASAKi |
| SDDK | 9 | maimai MURASAKi PLUS |
| SDDZ | 10 | maimai MiLK |
| SDDZ | 11 | maimai MiLK PLUS |
| SDEY | 12 | maimai FiNALE |
| SDEZ | 13 | maimai DX |
| SDEZ | 14 | maimai DX PLUS |
| SDEZ | 15 | maimai DX Splash |
| SDEZ | 16 | maimai DX Splash PLUS |
| SDEZ | 17 | maimai DX UNiVERSE |
| SDEZ | 18 | maimai DX UNiVERSE PLUS |
| SDEZ | 19 | maimai DX FESTiVAL |
| SDEZ | 20 | maimai DX FESTiVAL PLUS |
| SDEZ | 21 | maimai DX BUDDiES |
| SDEZ | 22 | maimai DX BUDDiES PLUS |
| SDEZ | 23 | maimai DX PRiSM |
| SDEZ | 24 | maimai DX PRiSM PLUS |
### Importer

View File

@ -83,6 +83,7 @@ Games listed below have been tested and confirmed working. Only game versions ol
+ BUDDiES
+ BUDDiES PLUS
+ PRiSM
+ PRiSM PLUS
+ O.N.G.E.K.I.
+ SUMMER

View File

@ -208,7 +208,8 @@ class CardMakerReader(BaseReader):
"1.35": Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS,
"1.40": Mai2Constants.VER_MAIMAI_DX_BUDDIES,
"1.45": Mai2Constants.VER_MAIMAI_DX_BUDDIES_PLUS,
"1.50": Mai2Constants.VER_MAIMAI_DX_PRISM
"1.50": Mai2Constants.VER_MAIMAI_DX_PRISM,
"1.55": Mai2Constants.VER_MAIMAI_DX_PRISM_PLUS
}
for root, dirs, files in os.walk(base_dir):

View File

@ -61,6 +61,7 @@ class Mai2Constants:
VER_MAIMAI_DX_BUDDIES = 21
VER_MAIMAI_DX_BUDDIES_PLUS = 22
VER_MAIMAI_DX_PRISM = 23
VER_MAIMAI_DX_PRISM_PLUS = 24
VERSION_STRING = (
"maimai",
@ -86,7 +87,8 @@ class Mai2Constants:
"maimai DX FESTiVAL PLUS",
"maimai DX BUDDiES",
"maimai DX BUDDiES PLUS",
"maimai DX PRiSM"
"maimai DX PRiSM",
"maimai DX PRiSM PLUS"
)
KALEIDXSCOPE_KEY_CONDITION={
1: [11009, 11008, 11100, 11097, 11098, 11099, 11163, 11162, 11161, 11228, 11229, 11231, 11463, 11464, 11465, 11538, 11539, 11541, 11620, 11622, 11623, 11737, 11738, 11164, 11230, 11466, 11540, 11621, 11739],
@ -94,9 +96,21 @@ class Mai2Constants:
2: [11102, 11234, 11300, 11529, 11542, 11612],
#白の扉: set Frame as "Latent Kingdom" (459504), play 3 or 4 songs by the composer 大国奏音 in 1 pc
3: [],
#紫の扉: need to enter redeem code 51090942171709440000
#紫の扉: JP: need to enter redeem code 51090942171709440000
4: [11023, 11106, 11221, 11222, 11300, 11374, 11458, 11523, 11619, 11663, 11746],
#の扉: Played 11 songs
#の扉: Played 11 songs
5: [11003, 11095, 11152, 11224, 11296, 11375, 11452, 11529, 11608, 11669, 11736, 11806],
#黄の扉: Use random selection to play one of the songs
6: [212, 213, 337, 270, 271, 11504, 339, 453, 11336, 11852],
#赤の扉: Played 10 songs
7: [],
#PRISM TOWER: Get the key after clearing six doors.
8: [],
#KALEIDXSCOPE_FIRST_STAGE: Clear Prism Tower
9: [],
#希望の扉: CLEAR KALEIDXSCOPE_FIRST_STAGE
10: []
#KALEIDXSCOPE_SECOND_STAGE: JP: scan the DXPASS of 希望の鍵, will automatically unlock after clearing 希望の扉 in artemis
}
MAI_VERSION_LUT = {
"100": VER_MAIMAI,
@ -125,7 +139,8 @@ class Mai2Constants:
"135": VER_MAIMAI_DX_FESTIVAL_PLUS,
"140": VER_MAIMAI_DX_BUDDIES,
"145": VER_MAIMAI_DX_BUDDIES_PLUS,
"150": VER_MAIMAI_DX_PRISM
"150": VER_MAIMAI_DX_PRISM,
"155": VER_MAIMAI_DX_PRISM_PLUS
}
@classmethod

View File

@ -32,6 +32,7 @@ from .festivalplus import Mai2FestivalPlus
from .buddies import Mai2Buddies
from .buddiesplus import Mai2BuddiesPlus
from .prism import Mai2Prism
from .prismplus import Mai2PrismPlus
class Mai2Servlet(BaseServlet):
@ -68,7 +69,8 @@ class Mai2Servlet(BaseServlet):
Mai2FestivalPlus,
Mai2Buddies,
Mai2BuddiesPlus,
Mai2Prism
Mai2Prism,
Mai2PrismPlus
]
self.logger = logging.getLogger("mai2")
@ -195,7 +197,7 @@ class Mai2Servlet(BaseServlet):
if proto == "" or proto == "https://":
t_port = f":{title_port_ssl_int}" if title_port_ssl_int != 443 else ""
else:
else:
t_port = f":{title_port_int}" if title_port_int != 80 else ""
return (
@ -343,10 +345,12 @@ class Mai2Servlet(BaseServlet):
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS
elif version >= 140 and version < 145: # BUDDiES
internal_ver = Mai2Constants.VER_MAIMAI_DX_BUDDIES
elif version >= 145 and version <150: # BUDDiES PLUS
elif version >= 145 and version < 150: # BUDDiES PLUS
internal_ver = Mai2Constants.VER_MAIMAI_DX_BUDDIES_PLUS
elif version >= 150: # PRiSM
elif version >= 150 and version < 155:
internal_ver = Mai2Constants.VER_MAIMAI_DX_PRISM
elif version >= 155:
internal_ver = Mai2Constants.VER_MAIMAI_DX_PRISM_PLUS
elif game_code == "SDGA": # Int
if version < 105: # 1.0
@ -367,10 +371,12 @@ class Mai2Servlet(BaseServlet):
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS
elif version >= 140 and version < 145: # BUDDiES
internal_ver = Mai2Constants.VER_MAIMAI_DX_BUDDIES
elif version >= 145 and version <150: # BUDDiES PLUS
elif version >= 145 and version < 150: # BUDDiES PLUS
internal_ver = Mai2Constants.VER_MAIMAI_DX_BUDDIES_PLUS
elif version >= 150: # PRiSM
elif version >= 150 and version < 155:
internal_ver = Mai2Constants.VER_MAIMAI_DX_PRISM
elif version >= 155:
internal_ver = Mai2Constants.VER_MAIMAI_DX_PRISM_PLUS
elif game_code == "SDGB": # Chn
if version < 110: # Muji
@ -386,6 +392,7 @@ class Mai2Servlet(BaseServlet):
elif version >= 150: # PRiSM
internal_ver = Mai2Constants.VER_MAIMAI_DX_PRISM
if all(c in string.hexdigits for c in endpoint) and len(endpoint) == 32:
if game_code == "SDGA":

141
titles/mai2/prismplus.py Normal file
View File

@ -0,0 +1,141 @@
from typing import Dict
from core.config import CoreConfig
from titles.mai2.prism import Mai2Prism
from titles.mai2.const import Mai2Constants
from titles.mai2.config import Mai2Config
class Mai2PrismPlus(Mai2Prism):
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
super().__init__(cfg, game_cfg)
self.version = Mai2Constants.VER_MAIMAI_DX_PRISM_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)
# hardcode lastDataVersion for CardMaker
user_data["lastDataVersion"] = "1.55.00"
return user_data
async def handle_upsert_client_play_time_api_request(self, data: Dict) -> Dict:
return{
"returnCode": 1,
"apiName": "UpsertClientPlayTimeApi"
}
async def handle_get_game_kaleidx_scope_api_request(self, data: Dict) -> Dict:
return {
"gameKaleidxScopeList": [
{"gateId": 1, "phaseId": 6},
{"gateId": 2, "phaseId": 6},
{"gateId": 3, "phaseId": 6},
{"gateId": 4, "phaseId": 6},
{"gateId": 5, "phaseId": 6},
{"gateId": 6, "phaseId": 6},
{"gateId": 7, "phaseId": 6},
{"gateId": 8, "phaseId": 6},
{"gateId": 9, "phaseId": 6},
{"gateId": 10, "phaseId": 13}
]
}
async def handle_get_user_kaleidx_scope_api_request(self, data: Dict) -> Dict:
# kaleidxscope keyget condition judgement
# player may get key before GateFound
for gate in range(1,11):
if gate == 1 or gate == 4 or gate == 6:
condition_satisfy = 0
for condition in Mai2Constants.KALEIDXSCOPE_KEY_CONDITION[gate]:
score_list = await self.data.score.get_best_scores(user_id=data["userId"], song_id=condition)
if score_list:
condition_satisfy = condition_satisfy + 1
if len(Mai2Constants.KALEIDXSCOPE_KEY_CONDITION[gate]) == condition_satisfy:
new_kaleidxscope = {'gateId': gate, "isKeyFound": True}
await self.data.score.put_user_kaleidxscope(data["userId"], new_kaleidxscope)
elif gate == 2:
user_profile = await self.data.profile.get_profile_detail(user_id=data["userId"], version=self.version)
user_frame = user_profile["frameId"]
if user_frame == 459504:
playlogs = await self.data.score.get_playlogs(user_id=data["userId"], idx=0, limit=0)
playlog_dict = {}
for playlog in playlogs:
playlog_id = playlog["playlogId"]
if playlog_id not in playlog_dict:
playlog_dict[playlog_id] = []
playlog_dict[playlog_id].append(playlog["musicId"])
valid_playlogs = []
allowed_music = set(Mai2Constants.KALEIDXSCOPE_KEY_CONDITION[2])
for playlog_id, music_ids in playlog_dict.items():
if len(music_ids) != len(set(music_ids)):
continue
all_valid = True
for mid in music_ids:
if mid not in allowed_music:
all_valid = False
break
if all_valid:
valid_playlogs.append(playlog_id)
if valid_playlogs:
new_kaleidxscope = {'gateId': 2, "isKeyFound": True}
await self.data.score.put_user_kaleidxscope(data["userId"], new_kaleidxscope)
elif gate == 5:
playlogs = await self.data.score.get_playlogs(user_id=data["userId"], idx=0, limit=0)
allowed_music = set(Mai2Constants.KALEIDXSCOPE_KEY_CONDITION[5])
valid_playlogs = []
for playlog in playlogs:
if playlog["extBool2"] == 1 and playlog["musicId"] in allowed_music:
valid_playlogs.append(playlog["playlogId"]) # 直接记录 playlogId
if valid_playlogs:
new_kaleidxscope = {'gateId': 5, "isKeyFound": True}
await self.data.score.put_user_kaleidxscope(data["userId"], new_kaleidxscope)
elif gate == 7:
played_kaleidxscope_list = await self.data.score.get_user_kaleidxscope_list(data["userId"])
check_results = {}
for i in range(1,7):
check_results[i] = False
for played_kaleidxscope in played_kaleidxscope_list:
if played_kaleidxscope[2] == i and played_kaleidxscope[5] == True:
check_results[i] = True
break
all_true = all(check_results.values())
if all_true:
new_kaleidxscope = {'gateId': 7, "isKeyFound": True}
await self.data.score.put_user_kaleidxscope(data["userId"], new_kaleidxscope)
elif gate == 10:
played_kaleidxscope_list = await self.data.score.get_user_kaleidxscope_list(data["userId"])
for played_kaleidxscope in played_kaleidxscope_list:
if played_kaleidxscope[2] == 9 and played_kaleidxscope[5] == True:
new_kaleidxscope = {'gateId': 10, "isGateFound": True, "isKeyFound": True}
await self.data.score.put_user_kaleidxscope(data["userId"], new_kaleidxscope)
kaleidxscope = await self.data.score.get_user_kaleidxscope_list(data["userId"])
if kaleidxscope is None:
return {"userId": data["userId"], "userKaleidxScopeList":[]}
kaleidxscope_list = []
for kaleidxscope_data in kaleidxscope:
tmp = kaleidxscope_data._asdict()
tmp.pop("user")
tmp.pop("id")
kaleidxscope_list.append(tmp)
return {
"userId": data["userId"],
"userKaleidxScopeList": kaleidxscope_list
}

View File

@ -148,7 +148,8 @@ playlog = Table(
Column("extNum2", Integer),
Column("extNum4", Integer),
Column("extBool1", Boolean), # new with buddies
Column("extBool2", Boolean), # new with prism
Column("extBool2", Boolean), # new with prism IsRandomSelect
Column("extBool3", Boolean), # new with prism+ IsTrackSkip
Column("trialPlayAchievement", Integer),
mysql_charset="utf8mb4",
)