Merge pull request 'develop' (#9) from develop into prism_plus_support

Reviewed-on: https://gitea.tendokyu.moe/SoulGateKey/artemis/pulls/9
This commit is contained in:
SoulGateKey
2025-04-08 04:37:17 +00:00
19 changed files with 634 additions and 105 deletions

View File

@@ -1,3 +1,6 @@
from typing import Optional
from core.utils import floor_to_nearest_005
class Mai2Constants:
GRADE = {
"D": 0,
@@ -86,7 +89,57 @@ class Mai2Constants:
"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],
#青の扉: Played 29 songs
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
4: [11023, 11106, 11221, 11222, 11300, 11374, 11458, 11523, 11619, 11663, 11746],
#青の扉: Played 11 songs
}
MAI_VERSION_LUT = {
"100": VER_MAIMAI,
"110": VER_MAIMAI_PLUS,
"120": VER_MAIMAI_GREEN,
"130": VER_MAIMAI_GREEN_PLUS,
"140": VER_MAIMAI_ORANGE,
"150": VER_MAIMAI_ORANGE_PLUS,
"160": VER_MAIMAI_PINK,
"170": VER_MAIMAI_PINK_PLUS,
"180": VER_MAIMAI_MURASAKI,
"185": VER_MAIMAI_MURASAKI_PLUS,
"190": VER_MAIMAI_MILK,
"195": VER_MAIMAI_MILK_PLUS,
"197": VER_MAIMAI_FINALE,
}
MAI2_VERSION_LUT = {
"100": VER_MAIMAI_DX,
"105": VER_MAIMAI_DX_PLUS,
"110": VER_MAIMAI_DX_SPLASH,
"115": VER_MAIMAI_DX_SPLASH_PLUS,
"120": VER_MAIMAI_DX_UNIVERSE,
"125": VER_MAIMAI_DX_UNIVERSE_PLUS,
"130": VER_MAIMAI_DX_FESTIVAL,
"135": VER_MAIMAI_DX_FESTIVAL_PLUS,
"140": VER_MAIMAI_DX_BUDDIES,
"145": VER_MAIMAI_DX_BUDDIES_PLUS,
"150": VER_MAIMAI_DX_PRISM
}
@classmethod
def game_ver_to_string(cls, ver: int):
""" Takes an internal game version (ex 13 for maimai DX) and returns a the full name of the version """
return cls.VERSION_STRING[ver]
@classmethod
def int_ver_to_game_ver(cls, ver: int, is_dx = True) -> Optional[int]:
""" Takes an int ver (ex 100 for 1.00) and returns an internal game version """
if is_dx:
return cls.MAI2_VERSION_LUT.get(str(floor_to_nearest_005(ver)), None)
else:
if ver >= 197:
return cls.VER_MAIMAI_FINALE
return cls.MAI_VERSION_LUT.get(str(floor_to_nearest_005(ver)), None)

View File

@@ -284,8 +284,8 @@ class Mai2DX(Mai2Base):
# added in PRiSM
if "userKaleidxScopeList" in upsert and len(upsert["userKaleidxScopeList"]) > 0:
for kaleidx_scope in upsert["userKaleidxScopeList"]:
await self.data.score.put_user_kaleidx_scope(user_id, kaleidx_scope)
for kaleidxscope in upsert["userKaleidxScopeList"]:
await self.data.score.put_user_kaleidxscope(user_id, kaleidxscope)
return {"returnCode": 1, "apiName": "UpsertUserAllApi"}

View File

@@ -4,7 +4,6 @@ from core.config import CoreConfig
from titles.mai2.buddiesplus import Mai2BuddiesPlus
from titles.mai2.const import Mai2Constants
from titles.mai2.config import Mai2Config
from titles.mai2.schema.score import kaleidx_scope
class Mai2Prism(Mai2BuddiesPlus):
@@ -44,24 +43,65 @@ class Mai2Prism(Mai2BuddiesPlus):
{"gateId": 2, "phaseId": 6},
{"gateId": 3, "phaseId": 6},
{"gateId": 4, "phaseId": 6},
{"gateId": 5, "phaseId": 6},
{"gateId": 6, "phaseId": 6}
]
}
async def handle_get_user_kaleidx_scope_api_request(self, data: Dict) -> Dict:
kaleidx_scope = await self.data.score.get_user_kaleidx_scope_list(data["userId"])
# kaleidxscope keyget condition judgement
# player may get key before GateFound
for gate in range(1,5):
if gate == 1 or gate == 4:
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)
if kaleidx_scope is None:
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)
kaleidxscope = await self.data.score.get_user_kaleidxscope_list(data["userId"])
if kaleidxscope is None:
return {"userId": data["userId"], "userKaleidxScopeList":[]}
kaleidx_scope_list = []
for kaleidx_scope_data in kaleidx_scope:
tmp = kaleidx_scope_data._asdict()
kaleidxscope_list = []
for kaleidxscope_data in kaleidxscope:
tmp = kaleidxscope_data._asdict()
tmp.pop("user")
tmp.pop("id")
kaleidx_scope_list.append(tmp)
kaleidxscope_list.append(tmp)
return {
"userId": data["userId"],
"userKaleidxScopeList": kaleidx_scope_list
"userKaleidxScopeList": kaleidxscope_list
}

View File

@@ -728,10 +728,11 @@ class Mai2ItemData(BaseData):
# Do an anti-join with the mai2_item_item table to exclude any
# items the users have already owned.
if exclude_owned:
sql = sql.join(
sql = sql.outerjoin(
item,
(present.c.itemKind == item.c.itemKind)
& (present.c.itemId == item.c.itemId)
& (item.c.user == user_id)
)
condition &= (item.c.itemKind.is_(None) & item.c.itemId.is_(None))

View File

@@ -176,8 +176,8 @@ playlog_2p = Table(
mysql_charset="utf8mb4",
)
kaleidx_scope = Table(
"mai2_score_kaleidx_scope",
kaleidxscope = Table(
"mai2_score_kaleidxscope",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column(
@@ -482,21 +482,21 @@ class Mai2ScoreData(BaseData):
return None
return result.scalar()
async def get_user_kaleidx_scope_list(self, user_id: int) -> Optional[List[Row]]:
sql = kaleidx_scope.select(kaleidx_scope.c.user == user_id)
async def get_user_kaleidxscope_list(self, user_id: int) -> Optional[List[Row]]:
sql = kaleidxscope.select(kaleidxscope.c.user == user_id)
result = await self.execute(sql)
if result is None:
return None
return result.fetchall()
async def put_user_kaleidx_scope(self, user_id: int, user_kaleidx_scope_data: Dict) -> Optional[int]:
user_kaleidx_scope_data["user"] = user_id
sql = insert(kaleidx_scope).values(**user_kaleidx_scope_data)
async def put_user_kaleidxscope(self, user_id: int, user_kaleidxscope_data: Dict) -> Optional[int]:
user_kaleidxscope_data["user"] = user_id
sql = insert(kaleidxscope).values(**user_kaleidxscope_data)
conflict = sql.on_duplicate_key_update(**user_kaleidx_scope_data)
conflict = sql.on_duplicate_key_update(**user_kaleidxscope_data)
result = await self.execute(conflict)
if result is None:
self.logger.error(f"put_user_kaleidx_scope: Failed to insert! user_id {user_id}")
self.logger.error(f"put_user_kaleidxscope: Failed to insert! user_id {user_id}")
return None
return result.lastrowid

View File

@@ -2,13 +2,27 @@ from core.data.schema.base import BaseData, metadata
from typing import Optional, Dict, List
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, Float
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, BIGINT, Float, INTEGER, BOOLEAN, VARCHAR
from sqlalchemy.schema import ForeignKey
from sqlalchemy.sql import func, select
from sqlalchemy.engine import Row
from sqlalchemy.dialects.mysql import insert
from datetime import datetime
opts = Table(
"mai2_static_opt",
metadata,
Column("id", BIGINT, primary_key=True, nullable=False),
Column("version", INTEGER, nullable=False),
Column("name", VARCHAR(4), nullable=False), # Axxx
Column("sequence", INTEGER, nullable=False), # release in DataConfig.xml
Column("cmReleaseVer", INTEGER, nullable=False),
Column("whenRead", TIMESTAMP, nullable=False, server_default=func.now()),
Column("isEnable", BOOLEAN, nullable=False, server_default="1"),
UniqueConstraint("version", "name", name="mai2_static_opt_uk"),
mysql_charset="utf8mb4",
)
event = Table(
"mai2_static_event",
metadata,
@@ -19,6 +33,7 @@ event = Table(
Column("name", String(255)),
Column("startDate", TIMESTAMP, server_default=func.now()),
Column("enabled", Boolean, server_default="1"),
Column("opt", ForeignKey("mai2_static_opt.id", ondelete="SET NULL", onupdate="cascade")),
UniqueConstraint("version", "eventId", "type", name="mai2_static_event_uk"),
mysql_charset="utf8mb4",
)
@@ -37,6 +52,7 @@ music = Table(
Column("addedVersion", String(255)),
Column("difficulty", Float),
Column("noteDesigner", String(255)),
Column("opt", ForeignKey("mai2_static_opt.id", ondelete="SET NULL", onupdate="cascade")),
UniqueConstraint("songId", "chartId", "version", name="mai2_static_music_uk"),
mysql_charset="utf8mb4",
)
@@ -51,6 +67,7 @@ ticket = Table(
Column("name", String(255)),
Column("price", Integer, server_default="1"),
Column("enabled", Boolean, server_default="1"),
Column("opt", ForeignKey("mai2_static_opt.id", ondelete="SET NULL", onupdate="cascade")),
UniqueConstraint("version", "ticketId", name="mai2_static_ticket_uk"),
mysql_charset="utf8mb4",
)
@@ -67,6 +84,7 @@ cards = Table(
Column("noticeStartDate", TIMESTAMP, server_default="2018-01-01 00:00:00.0"),
Column("noticeEndDate", TIMESTAMP, server_default="2038-01-01 00:00:00.0"),
Column("enabled", Boolean, server_default="1"),
Column("opt", ForeignKey("cm_static_opts.id", ondelete="SET NULL", onupdate="cascade")),
UniqueConstraint("version", "cardId", "cardName", name="mai2_static_cards_uk"),
mysql_charset="utf8mb4",
)