diff --git a/PyTaiko.py b/PyTaiko.py index d39c648..653fbaa 100644 --- a/PyTaiko.py +++ b/PyTaiko.py @@ -27,6 +27,7 @@ from libs.utils import ( global_data, global_tex, ) +from scenes.ai_battle.game import AIBattleGameScreen from scenes.ai_battle.song_select import AISongSelectScreen from scenes.dan.dan_result import DanResultScreen from scenes.dan.dan_select import DanSelectScreen @@ -70,6 +71,7 @@ class Screens: PRACTICE_SELECT = "PRACTICE_SELECT" GAME_PRACTICE = "GAME_PRACTICE" AI_SELECT = "AI_SELECT" + AI_GAME = "AI_GAME" SETTINGS = "SETTINGS" DEV_MENU = "DEV_MENU" LOADING = "LOADING" @@ -346,6 +348,7 @@ def main(): game_screen_practice = PracticeGameScreen('game') practice_select_screen = PracticeSongSelectScreen('song_select') ai_select_screen = AISongSelectScreen('song_select') + ai_game_screen = AIBattleGameScreen('game') result_screen = ResultScreen('result') result_screen_2p = TwoPlayerResultScreen('result') settings_screen = SettingsScreen('settings') @@ -364,6 +367,7 @@ def main(): Screens.GAME_2P: game_screen_2p, Screens.GAME_PRACTICE: game_screen_practice, Screens.AI_SELECT: ai_select_screen, + Screens.AI_GAME: ai_game_screen, Screens.RESULT: result_screen, Screens.RESULT_2P: result_screen_2p, Screens.SETTINGS: settings_screen, diff --git a/Skins/PyTaikoGreen b/Skins/PyTaikoGreen index bb3d90c..8e2707d 160000 --- a/Skins/PyTaikoGreen +++ b/Skins/PyTaikoGreen @@ -1 +1 @@ -Subproject commit bb3d90c5b9fe33380a9beb8ecaaf546e6b001fa2 +Subproject commit 8e2707de93e6e5aabc8ae5731fd0e469b9dfd4fd diff --git a/libs/global_data.py b/libs/global_data.py index ba47634..20032ef 100644 --- a/libs/global_data.py +++ b/libs/global_data.py @@ -44,6 +44,7 @@ class Modifiers: display: bool = False inverse: bool = False random: int = 0 + subdiff: int = 0 @dataclass class DanResultSong: diff --git a/libs/tja.py b/libs/tja.py index b1394cb..12d09e0 100644 --- a/libs/tja.py +++ b/libs/tja.py @@ -1148,7 +1148,6 @@ def modifier_inverse(notes: NoteList): def modifier_random(notes: NoteList, value: int): """Randomly modifies the type of the notes in the given NoteList. value: 1 == kimagure, 2 == detarame""" - #value: 1 == kimagure, 2 == detarame modded_notes = notes.play_notes.copy() percentage = int(len(modded_notes) / 5) * value selected_notes = random.sample(range(len(modded_notes)), percentage) @@ -1166,7 +1165,9 @@ def apply_modifiers(notes: NoteList, modifiers: Modifiers): play_notes = modifier_inverse(notes) play_notes = modifier_random(notes, modifiers.random) draw_notes, bars = modifier_speed(notes, modifiers.speed) - return deque(play_notes), deque(draw_notes), deque(bars) + play_notes = modifier_difficulty(notes, modifiers.subdiff) + draw_notes = modifier_difficulty(notes, modifiers.subdiff) + return play_notes, draw_notes, bars class Interval(IntEnum): UNKNOWN = 0 @@ -1188,7 +1189,7 @@ def modifier_difficulty(notes: NoteList, level: int): Modified list of notes """ # Levels with no changes: Easy (1), Normal (2-5), Hard (9), Oni (13) - if level in [1, 2, 3, 4, 5, 9, 13]: + if level in [0, 1, 2, 3, 4, 5, 9, 13]: return notes.play_notes modded_notes = notes.play_notes.copy() diff --git a/pyproject.toml b/pyproject.toml index ee4310c..8d4cfc4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,7 @@ readme = "README.md" requires-python = ">=3.13" dependencies = [ "av>=16.0.1", + "pyinstrument>=5.1.1", "pypresence>=4.6.1", "pytest>=9.0.2", "raylib-sdl>=5.5.0.2", diff --git a/scenes/ai_battle/game.py b/scenes/ai_battle/game.py new file mode 100644 index 0000000..86f5b25 --- /dev/null +++ b/scenes/ai_battle/game.py @@ -0,0 +1,67 @@ +import logging + +import pyray as ray + +from libs.audio import audio +from libs.global_data import global_data +from libs.utils import get_current_ms +from scenes.game import GameScreen + +logger = logging.getLogger(__name__) + +class AIBattleGameScreen(GameScreen): + def global_keys(self): + if ray.is_key_pressed(global_data.config["keys"]["restart_key"]): + if self.song_music is not None: + audio.stop_music_stream(self.song_music) + self.init_tja(global_data.session_data[global_data.player_num].selected_song) + audio.play_sound('restart', 'sound') + self.song_started = False + + if ray.is_key_pressed(global_data.config["keys"]["back_key"]): + if self.song_music is not None: + audio.stop_music_stream(self.song_music) + return self.on_screen_end('AI_SELECT') + + if ray.is_key_pressed(global_data.config["keys"]["pause_key"]): + self.pause_song() + + def update(self): + super(GameScreen, self).update() + current_time = get_current_ms() + self.transition.update(current_time) + if not self.paused: + self.current_ms = current_time - self.start_ms + if self.transition.is_finished: + self.start_song(self.current_ms) + else: + self.start_ms = current_time - self.tja.metadata.offset*1000 + self.update_background(current_time) + + if self.song_music is not None: + audio.update_music_stream(self.song_music) + + self.player_1.update(self.current_ms, current_time, self.background) + self.song_info.update(current_time) + self.result_transition.update(current_time) + if self.result_transition.is_finished and not audio.is_sound_playing('result_transition'): + logger.info("Result transition finished, moving to RESULT screen") + return self.on_screen_end('AI_SELECT') + elif self.current_ms >= self.player_1.end_time: + session_data = global_data.session_data[global_data.player_num] + session_data.result_data.score, session_data.result_data.good, session_data.result_data.ok, session_data.result_data.bad, session_data.result_data.max_combo, session_data.result_data.total_drumroll = self.player_1.get_result_score() + if self.player_1.gauge is not None: + session_data.result_data.gauge_length = self.player_1.gauge.gauge_length + if self.end_ms != 0: + if current_time >= self.end_ms + 1000: + if self.player_1.ending_anim is None: + self.spawn_ending_anims() + if current_time >= self.end_ms + 8533.34: + if not self.result_transition.is_started: + self.result_transition.start() + audio.play_sound('result_transition', 'voice') + logger.info("Result transition started and voice played") + else: + self.end_ms = current_time + + return self.global_keys() diff --git a/scenes/ai_battle/song_select.py b/scenes/ai_battle/song_select.py new file mode 100644 index 0000000..42edb03 --- /dev/null +++ b/scenes/ai_battle/song_select.py @@ -0,0 +1,263 @@ +import logging + +import pyray as ray + +from libs.audio import audio +from libs.file_navigator import SongFile +from libs.global_data import Difficulty, PlayerNum, global_data +from libs.texture import tex +from libs.utils import ( + is_l_don_pressed, + is_l_kat_pressed, + is_r_don_pressed, + is_r_kat_pressed, +) +from scenes.song_select import ( + ModifierSelector, + NeiroSelector, + SongSelectPlayer, + SongSelectScreen, + State, +) + +logger = logging.getLogger(__name__) + +class AISongSelectScreen(SongSelectScreen): + def on_screen_start(self): + super().on_screen_start() + self.player_1 = AISongSelectPlayer(global_data.player_num, self.text_fade_in) + global_data.modifiers[global_data.player_num].subdiff = 0 + + def update_players(self, current_time) -> str: + self.player_1.update(current_time) + if self.text_fade_out.is_finished: + self.player_1.selected_song = True + next_screen = "AI_GAME" + return next_screen + + def draw_background(self): + tex.draw_texture('ai_battle', 'background') + tex.draw_texture('ai_battle', 'background_overlay') + tex.draw_texture('ai_battle', 'background_overlay_2') + + def draw(self): + self.draw_background() + + if self.navigator.genre_bg is not None and self.state == State.BROWSING: + self.navigator.genre_bg.draw(tex.skin_config["boxes"].y) + + self.navigator.draw_boxes(self.move_away.attribute, self.player_1.is_ura, self.diff_fade_out.attribute) + + if self.state == State.BROWSING: + tex.draw_texture('global', 'arrow', index=0, x=-(self.blue_arrow_move.attribute*2), fade=self.blue_arrow_fade.attribute) + tex.draw_texture('global', 'arrow', index=1, mirror='horizontal', x=self.blue_arrow_move.attribute*2, fade=self.blue_arrow_fade.attribute) + tex.draw_texture('global', 'footer') + + self.ura_switch_animation.draw() + + if self.diff_sort_selector is not None: + self.diff_sort_selector.draw() + + if self.search_box is not None: + self.search_box.draw() + + if (self.player_1.selected_song and self.state == State.SONG_SELECTED): + tex.draw_texture('global', 'difficulty_select', fade=self.text_fade_in.attribute) + elif self.state == State.DIFF_SORTING: + tex.draw_texture('global', 'difficulty_select', fade=self.text_fade_in.attribute) + else: + tex.draw_texture('global', 'song_select', fade=self.text_fade_out.attribute) + + self.draw_players() + + if self.state == State.BROWSING and self.navigator.items != []: + curr_item = self.navigator.get_current_item() + if isinstance(curr_item, SongFile): + curr_item.box.draw_score_history() + + if self.player_1.subdiff_selector is not None and self.player_1.subdiff_selector.is_selected: + ray.draw_rectangle(0, 0, tex.screen_width, tex.screen_height, ray.fade(ray.BLACK, 0.5)) + self.player_1.subdiff_selector.draw() + + self.draw_overlay() + +class AISongSelectPlayer(SongSelectPlayer): + def __init__(self, player_num: PlayerNum, text_fade_in): + super().__init__(player_num, text_fade_in) + self.subdiff_selector = None + + def update(self, current_time): + super().update(current_time) + if self.subdiff_selector is not None: + self.subdiff_selector.update(current_time, self.selected_difficulty) + + def on_song_selected(self, selected_song: SongFile): + """Called when a song is selected""" + super().on_song_selected(selected_song) + self.subdiff_selector = SubdiffSelector(self.player_num, min(selected_song.tja.metadata.course_data)) + + def handle_input_selected(self, current_item): + """Handle input for selecting difficulty. Returns 'cancel', 'confirm', or None""" + if self.neiro_selector is not None: + if is_l_kat_pressed(self.player_num): + self.neiro_selector.move_left() + elif is_r_kat_pressed(self.player_num): + self.neiro_selector.move_right() + if is_l_don_pressed(self.player_num) or is_r_don_pressed(self.player_num): + audio.play_sound('don', 'sound') + self.neiro_selector.confirm() + return None + + if self.modifier_selector is not None: + if is_l_kat_pressed(self.player_num): + audio.play_sound('kat', 'sound') + self.modifier_selector.left() + elif is_r_kat_pressed(self.player_num): + audio.play_sound('kat', 'sound') + self.modifier_selector.right() + if is_l_don_pressed(self.player_num) or is_r_don_pressed(self.player_num): + audio.play_sound('don', 'sound') + self.modifier_selector.confirm() + return None + + if self.subdiff_selector is not None and self.subdiff_selector.is_selected: + if is_l_kat_pressed(self.player_num): + audio.play_sound('kat', 'sound') + self.subdiff_selector.move_left(self.selected_difficulty) + elif is_r_kat_pressed(self.player_num): + audio.play_sound('kat', 'sound') + self.subdiff_selector.move_right(self.selected_difficulty) + if is_l_don_pressed(self.player_num) or is_r_don_pressed(self.player_num): + audio.play_sound('don', 'sound') + self.subdiff_selector.confirm() + self.is_ready = True + return "confirm" + return None + + if is_l_don_pressed(self.player_num) or is_r_don_pressed(self.player_num): + if self.selected_difficulty == -3: + self.subdiff_selector = None + return "cancel" + elif self.selected_difficulty == -2: + audio.play_sound('don', 'sound') + self.modifier_selector = ModifierSelector(self.player_num) + return None + elif self.selected_difficulty == -1: + audio.play_sound('don', 'sound') + self.neiro_selector = NeiroSelector(self.player_num) + return None + else: + audio.play_sound('don', 'sound') + if self.subdiff_selector is not None: + self.subdiff_selector.is_selected = True + + if is_l_kat_pressed(self.player_num) or is_r_kat_pressed(self.player_num): + audio.play_sound('kat', 'sound') + selected_song = current_item + diffs = sorted(selected_song.tja.metadata.course_data) + prev_diff = self.selected_difficulty + ret_val = None + + if is_l_kat_pressed(self.player_num): + ret_val = self._navigate_difficulty_left(diffs) + elif is_r_kat_pressed(self.player_num): + ret_val = self._navigate_difficulty_right(diffs) + + if Difficulty.EASY <= self.selected_difficulty <= Difficulty.URA and self.selected_difficulty != prev_diff: + self.selected_diff_bounce.start() + self.selected_diff_fadein.start() + + + return ret_val + + if (ray.is_key_pressed(ray.KeyboardKey.KEY_TAB) and + self.selected_difficulty in [Difficulty.ONI, Difficulty.URA]): + return self._toggle_ura_mode() + + return None + + def draw(self, state: int, is_half: bool = False): + if (self.selected_song and state == State.SONG_SELECTED): + self.draw_selector(is_half) + + offset = 0 + if self.subdiff_selector is not None: + offset = -self.subdiff_selector.move.attribute*1.05 + if self.player_num == PlayerNum.P1: + self.nameplate.draw(tex.skin_config["song_select_nameplate_1p"].x, tex.skin_config["song_select_nameplate_1p"].y) + self.chara.draw(x=tex.skin_config["song_select_chara_1p"].x, y=tex.skin_config["song_select_chara_1p"].y + (offset*0.6)) + else: + self.nameplate.draw(tex.skin_config["song_select_nameplate_2p"].x, tex.skin_config["song_select_nameplate_2p"].y) + self.chara.draw(mirror=True, x=tex.skin_config["song_select_chara_2p"].x, y=tex.skin_config["song_select_chara_2p"].y + (offset*0.6)) + + if self.subdiff_selector is not None: + self.subdiff_selector.draw() + + if self.neiro_selector is not None: + self.neiro_selector.draw() + + if self.modifier_selector is not None: + self.modifier_selector.draw() + +class SubdiffSelector: + def __init__(self, player_num: PlayerNum, lowest_difficulty: int): + self.player_num = player_num + self.move = tex.get_animation(28, is_copy=True) + self.blue_arrow_fade = tex.get_animation(29, is_copy=True) + self.blue_arrow_move = tex.get_animation(30, is_copy=True) + self.move.start() + self.is_selected = False + self.selected_index = 0 + subdiffs_easy = [('subdiff_easy', 0), ('subdiff_normal', 0), ('subdiff_normal', 1), ('subdiff_normal', 2)] + subdiffs_normal = [('subdiff_normal', 0), ('subdiff_normal', 1), ('subdiff_normal', 2), ('subdiff_normal', 3)] + subdiffs_hard = [('subdiff_hard', 0), ('subdiff_hard', 1), ('subdiff_hard', 2), ('subdiff_hard', 3)] + subdiffs_oni = [('subdiff_oni', 0), ('subdiff_oni', 1), ('subdiff_oni', 2), ('subdiff_oni', 3)] + self.levels = [ + [1, 2, 3, 4], + [2, 3, 4, 5], + [6, 7, 8, 9], + [10, 11, 12, 13], + [10, 11, 12, 13] + ] + self.selected_level = 1 + + self.diff_map = { + Difficulty.EASY: subdiffs_easy, + Difficulty.NORMAL: subdiffs_normal, + Difficulty.HARD: subdiffs_hard, + Difficulty.ONI: subdiffs_oni, + Difficulty.URA: subdiffs_oni + } + self.selected_subdiff = self.diff_map[Difficulty(lowest_difficulty)] + + def update(self, current_ms: float, current_difficulty: int): + self.move.update(current_ms) + if current_difficulty in self.diff_map: + self.selected_subdiff = self.diff_map[Difficulty(current_difficulty)] + + def move_left(self, difficulty: int): + self.selected_index = max(0, self.selected_index - 1) + self.selected_level = self.levels[difficulty][self.selected_index] + + def move_right(self, difficulty: int): + self.selected_index = min(3, self.selected_index + 1) + self.selected_level = self.levels[difficulty][self.selected_index] + + def confirm(self): + global_data.modifiers[self.player_num].subdiff = self.selected_level + + def draw(self): + y = -self.move.attribute*1.05 + if self.is_selected: + tex.draw_texture('ai_battle', 'box_bg_blur', y=y) + tex.draw_texture('ai_battle', 'box_2', y=y) + tex.draw_texture('ai_battle', 'subdiff_select_text', y=y) + else: + tex.draw_texture('ai_battle', 'box_1', y=y) + + tex.draw_texture('ai_battle', 'subdiff_selector', x=self.selected_index*tex.textures['ai_battle']['subdiff_easy'].width, y=y) + for i, subdiff in enumerate(self.selected_subdiff): + name, frame = subdiff + tex.draw_texture('ai_battle', name, frame=frame, x=i*tex.textures['ai_battle'][name].width, y=y) + + tex.draw_texture('ai_battle', 'bottom_text', y=y) diff --git a/scenes/entry.py b/scenes/entry.py index 23a50c8..ebf2d4d 100644 --- a/scenes/entry.py +++ b/scenes/entry.py @@ -452,8 +452,9 @@ class BoxManager: self.box_titles: list[OutlinedText] = [ OutlinedText(tex.skin_config["entry_game"].text[global_data.config["general"]["language"]], tex.skin_config["entry_box_text"].font_size, ray.WHITE, outline_thickness=5, vertical=True), OutlinedText(tex.skin_config["entry_practice"].text[global_data.config["general"]["language"]], tex.skin_config["entry_box_text"].font_size, ray.WHITE, outline_thickness=5, vertical=True), - OutlinedText(tex.skin_config["entry_settings"].text[global_data.config["general"]["language"]], tex.skin_config["entry_box_text"].font_size, ray.WHITE, outline_thickness=5, vertical=True)] - self.box_locations = ["SONG_SELECT", "PRACTICE_SELECT", "SETTINGS"] + OutlinedText(tex.skin_config["entry_ai_battle"].text[global_data.config["general"]["language"]], tex.skin_config["entry_box_text"].font_size, ray.WHITE, outline_thickness=5, vertical=True), + OutlinedText(tex.skin_config["entry_settings"].text[global_data.config["general"]["language"]], tex.skin_config["entry_box_text"].font_size, ray.WHITE, outline_thickness=5, vertical=True),] + self.box_locations = ["SONG_SELECT", "PRACTICE_SELECT", "AI_SELECT", "SETTINGS"] self.num_boxes = len(self.box_titles) self.boxes = [Box(self.box_titles[i], self.box_locations[i]) for i in range(len(self.box_titles))] self.selected_box_index = 0 @@ -512,7 +513,7 @@ class BoxManager: def update(self, current_time_ms: float, is_2p: bool): self.is_2p = is_2p if self.is_2p: - self.box_locations = ["SONG_SELECT_2P", "PRACTICE_SELECT", "SETTINGS"] + self.box_locations = ["SONG_SELECT_2P", "PRACTICE_SELECT", "AI_SELECT", "SETTINGS"] for i, box in enumerate(self.boxes): box.location = self.box_locations[i] self.fade_out.update(current_time_ms) diff --git a/scenes/game.py b/scenes/game.py index 444f640..fe8eb3c 100644 --- a/scenes/game.py +++ b/scenes/game.py @@ -401,7 +401,7 @@ class Player: def reset_chart(self): notes, self.branch_m, self.branch_e, self.branch_n = self.tja.notes_to_position(self.difficulty) - self.play_notes, self.draw_note_list, self.draw_bar_list = apply_modifiers(notes, self.modifiers) + self.play_notes, self.draw_note_list, self.draw_bar_list = deque(apply_modifiers(notes, self.modifiers)[0]), deque(apply_modifiers(notes, self.modifiers)[1]), deque(apply_modifiers(notes, self.modifiers)[2]) self.don_notes = deque([note for note in self.play_notes if note.type in {NoteType.DON, NoteType.DON_L}]) self.kat_notes = deque([note for note in self.play_notes if note.type in {NoteType.KAT, NoteType.KAT_L}]) @@ -449,7 +449,7 @@ class Player: self.bpm = 120 if self.timeline and hasattr(self.timeline[self.timeline_index], 'bpm'): self.bpm = self.timeline[self.timeline_index].bpm - last_note = self.draw_note_list[0] + last_note = self.draw_note_list[0] if self.draw_note_list else self.branch_m[0].draw_notes[0] for note in chain(self.draw_note_list, self.draw_bar_list): self.get_load_time(note) if note.type == NoteType.TAIL: @@ -490,6 +490,7 @@ class Player: for branch in (self.branch_m, self.branch_e, self.branch_n): if branch: for section in branch: + section.play_notes, section.draw_notes, section.bars = apply_modifiers(section, self.modifiers) if section.draw_notes: for note in section.draw_notes: self.get_load_time(note) diff --git a/scenes/song_select.py b/scenes/song_select.py index 18d5748..b769136 100644 --- a/scenes/song_select.py +++ b/scenes/song_select.py @@ -381,7 +381,7 @@ class SongSelectScreen(Screen): def draw_players(self): self.player_1.draw(self.state) - def draw(self): + def draw_background(self): width = tex.textures['box']['background'].width genre_index = self.genre_index last_genre_index = self.last_genre_index @@ -394,6 +394,26 @@ class SongSelectScreen(Screen): tex.draw_texture('box', 'background', frame=genre_index, x=i-self.background_move.attribute, fade=1 - self.background_fade_change.attribute) ray.end_shader_mode() + def draw_overlay(self): + self.indicator.draw(tex.skin_config['song_select_indicator'].x, tex.skin_config['song_select_indicator'].y) + + tex.draw_texture('global', 'song_num_bg', fade=0.75, x=-(self.song_num.texture.width-127), x2=(self.song_num.texture.width-127)) + self.song_num.draw(ray.BLACK, x=tex.skin_config["song_num"].x-self.song_num.texture.width, y=tex.skin_config["song_num"].y) + if self.state == State.BROWSING or self.state == State.DIFF_SORTING: + self.timer_browsing.draw() + elif self.state == State.SONG_SELECTED: + self.timer_selected.draw() + self.coin_overlay.draw() + if self.game_transition is not None: + self.game_transition.draw() + + if self.dan_transition.is_started: + self.dan_transition.draw() + self.allnet_indicator.draw() + + def draw(self): + self.draw_background() + self.draw_background_diffs() if self.navigator.genre_bg is not None and self.state == State.BROWSING: @@ -428,21 +448,7 @@ class SongSelectScreen(Screen): if isinstance(curr_item, SongFile): curr_item.box.draw_score_history() - self.indicator.draw(tex.skin_config['song_select_indicator'].x, tex.skin_config['song_select_indicator'].y) - - tex.draw_texture('global', 'song_num_bg', fade=0.75, x=-(self.song_num.texture.width-127), x2=(self.song_num.texture.width-127)) - self.song_num.draw(ray.BLACK, x=tex.skin_config["song_num"].x-self.song_num.texture.width, y=tex.skin_config["song_num"].y) - if self.state == State.BROWSING or self.state == State.DIFF_SORTING: - self.timer_browsing.draw() - elif self.state == State.SONG_SELECTED: - self.timer_selected.draw() - self.coin_overlay.draw() - if self.game_transition is not None: - self.game_transition.draw() - - if self.dan_transition.is_started: - self.dan_transition.draw() - self.allnet_indicator.draw() + self.draw_overlay() class SongSelectPlayer: def __init__(self, player_num: PlayerNum, text_fade_in): @@ -639,8 +645,6 @@ class SongSelectPlayer: if is_l_kat_pressed(self.player_num) or is_r_kat_pressed(self.player_num): audio.play_sound('kat', 'sound') selected_song = current_item - if isinstance(selected_song, Directory): - raise Exception("Directory was chosen instead of song") diffs = sorted(selected_song.tja.metadata.course_data) prev_diff = self.selected_difficulty ret_val = None @@ -1235,7 +1239,7 @@ class ModifierSelector: } def __init__(self, player_num: PlayerNum): self.player_num = player_num - self.mods = fields(Modifiers) + self.mods = fields(Modifiers)[:-1] self.current_mod_index = 0 self.is_confirmed = False self.is_finished = False diff --git a/uv.lock b/uv.lock index 758ed96..7594be8 100644 --- a/uv.lock +++ b/uv.lock @@ -217,6 +217,38 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] +[[package]] +name = "pyinstrument" +version = "5.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/80/ce/824ee634994e612156f7b84eaf50b8523c676ebfed8d8dd12939a82f4c15/pyinstrument-5.1.1.tar.gz", hash = "sha256:bc401cda990b3c1cfe8e0e0473cbd605df3c63b73478a89ac4ab108f2184baa8", size = 264730, upload-time = "2025-08-12T11:35:43.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/d4/b94f47aa7d301f6cdf5924bb75caacd0d0a1852bd4e876e3a64fc5798dad/pyinstrument-5.1.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:45af421c60c943a7f1619afabeba4951d4cc16b4206490d7d5b7ef5a4e2dfd42", size = 130315, upload-time = "2025-08-12T11:34:52.91Z" }, + { url = "https://files.pythonhosted.org/packages/1e/42/1bc2f28e139f69a0918d5d5dc1d59e65c640d4da9dd153fa48c2a8a87dd9/pyinstrument-5.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2603db3d745a65de66c96929ab9b0fcce050511eb24e32856ea2458785b8917f", size = 122805, upload-time = "2025-08-12T11:34:54.201Z" }, + { url = "https://files.pythonhosted.org/packages/a8/85/2f0c9115cd8a01e0a18d0650d9f3f20ff71e8ca17bd4af60dd3a0cb76f8a/pyinstrument-5.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2fe32492100efaa1b0a488c237fe420fdaf141646733a31a97f96c4e1fa6bbf8", size = 148210, upload-time = "2025-08-12T11:34:55.662Z" }, + { url = "https://files.pythonhosted.org/packages/86/62/3c73a63e6913378cc7e9ffb5af1e50836511eee83b7c7bf252fad7ec24e4/pyinstrument-5.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:999b5373f8b1e846357923063ae5c9275ad8a85ed4e0a42960a349288d1f5007", size = 146995, upload-time = "2025-08-12T11:34:57.133Z" }, + { url = "https://files.pythonhosted.org/packages/ab/8b/d21f4b6d8849881e9572967818e3e6d2dcb212e7dfa89e4e356d359db32b/pyinstrument-5.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:58a2f69052178ec624e4df0cf546eda48b3a381572ac1cb3272b4c163888af9d", size = 147029, upload-time = "2025-08-12T11:34:58.255Z" }, + { url = "https://files.pythonhosted.org/packages/8a/4d/1e43cecf2bcf4a3dd1100f4fc7a3da6438a65d0b95ca7b8ab5d094ea7c0b/pyinstrument-5.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4d9bbc00d2e258edbefeb39b61ad4636099b08acd1effdd40d76883a13e7bf5a", size = 146668, upload-time = "2025-08-12T11:34:59.401Z" }, + { url = "https://files.pythonhosted.org/packages/34/48/00322b48e7adb665d04303b487454eb0c13a76ec0af8da20f452098fcc12/pyinstrument-5.1.1-cp313-cp313-win32.whl", hash = "sha256:cf2d8933e2aeaa02d4cb6279d83ef11ee882fb243fff96e3378153a730aadd6e", size = 124288, upload-time = "2025-08-12T11:35:00.514Z" }, + { url = "https://files.pythonhosted.org/packages/f5/14/d56515a110f74799aefc7489c1578ce4d99a4d731309559a427f954e7abc/pyinstrument-5.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:2402683a92617066b13a6d48f904396dcd15938016875b392534df027660eed4", size = 125041, upload-time = "2025-08-12T11:35:01.913Z" }, + { url = "https://files.pythonhosted.org/packages/18/2b/e4bdcabb5ae67de2ec3fa1f6e4eb4ae707b0bf460f895d4594792cdc919b/pyinstrument-5.1.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:688acba1c00cad73e43254e610f8e384a53ced3b0dbb5268fb44636e2b99663e", size = 130358, upload-time = "2025-08-12T11:35:03.569Z" }, + { url = "https://files.pythonhosted.org/packages/20/36/616f8db63997c096d3fb65e657cdf5bd2a63b53ed24a14750770dc500979/pyinstrument-5.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:116f5ad8cec4d6f5626305d7c1a104f5845a084bfb4b192d231eb8c41ea81f9a", size = 122827, upload-time = "2025-08-12T11:35:04.661Z" }, + { url = "https://files.pythonhosted.org/packages/af/7a/4f5d2bbc7c2466d46eb5ff47c6e667464eead47140e01a64be45215a59d4/pyinstrument-5.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d139d12a637001d3884344330054ce8335b2c8165dc3dd239726e1b358576bd", size = 147947, upload-time = "2025-08-12T11:35:05.786Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/c9b0081c0e52789a910390ce44e54c1318999d74386f15d92d0deb522aff/pyinstrument-5.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bc5b87b1e27bec94457fed8d03c755a3c09edb4f35d975dbdffd77d863173254", size = 146702, upload-time = "2025-08-12T11:35:07.202Z" }, + { url = "https://files.pythonhosted.org/packages/1e/1b/745ed7997da22ae68ff21b8f28e5e3a97b220335dce4ee7cf46d5eb17b32/pyinstrument-5.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15f4a2ed9562efab34b555e1208955cf9681b2272489d7a59cd0e289344ada2e", size = 146836, upload-time = "2025-08-12T11:35:08.297Z" }, + { url = "https://files.pythonhosted.org/packages/70/f0/05cefdcf79d1901f9d179e7f55f3acaadbc5fee7af955cebb3f555280638/pyinstrument-5.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1cb0c79bfa2b2b5734213429c9d7f455e5af664cfde785c69a5780f6c532c1fd", size = 146463, upload-time = "2025-08-12T11:35:09.483Z" }, + { url = "https://files.pythonhosted.org/packages/6c/cb/6a6f33316be3c7b8247f8ca0e418a2b6fb68d64c227169b7dbee50009366/pyinstrument-5.1.1-cp314-cp314-win32.whl", hash = "sha256:3b9f1216ae4848a8983dc405e1a42e46e75bd8ae96aaba328d4358b8fc80a7a0", size = 124950, upload-time = "2025-08-12T11:35:11.607Z" }, + { url = "https://files.pythonhosted.org/packages/d6/ea/99caeb29f446f57d077a83c7c5f2b7c27c1719984d425f679bf2ec1eb6b0/pyinstrument-5.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:26971d4a17e0d5d4f6737e71c9de7a7ce5c83ab7daf078c6bf330be41d65273b", size = 125720, upload-time = "2025-08-12T11:35:12.683Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d0/953b75d634565ef34f8ed559f2e4af7cd1f2d5f5b578092e8f1d8199e4b1/pyinstrument-5.1.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:62362843884d654401ec4c25fed35f4b4ded077d96b3396f1e791c31e4203d3e", size = 131258, upload-time = "2025-08-12T11:35:13.805Z" }, + { url = "https://files.pythonhosted.org/packages/a6/a4/4ec87cfd0974d79b2fcd72b3e20336fc65b96a5b08f2eb2867bf71b27b82/pyinstrument-5.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f2d640230b71c6d9ac8f27a9c5cd07fc8a6acad9196d1e48d9c33658b176fb80", size = 123276, upload-time = "2025-08-12T11:35:14.933Z" }, + { url = "https://files.pythonhosted.org/packages/eb/f8/6a210989c8ede85f91b7e4ba5d9730492f1d081762570c06c750d787536c/pyinstrument-5.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f54f7292c63461c75ddf193f5e733803e463ccbc54f2fb7c9591337ddea7d10", size = 155767, upload-time = "2025-08-12T11:35:16.124Z" }, + { url = "https://files.pythonhosted.org/packages/f4/a8/5ac81ffbfe36d2e5c3332a9452746a21540987da0d9491db751a905bba13/pyinstrument-5.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c156eb442f9f22960ae16bd195051863d5e8a68b877926e88bbaf8bbdc1456d1", size = 153423, upload-time = "2025-08-12T11:35:17.312Z" }, + { url = "https://files.pythonhosted.org/packages/3f/55/5620c2a61403cde044e81e33056c14fbf5793eea33f67f2223d61abec9ae/pyinstrument-5.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:caadaf67ad5926c46af784316024793c909b9e9ee550475855fd32171c4bd033", size = 153542, upload-time = "2025-08-12T11:35:18.729Z" }, + { url = "https://files.pythonhosted.org/packages/7a/83/a8f22466652250a847dfdf58f9a2717b470fdbbcb075c7f730bf608041a6/pyinstrument-5.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88ef2e8f483a5e1501d79a7ebdab592a597467810ed24d8db09ab6f568e938d3", size = 152337, upload-time = "2025-08-12T11:35:19.849Z" }, + { url = "https://files.pythonhosted.org/packages/0d/a6/cd4590da14deaeda6315519c26064874bbb9648a1358b80e8a8ca5d4add0/pyinstrument-5.1.1-cp314-cp314t-win32.whl", hash = "sha256:265bc4389f82e6521777bfab426a62a15c4940955e86f75db79a44e7349f9757", size = 125621, upload-time = "2025-08-12T11:35:21.201Z" }, + { url = "https://files.pythonhosted.org/packages/b3/30/177102e798539368aef25688a6a171d66ec92e6f16b6b651a89045a2bd13/pyinstrument-5.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:fa254f269a72a007b5d02c18cd4b67081e0efabbd33e18acdbd5e3be905afa06", size = 126528, upload-time = "2025-08-12T11:35:22.578Z" }, +] + [[package]] name = "pypresence" version = "4.6.1" @@ -232,6 +264,7 @@ version = "1.1" source = { virtual = "." } dependencies = [ { name = "av" }, + { name = "pyinstrument" }, { name = "pypresence" }, { name = "pytest" }, { name = "raylib-sdl" }, @@ -247,6 +280,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "av", specifier = ">=16.0.1" }, + { name = "pyinstrument", specifier = ">=5.1.1" }, { name = "pypresence", specifier = ">=4.6.1" }, { name = "pytest", specifier = ">=9.0.2" }, { name = "raylib-sdl", specifier = ">=5.5.0.2" },