'Small' update

- Add Course Mode
- Add support for downloading additional files
- Add support for `skill_fatalis` (Hikari booms your world.)

- Delete my hair X<

> WHERE IS MY TAIRITSU ?!
> Some other things are not supported now, such as... the feature which is one line above.
This commit is contained in:
Lost-MSth
2022-07-09 01:15:03 +08:00
parent af3e91b3e8
commit 9f41ef0a70
23 changed files with 1276 additions and 214 deletions

View File

@@ -128,6 +128,11 @@ class Character:
'''对外显示的uncap状态'''
return False if self.is_uncapped_override else self.is_uncapped
@property
def is_base_character(self) -> bool:
# 应该是只有对立这样
return self.character_id == 1
class UserCharacter(Character):
'''
@@ -212,7 +217,8 @@ class UserCharacter(Character):
def to_dict(self) -> dict:
if self.char_type is None:
self.select_character_info(self.user)
r = {"is_uncapped_override": self.is_uncapped_override,
r = {'base_character': self.is_base_character,
"is_uncapped_override": self.is_uncapped_override,
"is_uncapped": self.is_uncapped,
"uncap_cores": self.uncap_cores_to_dict(),
"char_type": self.char_type,
@@ -231,6 +237,10 @@ class UserCharacter(Character):
}
if self.voice:
r['voice'] = self.voice
if self.character_id == 55:
r['fatalis_is_limited'] = True # emmmmmmm
if self.character_id in [1, 6, 7, 17, 18, 24, 32, 35, 52]:
r['base_character_id'] = 1
return r
def change_uncap_override(self, user=None):

View File

@@ -17,9 +17,11 @@ class Constant:
ETO_UNCAP_BONUS_PROGRESS = 7
LUNA_UNCAP_BONUS_PROGRESS = 7
AYU_UNCAP_BONUS_PROGRESS = 5
SKILL_FATALIS_WORLD_LOCKED_TIME = 3600000
MAX_FRIEND_COUNT = 50
# You can change this to make another PTT mechanism.
BEST30_WEIGHT = 1 / 40
RECENT10_WEIGHT = 1 / 40
@@ -31,5 +33,7 @@ class Constant:
DOWNLOAD_TIME_GAP_LIMIT = Config.DOWNLOAD_TIME_GAP_LIMIT
DOWNLOAD_LINK_PREFIX = Config.DOWNLOAD_LINK_PREFIX
LINK_PLAY_UNLOCK_LENGTH = 512
LINK_PLAY_TIMEOUT = 10
LINK_PLAY_UNLOCK_LENGTH = 512 # Units: bytes
LINK_PLAY_TIMEOUT = 10 # Units: seconds
COURSE_STAMINA_COST = 4

View File

@@ -0,0 +1,289 @@
from .error import NoData
from .song import Chart
from .item import ItemFactory
class CourseChart(Chart):
def __init__(self, c=None, song_id: str = None, difficulty: int = None) -> None:
super().__init__(c, song_id, difficulty)
self.flag_as_hidden: bool = None
def from_dict(self, d: dict) -> 'CourseChart':
self.song_id = d['id']
self.difficulty = d['difficulty']
self.flag_as_hidden = d.get('flag_as_hidden', False)
return self
def to_dict(self) -> dict:
return {
'id': self.song_id,
'difficulty': self.difficulty,
'flag_as_hidden': self.flag_as_hidden
}
def insert_course_chart(self, course_id: str, song_index: int) -> None:
self.c.execute('insert into course_chart values (?,?,?,?,?)',
(course_id, self.song_id, self.difficulty, self.flag_as_hidden, song_index))
class Course:
def __init__(self, c=None) -> None:
self.c = c
self.course_id: str = None
self.course_name: str = None
self.dan_name: str = None
self.style: int = None
self.gauge_requirement: str = None
self.flag_as_hidden_when_requirements_not_met: bool = None
self.can_start: bool = None # 这有什么鬼用?
self.requirements: list = []
self.charts: list = [None, None, None, None]
self.items: list = None
def to_dict(self) -> dict:
if self.course_name is None:
self.select_course()
if not self.items:
self.select_course_item()
if not self.charts:
self.select_course_chart()
return {
'course_id': self.course_id,
'course_name': self.course_name,
'dan_name': self.dan_name,
'style': self.style,
'gauge_requirement': self.gauge_requirement,
'flag_as_hidden_when_requirements_not_met': self.flag_as_hidden_when_requirements_not_met,
'can_start': self.can_start,
'requirements': [{'value': x, 'type': 'course'} for x in self.requirements],
'songs': [x.to_dict() for x in self.charts],
'rewards': [str(x) for x in self.items]
}
def from_dict(self, d: dict) -> 'Course':
self.course_id = d['course_id']
self.course_name = d.get('course_name', '')
self.dan_name = d.get('dan_name', '')
self.style = d.get('style', 1)
self.gauge_requirement = d.get('gauge_requirement', 'default')
self.flag_as_hidden_when_requirements_not_met = d.get(
'flag_as_hidden_when_requirements_not_met', False)
self.can_start = d.get('can_start', True)
self.requirements = [x['value'] for x in d.get('requirements', [])]
self.charts = [CourseChart(self.c).from_dict(x)
for x in d.get('songs', [])]
self.items = [ItemFactory.from_str(x, self.c)
for x in d.get('rewards', [])]
return self
def from_list(self, l: list) -> 'Course':
self.course_id = l[0]
self.course_name = l[1]
self.dan_name = l[2]
self.style = l[3] if l[3] else 1
self.gauge_requirement = l[4] if l[4] else 'default'
self.flag_as_hidden_when_requirements_not_met = l[5] == 1
self.can_start = l[6] == 1
return self
def select_course(self, course_id: str = None) -> 'Course':
if course_id is not None:
self.course_id = course_id
self.c.execute(
'''select * from course where course_id = ?''', (self.course_id,))
x = self.c.fetchone()
if x is None:
raise NoData('The course `%s` is not found.' % self.course_id)
return self.from_list(x)
def select_course_chart(self) -> None:
self.c.execute(
'''select * from course_chart where course_id = ?''', (self.course_id,))
for i in self.c.fetchall():
self.charts[i[4]] = CourseChart(self.c).from_dict({
'id': i[1],
'difficulty': i[2],
'flag_as_hidden': i[3] == 1
})
def select_course_requirement(self) -> None:
self.c.execute(
'''select required_id from course_requirement where course_id = ?''', (self.course_id,))
self.requirements = [x[0] for x in self.c.fetchall()]
def select_course_item(self) -> None:
self.c.execute(
'''select * from course_item where course_id = ?''', (self.course_id,))
self.items = [ItemFactory.from_dict({
'item_id': x[1],
'type': x[2],
'amount': x[3] if x[3] else 1,
}, self.c) for x in self.c.fetchall()]
def insert_course(self) -> None:
self.c.execute(
'''insert into course values (?,?,?,?,?,?,?)''', (self.course_id, self.course_name, self.dan_name, self.style, self.gauge_requirement, self.flag_as_hidden_when_requirements_not_met, self.can_start))
def insert_course_item(self) -> None:
for i in self.items:
self.c.execute('''insert into course_item values (?,?,?,?)''',
(self.course_id, i.item_id, i.item_type, i.amount))
def insert_course_chart(self) -> None:
for i, x in enumerate(self.charts):
x.insert_course_chart(self.course_id, i)
def insert_course_requirement(self) -> None:
for i in self.requirements:
self.c.execute('''insert into course_requirement values (?,?)''',
(self.course_id, i))
def insert_all(self) -> None:
self.insert_course()
self.insert_course_item()
self.insert_course_chart()
self.insert_course_requirement()
class UserCourse(Course):
'''
用户课题类\
parameter: `user` - `User`类或子类的实例
'''
def __init__(self, c=None, user=None) -> None:
super().__init__(c)
self.user = user
self.is_completed: bool = False
self.high_score: int = None
self.best_clear_type: int = None
def to_dict(self) -> dict:
r = super().to_dict()
if self.is_completed is None:
self.select_user_course()
r.update({
'is_completed': self.is_completed,
'high_score': self.high_score,
'best_clear_type': self.best_clear_type
})
return r
def select_user_course(self, course_id: str = None) -> None:
if course_id is not None:
self.course_id = course_id
self.c.execute('''select * from user_course where user_id = ? and course_id = ?''',
(self.user.user_id, self.course_id))
x = self.c.fetchone()
if x is None:
self.is_completed = False
self.high_score = 0
self.best_clear_type = 0
else:
self.is_completed = True
self.high_score = x[2]
self.best_clear_type = x[3]
def insert_user_course(self) -> None:
self.c.execute('''insert into user_course values (?,?,?,?)''',
(self.user.user_id, self.course_id, self.high_score, self.best_clear_type))
def update_user_course(self) -> None:
self.c.execute('''update user_course set high_score = ?, best_clear_type = ? where user_id = ? and course_id = ?''',
(self.high_score, self.best_clear_type, self.user.user_id, self.course_id))
class UserCourseList:
'''
用户课题列表类\
parameter: `user` - `User`类或子类的实例
'''
def __init__(self, c=None, user=None) -> None:
self.c = c
self.user = user
self.courses: list = []
def to_dict_list(self) -> list:
return [x.to_dict() for x in self.courses]
def select_all(self) -> None:
self.c.execute('''select * from course''')
self.courses = [UserCourse(self.c, self.user).from_list(x)
for x in self.c.fetchall()]
for i in self.courses:
i.select_user_course()
i.select_course_chart()
i.select_course_item()
i.select_course_requirement()
class CoursePlay(UserCourse):
'''
课题模式打歌类联动UserPlay\
parameter: `user` - `UserOnline`类或子类的实例\
'user_play` - `UserPlay`类的实例
'''
def __init__(self, c=None, user=None, user_play=None) -> None:
super().__init__(c, user)
self.user_play = user_play
self.score: int = None
self.clear_type: int = None
def to_dict(self) -> dict:
return {
'rewards': [x.to_dict() for x in self.items],
"current_stamina": self.user.stamina.stamina,
"max_stamina_ts": self.user.stamina.max_stamina_ts,
'user_course_banners': self.user.course_banners
}
def update(self) -> None:
'''课题模式更新'''
if self.user_play.health < 0:
# 你挂了
self.user_play.course_play_state = 5
self.score = 0
self.clear_type = 0
self.user_play.update_play_state_for_course()
return None
self.user_play.course_play_state += 1
self.score += self.user_play.score
from .score import Score
if Score.get_song_state(self.clear_type) > Score.get_song_state(self.user_play.clear_type):
self.clear_type = self.user_play.clear_type
self.user_play.update_play_state_for_course()
if self.user_play.course_play_state == 4:
self.user.select_user_about_stamina()
self.select_course_item()
for i in self.items:
i.user_claim_item(self.user)
self.select_user_course()
if not self.is_completed:
self.high_score = self.score
self.best_clear_type = self.clear_type
self.is_completed = True
self.insert_user_course()
return None
flag = False
if self.score > self.high_score:
self.high_score = self.score
flag = True
if Score.get_song_state(self.clear_type) > Score.get_song_state(self.best_clear_type):
self.best_clear_type = self.clear_type
flag = True
if flag:
self.update_user_course()

View File

@@ -136,7 +136,7 @@ class DownloadList(UserDownload):
re = {}
for i in dir_list:
if os.path.isfile(os.path.join(Constant.SONG_FILE_FOLDER_PATH, song_id, i)) and i in ['0.aff', '1.aff', '2.aff', '3.aff', 'base.ogg', '3.ogg']:
if os.path.isfile(os.path.join(Constant.SONG_FILE_FOLDER_PATH, song_id, i)) and i in ['0.aff', '1.aff', '2.aff', '3.aff', 'base.ogg', '3.ogg', 'video.mp4', 'video_audio.ogg']:
x = UserDownload(self.c, self.user)
# self.downloads.append(x) # 这实际上没有用
x.song_id = song_id
@@ -156,6 +156,16 @@ class DownloadList(UserDownload):
re['audio']['3'] = {"checksum": x.hash, "url": x.url}
else:
re['audio']['3'] = {"checksum": x.hash}
elif i == 'video.mp4' or i == 'video_audio.ogg':
if 'additional_files' not in re:
re['additional_files'] = []
if self.url_flag:
re['additional_files'].append(
{"checksum": x.hash, "url": x.url, 'file_name': i})
else:
re['additional_files'].append(
{"checksum": x.hash, 'file_name': i})
else:
if 'chart' not in re:
re['chart'] = {}

View File

@@ -45,7 +45,8 @@ class UserItem(Item):
查询用户item\
parameter: `user` - `User`类或子类的实例
'''
self.user = user
if user is not None:
self.user = user
self.c.execute('''select amount from user_item where user_id=? and item_id=? and type=?''',
(self.user.user_id, self.item_id, self.item_type))
x = self.c.fetchone()
@@ -88,6 +89,7 @@ class PositiveItem(UserItem):
self.c = c
def user_claim_item(self, user):
'''添加或使用用户item注意是+amount'''
self.user = user
self.c.execute('''select amount from user_item where user_id=? and item_id=? and type=?''',
(self.user.user_id, self.item_id, self.item_type))
@@ -116,6 +118,9 @@ class ItemCore(PositiveItem):
self.item_id = core.item_id
self.amount = - core.amount if reverse else core.amount
def __str__(self) -> str:
return self.item_id + '_' + str(self.amount)
class ItemCharacter(UserItem):
item_type = 'character'
@@ -179,6 +184,9 @@ class Fragment(UserItem):
def user_claim_item(self, user):
pass
def __str__(self) -> str:
return 'fragment' + str(self.amount)
class Anni5tix(PositiveItem):
item_type = 'anni5tix'
@@ -204,6 +212,17 @@ class WorldUnlock(NormalItem):
self.is_available = True
class CourseBanner(NormalItem):
item_type = 'course_banner'
def __init__(self, c) -> None:
super().__init__(c)
self.is_available = True
def __str__(self) -> str:
return str(self.item_id)
class Single(NormalItem):
item_type = 'single'
@@ -229,7 +248,7 @@ class ProgBoost(UserItem):
世界模式prog_boost\
parameters: `user` - `UserOnline`类或子类的实例
'''
user.update_prog_boost(1)
user.update_user_one_column('prog_boost', 1)
class Stamina6(UserItem):
@@ -240,11 +259,13 @@ class Stamina6(UserItem):
def user_claim_item(self, user):
'''
世界模式记忆源点买体力
世界模式记忆源点或残片买体力+6\
顺手清一下世界模式过载状态
'''
user.select_user_about_stamina()
user.stamina.stamina += 6
user.stamina.update()
user.update_user_one_column('world_mode_locked_end_ts', -1)
class ItemFactory:
@@ -278,6 +299,8 @@ class ItemFactory:
return ProgBoost(self.c)
elif item_type == 'stamina6':
return Stamina6(self.c)
elif item_type == 'course_banner':
return CourseBanner(self.c)
else:
raise InputError('The item type `%s` is wrong.' % item_type)
@@ -303,6 +326,31 @@ class ItemFactory:
i.is_available = d.get('is_available', True)
return i
@classmethod
def from_str(cls, s: str, c=None):
if s.startswith('fragment'):
item_type = 'fragment'
item_id = 'fragment'
amount = int(s[8:])
elif s.startswith('core'):
item_type = 'core'
x = s.split('_')
item_id = x[0] + '_' + x[1]
amount = int(x[-1])
elif s.startswith('course_banner'):
item_type = 'course_banner'
item_id = s
amount = 1
else:
raise InputError('The string of item is wrong.')
i = cls().get_item(item_type)
if c is not None:
i.c = c
i.item_id = item_id
i.amount = amount
i.is_available = True
return i
class UserItemList:
'''

View File

@@ -107,7 +107,7 @@ class LocalMultiPlayer:
'''创建房间'''
if user is not None:
self.user = user
user.select_user_about_name()
user.select_user_one_column('name')
self.data_swap((1, self.user.name, self.user.song_unlock))
self.room = Room()
self.room.room_code = self.data_recv[1]
@@ -124,7 +124,7 @@ class LocalMultiPlayer:
if room is not None:
self.room = room
self.user.select_user_about_name()
self.user.select_user_one_column('name')
self.data_swap(
(2, self.user.name, self.user.song_unlock, room.room_code))
self.room.room_code = self.data_recv[1]

View File

@@ -110,7 +110,7 @@ class Purchase:
self.select()
if not self.items:
self.select_items()
self.user.select_user_about_ticket()
self.user.select_user_one_column('ticket', 0)
price_used = self.price_displayed
if self.user.ticket < price_used:
@@ -125,7 +125,7 @@ class Purchase:
x.user_claim_item(self.user)
else:
self.user.ticket -= price_used
self.user.update_user_about_ticket()
self.user.update_user_one_column('ticket')
for i in self.items:
i.user_claim_item(self.user)

View File

@@ -13,10 +13,11 @@ class SaveData:
self.clearlamps_data = []
self.clearedsongs_data = []
self.unlocklist_data = []
self.installid_data = ''
self.devicemodelname_data = ''
self.installid_data: str = ''
self.devicemodelname_data: str = ''
self.story_data = []
self.createdAt = 0
self.createdAt: int = 0
self.finalestate_data: str = "0|100|5616|85146|402821|148126|629916|492991|982851|510091|1912|942819|100606|919245|26270|781178|265839|354540|1611284|6478221|7178089|9580111|139100|2757121|1411969|2249637|3927929|26270|781178|265839|7692918|1245269|5628557|6199755|8340388|6897967|9435206|8853182|6483214|4923592|718524|8922556|7939972|1762215|877327|7939972|3229801|3217716|1642203|2487749|1624592|5357186|5362614|4202613|27003|7178029|4047038|9202383|8677179|4916716|2126424|2140654|5258529|1844588|7228940|3956629|65189|8123987|74181243|9173764|6123461|37167213|671214|171272315|1337"
def to_dict(self):
return {
@@ -45,7 +46,10 @@ class SaveData:
"version": {
"val": 1
},
"createdAt": self.createdAt
"createdAt": self.createdAt,
# 'finalestate': {
# 'val': self.finalestate_data
# }
}
def select_all(self, user) -> None:

View File

@@ -1,7 +1,11 @@
from base64 import b64encode
from os import urandom
from time import time
from .constant import Constant
from .course import CoursePlay
from .error import NoData, StaminaNotEnough
from .item import ItemCore
from .song import Chart
from .sql import Query, Sql
from .util import md5
@@ -205,16 +209,22 @@ class UserPlay(UserScore):
self.ptt: Potential = None # 临时用来计算用户ptt的
self.world_play: 'WorldPlay' = None
self.course_play_state: int = None
self.course_play: 'CoursePlay' = None
def to_dict(self) -> dict:
if self.is_world_mode is None:
if self.is_world_mode is None or self.course_play_state is None:
return {}
elif not self.is_world_mode:
return {'global_rank': self.user.global_rank, 'user_rating': self.user.rating_ptt}
else:
if self.course_play_state == 4:
r = self.course_play.to_dict()
elif self.is_world_mode:
r = self.world_play.to_dict()
r['user_rating'] = self.user.rating_ptt
r['global_rank'] = self.user.global_rank
return r
else:
r = {}
r['user_rating'] = self.user.rating_ptt
r['global_rank'] = self.user.global_rank
r['finale_play_value'] = 0 # emmmm
return r
@property
def is_protected(self) -> bool:
@@ -243,19 +253,31 @@ class UserPlay(UserScore):
return True
def get_play_state(self) -> None:
'''本应该是检查是否有token当然这里不管有没有是用来判断世界模式的'''
self.c.execute('''select stamina_multiply,fragment_multiply,prog_boost_multiply from world_songplay where user_id=:a and song_id=:b and difficulty=:c''', {
'a': self.user.user_id, 'b': self.song.song_id, 'c': self.song.difficulty})
'''检查token当然这里不管有没有是用来判断世界模式和课题模式'''
self.c.execute(
'''select * from songplay_token where token=:a ''', {'a': self.song_token})
x = self.c.fetchone()
if not x:
self.is_world_mode = False
self.course_play_state = -1
return None
self.stamina_multiply = int(x[0])
self.fragment_multiply = int(x[1])
self.prog_boost_multiply = int(x[2])
self.is_world_mode = True
self.song.set_chart(x[2], x[3])
if x[4]:
self.course_play = CoursePlay(self.c, self.user, self)
self.course_play.course_id = x[4]
self.course_play.score = x[6]
self.course_play.clear_type = x[7]
self.is_world_mode = False
self.course_play_state = x[5]
else:
self.stamina_multiply = int(x[8])
self.fragment_multiply = int(x[9])
self.prog_boost_multiply = int(x[10])
self.is_world_mode = True
self.course_play_state = -1
def set_play_state(self, stamina_multiply: int = 1, fragment_multiply: int = 100, prog_boost_multiply: int = 0) -> None:
def set_play_state_for_world(self, stamina_multiply: int = 1, fragment_multiply: int = 100, prog_boost_multiply: int = 0) -> None:
self.song_token = b64encode(urandom(64)).decode()
self.stamina_multiply = int(stamina_multiply)
self.fragment_multiply = int(fragment_multiply)
self.prog_boost_multiply = int(prog_boost_multiply)
@@ -266,10 +288,9 @@ class UserPlay(UserScore):
if x and x[0] == 1:
self.prog_boost_multiply = 300
self.c.execute('''delete from world_songplay where user_id=:a and song_id=:b and difficulty=:c''', {
'a': self.user.user_id, 'b': self.song.song_id, 'c': self.song.difficulty})
self.c.execute('''insert into world_songplay values(:a,:b,:c,:d,:e,:f)''', {
'a': self.user.user_id, 'b': self.song.song_id, 'c': self.song.difficulty, 'd': self.stamina_multiply, 'e': self.fragment_multiply, 'f': self.prog_boost_multiply})
self.clear_play_state()
self.c.execute('''insert into songplay_token values(:t,:a,:b,:c,'',-1,0,0,:d,:e,:f)''', {
'a': self.user.user_id, 'b': self.song.song_id, 'c': self.song.difficulty, 'd': self.stamina_multiply, 'e': self.fragment_multiply, 'f': self.prog_boost_multiply, 't': self.song_token})
self.user.select_user_about_current_map()
self.user.current_map.select_map_info()
@@ -277,12 +298,59 @@ class UserPlay(UserScore):
self.user.select_user_about_stamina()
if self.user.stamina.stamina < self.user.current_map.stamina_cost * self.stamina_multiply:
raise StaminaNotEnough('Stamina is not enough.')
self.user.select_user_about_character()
if not self.user.is_skill_sealed:
self.user.character.select_character_info()
if self.user.character.skill_id_displayed == 'skill_fatalis':
# 特殊判断hikari fatalis的双倍体力消耗
self.user.stamina.stamina -= self.user.current_map.stamina_cost * \
self.stamina_multiply * 2
self.user.stamina.update()
return None
self.user.stamina.stamina -= self.user.current_map.stamina_cost * self.stamina_multiply
self.user.stamina.update()
def set_play_state_for_course(self, use_course_skip_purchase: bool, course_id: str = None) -> None:
'''课题模式打歌初始化'''
self.song_token = 'c_' + b64encode(urandom(64)).decode()
if course_id is not None:
self.course_play.course_id = course_id
self.course_play_state = 0
self.course_play.score = 0
self.course_play.clear_type = 3 # 设置为PM即最大值
self.c.execute('''insert into songplay_token values(?,?,?,?,?,?,?,?,1,100,0)''', (self.song_token, self.user.user_id, self.song.song_id,
self.song.difficulty, self.course_play.course_id, self.course_play_state, self.course_play.score, self.course_play.clear_type))
self.user.select_user_about_stamina()
if use_course_skip_purchase:
x = ItemCore(self.c)
x.item_id = 'core_course_skip_purchase'
x.amount = -1
x.user_claim_item(self.user)
else:
if self.user.stamina.stamina < Constant.COURSE_STAMINA_COST:
raise StaminaNotEnough('Stamina is not enough.')
self.user.stamina.stamina -= Constant.COURSE_STAMINA_COST
self.user.stamina.update()
def update_token_for_course(self) -> None:
'''课题模式更新token并查用户体力'''
previous_token = self.song_token
self.song_token = 'c_' + b64encode(urandom(64)).decode()
self.c.execute('''update songplay_token set token=? where token=?''',
(self.song_token, previous_token))
self.user.select_user_about_stamina()
def update_play_state_for_course(self) -> None:
self.c.execute('''update songplay_token set course_state=?, course_score=?, course_clear_type=? where token=?''',
(self.course_play_state, self.course_play.score, self.course_play.clear_type, self.song_token))
def clear_play_state(self) -> None:
self.c.execute('''delete from world_songplay where user_id=:a and song_id=:b and difficulty=:c''', {
'a': self.user.user_id, 'b': self.song.song_id, 'c': self.song.difficulty})
self.c.execute('''delete from songplay_token where user_id=:a ''', {
'a': self.user.user_id})
def update_recent30(self) -> None:
'''更新此分数对应用户的recent30'''
@@ -377,6 +445,10 @@ class UserPlay(UserScore):
self.world_play = WorldPlay(self.c, self.user, self)
self.world_play.update()
# 课题模式判断
if self.course_play_state >= 0:
self.course_play.update()
class Potential:
'''

View File

@@ -32,8 +32,10 @@ class Chart:
'''select rating_pst, rating_prs, rating_ftr, rating_byn from chart where song_id=:a''', {'a': self.song_id})
x = self.c.fetchone()
if x is None:
raise NoData('The song `%s` does not exist.' % self.song_id)
self.defnum = x[self.difficulty]
self.defnum = -10
# raise NoData('The song `%s` does not exist.' % self.song_id)
else:
self.defnum = x[self.difficulty]
class Song:

View File

@@ -286,6 +286,8 @@ class UserInfo(User):
self.favorite_character = None
self.max_stamina_notification_enabled = False
self.prog_boost = 0
self.next_fragstam_ts: int = None
self.world_mode_locked_end_ts: int = None
self.__cores: list = None
self.__packs: list = None
@@ -295,6 +297,7 @@ class UserInfo(User):
self.__world_unlocks: list = None
self.__world_songs: list = None
self.curr_available_maps: list = None
self.__course_banners: list = None
@property
def cores(self) -> list:
@@ -339,6 +342,15 @@ class UserInfo(User):
return self.__world_songs
@property
def course_banners(self) -> list:
if self.__course_banners is None:
x = UserItemList(
self.c, self.user_id).select_from_type('course_banner')
self.__course_banners = [i.item_id for i in x.items]
return self.__course_banners
def select_characters(self) -> None:
self.characters = UserCharacterList(self.c, self)
self.characters.select_user_characters()
@@ -471,7 +483,11 @@ class UserInfo(User):
"max_friend": Constant.MAX_FRIEND_COUNT,
"rating": self.rating_ptt,
"join_date": self.join_date,
"global_rank": self.global_rank
"global_rank": self.global_rank,
'country': '',
'course_banners': self.course_banners,
'world_mode_locked_end_ts': self.world_mode_locked_end_ts,
'locked_char_ids': [] # [1]
}
def from_list(self, x: list) -> 'UserInfo':
@@ -510,6 +526,7 @@ class UserInfo(User):
self.stamina = UserStamina(self.c, self)
self.stamina.set_value(x[32], x[33])
self.world_mode_locked_end_ts = x[34] if x[34] else -1
return self
@@ -563,7 +580,7 @@ class UserInfo(User):
查询user表有关世界模式打歌的信息
'''
self.c.execute(
'''select character_id, max_stamina_ts, stamina, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, current_map from user where user_id=?''', (self.user_id,))
'''select character_id, max_stamina_ts, stamina, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, current_map, world_mode_locked_end_ts from user where user_id=?''', (self.user_id,))
x = self.c.fetchone()
if not x:
raise NoData('No user.', 108, -3)
@@ -575,24 +592,13 @@ class UserInfo(User):
self.character.is_uncapped = x[4] == 1
self.character.is_uncapped_override = x[5] == 1
self.current_map = UserMap(self.c, x[6], self)
def select_user_about_world_rank_score(self) -> None:
'''
查询user表有关世界模式排名的信息
'''
self.c.execute(
'''select world_rank_score from user where user_id=?''', (self.user_id,))
x = self.c.fetchone()
if not x:
raise NoData('No user.', 108, -3)
self.world_rank_score = x[0]
self.world_mode_locked_end_ts = x[7] if x[7] else -1
@property
def global_rank(self) -> int:
'''用户世界排名如果超过设定最大值返回0'''
if self.world_rank_score is None:
self.select_user_about_world_rank_score()
self.select_user_one_column('world_rank_score', 0)
if self.world_rank_score is None:
return 0
@@ -643,55 +649,32 @@ class UserInfo(User):
self.world_rank_score = score_sum
def select_user_about_ticket(self) -> None:
def select_user_one_column(self, column_name: str, default_value=None) -> None:
'''
查询user表有关记忆源点的信息
查询user表的某个属性\
请注意必须是一个普通属性,不能是一个类的实例
'''
self.c.execute('''select ticket from user where user_id = :a''', {
'a': self.user_id})
if column_name not in self.__dict__:
raise InputError('No such column.')
self.c.execute('''select %s from user where user_id = :a''' %
column_name, {'a': self.user_id})
x = self.c.fetchone()
if not x:
raise NoData('No user.', 108, -3)
self.ticket = x[0]
self.__dict__[column_name] = x[0] if x[0] else default_value
def update_user_about_ticket(self, ticket: int = None) -> None:
'''更新记忆源点'''
if ticket is not None:
self.ticket = ticket
self.c.execute('''update user set ticket = :a where user_id = :b''', {
'a': self.ticket, 'b': self.user_id})
def select_user_about_fragstam(self) -> None:
def update_user_one_column(self, column_name: str, value=None) -> None:
'''
查询user表有关碎片购买体力时间的信息
更新user表的某个属性\
请注意必须是一个普通属性,不能是一个类的实例
'''
self.c.execute('''select next_fragstam_ts from user where user_id = :a''', {
'a': self.user_id})
x = self.c.fetchone()
if not x:
raise NoData('No user.', 108, -3)
self.next_fragstam_ts = x[0] if x[0] else 0
def update_user_about_fragstam(self, next_fragstam_ts: int = None) -> None:
'''更新碎片购买体力时间'''
if next_fragstam_ts is not None:
self.next_fragstam_ts = next_fragstam_ts
self.c.execute('''update user set next_fragstam_ts = :a where user_id = :b''', {
'a': self.next_fragstam_ts, 'b': self.user_id})
def select_user_about_name(self) -> None:
'''
查询user表有关用户名的信息
'''
self.c.execute('''select name from user where user_id = :a''', {
'a': self.user_id})
x = self.c.fetchone()
if not x:
raise NoData('No user.', 108, -3)
self.name = x[0]
if column_name not in self.__dict__:
raise InputError('No such column.')
if value is not None:
self.__dict__[column_name] = value
self.c.execute('''update user set %s = :a where user_id = :b''' %
column_name, {'a': self.__dict__[column_name], 'b': self.user_id})
class UserOnline(UserInfo):
@@ -731,29 +714,8 @@ class UserOnline(UserInfo):
else:
raise FriendError('No user or the user is not your friend.', 401)
def update_prog_boost(self, prog_boost: int = None) -> None:
'''更新`prog_boost`'''
if prog_boost:
self.prog_boost = prog_boost
self.c.execute('''update user set prog_boost = :a where user_id = :b''',
{'a': self.prog_boost, 'b': self.user_id})
def change_favorite_character(self, character_id: int) -> None:
'''更改用户的favorite_character'''
self.favorite_character = UserCharacter(self.c, character_id, self)
self.c.execute('''update user set favorite_character = :a where user_id = :b''',
{'a': self.favorite_character.character_id, 'b': self.user_id})
def change_is_hide_rating(self, is_hide_rating: bool = None) -> None:
'''更改用户的is_hide_rating'''
if is_hide_rating is not None:
self.is_hide_rating = is_hide_rating
self.c.execute('''update user set is_hide_rating = :a where user_id = :b''',
{'a': self.is_hide_rating, 'b': self.user_id})
def change_max_stamina_notification_enabled(self, max_stamina_notification_enabled: bool = None) -> None:
'''更改用户的max_stamina_notification_enabled'''
if max_stamina_notification_enabled is not None:
self.max_stamina_notification_enabled = max_stamina_notification_enabled
self.c.execute('''update user set max_stamina_notification_enabled = :a where user_id = :b''',
{'a': self.max_stamina_notification_enabled, 'b': self.user_id})

View File

@@ -374,8 +374,8 @@ class Stamina:
@property
def stamina(self) -> int:
'''通过计算得到当前的正确体力值'''
stamina = int(Constant.MAX_STAMINA - (self.max_stamina_ts -
int(time()*1000)) / Constant.STAMINA_RECOVER_TICK)
stamina = round(Constant.MAX_STAMINA - (self.max_stamina_ts -
int(time()*1000)) / Constant.STAMINA_RECOVER_TICK)
if stamina >= Constant.MAX_STAMINA:
if self.__stamina >= Constant.MAX_STAMINA:
@@ -383,12 +383,12 @@ class Stamina:
else:
stamina = Constant.MAX_STAMINA
return stamina if stamina > 0 else 0
return stamina
@stamina.setter
def stamina(self, value: int) -> None:
'''设置体力值此处会导致max_stamina_ts变化'''
self.__stamina = int(value)
self.__stamina = round(value)
self.max_stamina_ts = int(
time()*1000) - (self.__stamina-Constant.MAX_STAMINA) * Constant.STAMINA_RECOVER_TICK
@@ -464,7 +464,8 @@ class WorldPlay:
"overdrive": self.character_used.overdrive.get_value(self.character_used.level)
},
"current_stamina": self.user.stamina.stamina,
"max_stamina_ts": self.user.stamina.max_stamina_ts
"max_stamina_ts": self.user.stamina.max_stamina_ts,
'world_mode_locked_end_ts': self.user.world_mode_locked_end_ts
}
if self.overdrive_extra is not None:
@@ -536,7 +537,7 @@ class WorldPlay:
def update(self) -> None:
'''世界模式更新'''
if self.user_play.prog_boost_multiply != 0:
self.user.update_prog_boost(0)
self.user.update_user_one_column('prog_boost', 0)
self.user_play.clear_play_state()
self.user.select_user_about_world_play()
@@ -591,8 +592,8 @@ class WorldPlay:
self._skill_vita()
def after_climb(self) -> None:
factory_dict = {'eto_uncap': self._eto_uncap,
'ayu_uncap': self._ayu_uncap, 'luna_uncap': self._luna_uncap}
factory_dict = {'eto_uncap': self._eto_uncap, 'ayu_uncap': self._ayu_uncap,
'luna_uncap': self._luna_uncap, 'skill_fatalis': self._skill_fatalis}
if self.character_used.skill_id_displayed in factory_dict:
factory_dict[self.character_used.skill_id_displayed]()
@@ -658,3 +659,10 @@ class WorldPlay:
self.step_value = 0
self.user.current_map.reclimb(self.step_value)
def _skill_fatalis(self) -> None:
'''hikari fatalis技能世界模式超载打完休息60分钟'''
self.user.world_mode_locked_end_ts = int(
time()*1000) + Constant.SKILL_FATALIS_WORLD_LOCKED_TIME
self.user.update_user_one_column('world_mode_locked_end_ts')

View File

@@ -0,0 +1,449 @@
[
{
"course_id": "4.0-dan-1",
"style": 1,
"songs": [
{
"flag_as_hidden": false,
"difficulty": 1,
"id": "inkarusi"
},
{
"flag_as_hidden": false,
"difficulty": 1,
"id": "snowwhite"
},
{
"flag_as_hidden": false,
"difficulty": 1,
"id": "sakurafubuki"
},
{
"flag_as_hidden": false,
"difficulty": 1,
"id": "purgatorium"
}
],
"gauge_requirement": "default",
"requirements": [],
"flag_as_hidden_when_requirements_not_met": false,
"dan_name": "Phase 1",
"course_name": "新世界的第一步",
"can_start": true,
"is_completed": false,
"high_score": 0,
"best_clear_type": -1,
"rewards": [
"fragment300",
"course_banner_1"
]
},
{
"course_id": "4.0-dan-2",
"style": 2,
"songs": [
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "vexaria"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "clotho"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "brandnewworld"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "oblivia"
}
],
"gauge_requirement": "default",
"requirements": [],
"flag_as_hidden_when_requirements_not_met": false,
"dan_name": "Phase 2",
"course_name": "一次心跳的加速",
"can_start": true,
"is_completed": false,
"high_score": 0,
"best_clear_type": -1,
"rewards": [
"fragment400",
"course_banner_2"
]
},
{
"course_id": "4.0-dan-3",
"style": 3,
"songs": [
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "suomi"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "grimheart"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "bookmaker"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "avril"
}
],
"gauge_requirement": "default",
"requirements": [],
"flag_as_hidden_when_requirements_not_met": false,
"dan_name": "Phase 3",
"course_name": "永不屈服的精神",
"can_start": true,
"is_completed": false,
"high_score": 0,
"best_clear_type": -1,
"rewards": [
"fragment500",
"course_banner_3"
]
},
{
"course_id": "4.0-dan-4",
"style": 4,
"songs": [
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "gekka"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "solitarydream"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "kanagawa"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "iconoclast"
}
],
"gauge_requirement": "default",
"requirements": [],
"flag_as_hidden_when_requirements_not_met": false,
"dan_name": "Phase 4",
"course_name": "光芒的永恒之地",
"can_start": true,
"is_completed": false,
"high_score": 0,
"best_clear_type": -1,
"rewards": [
"fragment600",
"course_banner_4"
]
},
{
"course_id": "4.0-dan-5",
"style": 5,
"songs": [
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "faintlight"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "eveninginscarlet"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "kyogenkigo"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "blrink"
}
],
"gauge_requirement": "default",
"requirements": [],
"flag_as_hidden_when_requirements_not_met": false,
"dan_name": "Phase 5",
"course_name": "玻璃之中的残酷",
"can_start": true,
"is_completed": false,
"high_score": 0,
"best_clear_type": -1,
"rewards": [
"fragment700",
"course_banner_5"
]
},
{
"course_id": "4.0-dan-6",
"style": 6,
"songs": [
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "vindication"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "heartjackin"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "ascent"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "macromod"
}
],
"gauge_requirement": "default",
"requirements": [],
"flag_as_hidden_when_requirements_not_met": false,
"dan_name": "Phase 6",
"course_name": "沉浸于悲喜交加",
"can_start": true,
"is_completed": false,
"high_score": 0,
"best_clear_type": -1,
"rewards": [
"fragment800",
"course_banner_6"
]
},
{
"course_id": "4.0-dan-7",
"style": 7,
"songs": [
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "galaxyfriends"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "neokosmo"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "conflict"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "redolentshape"
}
],
"gauge_requirement": "default",
"requirements": [],
"flag_as_hidden_when_requirements_not_met": false,
"dan_name": "Phase 7",
"course_name": "在命运到来之际",
"can_start": true,
"is_completed": false,
"high_score": 0,
"best_clear_type": -1,
"rewards": [
"fragment900",
"course_banner_7"
]
},
{
"course_id": "4.0-dan-8",
"style": 8,
"songs": [
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "corruption"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "neowings"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "valhallazero"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "izana"
}
],
"gauge_requirement": "default",
"requirements": [],
"flag_as_hidden_when_requirements_not_met": false,
"dan_name": "Phase 8",
"course_name": "趋于混沌的时空",
"can_start": true,
"is_completed": false,
"high_score": 0,
"best_clear_type": -1,
"rewards": [
"fragment1000",
"course_banner_8"
]
},
{
"course_id": "4.0-dan-9",
"style": 9,
"songs": [
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "felis"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "lightningscrew"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "seclusion"
},
{
"flag_as_hidden": false,
"difficulty": 2,
"id": "livefastdieyoung"
}
],
"gauge_requirement": "default",
"requirements": [
{
"value": "4.0-dan-8",
"type": "course"
}
],
"flag_as_hidden_when_requirements_not_met": true,
"dan_name": "Phase 9",
"course_name": "分崩离析的自我",
"can_start": true,
"is_completed": false,
"high_score": 0,
"best_clear_type": -1,
"rewards": [
"fragment1000",
"core_generic_10",
"course_banner_9"
]
},
{
"course_id": "4.0-dan-10",
"style": 10,
"songs": [
{
"flag_as_hidden": true,
"difficulty": 2,
"id": "cyaegha"
},
{
"flag_as_hidden": true,
"difficulty": 2,
"id": "divinelight"
},
{
"flag_as_hidden": true,
"difficulty": 2,
"id": "dantalion"
},
{
"flag_as_hidden": true,
"difficulty": 2,
"id": "aegleseeker"
}
],
"gauge_requirement": "default",
"requirements": [
{
"value": "4.0-dan-9",
"type": "course"
}
],
"flag_as_hidden_when_requirements_not_met": true,
"dan_name": "Phase 10",
"course_name": "光与对立的洪流",
"can_start": true,
"is_completed": false,
"high_score": 0,
"best_clear_type": -1,
"rewards": [
"fragment1000",
"core_generic_10",
"course_banner_10"
]
},
{
"course_id": "4.0-dan-11",
"style": 11,
"songs": [
{
"flag_as_hidden": true,
"difficulty": 3,
"id": "infinitestrife"
},
{
"flag_as_hidden": true,
"difficulty": 2,
"id": "grievouslady"
},
{
"flag_as_hidden": true,
"difficulty": 2,
"id": "fractureray"
},
{
"flag_as_hidden": true,
"difficulty": 3,
"id": "tempestissimo"
}
],
"gauge_requirement": "default",
"requirements": [
{
"value": "4.0-dan-10",
"type": "course"
}
],
"flag_as_hidden_when_requirements_not_met": true,
"dan_name": "Phase 11",
"course_name": "创世之曙光",
"can_start": true,
"is_completed": false,
"high_score": 0,
"best_clear_type": -1,
"rewards": [
"fragment2000",
"core_generic_20",
"course_banner_11"
]
}
]

View File

@@ -4,7 +4,7 @@ import json
# 数据库初始化文件删掉arcaea_database.db文件后运行即可谨慎使用
ARCAEA_SERVER_VERSION = 'v2.8.7.dev'
ARCAEA_SERVER_VERSION = 'v2.8.9.dev.616sb'
def main(path='./'):
@@ -48,7 +48,8 @@ def main(path='./'):
ban_flag text,
next_fragstam_ts int,
max_stamina_ts int,
stamina int
stamina int,
world_mode_locked_end_ts int
);''')
c.execute('''create table if not exists login(access_token text,
user_id int,
@@ -187,13 +188,17 @@ def main(path='./'):
is_locked int,
primary key(user_id, map_id)
);''')
c.execute('''create table if not exists world_songplay(user_id int,
c.execute('''create table if not exists songplay_token(token text primary key,
user_id int,
song_id text,
difficulty int,
course_id text,
course_state int,
course_score int,
course_clear_type int,
stamina_multiply int,
fragment_multiply int,
prog_boost_multiply int,
primary key(user_id, song_id, difficulty)
prog_boost_multiply int
);''')
c.execute('''create table if not exists download_token(user_id int,
song_id text,
@@ -299,49 +304,80 @@ def main(path='./'):
login_ip text,
primary key(user_id, token)
);''')
c.execute('''create table if not exists course(course_id text primary key,
course_name text,
dan_name text,
style int,
gauge_requirement text,
flag_as_hidden_when_requirements_not_met int,
can_start int
);''')
c.execute('''create table if not exists user_course(user_id int,
course_id text,
high_score int,
best_clear_type int,
primary key(user_id, course_id)
);''')
c.execute('''create table if not exists course_chart(course_id text,
song_id text,
difficulty int,
flag_as_hidden int,
song_index int,
primary key(course_id, song_id, difficulty)
);''')
c.execute('''create table if not exists course_requirement(course_id text,
required_id text,
primary key(course_id, required_id)
);''')
c.execute('''create table if not exists course_item(course_id text,
item_id text,
type text,
amount int,
primary key(course_id, item_id, type)
);''')
# 搭档初始化
char = ['hikari', 'tairitsu', 'kou', 'sapphire', 'lethe', '', 'Tairitsu(Axium)', 'Tairitsu(Grievous Lady)', 'stella', 'Hikari & Fisica', 'ilith', 'eto', 'luna', 'shirabe', 'Hikari(Zero)', 'Hikari(Fracture)', 'Hikari(Summer)', 'Tairitsu(Summer)', 'Tairitsu & Trin',
'ayu', 'Eto & Luna', 'yume', 'Seine & Hikari', 'saya', 'Tairitsu & Chuni Penguin', 'Chuni Penguin', 'haruna', 'nono', 'MTA-XXX', 'MDA-21', 'kanae', 'Hikari(Fantasia)', 'Tairitsu(Sonata)', 'sia', 'DORO*C', 'Tairitsu(Tempest)', 'brillante', 'Ilith(Summer)', 'etude', 'Alice & Tenniel', 'Luna & Mia', 'areus', 'seele', 'isabelle', 'mir', 'lagrange', 'linka', 'nami', 'Saya & Elizabeth', 'lily', 'kanae(midsummer)', 'alice&tenniel(minuet)', 'tairitsu(elegy)', 'marija', 'vita']
'ayu', 'Eto & Luna', 'yume', 'Seine & Hikari', 'saya', 'Tairitsu & Chuni Penguin', 'Chuni Penguin', 'haruna', 'nono', 'MTA-XXX', 'MDA-21', 'kanae', 'Hikari(Fantasia)', 'Tairitsu(Sonata)', 'sia', 'DORO*C', 'Tairitsu(Tempest)', 'brillante', 'Ilith(Summer)', 'etude', 'Alice & Tenniel', 'Luna & Mia', 'areus', 'seele', 'isabelle', 'mir', 'lagrange', 'linka', 'nami', 'Saya & Elizabeth', 'lily', 'kanae(midsummer)', 'alice&tenniel(minuet)', 'tairitsu(elegy)', 'marija', 'vita', 'hikari(fatalis)']
skill_id = ['gauge_easy', '', '', '', 'note_mirror', '', '', 'gauge_hard', 'frag_plus_10_pack_stellights', 'gauge_easy|frag_plus_15_pst&prs', 'gauge_hard|fail_frag_minus_100', 'frag_plus_5_side_light', 'visual_hide_hp', 'frag_plus_5_side_conflict', 'challenge_fullcombo_0gauge', 'gauge_overflow', 'gauge_easy|note_mirror', 'note_mirror', 'visual_tomato_pack_tonesphere',
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', '', '', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', '', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange', 'gauge_safe_10', 'frags_nami', 'skill_elizabeth', 'skill_lily', 'skill_kanae_midsummer', '', '', 'visual_ghost_skynotes', 'skill_vita']
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', '', '', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', '', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange', 'gauge_safe_10', 'frags_nami', 'skill_elizabeth', 'skill_lily', 'skill_kanae_midsummer', '', '', 'visual_ghost_skynotes', 'skill_vita', 'skill_fatalis']
skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', 'eto_uncap', 'luna_uncap', 'shirabe_entry_fee',
'', '', '', '', '', 'ayu_uncap', '', 'frags_yume', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
'', '', '', '', '', 'ayu_uncap', '', 'frags_yume', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
skill_unlock_level = [0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 0, 0, 0, 0, 0,
0, 0, 0, 8, 0, 14, 0, 0, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0]
0, 0, 0, 8, 0, 14, 0, 0, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0]
frag1 = [55, 55, 60, 50, 47, 0, 47, 57, 41, 22, 50, 54, 60, 56, 78, 42, 41, 61, 52, 50, 52, 32,
42, 55, 45, 58, 43, 0.5, 68, 50, 62, 45, 45, 52, 44, 27, 59, 0, 45, 50, 50, 47, 47, 61, 43, 42, 38, 25, 58, 50, 61, 45, 45, 38, 34]
42, 55, 45, 58, 43, 0.5, 68, 50, 62, 45, 45, 52, 44, 27, 59, 0, 45, 50, 50, 47, 47, 61, 43, 42, 38, 25, 58, 50, 61, 45, 45, 38, 34, 27]
prog1 = [35, 55, 47, 50, 60, 0, 60, 70, 58, 45, 70, 45, 42, 46, 61, 67, 49, 44, 28, 45, 24, 46, 52,
59, 62, 33, 58, 25, 63, 69, 50, 45, 45, 51, 34, 70, 62, 70, 45, 32, 32, 61, 47, 47, 37, 42, 50, 50, 45, 41, 61, 45, 45, 58, 50]
59, 62, 33, 58, 25, 63, 69, 50, 45, 45, 51, 34, 70, 62, 70, 45, 32, 32, 61, 47, 47, 37, 42, 50, 50, 45, 41, 61, 45, 45, 58, 50, 60]
overdrive1 = [35, 55, 25, 50, 47, 0, 72, 57, 41, 7, 10, 32, 65, 31, 61, 53, 31, 47, 38, 12, 39, 18,
48, 65, 45, 55, 44, 25, 46, 44, 33, 45, 45, 37, 25, 27, 50, 20, 45, 63, 21, 47, 61, 47, 65, 80, 38, 30, 49, 15, 34, 45, 45, 38, 67]
48, 65, 45, 55, 44, 25, 46, 44, 33, 45, 45, 37, 25, 27, 50, 20, 45, 63, 21, 47, 61, 47, 65, 80, 38, 30, 49, 15, 34, 45, 45, 38, 67, 65]
frag20 = [78, 80, 90, 75, 70, 0, 70, 79, 65, 40, 50, 80, 90, 82, 0, 61, 67, 92, 85, 50, 86, 52,
65, 85, 67, 88, 64, 0.5, 95, 70, 95, 50, 80, 87, 71, 50, 85, 0, 80, 75, 50, 70, 70, 90, 65, 80, 61, 50, 68, 60, 90, 67, 50, 60, 51]
65, 85, 67, 88, 64, 0.5, 95, 70, 95, 50, 80, 87, 71, 50, 85, 0, 80, 75, 50, 70, 70, 90, 65, 80, 61, 50, 68, 60, 90, 67, 50, 60, 51, 50]
prog20 = [61, 80, 70, 75, 90, 0, 90, 102, 84, 78, 105, 67, 63, 68, 0, 99, 80, 66, 46, 83, 40, 73,
80, 90, 93, 50, 86, 78, 89, 98, 75, 80, 50, 64, 55, 100, 90, 110, 80, 50, 74, 90, 70, 70, 56, 80, 79, 55, 65, 59, 90, 50, 90, 90, 75]
80, 90, 93, 50, 86, 78, 89, 98, 75, 80, 50, 64, 55, 100, 90, 110, 80, 50, 74, 90, 70, 70, 56, 80, 79, 55, 65, 59, 90, 50, 90, 90, 75, 100]
overdrive20 = [61, 80, 47, 75, 70, 0, 95, 79, 65, 31, 50, 59, 90, 58, 0, 78, 50, 70, 62, 49, 64,
46, 73, 95, 67, 84, 70, 78, 69, 70, 50, 80, 80, 63, 25, 50, 72, 55, 50, 95, 55, 70, 90, 70, 99, 80, 61, 40, 69, 62, 51, 90, 67, 60, 100]
46, 73, 95, 67, 84, 70, 78, 69, 70, 50, 80, 80, 63, 25, 50, 72, 55, 50, 95, 55, 70, 90, 70, 99, 80, 61, 40, 69, 62, 51, 90, 67, 60, 100, 105]
frag30 = [88, 90, 100, 75, 80, 0, 70, 79, 65, 40, 50, 90, 100, 92, 0, 61, 67, 92, 85, 50, 86, 62,
65, 85, 67, 88, 74, 0.5, 105, 80, 95, 50, 80, 87, 71, 50, 95, 0, 80, 75, 50, 70, 80, 100, 65, 80, 61, 50, 68, 60, 90, 67, 50, 60, 51]
65, 85, 67, 88, 74, 0.5, 105, 80, 95, 50, 80, 87, 71, 50, 95, 0, 80, 75, 50, 70, 80, 100, 65, 80, 61, 50, 68, 60, 90, 67, 50, 60, 51, 50]
prog30 = [71, 90, 80, 75, 100, 0, 90, 102, 84, 78, 105, 77, 73, 78, 0, 99, 80, 66, 46, 93, 40, 83,
80, 90, 93, 50, 96, 88, 99, 108, 75, 80, 50, 64, 55, 100, 100, 110, 80, 50, 74, 90, 80, 80, 56, 80, 79, 55, 65, 59, 90, 50, 90, 90, 75]
80, 90, 93, 50, 96, 88, 99, 108, 75, 80, 50, 64, 55, 100, 100, 110, 80, 50, 74, 90, 80, 80, 56, 80, 79, 55, 65, 59, 90, 50, 90, 90, 75, 100]
overdrive30 = [71, 90, 57, 75, 80, 0, 95, 79, 65, 31, 50, 69, 100, 68, 0, 78, 50, 70, 62, 59, 64,
56, 73, 95, 67, 84, 80, 88, 79, 80, 50, 80, 80, 63, 25, 50, 82, 55, 50, 95, 55, 70, 100, 80, 99, 80, 61, 40, 69, 62, 51, 90, 67, 60, 100]
56, 73, 95, 67, 84, 80, 88, 79, 80, 50, 80, 80, 63, 25, 50, 82, 55, 50, 95, 55, 70, 100, 80, 99, 80, 61, 40, 69, 62, 51, 90, 67, 60, 100, 105]
char_type = [1, 0, 0, 0, 0, 0, 0, 2, 0, 1, 2, 0, 0, 0, 2, 3, 1, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2]
0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3]
char_core = {
0: [{'core_id': 'core_hollow', 'amount': 25}, {'core_id': 'core_desolate', 'amount': 5}],
@@ -362,7 +398,7 @@ def main(path='./'):
19: [{'core_id': 'core_colorful', 'amount': 30}]
}
for i in range(0, 55):
for i in range(0, 56):
skill_requires_uncap = 1 if i == 2 else 0
if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21, 42, 43, 11, 12, 19]:
@@ -370,10 +406,11 @@ def main(path='./'):
c.execute(sql, (i, char[i], frag1[i], prog1[i], overdrive1[i], frag20[i], prog20[i], overdrive20[i],
frag30[i], prog30[i], overdrive30[i], skill_id[i], skill_unlock_level[i], skill_requires_uncap, skill_id_uncap[i], char_type[i]))
else:
if i != 5:
sql = '''insert into character values(?,?,20,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0)'''
c.execute(sql, (i, char[i], frag1[i], prog1[i], overdrive1[i], frag20[i], prog20[i], overdrive20[i],
frag30[i], prog30[i], overdrive30[i], skill_id[i], skill_unlock_level[i], skill_requires_uncap, skill_id_uncap[i], char_type[i]))
if i == 5:
continue
sql = '''insert into character values(?,?,20,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0)'''
c.execute(sql, (i, char[i], frag1[i], prog1[i], overdrive1[i], frag20[i], prog20[i], overdrive20[i],
frag30[i], prog30[i], overdrive30[i], skill_id[i], skill_unlock_level[i], skill_requires_uncap, skill_id_uncap[i], char_type[i]))
c.execute('''insert into character values(?,?,20,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0)''', (99,
'shirahime', 38, 33, 28, 66, 58, 50, 66, 58, 50, 'frags_preferred_song', 0, 0, '', 0))
@@ -383,7 +420,7 @@ def main(path='./'):
c.execute('''insert into char_item values(?,?,'core',?)''',
(i, j['core_id'], j['amount']))
cores = ['core_hollow', 'core_desolate', 'core_chunithm', 'core_crimson',
'core_ambivalent', 'core_scarlet', 'core_groove', 'core_generic', 'core_binary', 'core_colorful']
'core_ambivalent', 'core_scarlet', 'core_groove', 'core_generic', 'core_binary', 'core_colorful', 'core_course_skip_purchase']
for i in cores:
c.execute('''insert into item values(?,"core",1,'')''', (i,))
@@ -394,10 +431,14 @@ def main(path='./'):
c.execute('''insert into item values(?,"world_song",1,'')''', (i,))
world_unlocks = ["scenery_chap1", "scenery_chap2",
"scenery_chap3", "scenery_chap4", "scenery_chap5", "scenery_chap6"]
"scenery_chap3", "scenery_chap4", "scenery_chap5", "scenery_chap6", "scenery_chap7"]
for i in world_unlocks:
c.execute('''insert into item values(?,"world_unlock",1,'')''', (i,))
course_banners = ['course_banner_' + str(i) for i in range(1, 12)]
for i in course_banners:
c.execute('''insert into item values(?,"course_banner",1,'')''', (i,))
c.execute('''insert into item values(?,?,?,?)''',
('fragment', 'fragment', 1, ''))
c.execute('''insert into item values(?,?,?,?)''',
@@ -450,7 +491,25 @@ def main(path='./'):
f.close()
insert_items(c, packs)
# api权限与权限组初始化
# course初始化
try:
f = open(path+'courses.json', 'r', encoding="utf-8")
courses = json.load(f)
f.close()
except:
courses = []
if courses:
if path == './':
# 有什么好办法吗
import sys
sys.path.append('..')
from core.course import Course
for i in courses:
x = Course(c).from_dict(i)
x.insert_all()
# api权限与权限组初始化
role = ['admin', 'user', 'selecter']
role_caption = ['管理员', '用户', '查询接口']

View File

@@ -41,8 +41,8 @@
],
"price": 500,
"orig_price": 500,
"discount_from": 1615248000000,
"discount_to": 1615852799000
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "dynamix",
@@ -66,8 +66,8 @@
],
"price": 500,
"orig_price": 500,
"discount_from": 1615248000000,
"discount_to": 1615852799000
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "mirai",
@@ -91,8 +91,8 @@
],
"price": 500,
"orig_price": 500,
"discount_from": 1615248000000,
"discount_to": 1615852799000
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "yugamu",
@@ -111,8 +111,8 @@
],
"price": 500,
"orig_price": 500,
"discount_from": 1615248000000,
"discount_to": 1615852799000
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "lanota",
@@ -131,8 +131,8 @@
],
"price": 500,
"orig_price": 500,
"discount_from": 1615248000000,
"discount_to": 1615852799000
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "nijuusei",
@@ -171,8 +171,8 @@
],
"price": 500,
"orig_price": 500,
"discount_from": 1615248000000,
"discount_to": 1615852799000
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "tonesphere",
@@ -191,8 +191,8 @@
],
"price": 500,
"orig_price": 500,
"discount_from": 1615248000000,
"discount_to": 1615852799000
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "groovecoaster",
@@ -211,8 +211,8 @@
],
"price": 500,
"orig_price": 500,
"discount_from": 1615248000000,
"discount_to": 1615852799000
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "zettai",
@@ -231,8 +231,8 @@
],
"price": 500,
"orig_price": 500,
"discount_from": 1615248000000,
"discount_to": 1615852799000
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "chunithm",
@@ -269,8 +269,8 @@
],
"price": 400,
"orig_price": 400,
"discount_from": 1615248000000,
"discount_to": 1615852799000
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "omatsuri",
@@ -289,8 +289,8 @@
],
"price": 500,
"orig_price": 500,
"discount_from": 1615248000000,
"discount_to": 1615852799000
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "vs",
@@ -309,8 +309,8 @@
],
"price": 500,
"orig_price": 500,
"discount_from": 1615248000000,
"discount_to": 1615852799000
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "extend",
@@ -329,8 +329,9 @@
],
"price": 0,
"orig_price": 700,
"discount_from": 1615248000000,
"discount_to": 1615852799000
"discount_from": 1646784000000,
"discount_to": 1647388799000,
"discount_reason": "extend"
},
{
"name": "alice",
@@ -348,7 +349,9 @@
}
],
"orig_price": 500,
"price": 500
"price": 500,
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "alice_append_1",
@@ -366,7 +369,9 @@
}
],
"orig_price": 300,
"price": 300
"price": 300,
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "ongeki",
@@ -384,7 +389,9 @@
}
],
"orig_price": 400,
"price": 400
"price": 400,
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "maimai",
@@ -402,7 +409,9 @@
}
],
"orig_price": 400,
"price": 400
"price": 400,
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "chunithm_append_1",
@@ -438,7 +447,9 @@
}
],
"orig_price": 300,
"price": 300
"price": 300,
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "observer",
@@ -456,7 +467,9 @@
}
],
"orig_price": 500,
"price": 500
"price": 500,
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "observer_append_2",
@@ -474,7 +487,9 @@
}
],
"orig_price": 300,
"price": 300
"price": 300,
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "wacca",
@@ -492,7 +507,9 @@
}
],
"orig_price": 500,
"price": 500
"price": 500,
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "nijuusei_append_1",
@@ -528,7 +545,9 @@
}
],
"orig_price": 500,
"price": 500
"price": 500,
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "musedash",
@@ -538,20 +557,17 @@
"id": "musedash",
"is_available": true
},
{
"type": "character",
"id": "marija",
"is_available": true
},
{
"type": "core",
"amount": 5,
"amount": 4,
"id": "core_generic",
"is_available": true
}
],
"orig_price": 400,
"price": 400,
"orig_price": 400
"discount_from": 1646784000000,
"discount_to": 1647388799000
},
{
"name": "lanota_append_1",
@@ -570,5 +586,23 @@
],
"orig_price": 300,
"price": 300
},
{
"name": "finale",
"items": [
{
"type": "pack",
"id": "finale",
"is_available": true
},
{
"type": "core",
"amount": 5,
"id": "core_generic",
"is_available": true
}
],
"orig_price": 500,
"price": 500
}
]

View File

@@ -9,6 +9,7 @@ from . import purchase
from . import present
from . import others
from . import multiplayer
from . import course
bp = Blueprint('server', __name__, url_prefix=Config.GAME_API_PREFIX)
bp.register_blueprint(user.bp)
@@ -20,3 +21,4 @@ bp.register_blueprint(purchase.bp)
bp.register_blueprint(present.bp)
bp.register_blueprint(others.bp)
bp.register_blueprint(multiplayer.bp)
bp.register_blueprint(course.bp)

View File

@@ -0,0 +1,33 @@
from core.constant import Constant
from core.course import UserCourseList
from core.error import ArcError
from core.item import ItemCore
from core.sql import Connect
from core.user import UserOnline
from flask import Blueprint, request
from .auth import auth_required
from .func import error_return, success_return
bp = Blueprint('course', __name__, url_prefix='/course')
@bp.route('/me', methods=['GET'])
@auth_required(request)
def course_me(user_id):
with Connect() as c:
try:
user = UserOnline(c, user_id)
core = ItemCore(c)
core.item_id = 'core_course_skip_purchase'
core.select(user)
x = UserCourseList(c, user)
x.select_all()
return success_return({
'courses': x.to_dict_list(),
"stamina_cost": Constant.COURSE_STAMINA_COST,
"course_skip_purchase_ticket": core.amount
})
except ArcError as e:
return error_return(e)
return error_return()

View File

@@ -12,7 +12,7 @@ from werkzeug.datastructures import ImmutableMultiDict
from .auth import auth_required
from .func import error_return, success_return
from .present import present_info
from .purchase import bundle_pack
from .purchase import bundle_pack, bundle_bundle
from .score import song_score_friend
from .user import user_me
from .world import world_all
@@ -44,13 +44,20 @@ def download_song(user_id):
return error_return()
@bp.route('/finale/progress', methods=['GET'])
def finale_progress():
return success_return({'percentage': 100000})
map_dict = {'/user/me': user_me,
'/purchase/bundle/pack': bundle_pack,
'/serve/download/me/song': download_song,
'/game/info': game_info,
'/present/me': present_info,
'/world/map/me': world_all,
'/score/song/friend': song_score_friend}
'/score/song/friend': song_score_friend,
'/purchase/bundle/bundle': bundle_bundle,
'/finale/progress': finale_progress}
@bp.route('/compose/aggregate', methods=['GET']) # 集成式请求

View File

@@ -1,7 +1,7 @@
from time import time
from core.error import ArcError, ItemUnavailable
from core.item import ItemFactory
from core.item import ItemFactory, Stamina6
from core.purchase import Purchase, PurchaseList
from core.redeem import UserRedeem
from core.sql import Connect
@@ -40,6 +40,35 @@ def get_single(user_id):
return error_return()
@bp.route('/bundle/bundle', methods=['GET']) # 捆绑包
def bundle_bundle():
return success_return([])
# return success_return([{
# "name": "chronicles",
# "items": [{
# "type": "bundle",
# "id": "core"
# }, {
# "type": "bundle",
# "id": "prelude"
# }, {
# "type": "bundle",
# "id": "rei"
# }, {
# "type": "bundle",
# "id": "yugamu"
# }, {
# "type": "bundle",
# "id": "vs"
# }],
# "orig_price": 2000,
# "price": 2000,
# "discount_from": 1657152000000,
# "discount_to": 1758447999000,
# "discount_reason": "chronicle"
# }])
@bp.route('/me/pack', methods=['POST']) # 曲包和单曲购买
@auth_required(request)
def buy_pack_or_single(user_id):
@@ -89,7 +118,7 @@ def buy_special(user_id):
if item_id == 'stamina6':
r['stamina'] = x.user.stamina.stamina
r['max_stamina_ts'] = x.user.stamina.max_stamina_ts
r['world_mode_locked_end_ts'] = -1
return success_return(r)
except ArcError as e:
return error_return(e)
@@ -105,21 +134,21 @@ def purchase_stamina(user_id, buy_stamina_type):
return error_return()
user = UserOnline(c, user_id)
user.select_user_about_fragstam()
user.select_user_one_column('next_fragstam_ts', -1)
now = int(time()*1000)
if user.next_fragstam_ts > now:
return ItemUnavailable('Buying stamina by fragment is not available yet.', 905)
user.update_user_about_fragstam(now + 24 * 3600 * 1000)
user.select_user_about_stamina()
user.stamina.stamina += 6
user.stamina.update()
user.update_user_one_column(
'next_fragstam_ts', now + 24 * 3600 * 1000)
s = Stamina6(c)
s.user_claim_item(user)
return success_return({
"user_id": user.user_id,
"stamina": user.stamina.stamina,
"max_stamina_ts": user.stamina.max_stamina_ts,
"next_fragstam_ts": user.next_fragstam_ts
"next_fragstam_ts": user.next_fragstam_ts,
'world_mode_locked_end_ts': -1
})
except ArcError as e:
return error_return(e)

View File

@@ -1,4 +1,5 @@
from time import time
from core.course import CoursePlay
from core.error import ArcError, InputError
from core.rank import RankList
@@ -18,7 +19,7 @@ def score_token():
return success_return({'token': '1145141919810'})
@bp.route('/token/world', methods=['GET']) # 世界模式成绩上传所需的token,无验证
@bp.route('/token/world', methods=['GET']) # 世界模式成绩上传所需的token
@auth_required(request)
def score_token_world(user_id):
@@ -33,12 +34,12 @@ def score_token_world(user_id):
x = UserPlay(c, UserOnline(c, user_id))
x.song.set_chart(request.args['song_id'], int(
request.args['difficulty']))
x.set_play_state(stamina_multiply,
fragment_multiply, prog_boost_multiply)
x.set_play_state_for_world(stamina_multiply,
fragment_multiply, prog_boost_multiply)
return success_return({
"stamina": x.user.stamina.stamina,
"max_stamina_ts": x.user.stamina.max_stamina_ts,
"token": "13145201919810"
"token": x.song_token
}
)
except ArcError as e:
@@ -46,6 +47,47 @@ def score_token_world(user_id):
return error_return()
@bp.route('/token/course', methods=['GET']) # 课题模式成绩上传所需的token
@auth_required(request)
def score_token_course(user_id):
with Connect() as c:
try:
use_course_skip_purchase = request.args.get(
'use_course_skip_purchase', 'false') == 'true'
user = UserOnline(c, user_id)
user_play = UserPlay(c, user)
user_play.song_token = request.args.get('previous_token', None)
user_play.get_play_state()
status = 'created'
if user_play.course_play_state == -1:
# 没有token课题模式刚开始
course_play = CoursePlay(c, user, user_play)
course_play.course_id = request.args['course_id']
user_play.course_play = course_play
user_play.set_play_state_for_course(
use_course_skip_purchase)
elif 0 <= user_play.course_play_state <= 3:
# 验证token
user_play.update_token_for_course()
else:
# 课题模式已经结束
user_play.clear_play_state()
user.select_user_about_stamina()
status = 'cleared' if user_play.course_play_state == 4 else 'failed'
return success_return({
"stamina": user.stamina.stamina,
"max_stamina_ts": user.stamina.max_stamina_ts,
"token": user_play.song_token,
'status': status
})
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/song', methods=['POST']) # 成绩上传
@auth_required(request)
def song_score_post(user_id):

View File

@@ -177,10 +177,8 @@ def sys_set(user_id, set_arg):
user.change_favorite_character(int(value))
else:
value = 'true' == value
if 'is_hide_rating' == set_arg:
user.change_is_hide_rating(value)
elif 'max_stamina_notification_enabled' == set_arg:
user.change_max_stamina_notification_enabled(value)
if 'is_hide_rating' == set_arg or 'max_stamina_notification_enabled' == set_arg:
user.update_user_one_column(set_arg, value)
return success_return(user.to_dict())
except ArcError as e:
return error_return(e)

View File

@@ -19,7 +19,7 @@ class Config():
游戏API地址前缀
Game API's URL prefix
'''
GAME_API_PREFIX = '/years/19'
GAME_API_PREFIX = '/divide/20'
'''
--------------------
'''
@@ -30,7 +30,7 @@ class Config():
Allowed game versions
If it is blank, all are allowed.
'''
ALLOW_APPVERSION = ['3.12.6', '3.12.6c', '3.12.10', '3.12.10c']
ALLOW_APPVERSION = ['3.12.6', '3.12.6c', '4.0.0', '4.0.0c']
'''
--------------------
'''
@@ -218,7 +218,7 @@ class Config():
是否全解锁搭档
If unlocking all partners is enabled
'''
CHARACTER_FULL_UNLOCK = False
CHARACTER_FULL_UNLOCK = True
'''
--------------------
'''

View File

@@ -423,7 +423,7 @@ def all_character():
def change_character():
# 修改角色数据
skill_ids = ['No_skill', 'gauge_easy', 'note_mirror', 'gauge_hard', 'frag_plus_10_pack_stellights', 'gauge_easy|frag_plus_15_pst&prs', 'gauge_hard|fail_frag_minus_100', 'frag_plus_5_side_light', 'visual_hide_hp', 'frag_plus_5_side_conflict', 'challenge_fullcombo_0gauge', 'gauge_overflow', 'gauge_easy|note_mirror', 'note_mirror', 'visual_tomato_pack_tonesphere',
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', 'frags_kou', 'visual_ink', 'shirabe_entry_fee', 'frags_yume', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange', 'gauge_safe_10', 'frags_nami', 'skill_elizabeth', 'skill_lily', 'skill_kanae_midsummer', 'eto_uncap', 'luna_uncap', 'frags_preferred_song', 'visual_ghost_skynotes', 'ayu_uncap', 'skill_vita']
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', 'frags_kou', 'visual_ink', 'shirabe_entry_fee', 'frags_yume', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange', 'gauge_safe_10', 'frags_nami', 'skill_elizabeth', 'skill_lily', 'skill_kanae_midsummer', 'eto_uncap', 'luna_uncap', 'frags_preferred_song', 'visual_ghost_skynotes', 'ayu_uncap', 'skill_vita', 'skill_fatalis']
return render_template('web/changechar.html', skill_ids=skill_ids)