from typing import Any, List, Dict from datetime import datetime, timedelta import json 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