From 0999a2034b6bd603f4b4a6243d07cbf90620a491 Mon Sep 17 00:00:00 2001 From: Anthony Samms Date: Sun, 12 Oct 2025 02:51:43 -0400 Subject: [PATCH] add clear/fc/fail animation --- libs/animation.py | 21 +++-- libs/global_objects.py | 1 - libs/texture.py | 1 - libs/tja.py | 2 - scenes/devtest.py | 14 +--- scenes/game.py | 178 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 195 insertions(+), 22 deletions(-) diff --git a/libs/animation.py b/libs/animation.py index c3917f3..96ae6bd 100644 --- a/libs/animation.py +++ b/libs/animation.py @@ -223,17 +223,21 @@ class TextureChangeAnimation(BaseAnimation): self.is_finished = True class TextStretchAnimation(BaseAnimation): - def __init__(self, duration: float, loop: bool = False, lock_input: bool = False) -> None: - super().__init__(duration, loop=loop, lock_input=lock_input) + def __init__(self, duration: float, loop: bool = False, lock_input: bool = False, delay: float = 0.0) -> None: + super().__init__(duration, loop=loop, lock_input=lock_input, delay=delay) def update(self, current_time_ms: float) -> None: if not self.is_started: return super().update(current_time_ms) elapsed_time = current_time_ms - self.start_ms - if elapsed_time <= self.duration: - self.attribute = 2 + 5 * (elapsed_time // 25) - elif elapsed_time <= self.duration + 116: - frame_time = (elapsed_time - self.duration) // 16.57 + if elapsed_time < self.delay: + return + + animation_time = elapsed_time - self.delay + if animation_time <= self.duration: + self.attribute = 2 + 5 * (animation_time // 25) + elif animation_time <= self.duration + 116: + frame_time = (animation_time - self.duration) // 16.57 self.attribute = 2 + 10 - (2 * (frame_time + 1)) else: self.attribute = 0 @@ -346,13 +350,14 @@ class Animation: return TextureChangeAnimation(duration, **kwargs) @staticmethod - def create_text_stretch(duration: float) -> TextStretchAnimation: + def create_text_stretch(duration: float, **kwargs) -> TextStretchAnimation: """Create a text stretch animation. Args: duration: Length of the stretch in milliseconds + delay: Time to wait before starting the stretch """ - return TextStretchAnimation(duration) + return TextStretchAnimation(duration, **kwargs) @staticmethod def create_texture_resize(duration: float, **kwargs) -> TextureResizeAnimation: diff --git a/libs/global_objects.py b/libs/global_objects.py index 7d5fbe4..739450b 100644 --- a/libs/global_objects.py +++ b/libs/global_objects.py @@ -8,7 +8,6 @@ class Nameplate: def __init__(self, name: str, title: str, player_num: int, dan: int, is_gold: bool): self.name = OutlinedText(name, 22, ray.WHITE, ray.BLACK, outline_thickness=3.0) self.title = OutlinedText(title, 20, ray.BLACK, ray.WHITE, outline_thickness=0) - print(self.name.texture.width) self.dan_index = dan self.player_num = player_num self.is_gold = is_gold diff --git a/libs/texture.py b/libs/texture.py index 992078c..2ad48aa 100644 --- a/libs/texture.py +++ b/libs/texture.py @@ -99,7 +99,6 @@ class TextureWrapper: def load_zip(self, screen_name: str, subset: str): zip = (self.graphics_path / screen_name / subset).with_suffix('.zip') if screen_name in self.textures and subset in self.textures[screen_name]: - print(screen_name, subset) return with zipfile.ZipFile(zip, 'r') as zip_ref: if 'texture.json' not in zip_ref.namelist(): diff --git a/libs/tja.py b/libs/tja.py index e0ad98c..53eee46 100644 --- a/libs/tja.py +++ b/libs/tja.py @@ -698,8 +698,6 @@ class TJAParser: bisect.insort(curr_draw_list, note, key=lambda x: x.load_ms) self.get_moji(curr_note_list, ms_per_measure) index += 1 - if hasattr(curr_bar_list[-1], 'branch_params'): - print(curr_note_list[-1]) # https://stackoverflow.com/questions/72899/how-to-sort-a-list-of-dictionaries-by-a-value-of-the-dictionary-in-python # Sorting by load_ms is necessary for drawing, as some notes appear on the # screen slower regardless of when they reach the judge circle diff --git a/scenes/devtest.py b/scenes/devtest.py index 0e71c9d..ea29568 100644 --- a/scenes/devtest.py +++ b/scenes/devtest.py @@ -2,7 +2,7 @@ import pyray as ray from libs.utils import get_current_ms from libs.texture import tex -from scenes.game import BranchIndicator +from scenes.game import FCAnimation class DevScreen: @@ -16,7 +16,7 @@ class DevScreen: if not self.screen_init: self.screen_init = True tex.load_screen_textures('game') - self.obj = BranchIndicator() + self.obj = FCAnimation() def on_screen_end(self, next_screen: str): self.screen_init = False @@ -27,14 +27,8 @@ class DevScreen: self.obj.update(get_current_ms()) if ray.is_key_pressed(ray.KeyboardKey.KEY_ENTER): return self.on_screen_end('GAME') - elif ray.is_key_pressed(ray.KeyboardKey.KEY_UP): - self.obj.level_up('master') - elif ray.is_key_pressed(ray.KeyboardKey.KEY_DOWN): - self.obj.level_down('expert') - elif ray.is_key_pressed(ray.KeyboardKey.KEY_LEFT): - self.obj.level_up('expert') - elif ray.is_key_pressed(ray.KeyboardKey.KEY_RIGHT): - self.obj.level_down('normal') + if ray.is_key_pressed(ray.KeyboardKey.KEY_SPACE): + self.obj = FCAnimation() def draw(self): ray.draw_rectangle(0, 0, 1280, 720, ray.GREEN) diff --git a/scenes/game.py b/scenes/game.py index a3240d9..5d3be08 100644 --- a/scenes/game.py +++ b/scenes/game.py @@ -183,6 +183,14 @@ class GameScreen: session_data.result_score, session_data.result_good, session_data.result_ok, session_data.result_bad, session_data.result_max_combo, session_data.result_total_drumroll = self.player_1.get_result_score() session_data.result_gauge_length = self.player_1.gauge.gauge_length if self.end_ms != 0: + if current_time >= self.end_ms + 500: + if self.player_1.ending_anim is None: + if session_data.result_bad == 0: + self.player_1.ending_anim = FCAnimation() + elif self.player_1.gauge.is_clear: + self.player_1.ending_anim = ClearAnimation() + elif not self.player_1.gauge.is_clear: + self.player_1.ending_anim = FailAnimation() if current_time >= self.end_ms + 8533.34: if not self.result_transition.is_started: self.result_transition.start() @@ -298,6 +306,7 @@ class Player: self.gogo_time: Optional[GogoTime] = None self.combo_announce = ComboAnnounce(self.combo, 0) self.branch_indicator = BranchIndicator() if tja and tja.metadata.course_data[self.difficulty].is_branching else None + self.ending_anim: Optional[FailAnimation | ClearAnimation | FCAnimation] = None self.is_gogo_time = False plate_info = global_data.config['nameplate'] self.nameplate = Nameplate(plate_info['name'], plate_info['title'], global_data.player_num, plate_info['dan'], plate_info['gold']) @@ -796,6 +805,8 @@ class Player: self.gauge.update(current_time) if self.branch_indicator is not None: self.branch_indicator.update(current_time) + if self.ending_anim is not None: + self.ending_anim.update(current_time) if self.is_branch: self.evaluate_branch(game_screen.current_ms) @@ -961,6 +972,8 @@ class Player: tex.draw_texture('lane', 'drum') if global_data.modifiers.auto: tex.draw_texture('lane', 'auto_icon') + if self.ending_anim is not None: + self.ending_anim.draw() # Group 5: Hit effects and animations for anim in self.draw_drum_hit_list: @@ -1756,6 +1769,171 @@ class BranchIndicator: tex.draw_texture('branch', self.diff_2, y=(self.diff_down.attribute - self.diff_up.attribute) * self.direction, fade=self.diff_fade.attribute) tex.draw_texture('branch', self.difficulty, y=(self.diff_up.attribute * (self.direction*-1)) - (70*self.direction*-1), fade=1 - self.diff_fade.attribute) +class FailAnimation: + def __init__(self): + self.bachio_fade_in = Animation.create_fade(150, initial_opacity=0.0, final_opacity=1.0) + self.bachio_fade_in.start() + self.bachio_texture_change = Animation.create_texture_change(266.67, textures=[(0, 150, 0), (150, 266.67, 1)], delay=self.bachio_fade_in.duration) + self.bachio_fall = Animation.create_texture_change(500, textures=[[0, 495, 0], [495, 500, 1]], delay=self.bachio_texture_change.duration) + self.bachio_texture_change.start() + self.bachio_fall.start() + self.bachio_move_out = Animation.create_move(116.67, total_distance=150, delay=self.bachio_fade_in.duration, ease_out='quadratic') + self.bachio_move_out.start() + self.bachio_boom_fade_in = Animation.create_fade(66.67, initial_opacity=0.0, final_opacity=1.0, reverse_delay=0, delay=self.bachio_fade_in.duration + self.bachio_move_out.duration) + self.bachio_boom_scale = Animation.create_texture_resize(133.34, initial_size=0.5, final_size=1.0, delay=self.bachio_fade_in.duration + self.bachio_move_out.duration) + self.bachio_boom_fade_in.start() + self.bachio_boom_scale.start() + self.bachio_up = Animation.create_move(416.67, total_distance=60, delay=self.bachio_fade_in.duration + self.bachio_move_out.duration, ease_out='quadratic') + self.bachio_down = Animation.create_move(100, total_distance=60, delay=self.bachio_fade_in.duration + self.bachio_move_out.duration + self.bachio_up.duration, ease_out='quadratic') + self.bachio_up.start() + self.bachio_down.start() + self.text_fade_in = Animation.create_fade(283.33, initial_opacity=0.0, final_opacity=1.0, delay=self.bachio_fade_in.duration + self.bachio_move_out.duration + self.bachio_up.duration/2) + self.text_fade_in.start() + self.name = 'in' + self.frame = self.bachio_texture_change.attribute + def update(self, current_time_ms: float): + self.bachio_fade_in.update(current_time_ms) + self.bachio_texture_change.update(current_time_ms) + self.bachio_fall.update(current_time_ms) + self.bachio_move_out.update(current_time_ms) + self.bachio_boom_fade_in.update(current_time_ms) + self.bachio_boom_scale.update(current_time_ms) + self.bachio_up.update(current_time_ms) + self.bachio_down.update(current_time_ms) + self.text_fade_in.update(current_time_ms) + if self.bachio_texture_change.is_finished: + self.name = 'fall' + self.frame = self.bachio_fall.attribute + else: + self.frame = self.bachio_texture_change.attribute + def draw(self): + tex.draw_texture('ending_anim', 'fail', fade=self.text_fade_in.attribute) + tex.draw_texture('ending_anim', 'bachio_l_' + self.name, x=-self.bachio_move_out.attribute - (self.bachio_up.attribute/2), y=self.bachio_down.attribute - self.bachio_up.attribute, frame=self.frame, fade=self.bachio_fade_in.attribute) + tex.draw_texture('ending_anim', 'bachio_r_' + self.name, x=self.bachio_move_out.attribute + (self.bachio_up.attribute/2), y=self.bachio_down.attribute - self.bachio_up.attribute, frame=self.frame, fade=self.bachio_fade_in.attribute) + tex.draw_texture('ending_anim', 'bachio_boom', index=0, fade=self.bachio_boom_fade_in.attribute, center=True, scale=self.bachio_boom_scale.attribute) + tex.draw_texture('ending_anim', 'bachio_boom', index=1, fade=self.bachio_boom_fade_in.attribute, center=True, scale=self.bachio_boom_scale.attribute) + +class ClearAnimation: + def __init__(self): + self.bachio_fade_in = Animation.create_fade(150, initial_opacity=0.0, final_opacity=1.0) + self.bachio_fade_in.start() + self.bachio_texture_change = Animation.create_texture_change(266.67, textures=[(0, 150, 0), (150, 266.67, 1)], delay=self.bachio_fade_in.duration) + self.bachio_texture_change.start() + self.bachio_out = Animation.create_texture_change(200, textures=[[0, 50, 0], [50, 100, 1], [100, 150, 2], [150, 200, 3]], delay=self.bachio_texture_change.duration+100) + self.bachio_out.start() + self.bachio_move_out = Animation.create_move(116.67, total_distance=200, delay=self.bachio_fade_in.duration, ease_out='quadratic') + self.bachio_move_out.start() + self.clear_separate_fade_in = [Animation.create_fade(100, initial_opacity=0.0, final_opacity=1.0, delay=i*50) for i in range(5)] + for fade in self.clear_separate_fade_in: + fade.start() + self.clear_separate_stretch = [Animation.create_text_stretch(200, delay=i*50) for i in range(5)] + for stretch in self.clear_separate_stretch: + stretch.start() + self.clear_highlight_fade_in = Animation.create_fade(183, initial_opacity=0.0, final_opacity=1.0, reverse_delay=0, delay=450) + self.clear_highlight_fade_in.start() + self.draw_clear_full = False + self.name = 'in' + self.frame = 0 + + def update(self, current_time_ms: float): + self.bachio_fade_in.update(current_time_ms) + self.bachio_texture_change.update(current_time_ms) + self.bachio_out.update(current_time_ms) + self.bachio_move_out.update(current_time_ms) + self.clear_highlight_fade_in.update(current_time_ms) + if self.clear_highlight_fade_in.attribute == 1.0: + self.draw_clear_full = True + for fade in self.clear_separate_fade_in: + fade.update(current_time_ms) + for stretch in self.clear_separate_stretch: + stretch.update(current_time_ms) + if self.bachio_texture_change.is_finished: + self.name = 'out' + self.frame = self.bachio_out.attribute + else: + self.frame = self.bachio_texture_change.attribute + def draw(self): + if self.draw_clear_full: + tex.draw_texture('ending_anim', 'clear') + else: + for i in range(4, -1, -1): + tex.draw_texture('ending_anim', 'clear_separated', frame=i, fade=self.clear_separate_fade_in[i].attribute, x=i*60, y=-self.clear_separate_stretch[i].attribute, y2=self.clear_separate_stretch[i].attribute) + tex.draw_texture('ending_anim', 'clear_highlight', fade=self.clear_highlight_fade_in.attribute) + tex.draw_texture('ending_anim', 'bachio_l_' + self.name, x=-self.bachio_move_out.attribute, frame=self.frame, fade=self.bachio_fade_in.attribute) + tex.draw_texture('ending_anim', 'bachio_r_' + self.name, x=self.bachio_move_out.attribute, frame=self.frame, fade=self.bachio_fade_in.attribute) + +class FCAnimation: + def __init__(self): + self.bachio_fade_in = Animation.create_fade(150, initial_opacity=0.0, final_opacity=1.0) + self.bachio_fade_in.start() + self.bachio_texture_change = Animation.create_texture_change(266.67, textures=[(0, 150, 0), (150, 266.67, 1)], delay=self.bachio_fade_in.duration) + self.bachio_texture_change.start() + self.bachio_out = Animation.create_texture_change(200, textures=[[0, 50, 0], [50, 100, 1], [100, 150, 2], [150, 200, 3]], delay=self.bachio_texture_change.duration+100) + self.bachio_out.start() + self.bachio_move_out = Animation.create_move(116.67, total_distance=200, delay=self.bachio_fade_in.duration, ease_out='quadratic') + self.bachio_move_out.start() + self.clear_separate_fade_in = [Animation.create_fade(100, initial_opacity=0.0, final_opacity=1.0, delay=i*50) for i in range(5)] + for fade in self.clear_separate_fade_in: + fade.start() + self.clear_separate_stretch = [Animation.create_text_stretch(200, delay=i*50) for i in range(5)] + for stretch in self.clear_separate_stretch: + stretch.start() + self.clear_highlight_fade_in = Animation.create_fade(183, initial_opacity=0.0, final_opacity=1.0, reverse_delay=0, delay=450) + self.clear_highlight_fade_in.start() + self.fc_highlight_up = Animation.create_move(133, total_distance=20, reverse_delay=216.67, delay=450 + self.clear_highlight_fade_in.duration, ease_out='quadratic') + self.fc_highlight_up.start() + self.fc_highlight_fade_out = Animation.create_fade(133) + self.bachio_move_out_2 = Animation.create_move(700, total_distance=150, ease_in='quadratic', ease_out='quadratic') + self.bachio_move_up = Animation.create_move(350, total_distance=150, reverse_delay=0, ease_in='quadratic') + self.fan_fade_in = Animation.create_fade(183, initial_opacity=0.0, final_opacity=1.0) + self.fan_texture_change = Animation.create_texture_change(100, textures=[[0, 16.67, 0], [16.67, 33.33, 1], [33.33, 50, 2], [50, 66.67, 3], [66.67, 83.33, 4], [83.33, 100, 5]], delay=self.fan_fade_in.duration) + self.draw_clear_full = False + self.name = 'in' + self.frame = 0 + + def update(self, current_time_ms: float): + self.bachio_fade_in.update(current_time_ms) + self.bachio_texture_change.update(current_time_ms) + self.bachio_out.update(current_time_ms) + self.bachio_move_out.update(current_time_ms) + self.clear_highlight_fade_in.update(current_time_ms) + self.fc_highlight_up.update(current_time_ms) + self.fc_highlight_fade_out.update(current_time_ms) + self.bachio_move_out_2.update(current_time_ms) + self.bachio_move_up.update(current_time_ms) + self.fan_fade_in.update(current_time_ms) + self.fan_texture_change.update(current_time_ms) + if self.fc_highlight_up.is_finished and not self.fc_highlight_fade_out.is_started: + self.fc_highlight_fade_out.start() + self.bachio_move_out_2.start() + self.bachio_move_up.start() + self.fan_fade_in.start() + self.fan_texture_change.start() + if self.clear_highlight_fade_in.attribute == 1.0: + self.draw_clear_full = True + for fade in self.clear_separate_fade_in: + fade.update(current_time_ms) + for stretch in self.clear_separate_stretch: + stretch.update(current_time_ms) + if self.bachio_texture_change.is_finished: + self.name = 'out' + self.frame = self.bachio_out.attribute + else: + self.frame = self.bachio_texture_change.attribute + def draw(self): + if self.draw_clear_full: + tex.draw_texture('ending_anim', 'full_combo_overlay', y=-self.fc_highlight_up.attribute, fade=0.5) + tex.draw_texture('ending_anim', 'full_combo', y=-self.fc_highlight_up.attribute) + tex.draw_texture('ending_anim', 'full_combo_highlight', y=-self.fc_highlight_up.attribute, fade=self.fc_highlight_fade_out.attribute) + tex.draw_texture('ending_anim', 'fan_l', frame=self.fan_texture_change.attribute, fade=self.fan_fade_in.attribute) + tex.draw_texture('ending_anim', 'fan_r', frame=self.fan_texture_change.attribute, fade=self.fan_fade_in.attribute) + else: + for i in range(4, -1, -1): + tex.draw_texture('ending_anim', 'clear_separated', frame=i, fade=self.clear_separate_fade_in[i].attribute, x=i*60, y=-self.clear_separate_stretch[i].attribute, y2=self.clear_separate_stretch[i].attribute) + tex.draw_texture('ending_anim', 'clear_highlight', fade=self.clear_highlight_fade_in.attribute) + tex.draw_texture('ending_anim', 'bachio_l_' + self.name, x=(-self.bachio_move_out.attribute - self.bachio_move_out_2.attribute)*1.15, y=-self.bachio_move_up.attribute, frame=self.frame, fade=self.bachio_fade_in.attribute) + tex.draw_texture('ending_anim', 'bachio_r_' + self.name, x=(self.bachio_move_out.attribute + self.bachio_move_out_2.attribute)*1.15, y=-self.bachio_move_up.attribute, frame=self.frame, fade=self.bachio_fade_in.attribute) + class Gauge: def __init__(self, player_num: str, difficulty: int, level: int, total_notes: int): self.player_num = player_num