diff --git a/latest version/core/config_manager.py b/latest version/core/config_manager.py index 5b2aaab..a63ccba 100644 --- a/latest version/core/config_manager.py +++ b/latest version/core/config_manager.py @@ -12,7 +12,7 @@ class Config: SONG_FILE_HASH_PRE_CALCULATE = True - GAME_API_PREFIX = '/autumnequinox/33' # str | list[str] + GAME_API_PREFIX = '/coldwind/35' # 6.0.0 '/fallingleaves/34' # str | list[str] OLD_GAME_API_PREFIX = [] # str | list[str] ALLOW_APPVERSION = [] # list[str] @@ -86,6 +86,9 @@ class Config: BEST30_WEIGHT = 1 / 40 RECENT10_WEIGHT = 1 / 40 + INVASION_START_WEIGHT = 0.1 + INVASION_HARD_WEIGHT = 0.1 + MAX_FRIEND_COUNT = 50 WORLD_MAP_FOLDER_PATH = './database/map/' diff --git a/latest version/core/constant.py b/latest version/core/constant.py index f1248f1..c005353 100644 --- a/latest version/core/constant.py +++ b/latest version/core/constant.py @@ -1,7 +1,7 @@ from .config_manager import Config -ARCAEA_SERVER_VERSION = 'v2.12.0.1' -ARCAEA_DATABASE_VERSION = 'v2.12.0.1' +ARCAEA_SERVER_VERSION = 'v2.12.0.2' +ARCAEA_DATABASE_VERSION = 'v2.12.0.2' ARCAEA_LOG_DATBASE_VERSION = 'v1.1' @@ -11,6 +11,10 @@ class Constant: MAX_STAMINA = 12 + # INSIGHT_STATES = [x for x in range(7)] + INSIGHT_TOGGLE_STATES = [3, 4, 5, 6] + # DEFAULT_INSIGHT_STATE = Config.DEFAULT_INSIGHT_STATE + STAMINA_RECOVER_TICK = 1800000 FRAGSTAM_RECOVER_TICK = 23 * 3600 * 1000 @@ -40,6 +44,8 @@ class Constant: BEST30_WEIGHT = Config.BEST30_WEIGHT RECENT10_WEIGHT = Config.RECENT10_WEIGHT + INVASION_START_WEIGHT = Config.INVASION_START_WEIGHT + INVASION_HARD_WEIGHT = Config.INVASION_HARD_WEIGHT WORLD_MAP_FOLDER_PATH = Config.WORLD_MAP_FOLDER_PATH SONG_FILE_FOLDER_PATH = Config.SONG_FILE_FOLDER_PATH diff --git a/latest version/core/score.py b/latest version/core/score.py index 5b030c8..3035a21 100644 --- a/latest version/core/score.py +++ b/latest version/core/score.py @@ -1,5 +1,6 @@ from base64 import b64encode from os import urandom +from random import choices from time import time from .bgtask import BGTask, logdb_execute @@ -244,10 +245,14 @@ class UserPlay(UserScore): self.course_play: 'CoursePlay' = None self.combo_interval_bonus: int = None # 不能给 None 以外的默认值 + self.hp_interval_bonus: int = None # 不能给 None 以外的默认值 self.skill_cytusii_flag: str = None + self.skill_chinatsu_flag: str = None self.highest_health: int = None self.lowest_health: int = None + self.invasion_flag: int = None # 1: invasion_start, 2: invasion_hard + def to_dict(self) -> dict: # 不能super if self.is_world_mode is None or self.course_play_state is None: @@ -288,6 +293,9 @@ class UserPlay(UserScore): return False x = x + str(self.combo_interval_bonus) + if self.hp_interval_bonus is not None and self.hp_interval_bonus < 0: + return False + y = f'{self.user.user_id}{self.song_hash}' checksum = md5(x+md5(y)) @@ -326,16 +334,27 @@ class UserPlay(UserScore): self.prog_boost_multiply = int(x[10]) self.beyond_boost_gauge_usage = int(x[11]) self.skill_cytusii_flag = x[12] + self.skill_chinatsu_flag = x[13] + self.invasion_flag = x[14] self.is_world_mode = True self.course_play_state = -1 - def set_play_state_for_world(self, stamina_multiply: int = 1, fragment_multiply: int = 100, prog_boost_multiply: int = 0, beyond_boost_gauge_usage: int = 0, skill_cytusii_flag: str = None) -> None: + def set_play_state_for_world( + self, + stamina_multiply: int = 1, + fragment_multiply: int = 100, + prog_boost_multiply: int = 0, + beyond_boost_gauge_usage: int = 0, + skill_cytusii_flag: str = None, + skill_chinatsu_flag: str = None + ) -> 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) self.beyond_boost_gauge_usage = int(beyond_boost_gauge_usage) self.skill_cytusii_flag = skill_cytusii_flag + self.skill_chinatsu_flag = skill_chinatsu_flag if self.prog_boost_multiply != 0 or self.beyond_boost_gauge_usage != 0: self.c.execute('''select prog_boost, beyond_boost_gauge from user where user_id=:a''', { 'a': self.user.user_id}) @@ -346,10 +365,6 @@ class UserPlay(UserScore): # 注意:偷懒了,没判断是否是beyond图 self.beyond_boost_gauge_usage = 0 - self.clear_play_state() - self.c.execute('''insert into songplay_token values(:t,:a,:b,:c,'',-1,0,0,:d,:e,:f,:g,:h)''', { - '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, 'g': self.beyond_boost_gauge_usage, 'h': self.skill_cytusii_flag, 't': self.song_token}) - self.user.select_user_about_current_map() self.user.current_map.select_map_info() @@ -360,13 +375,23 @@ class UserPlay(UserScore): 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': + # invasion 扔骰子 + _flag = choices([0, 1, 2], [ + max(1 - Constant.INVASION_START_WEIGHT - Constant.INVASION_HARD_WEIGHT, 0), Constant.INVASION_START_WEIGHT, Constant.INVASION_HARD_WEIGHT])[0] + if self.user.is_insight_enabled and _flag != 0: + self.invasion_flag = _flag + + elif 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.clear_play_state() + self.c.execute('''insert into songplay_token values(:t,:a,:b,:c,'',-1,0,0,:d,:e,:f,:g,:h,:i,:j)''', { + '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, 'g': self.beyond_boost_gauge_usage, 'h': self.skill_cytusii_flag, 'i': self.skill_chinatsu_flag, 'j': self.invasion_flag, 't': self.song_token}) + self.user.stamina.stamina -= self.user.current_map.stamina_cost * self.stamina_multiply self.user.stamina.update() @@ -380,8 +405,8 @@ class UserPlay(UserScore): self.course_play.score = 0 self.course_play.clear_type = 3 # 设置为PM,即最大值 - self.c.execute('''insert into songplay_token values(?,?,?,?,?,?,?,?,1,100,0,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.c.execute('''insert into songplay_token values(?,?,?,?,?,?,?,?,1,100,0,0,"","",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) diff --git a/latest version/core/user.py b/latest version/core/user.py index ae168a0..cfb9bec 100644 --- a/latest version/core/user.py +++ b/latest version/core/user.py @@ -326,6 +326,8 @@ class UserInfo(User): self.current_map: 'Map' = None self.stamina: 'UserStamina' = None + self.insight_state: int = None + self.__cores: list = None self.__packs: list = None self.__singles: list = None @@ -336,6 +338,12 @@ class UserInfo(User): self.curr_available_maps: list = None self.__course_banners: list = None + @property + def is_insight_enabled(self) -> bool: + if self.insight_state is None: + self.select_user_one_column('insight_state', 4, int) + return self.insight_state == 3 or self.insight_state == 5 + @property def cores(self) -> list: if self.__cores is None: @@ -547,6 +555,7 @@ class UserInfo(User): # 'custom_banner': 'online_banner_2024_06', # 'subscription_multiplier': 114, # 'memory_boost_ticket': 5, + 'insight_state': self.insight_state, # 0~2 不可选,3 技能激活,4 未激活,5 激活可选,6 未激活可选 } def from_list(self, x: list) -> 'UserInfo': @@ -591,6 +600,8 @@ class UserInfo(User): self.mp_notification_enabled = x[37] == 1 + self.insight_state = x[38] + return self def select_user(self) -> None: @@ -795,6 +806,19 @@ class UserOnline(UserInfo): self.c.execute('''update user set favorite_character = :a where user_id = :b''', {'a': self.favorite_character.character_id, 'b': self.user_id}) + def toggle_invasion(self) -> None: + self.c.execute( + '''select insight_state from user where user_id = ?''', (self.user_id,)) + x = self.c.fetchone() + if not x: + raise NoData('No user.', 108, -3) + self.insight_state = x[0] + rq = Constant.INSIGHT_TOGGLE_STATES + self.insight_state = rq[(rq.index(self.insight_state) + 1) % len(rq)] + + self.c.execute( + '''update user set insight_state = ? where user_id = ?''', (self.insight_state, self.user_id)) + class UserChanger(UserInfo, UserRegister): diff --git a/latest version/core/world.py b/latest version/core/world.py index 76774c4..bf94c8e 100644 --- a/latest version/core/world.py +++ b/latest version/core/world.py @@ -4,7 +4,7 @@ from json import load from random import randint from time import time -from .character import Character +from .character import Character, UserCharacter from .constant import Constant from .error import InputError, MapLocked, NoData from .item import ItemFactory @@ -133,6 +133,7 @@ class Map: self.chain_info: dict = None # self.requires: list[dict] = None + self.requires_any: 'list[dict]' = None self.disable_over: bool = None self.new_law: str = None @@ -188,6 +189,8 @@ class Map: r['disable_over'] = self.disable_over if self.new_law is not None and self.new_law != '': r['new_law'] = self.new_law + if self.requires_any: + r['requires_any'] = self.requires_any return r def from_dict(self, raw_dict: dict) -> 'Map': @@ -216,6 +219,7 @@ class Map: self.disable_over = raw_dict.get('disable_over') self.new_law = raw_dict.get('new_law') + self.requires_any = raw_dict.get('requires_any') return self def select_map_info(self): @@ -474,7 +478,8 @@ class WorldSkillMixin: 'skill_ilith_ivy': self._skill_ilith_ivy, 'ilith_awakened_skill': self._ilith_awakened_skill, 'skill_hikari_vanessa': self._skill_hikari_vanessa, - 'skill_mithra': self._skill_mithra + 'skill_mithra': self._skill_mithra, + 'skill_chinatsu': self._skill_chinatsu, } if self.user_play.beyond_gauge == 0 and self.character_used.character_id == 35 and self.character_used.skill_id_displayed: self._special_tempest() @@ -492,6 +497,7 @@ class WorldSkillMixin: 'luna_uncap': self._luna_uncap, 'skill_kanae_uncap': self._skill_kanae_uncap, 'skill_eto_hoppe': self._skill_eto_hoppe, + 'skill_intruder': self._skill_intruder, } if self.character_used.skill_id_displayed in factory_dict: factory_dict[self.character_used.skill_id_displayed]() @@ -630,9 +636,11 @@ class WorldSkillMixin: kanae 觉醒技能,保存世界模式 progress 并在下次结算 直接加减在 progress 最后 技能存储 base_progress * PROG / 50,下一次消耗全部存储值(无视技能和搭档,但需要非技能隐藏状态) + 6.0 更新:需要体力消耗才存 ''' - self.kanae_stored_progress = self.progress_normalized - self.user.current_map.reclimb(self.final_progress) + if self.user.current_map.stamina_cost > 0: + self.kanae_stored_progress = self.progress_normalized + self.user.current_map.reclimb(self.final_progress) def _skill_eto_hoppe(self) -> None: ''' @@ -642,6 +650,26 @@ class WorldSkillMixin: self.character_bonus_progress_normalized = self.progress_normalized self.user.current_map.reclimb(self.final_progress) + def _skill_chinatsu(self) -> None: + ''' + chinatsu 技能,hp 超过时提高搭档能力值 + ''' + _flag = self.user_play.skill_chinatsu_flag + if not self.user_play.hp_interval_bonus or not _flag: + return + + x = _flag[:min(len(_flag), self.user_play.hp_interval_bonus)] + self.over_skill_increase = x.count('2') * 5 + self.prog_skill_increase = x.count('1') * 5 + + def _skill_intruder(self) -> None: + ''' + intruder 技能,夺舍后世界进度翻倍 + ''' + if self.user_play.invasion_flag: + self.character_bonus_progress_normalized = self.progress_normalized + self.user.current_map.reclimb(self.final_progress) + class BaseWorldPlay(WorldSkillMixin): ''' @@ -690,9 +718,13 @@ class BaseWorldPlay(WorldSkillMixin): 'world_mode_locked_end_ts': self.user.world_mode_locked_end_ts, 'beyond_boost_gauge': self.user.beyond_boost_gauge, # 'wpaid': 'helloworld', # world play id ??? - # progress_before_sub_boost - # progress_sub_boost_amount - # subscription_multiply + 'progress_before_sub_boost': self.final_progress, + 'progress_sub_boost_amount': 0, + # 'subscription_multiply' + + # lephon_final: bool dynamic map info + # lephon_active: bool dynamic map info + # 'steps_modified': False, } if self.character_used.skill_id_displayed == 'skill_maya': @@ -751,6 +783,11 @@ class BaseWorldPlay(WorldSkillMixin): if self.user_play.beyond_gauge == 0 and self.user.kanae_stored_prog > 0: # 实在不想拆开了,在这里判断一下,注意这段不会在 BeyondWorldPlay 中执行 self.kanae_added_progress = self.user.kanae_stored_prog + + if self.user_play.invasion_flag: # not None and != 0 + # 这里硬编码了搭档 id 72 + self.character_used = UserCharacter(self.c, 72, self.user) + self.character_used.select_character_info() else: self.character_used.character_id = self.user.character.character_id self.character_used.level.level = self.user.character.level.level @@ -808,6 +845,7 @@ class WorldPlay(BaseWorldPlay): def to_dict(self) -> dict: r = super().to_dict() + # 基础进度加上搭档倍数 不带 character_bonus_progress 但是带 kanae 技能 r['progress_partial_after_stat'] = self.progress_normalized @@ -932,6 +970,7 @@ class BeyondWorldPlay(BaseWorldPlay): r['char_stats']['over_skill_increase'] = self.over_skill_increase r["user_map"]["steps"] = len(self.user.current_map.steps_for_climbing) + r['affinity_multiply'] = self.affinity_multiplier if self.user_play.beyond_boost_gauge_usage != 0: r['beyond_boost_gauge_usage'] = self.user_play.beyond_boost_gauge_usage diff --git a/latest version/database/init/arc_data.py b/latest version/database/init/arc_data.py index 92ab257..ea22faf 100644 --- a/latest version/database/init/arc_data.py +++ b/latest version/database/init/arc_data.py @@ -1,45 +1,45 @@ class InitData: char = ['hikari', 'tairitsu', 'kou', 'sapphire', 'lethe', 'hikari&tairitsu(reunion)', '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', 'hikari(fatalis)', 'saki', 'setsuna', 'amane', 'kou(winter)', 'lagrange(aria)', 'lethe(apophenia)', 'shama(UNiVERSE)', 'milk(UNiVERSE)', 'shikoku', 'mika yurisaki', 'Mithra Tercera', 'Toa Kozukata', 'Nami(Twilight)', 'Ilith & Ivy', 'Hikari & Vanessa', 'Maya', 'Insight(Ascendant - 8th Seeker)', 'Luin', 'Vita(Cadenza)', 'Ai-chan', 'Luna & Ilot', 'Eto & Hoppe'] + '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)', 'saki', 'setsuna', 'amane', 'kou(winter)', 'lagrange(aria)', 'lethe(apophenia)', 'shama(UNiVERSE)', 'milk(UNiVERSE)', 'shikoku', 'mika yurisaki', 'Mithra Tercera', 'Toa Kozukata', 'Nami(Twilight)', 'Ilith & Ivy', 'Hikari & Vanessa', 'Maya', 'Insight(Ascendant - 8th Seeker)', 'Luin', 'Vita(Cadenza)', 'Ai-chan', 'Luna & Ilot', 'Eto & Hoppe', 'Forlorn(Ascendant - 6th Seeker)', 'Chinatsu', 'Tsumugi'] skill_id = ['gauge_easy', '', '', '', 'note_mirror', 'skill_reunion', '', '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', 'skill_fatalis', 'frags_ongeki_slash', 'frags_ongeki_hard', 'skill_amane', 'skill_kou_winter', '', 'gauge_hard|note_mirror', 'skill_shama', 'skill_milk', 'skill_shikoku', 'skill_mika', 'skill_mithra', 'skill_toa', 'skill_nami_twilight', 'skill_ilith_ivy', 'skill_hikari_vanessa', 'skill_maya', 'skill_intruder', 'skill_luin', '', 'skill_aichan', 'skill_luna_ilot', 'skill_eto_hoppe'] + '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', 'frags_ongeki_slash', 'frags_ongeki_hard', 'skill_amane', 'skill_kou_winter', '', 'gauge_hard|note_mirror', 'skill_shama', 'skill_milk', 'skill_shikoku', 'skill_mika', 'skill_mithra', 'skill_toa', 'skill_nami_twilight', 'skill_ilith_ivy', 'skill_hikari_vanessa', 'skill_maya', 'skill_intruder', 'skill_luin', '', 'skill_aichan', 'skill_luna_ilot', 'skill_eto_hoppe', 'skill_nell', 'skill_chinatsu', 'skill_tsumugi'] skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', 'ilith_awakened_skill', 'eto_uncap', 'luna_uncap', 'shirabe_entry_fee', - '', '', '', '', '', 'ayu_uncap', '', 'frags_yume', '', 'skill_saya_uncap', '', '', '', '', '', '', 'skill_kanae_uncap', '', '', '', 'skill_doroc_uncap', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'skill_luin_uncap', '', '', '', ''] + '', '', '', '', '', 'ayu_uncap', '', 'frags_yume', '', 'skill_saya_uncap', '', '', '', '', '', '', 'skill_kanae_uncap', '', '', '', 'skill_doroc_uncap', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'skill_luin_uncap', '', '', '', '', '', '', ''] 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, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] frag1 = [55, 55, 60, 50, 47, 79, 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, 27, 18, 56, 47, 30, 45, 57, 56, 47, 33, 26, 29, 66, 40, 33, 51, 27, 50, 60, 45, 50, 38, 22] + 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, 18, 56, 47, 30, 45, 57, 56, 47, 33, 26, 29, 66, 40, 33, 51, 27, 50, 60, 45, 50, 38, 22, 63, 37, 23] prog1 = [35, 55, 47, 50, 60, 70, 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, 130, 18, 57, 55, 50, 45, 70, 37.5, 29, 44, 26, 26, 35, 40, 33, 58, 31, 50, 50, 45, 41, 12, 31] + 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, 130, 18, 57, 55, 50, 45, 70, 37.5, 29, 44, 26, 26, 35, 40, 33, 58, 31, 40, 50, 45, 41, 12, 31, 72, 40, 16] overdrive1 = [35, 55, 25, 50, 47, 70, 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, 120, 44, 33, 55, 50, 45, 57, 31, 29, 65, 26, 29, 42.5, 40, 33, 58, 31, 50, 34, 45, 41, 12, 19] + 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, 120, 44, 33, 55, 50, 45, 57, 31, 29, 65, 26, 29, 42.5, 40, 33, 58, 31, 35, 34, 45, 41, 12, 19, 38, 40, 26] frag20 = [78, 80, 90, 75, 70, 79, 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, 50, 35, 85, 47, 50, 75, 80, 90, 80, 50, 51, 54, 100, 50, 58, 51, 40, 50, 70, 50, 61.6, 48, 37] + 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, 35, 85, 47, 50, 75, 80, 90, 80, 50, 51, 54, 100, 50, 58, 51, 40, 115, 70, 50, 61.6, 48, 37, 90, 60, 50] prog20 = [61, 80, 70, 75, 90, 70, 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, 210, 35, 86, 92, 80, 75, 100, 60, 50, 68, 51, 50, 53, 85, 58, 96, 47, 50, 80, 67, 41, 55, 50] + 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, 210, 35, 86, 92, 80, 75, 100, 60, 50, 68, 51, 50, 53, 85, 58, 96, 47, 80, 80, 67, 41, 55, 50, 103, 66, 35] overdrive20 = [61, 80, 47, 75, 70, 70, 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, 200, 85, 50, 92, 50, 75, 80, 49.5, 50, 100, 51, 54, 65.5, 59.5, 58, 96, 47, 50, 54, 90, 41, 34, 30] + 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, 200, 85, 50, 92, 50, 75, 80, 49.5, 50, 100, 51, 54, 65.5, 59.5, 58, 96, 47, 75, 54, 90, 41, 34, 30, 55, 66, 55] frag30 = [88, 90, 100, 75, 80, 89, 70, 79, 65, 40, 50, 90, 100, 92, 0, 61, 67, 92, 85, 50, 86, 62, - 65, 95, 67, 88, 74, 0.5, 105, 80, 105, 50, 80, 87, 81, 50, 95, 0, 80, 75, 50, 70, 80, 100, 65, 80, 61, 50, 68, 60, 90, 67, 50, 60, 51, 50, 35, 85, 47, 50, 75, 80, 90, 80, 50, 51, 64, 100, 50, 58, 51, 40, 50, 80, 50, 61.6, 48, 37] + 65, 95, 67, 88, 74, 0.5, 105, 80, 105, 50, 80, 87, 81, 50, 95, 0, 80, 75, 50, 70, 80, 100, 65, 80, 61, 50, 68, 60, 90, 67, 50, 60, 51, 50, 35, 85, 47, 50, 75, 80, 90, 80, 50, 51, 64, 100, 50, 58, 51, 40, 115, 80, 50, 61.6, 48, 37, 90, 60, 50] prog30 = [71, 90, 80, 75, 100, 80, 90, 102, 84, 78, 110, 77, 73, 78, 0, 99, 80, 66, 46, 93, 40, 83, - 80, 100, 93, 50, 96, 88, 99, 108, 85, 80, 50, 64, 65, 100, 100, 110, 80, 50, 74, 90, 80, 80, 56, 80, 79, 55, 65, 59, 90, 50, 90, 90, 75, 210, 35, 86, 92, 80, 75, 100, 60, 50, 68, 51, 60, 53, 85, 58, 96, 47, 50, 90, 67, 41, 55, 50] + 80, 100, 93, 50, 96, 88, 99, 108, 85, 80, 50, 64, 65, 100, 100, 110, 80, 50, 74, 90, 80, 80, 56, 80, 79, 55, 65, 59, 90, 50, 90, 90, 75, 210, 35, 86, 92, 80, 75, 100, 60, 50, 68, 51, 60, 53, 85, 58, 96, 47, 80, 90, 67, 41, 55, 50, 103, 66, 35] overdrive30 = [71, 90, 57, 75, 80, 80, 95, 79, 65, 31, 50, 69, 100, 68, 0, 78, 50, 70, 62, 59, 64, - 56, 73, 105, 67, 84, 80, 88, 79, 80, 60, 80, 80, 63, 35, 50, 82, 55, 50, 95, 55, 70, 100, 80, 99, 80, 61, 40, 69, 62, 51, 90, 67, 60, 100, 200, 85, 50, 92, 50, 75, 80, 49.5, 50, 100, 51, 64, 65.5, 59.5, 58, 96, 47, 50, 64, 90, 41, 34, 30] + 56, 73, 105, 67, 84, 80, 88, 79, 80, 60, 80, 80, 63, 35, 50, 82, 55, 50, 95, 55, 70, 100, 80, 99, 80, 61, 40, 69, 62, 51, 90, 67, 60, 100, 200, 85, 50, 92, 50, 75, 80, 49.5, 50, 100, 51, 64, 65.5, 59.5, 58, 96, 47, 75, 64, 90, 41, 34, 30, 55, 66, 55] 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, 3, 0, 2, 2, 0, 0, 2, 0, 0, 2, 0, 2, 2, 1, 0, 2, 0, 0, 2, 0, 0, 0, 0] + 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, 0, 2, 2, 0, 0, 2, 0, 0, 2, 0, 2, 2, 1, 0, 2, 0, 4, 2, 0, 0, 0, 0, 4, 0, 0] char_core = { 0: [{'core_id': 'core_hollow', 'amount': 25}, {'core_id': 'core_desolate', 'amount': 5}], @@ -71,7 +71,7 @@ class InitData: 'core_ambivalent', 'core_scarlet', 'core_groove', 'core_generic', 'core_binary', 'core_colorful', 'core_course_skip_purchase', 'core_umbral', 'core_wacca', 'core_sunset', 'core_tanoc', 'core_serene'] world_songs = ["babaroque", "shadesoflight", "kanagawa", "lucifer", "anokumene", "ignotus", "rabbitintheblackroom", "qualia", "redandblue", "bookmaker", "darakunosono", "espebranch", "blacklotus", "givemeanightmare", "vividtheory", "onefr", "gekka", "vexaria3", "infinityheaven3", "fairytale3", "goodtek3", "suomi", "rugie", "faintlight", "harutopia", "goodtek", "dreaminattraction", "syro", "diode", "freefall", "grimheart", "blaster", - "cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take", "lifeispiano", "vandalism", "nexttoyou3", "lostcivilization3", "turbocharger", "bookmaker3", "laqryma3", "kyogenkigo", "hivemind", "seclusion", "quonwacca3", "bluecomet", "energysynergymatrix", "gengaozo", "lastendconductor3", "antithese3", "qualia3", "kanagawa3", "heavensdoor3", "pragmatism3", "nulctrl", "avril", "ddd", "merlin3", "omakeno3", "nekonote", "sanskia", 'altair', 'mukishitsu', 'trapcrow', 'redandblue3', 'ignotus3', 'singularity3', 'dropdead3', 'arcahv', 'freefall3', 'partyvinyl3', 'tsukinimurakumo', 'mantis', 'worldfragments', 'astrawalkthrough', 'chronicle', 'trappola3', 'letsrock', 'shadesoflight3', 'teriqma3', 'impact3', 'lostemotion', 'gimmick', 'lawlesspoint', 'hybris', 'ultimatetaste', 'rgb', 'matenrou', 'dynitikos', 'amekagura', 'fantasy', 'aloneandlorn', 'felys', 'onandon', 'hotarubinoyuki', 'oblivia3', 'libertas3', 'einherjar3', 'purpleverse3', 'viciousheroism3', 'inkarusi3', 'cyberneciacatharsis3', 'alephzero', 'hellohell', 'ichirin', 'awakeninruins', 'morningloom', 'lethalvoltage', 'leaveallbehind', 'desive', 'oldschoolsalvage', 'distortionhuman', 'epitaxy', 'hailstone', 'furetemitai', 'prayer'] + "cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take", "lifeispiano", "vandalism", "nexttoyou3", "lostcivilization3", "turbocharger", "bookmaker3", "laqryma3", "kyogenkigo", "hivemind", "seclusion", "quonwacca3", "bluecomet", "energysynergymatrix", "gengaozo", "lastendconductor3", "antithese3", "qualia3", "kanagawa3", "heavensdoor3", "pragmatism3", "nulctrl", "avril", "ddd", "merlin3", "omakeno3", "nekonote", "sanskia", 'altair', 'mukishitsu', 'trapcrow', 'redandblue3', 'ignotus3', 'singularity3', 'dropdead3', 'arcahv', 'freefall3', 'partyvinyl3', 'tsukinimurakumo', 'mantis', 'worldfragments', 'astrawalkthrough', 'chronicle', 'trappola3', 'letsrock', 'shadesoflight3', 'teriqma3', 'impact3', 'lostemotion', 'gimmick', 'lawlesspoint', 'hybris', 'ultimatetaste', 'rgb', 'matenrou', 'dynitikos', 'amekagura', 'fantasy', 'aloneandlorn', 'felys', 'onandon', 'hotarubinoyuki', 'oblivia3', 'libertas3', 'einherjar3', 'purpleverse3', 'viciousheroism3', 'inkarusi3', 'cyberneciacatharsis3', 'alephzero', 'hellohell', 'ichirin', 'awakeninruins', 'morningloom', 'lethalvoltage', 'leaveallbehind', 'desive', 'oldschoolsalvage', 'distortionhuman', 'epitaxy', 'hailstone', 'furetemitai', 'prayer', 'astralexe'] world_unlocks = ["scenery_chap1", "scenery_chap2", "scenery_chap3", "scenery_chap4", "scenery_chap5", "scenery_chap6", "scenery_chap7", "scenery_beyond"] diff --git a/latest version/database/init/courses.json b/latest version/database/init/courses.json index 44069b6..eafb705 100644 --- a/latest version/database/init/courses.json +++ b/latest version/database/init/courses.json @@ -445,5 +445,50 @@ "core_generic_20", "course_banner_11" ] + }, + { + "course_id": "6.0-dan-12", + "style": 12, + "songs": [ + { + "flag_as_hidden": true, + "difficulty": 2, + "id": "abstrusedilemma" + }, + { + "flag_as_hidden": true, + "difficulty": 3, + "id": "worldender" + }, + { + "flag_as_hidden": true, + "difficulty": 4, + "id": "alterego" + }, + { + "flag_as_hidden": true, + "difficulty": 3, + "id": "designant" + } + ], + "gauge_requirement": "default", + "requirements": [ + { + "value": "4.0-dan-11", + "type": "course" + } + ], + "flag_as_hidden_when_requirements_not_met": true, + "dan_name": "Phase 12", + "course_name": "侵蚀万物的色彩", + "can_start": true, + "is_completed": false, + "high_score": 0, + "best_clear_type": -1, + "rewards": [ + "fragment2000", + "core_generic_20", + "course_banner_12" + ] } ] \ No newline at end of file diff --git a/latest version/database/init/packs.json b/latest version/database/init/packs.json index 791f749..152a357 100644 --- a/latest version/database/init/packs.json +++ b/latest version/database/init/packs.json @@ -862,5 +862,41 @@ ], "orig_price": 500, "price": 500 + }, + { + "name": "ongeki_append_2", + "items": [ + { + "type": "pack", + "id": "ongeki_append_2", + "is_available": true + }, + { + "type": "core", + "amount": 4, + "id": "core_generic", + "is_available": true + } + ], + "orig_price": 400, + "price": 400 + }, + { + "name": "lephon", + "items": [ + { + "type": "pack", + "id": "lephon", + "is_available": true + }, + { + "type": "core", + "amount": 5, + "id": "core_generic", + "is_available": true + } + ], + "orig_price": 500, + "price": 500 } ] \ No newline at end of file diff --git a/latest version/database/init/tables.sql b/latest version/database/init/tables.sql index 2e79e47..c5bd2f4 100644 --- a/latest version/database/init/tables.sql +++ b/latest version/database/init/tables.sql @@ -36,7 +36,8 @@ stamina int, world_mode_locked_end_ts int, beyond_boost_gauge real default 0, kanae_stored_prog real default 0, -mp_notification_enabled int default 1 +mp_notification_enabled int default 1, +insight_state default 4 -- 这里写 4 是为了避免不能切换状态和无故被侵入 ); create table if not exists login(access_token text, user_id int, @@ -145,7 +146,9 @@ stamina_multiply int, fragment_multiply int, prog_boost_multiply int, beyond_boost_gauge_usage int, -skill_cytusii_flag text +skill_cytusii_flag text, +skill_chinatsu_flag text, +invasion_flag int ); create table if not exists item(item_id text, type text, diff --git a/latest version/server/score.py b/latest version/server/score.py index 5acf494..27379fd 100644 --- a/latest version/server/score.py +++ b/latest version/server/score.py @@ -29,11 +29,12 @@ def score_token_world(user_id): d = request.args.get - stamina_multiply = int(d('stamina_multiply', 1)) - fragment_multiply = int(d('fragment_multiply', 100)) - prog_boost_multiply = int(d('prog_boost_multiply', 0)) - beyond_boost_gauge_use = int(d('beyond_boost_gauge_use', 0)) + stamina_multiply = d('stamina_multiply', 1, type=int) + fragment_multiply = d('fragment_multiply', 100, type=int) + prog_boost_multiply = d('prog_boost_multiply', 0, type=int) + beyond_boost_gauge_use = d('beyond_boost_gauge_use', 0, type=int) skill_cytusii_flag = None + skill_chinatsu_flag = None skill_id = d('skill_id') if (skill_id == 'skill_ilith_ivy' or skill_id == 'skill_hikari_vanessa') and d('is_skill_sealed') == 'false': @@ -41,23 +42,34 @@ def score_token_world(user_id): # TODO: 需要重构整个 user_play,世界模式 / 课题模式,所以现在临时 work 一下 skill_cytusii_flag = ''.join([str(randint(0, 2)) for _ in range(5)]) + if skill_id == 'skill_chinatsu' and d('is_skill_sealed') == 'false': + skill_chinatsu_flag = ''.join([str(randint(0, 2)) for _ in range(7)]) + + skill_flag = skill_cytusii_flag or skill_chinatsu_flag + with Connect() as c: x = UserPlay(c, UserOnline(c, user_id)) - x.song.set_chart(request.args['song_id'], int( - request.args['difficulty'])) + x.song.set_chart(d('song_id'), d('difficulty', type=int)) x.set_play_state_for_world( - stamina_multiply, fragment_multiply, prog_boost_multiply, beyond_boost_gauge_use, skill_cytusii_flag) + stamina_multiply, fragment_multiply, prog_boost_multiply, beyond_boost_gauge_use, skill_cytusii_flag, skill_chinatsu_flag) r = { "stamina": x.user.stamina.stamina, "max_stamina_ts": x.user.stamina.max_stamina_ts, "token": x.song_token, + 'play_parameters': {}, } - if skill_cytusii_flag and skill_id: + if skill_flag and skill_id: r['play_parameters'] = { skill_id: list( - map(lambda x: Constant.WORLD_VALUE_NAME_ENUM[int(x)], skill_cytusii_flag)) + map(lambda x: Constant.WORLD_VALUE_NAME_ENUM[int(x)], skill_flag)), } + + if x.invasion_flag == 1: + r['play_parameters']['invasion_start'] = True + elif x.invasion_flag == 2: + r['play_parameters']['invasion_hard'] = True + return success_return(r) @@ -115,11 +127,14 @@ def song_score_post(user_id): x.submission_hash = request.form['submission_hash'] if 'combo_interval_bonus' in request.form: x.combo_interval_bonus = int(request.form['combo_interval_bonus']) + if 'hp_interval_bonus' in request.form: + x.hp_interval_bonus = int(request.form['hp_interval_bonus']) x.highest_health = request.form.get("highest_health", type=int) x.lowest_health = request.form.get("lowest_health", type=int) if not x.is_valid: raise InputError('Invalid score.', 107) x.upload_score() + # room_code??? return success_return(x.to_dict()) diff --git a/latest version/server/user.py b/latest version/server/user.py index 9a08fcc..2064a86 100644 --- a/latest version/server/user.py +++ b/latest version/server/user.py @@ -50,6 +50,16 @@ def user_me(user_id): return success_return(UserOnline(c, user_id).to_dict()) +@bp.route('/me/toggle_invasion', methods=['POST']) # insight skill +@auth_required(request) +@arc_try +def toggle_invasion(user_id): + with Connect() as c: + user = UserOnline(c, user_id) + user.toggle_invasion() + return success_return({'user_id': user.user_id, 'insight_state': user.insight_state}) + + @bp.route('/me/character', methods=['POST']) # 角色切换 @auth_required(request) @arc_try diff --git a/latest version/web/index.py b/latest version/web/index.py index 128a77a..2e451ab 100644 --- a/latest version/web/index.py +++ b/latest version/web/index.py @@ -443,7 +443,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', 'skill_fatalis', 'skill_reunion', 'frags_ongeki_slash', 'frags_ongeki_hard', 'skill_amane', 'skill_kou_winter', 'gauge_hard|note_mirror', 'skill_shama', 'skill_milk', 'skill_shikoku', 'skill_mika', 'ilith_awakened_skill', 'skill_mithra', 'skill_toa', 'skill_nami_twilight', 'skill_ilith_ivy', 'skill_hikari_vanessa', 'skill_maya', 'skill_luin', 'skill_luin_uncap', 'skill_kanae_uncap', 'skill_doroc_uncap', 'skill_saya_uncap', 'skill_luna_ilot', 'skill_eto_hoppe', 'skill_aichan'] + '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', 'skill_reunion', 'frags_ongeki_slash', 'frags_ongeki_hard', 'skill_amane', 'skill_kou_winter', 'gauge_hard|note_mirror', 'skill_shama', 'skill_milk', 'skill_shikoku', 'skill_mika', 'ilith_awakened_skill', 'skill_mithra', 'skill_toa', 'skill_nami_twilight', 'skill_ilith_ivy', 'skill_hikari_vanessa', 'skill_maya', 'skill_luin', 'skill_luin_uncap', 'skill_kanae_uncap', 'skill_doroc_uncap', 'skill_saya_uncap', 'skill_luna_ilot', 'skill_eto_hoppe', 'skill_aichan', 'skill_nell', 'skill_chinatsu', 'skill_tsumugi'] return render_template('web/changechar.html', skill_ids=skill_ids)