from typing import Dict from datetime import timedelta from core.config import CoreConfig from titles.wacca.lilyr import WaccaLilyR from titles.wacca.config import WaccaConfig from titles.wacca.const import WaccaConstants from titles.wacca.handlers import * class WaccaReverse(WaccaLilyR): def __init__(self, cfg: CoreConfig, game_cfg: WaccaConfig) -> None: super().__init__(cfg, game_cfg) self.version = WaccaConstants.VER_WACCA_REVERSE self.season = 3 self.OPTIONS_DEFAULTS["set_nav_id"] = 310001 self.allowed_stages = [ (3014, 14), (3013, 13), (3012, 12), (3011, 11), (3010, 10), (3009, 9), (3008, 8), (3007, 7), (3006, 6), (3005, 5), (3004, 4), (3003, 3), (3002, 2), (3001, 1), # Touhou (210001, 0), (210002, 0), (210003, 0), # Final spurt (310001, 0), (310002, 0), (310003, 0), # boss remix (310004, 0), (310005, 0), (310006, 0), ] async def handle_user_status_login_request(self, data: Dict) -> Dict: resp = await super().handle_user_status_login_request(data) resp["params"].append([]) return resp async def handle_user_status_getDetail_request(self, data: Dict) -> Dict: req = UserStatusGetDetailRequest(data) resp = UserStatusGetDetailResponseV4() profile = await self.data.profile.get_profile(req.userId) if profile is None: self.logger.warning(f"Unknown profile {req.userId}") return resp.make() self.logger.info(f"Get detail for profile {req.userId}") user_id = profile["user"] profile_scores = await self.data.score.get_best_scores(user_id) profile_items = await self.data.item.get_items(user_id) profile_song_unlocks = await self.data.item.get_song_unlocks(user_id) profile_options = await self.data.profile.get_options(user_id) profile_favorites = await self.data.profile.get_favorite_songs(user_id) profile_gates = await self.data.profile.get_gates(user_id) profile_bingo = await self.data.profile.get_bingo(user_id) profile_trophies = await self.data.item.get_trophies(user_id) profile_tickets = await self.data.item.get_tickets(user_id) if profile["gate_tutorial_flags"] is not None: for x in profile["gate_tutorial_flags"]: resp.gateTutorialFlags.append(GateTutorialFlag(x[0], x[1])) if profile["vip_expire_time"] is None: resp.userStatus.vipExpireTime = 0 else: resp.userStatus.vipExpireTime = int(profile["vip_expire_time"].timestamp()) if profile["always_vip"] or self.game_config.mods.always_vip: resp.userStatus.vipExpireTime = int( (self.srvtime + timedelta(days=31)).timestamp() ) resp.songUpdateTime = int(profile["last_login_date"].timestamp()) resp.lastSongInfo = LastSongDetail( profile["last_song_id"], profile["last_song_difficulty"], profile["last_folder_order"], profile["last_folder_id"], profile["last_song_order"], ) resp.songPlayStatus = [resp.lastSongInfo.lastSongId, 1] resp.userStatus.userId = profile["id"] resp.userStatus.username = profile["username"] resp.userStatus.xp = profile["xp"] resp.userStatus.danLevel = profile["dan_level"] resp.userStatus.danType = profile["dan_type"] resp.userStatus.wp = profile["wp"] resp.userStatus.useCount = profile["login_count"] resp.userStatus.loginDays = profile["login_count_days"] resp.userStatus.loginConsecutiveDays = profile["login_count_days_consec"] resp.userStatus.loginsToday = profile["login_count_today"] resp.userStatus.rating = profile["rating"] if self.game_config.mods.infinite_wp: resp.userStatus.wp = 999999 for fav in profile_favorites: resp.favorites.append(fav["song_id"]) if profile["friend_view_1"] is not None: pass if profile["friend_view_2"] is not None: pass if profile["friend_view_3"] is not None: pass resp.seasonalPlayModeCounts.append( PlayModeCounts(self.season, 1, profile["playcount_single"]) ) resp.seasonalPlayModeCounts.append( PlayModeCounts(self.season, 2, profile["playcount_multi_vs"]) ) resp.seasonalPlayModeCounts.append( PlayModeCounts(self.season, 3, profile["playcount_multi_coop"]) ) resp.seasonalPlayModeCounts.append( PlayModeCounts(self.season, 4, profile["playcount_stageup"]) ) resp.seasonalPlayModeCounts.append( PlayModeCounts(self.season, 5, profile["playcount_time_free"]) ) # For some fucking reason if this isn't here time play is disabled resp.seasonalPlayModeCounts.append(PlayModeCounts(0, 1, 1)) for opt in profile_options: resp.options.append(UserOption(opt["opt_id"], opt["value"])) if profile_bingo is not None: resp.bingoStatus = BingoDetail(profile_bingo["page_number"]) for x in profile_bingo["page_progress"]: resp.bingoStatus.pageStatus.append(BingoPageStatus(x[0], x[1], x[2])) for gate in self.game_config.gates.enabled_gates: added_gate = False for user_gate in profile_gates: if user_gate["gate_id"] == gate: resp.gateInfo.append( GateDetailV2( user_gate["gate_id"], user_gate["page"], user_gate["progress"], user_gate["loops"], int(user_gate["last_used"].timestamp()), user_gate["mission_flag"], ) ) resp.seasonInfo.cumulativeGatePts += user_gate["total_points"] added_gate = True break if not added_gate: resp.gateInfo.append(GateDetailV2(gate)) for unlock in profile_song_unlocks: for x in range(1, unlock["highest_difficulty"] + 1): resp.userItems.songUnlocks.append( SongUnlock( unlock["song_id"], x, 0, int(unlock["acquire_date"].timestamp()) ) ) for song in profile_scores: resp.seasonInfo.cumulativeScore += song["score"] clear_cts = SongDetailClearCounts( song["play_ct"], song["clear_ct"], song["missless_ct"], song["fullcombo_ct"], song["allmarv_ct"], ) grade_cts = SongDetailGradeCountsV2( song["grade_d_ct"], song["grade_c_ct"], song["grade_b_ct"], song["grade_a_ct"], song["grade_aa_ct"], song["grade_aaa_ct"], song["grade_s_ct"], song["grade_ss_ct"], song["grade_sss_ct"], song["grade_master_ct"], song["grade_sp_ct"], song["grade_ssp_ct"], song["grade_sssp_ct"], ) deets = BestScoreDetailV2(song["song_id"], song["chart_id"]) deets.clearCounts = clear_cts deets.clearCountsSeason = clear_cts deets.gradeCounts = grade_cts deets.score = song["score"] deets.bestCombo = song["best_combo"] deets.lowestMissCtMaybe = song["lowest_miss_ct"] deets.rating = song["rating"] resp.scores.append(deets) for trophy in profile_trophies: resp.userItems.trophies.append( TrophyItem( trophy["trophy_id"], trophy["season"], trophy["progress"], trophy["badge_type"], ) ) if self.game_config.mods.infinite_tickets: for x in range(5): resp.userItems.tickets.append(TicketItem(x, 106002, 0)) else: for ticket in profile_tickets: if ticket["expire_date"] is None: expire = int((self.srvtime + timedelta(days=30)).timestamp()) else: expire = int(ticket["expire_date"].timestamp()) resp.userItems.tickets.append( TicketItem(ticket["id"], ticket["ticket_id"], expire) ) if profile_items: for item in profile_items: try: if item["type"] == WaccaConstants.ITEM_TYPES["icon"]: resp.userItems.icons.append( IconItem( item["item_id"], 1, item["use_count"], int(item["acquire_date"].timestamp()), ) ) elif item["type"] == WaccaConstants.ITEM_TYPES["navigator"]: resp.userItems.navigators.append( NavigatorItem( item["item_id"], 1, int(item["acquire_date"].timestamp()), item["use_count"], item["use_count"], ) ) else: itm_send = GenericItemSend( item["item_id"], 1, int(item["acquire_date"].timestamp()) ) if item["type"] == WaccaConstants.ITEM_TYPES["title"]: resp.userItems.titles.append(itm_send) elif item["type"] == WaccaConstants.ITEM_TYPES["user_plate"]: resp.userItems.plates.append(itm_send) elif item["type"] == WaccaConstants.ITEM_TYPES["touch_effect"]: resp.userItems.touchEffect.append(itm_send) elif item["type"] == WaccaConstants.ITEM_TYPES["note_color"]: resp.userItems.noteColors.append(itm_send) elif item["type"] == WaccaConstants.ITEM_TYPES["note_sound"]: resp.userItems.noteSounds.append(itm_send) except Exception: self.logger.error( f"{__name__} Failed to load item {item['item_id']} for user {profile['user']}" ) resp.seasonInfo.level = profile["xp"] resp.seasonInfo.wpObtained = profile["wp_total"] resp.seasonInfo.wpSpent = profile["wp_spent"] resp.seasonInfo.titlesObtained = len(resp.userItems.titles) resp.seasonInfo.iconsObtained = len(resp.userItems.icons) resp.seasonInfo.noteColorsObtained = len(resp.userItems.noteColors) resp.seasonInfo.noteSoundsObtained = len(resp.userItems.noteSounds) resp.seasonInfo.platesObtained = len(resp.userItems.plates) return resp.make() async def handle_user_status_create_request(self, data: Dict) -> Dict: req = UserStatusCreateRequest(data) resp = await super().handle_user_status_create_request(data) await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 310001 ) # Added reverse await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 310002 ) # Added reverse await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["touch_effect"], 312000 ) # Added reverse await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["touch_effect"], 312001 ) # Added reverse return resp