diff --git a/libs/animation.py b/libs/animation.py index 209269d..8644bf3 100644 --- a/libs/animation.py +++ b/libs/animation.py @@ -23,6 +23,10 @@ class BaseAnimation(): """Update the animation based on the current time.""" pass + def restart(self) -> None: + self.start_ms = get_current_ms() + self.is_finished = False + def _ease_in(self, progress: float, ease_type: str) -> float: if ease_type == "quadratic": return progress * progress @@ -57,9 +61,18 @@ class FadeAnimation(BaseAnimation): super().__init__(duration, delay) self.initial_opacity = initial_opacity self.final_opacity = final_opacity + self.initial_opacity_saved = initial_opacity + self.final_opacity_saved = final_opacity self.ease_in = ease_in self.ease_out = ease_out self.reverse_delay = reverse_delay + self.reverse_delay_saved = reverse_delay + + def restart(self): + super().restart() + self.reverse_delay = self.reverse_delay_saved + self.initial_opacity = self.initial_opacity_saved + self.final_opacity = self.final_opacity_saved def update(self, current_time_ms: float): elapsed_time = current_time_ms - self.start_ms @@ -90,11 +103,20 @@ class MoveAnimation(BaseAnimation): ease_in: Optional[str] = None, ease_out: Optional[str] = None): super().__init__(duration, delay) self.reverse_delay = reverse_delay + self.reverse_delay_saved = reverse_delay self.total_distance = total_distance self.start_position = start_position + self.total_distance_saved = total_distance + self.start_position_saved = start_position self.ease_in = ease_in self.ease_out = ease_out + def restart(self): + super().restart() + self.reverse_delay = self.reverse_delay_saved + self.total_distance = self.total_distance_saved + self.start_position = self.start_position_saved + def update(self, current_time_ms: float): elapsed_time = current_time_ms - self.start_ms if elapsed_time < self.delay: @@ -120,6 +142,7 @@ class TextureChangeAnimation(BaseAnimation): super().__init__(duration) self.textures = textures self.delay = delay + def update(self, current_time_ms: float): elapsed_time = current_time_ms - self.start_ms - self.delay if elapsed_time <= self.duration: @@ -151,6 +174,16 @@ class TextureResizeAnimation(BaseAnimation): self.initial_size = initial_size self.final_size = final_size self.reverse_delay = reverse_delay + self.initial_size_saved = initial_size + self.final_size_saved = final_size + self.reverse_delay_saved = reverse_delay + + def restart(self): + super().restart() + self.reverse_delay = self.reverse_delay_saved + self.initial_size = self.initial_size_saved + self.final_size = self.final_size_saved + def update(self, current_time_ms: float): elapsed_time = current_time_ms - self.start_ms diff --git a/libs/backgrounds.py b/libs/backgrounds.py index 45c55c1..61474e4 100644 --- a/libs/backgrounds.py +++ b/libs/backgrounds.py @@ -12,7 +12,7 @@ class Background: self.screen_width = screen_width self.screen_height = screen_height self.donbg = DonBG.create(self.screen_width, self.screen_height, random.randint(6, 6), 1) - self.bg_normal = BGNormal.create(self.screen_width, self.screen_height, 1) + self.bg_normal = BGNormal.create(self.screen_width, self.screen_height, random.randint(1, 5)) self.bg_fever = BGFever.create(self.screen_width, self.screen_height, 4) self.footer = Footer(self.screen_width, self.screen_height, random.randint(1, 3)) self.is_clear = False @@ -50,7 +50,7 @@ class DonBGBase: self.is_clear = is_clear self.move.update(current_time_ms) if self.move.is_finished: - self.move = Animation.create_move(3000, start_position=0, total_distance=-self.textures[self.name + f'_{self.player_num}p'][0].width) + self.move.restart() class DonBG1(DonBGBase): def __init__(self, index: int, screen_width: int, screen_height: int, player_num: int): @@ -60,7 +60,7 @@ class DonBG1(DonBGBase): super().update(current_time_ms, is_clear) self.overlay_move.update(current_time_ms) if self.overlay_move.is_finished: - self.overlay_move = Animation.create_move(1000, start_position=0, total_distance=20, reverse_delay=0) + self.overlay_move.restart() def draw(self): texture_index = 0 if self.is_clear: @@ -81,7 +81,7 @@ class DonBG6(DonBGBase): super().update(current_time_ms, is_clear) self.overlay_move.update(current_time_ms) if self.overlay_move.is_finished: - self.overlay_move = Animation.create_move(1000, start_position=0, total_distance=20, reverse_delay=0) + self.overlay_move.restart() def draw(self): texture_index = 0 if self.is_clear: @@ -103,7 +103,7 @@ class BGNormal: @staticmethod def create(screen_width: int, screen_height: int, index: int): - map = [None, BGNormal1] + map = [None, BGNormal1, BGNormal2, BGNormal3, BGNormal4, BGNormal5] selected_obj = map[index] return selected_obj(index, screen_width, screen_height) @@ -117,15 +117,156 @@ class BGNormalBase: class BGNormal1(BGNormalBase): def __init__(self, index: int, screen_width: int, screen_height: int): super().__init__(index, screen_width, screen_height) - self.flicker = Animation.create_fade(16.67*2, initial_opacity=0.5, final_opacity=0.25, reverse_delay=0) + self.flicker = Animation.create_fade(16.67*4, initial_opacity=0.5, final_opacity=0.4, reverse_delay=0) def update(self, current_time_ms: float): self.flicker.update(current_time_ms) if self.flicker.is_finished: - self.flicker = Animation.create_fade(16.67*2, initial_opacity=0.5, final_opacity=0.25, reverse_delay=0) + self.flicker.restart() def draw(self): ray.draw_texture(self.textures[self.name][0], 0, 360, ray.WHITE) ray.draw_texture(self.textures[self.name][1], 0, 360, ray.fade(ray.WHITE, self.flicker.attribute)) +class BGNormal2(BGNormalBase): + def __init__(self, index: int, screen_width: int, screen_height: int): + super().__init__(index, screen_width, screen_height) + self.flicker = Animation.create_fade(16.67*4, initial_opacity=0.5, final_opacity=0.4, reverse_delay=0) + def update(self, current_time_ms: float): + self.flicker.update(current_time_ms) + if self.flicker.is_finished: + self.flicker.restart() + def draw(self): + ray.draw_texture(self.textures[self.name][0], 0, 360, ray.WHITE) + ray.draw_texture(self.textures[self.name][1], 0, 360, ray.fade(ray.WHITE, self.flicker.attribute)) + +class BGNormal3(BGNormalBase): + def __init__(self, index: int, screen_width: int, screen_height: int): + super().__init__(index, screen_width, screen_height) + self.flicker = Animation.create_fade(16.67*10, initial_opacity=0.5, final_opacity=0.4, reverse_delay=0) + def update(self, current_time_ms): + self.flicker.update(current_time_ms) + if self.flicker.is_finished: + self.flicker.restart() + def draw(self): + src = ray.Rectangle(0, 0, self.textures[self.name][0].width, self.textures[self.name][0].height) + dest = ray.Rectangle(0, 360, self.screen_width, self.textures[self.name][0].height) + ray.draw_texture_pro(self.textures[self.name][0], src, dest, ray.Vector2(0, 0), 0, ray.WHITE) + ray.draw_texture(self.textures[self.name][3], (self.screen_width//2) - (self.textures[self.name][3].width//2), 360, ray.WHITE) + ray.draw_texture(self.textures[self.name][6], 0, 360, ray.WHITE) + + src = ray.Rectangle(0, 0, -self.textures[self.name][7].width, self.textures[self.name][7].height) + dest = ray.Rectangle((self.screen_width//2) - 170, 490, self.textures[self.name][7].width, self.textures[self.name][7].height) + ray.draw_texture_pro(self.textures[self.name][7], src, dest, ray.Vector2(0, 0), 0, ray.WHITE) + ray.draw_texture(self.textures[self.name][7], (self.screen_width//2) + 50, 490, ray.WHITE) + + + #Orange + color = ray.fade(ray.WHITE, self.flicker.attribute) + ray.draw_texture(self.textures[self.name][1], (self.screen_width//2) + 180, 300, color) + ray.draw_texture(self.textures[self.name][1], (self.screen_width//2) - 380, 300, color) + + #Red Green Orange + ray.draw_texture(self.textures[self.name][2], (self.screen_width//2) - 220, 350, color) + ray.draw_texture(self.textures[self.name][4], (self.screen_width//2) - 100, 350, color) + ray.draw_texture(self.textures[self.name][1], (self.screen_width//2) + 10, 350, color) + + #Yellow + ray.draw_texture(self.textures[self.name][5], (self.screen_width//2) - 220, 500, color) + ray.draw_texture(self.textures[self.name][5], (self.screen_width//2) + 10, 500, color) + + ray.draw_texture(self.textures[self.name][9], (self.screen_width//2) - 320, 520, ray.WHITE) + ray.draw_texture(self.textures[self.name][10], 100, 360, ray.WHITE) + ray.draw_texture(self.textures[self.name][11], self.screen_width - 100 - self.textures[self.name][11].width, 360, ray.WHITE) + +class BGNormal4(BGNormalBase): + class Petal: + def __init__(self): + self.spawn_point = self.random_excluding_range() + duration = random.randint(1400, 2000) + self.move_y = Animation.create_move(duration, total_distance=360) + self.move_x = Animation.create_move(duration, total_distance=random.randint(-300, 300)) + def random_excluding_range(self): + while True: + num = random.randint(0, 1280) + if num < 260 or num > 540: + return num + def update(self, current_time_ms): + self.move_x.update(current_time_ms) + self.move_y.update(current_time_ms) + def draw(self, texture): + ray.draw_texture(texture, self.spawn_point + int(self.move_x.attribute), 360+int(self.move_y.attribute), ray.fade(ray.WHITE, 0.75)) + def __init__(self, index: int, screen_width: int, screen_height: int): + super().__init__(index, screen_width, screen_height) + self.flicker = Animation.create_fade(16.67*3, initial_opacity=0.5, final_opacity=0.4, reverse_delay=0) + self.turtle_move = Animation.create_move(3333*2, start_position=screen_width+112, total_distance=-(screen_width+(112*4))) + textures = ((0, 100, 3), (100, 200, 4), (200, 300, 5), (300, 400, 6), (400, 500, 7), (500, 600, 8)) + self.turtle_change = Animation.create_texture_change(600, textures=textures) + self.petals = {self.Petal(), self.Petal(), self.Petal(), self.Petal(), self.Petal()} + def update(self, current_time_ms: float): + self.flicker.update(current_time_ms) + if self.flicker.is_finished: + self.flicker.restart() + self.turtle_move.update(current_time_ms) + if self.turtle_move.is_finished: + self.turtle_move.restart() + self.turtle_change.update(current_time_ms) + if self.turtle_change.is_finished: + self.turtle_change.restart() + for petal in self.petals: + petal.update(current_time_ms) + if petal.move_y.is_finished: + self.petals.remove(petal) + self.petals.add(self.Petal()) + def draw(self): + ray.draw_texture(self.textures[self.name][0], 0, 360, ray.WHITE) + ray.draw_texture(self.textures[self.name][2], self.screen_width//2 - 20, 380, ray.WHITE) + ray.draw_texture(self.textures[self.name][self.turtle_change.attribute], int(self.turtle_move.attribute), 550, ray.WHITE) + + ray.draw_texture(self.textures[self.name][9], 0, 360, ray.WHITE) + + for petal in self.petals: + petal.draw(self.textures[self.name][10]) + +class BGNormal5(BGNormalBase): + def __init__(self, index: int, screen_width: int, screen_height: int): + super().__init__(index, screen_width, screen_height) + self.flicker = Animation.create_fade(16.67*10, initial_opacity=0.75, final_opacity=0.4, reverse_delay=0) + def update(self, current_time_ms: float): + self.flicker.update(current_time_ms) + if self.flicker.is_finished: + self.flicker.restart() + def draw(self): + ray.draw_texture(self.textures[self.name][0], 0, 360, ray.WHITE) + + ray.draw_texture(self.textures[self.name][13], -35, 340, ray.WHITE) + ray.draw_texture(self.textures[self.name][12], 103, 380, ray.WHITE) + ray.draw_texture(self.textures[self.name][11], 241, 400, ray.WHITE) + ray.draw_texture(self.textures[self.name][10], 380, 380, ray.WHITE) + ray.draw_texture(self.textures[self.name][9], 518, 340, ray.WHITE) + ray.draw_texture(self.textures[self.name][4], 657, 340, ray.WHITE) + ray.draw_texture(self.textures[self.name][5], 795, 380, ray.WHITE) + ray.draw_texture(self.textures[self.name][6], 934, 400, ray.WHITE) + ray.draw_texture(self.textures[self.name][7], 1072, 380, ray.WHITE) + ray.draw_texture(self.textures[self.name][8], 1211, 340, ray.WHITE) + + color = ray.fade(ray.WHITE, self.flicker.attribute) + ray.draw_texture(self.textures[self.name][14], -35 - 10, 340 - 10, color) + ray.draw_texture(self.textures[self.name][14], 103 - 10, 380 - 10, color) + ray.draw_texture(self.textures[self.name][14], 241 - 10, 400 - 10, color) + ray.draw_texture(self.textures[self.name][14], 380 - 10, 380 - 10, color) + ray.draw_texture(self.textures[self.name][14], 518 - 10, 340 - 10, color) + ray.draw_texture(self.textures[self.name][14], 657 - 10, 340 - 10, color) + ray.draw_texture(self.textures[self.name][14], 795 - 10, 380 - 10, color) + ray.draw_texture(self.textures[self.name][14], 934 - 10, 400 - 10, color) + ray.draw_texture(self.textures[self.name][14], 1072 - 10, 380 - 10, color) + ray.draw_texture(self.textures[self.name][14], 1211 - 10, 340 - 10, color) + + ray.draw_texture(self.textures[self.name][3], (self.screen_width//2) - (self.textures[self.name][3].width//2), 360+172, ray.fade(ray.WHITE, 0.75)) + + ray.draw_texture(self.textures[self.name][1], 50, 600, ray.fade(ray.WHITE, 0.75)) + ray.draw_texture(self.textures[self.name][1], self.screen_width-50 - self.textures[self.name][2].width, 600, ray.fade(ray.WHITE, 0.75)) + ray.draw_texture(self.textures[self.name][2], self.screen_width-50 - self.textures[self.name][2].width, 600, ray.WHITE) + ray.draw_texture(self.textures[self.name][2], 50, 600, ray.WHITE) + class BGFever: @staticmethod @@ -149,10 +290,10 @@ class BGFever4(BGFeverBase): def update(self, current_time_ms: float): self.vertical_move.update(current_time_ms) if self.vertical_move.is_finished: - self.vertical_move = Animation.create_move(1300, start_position=0, total_distance=50, reverse_delay=0) + self.vertical_move.restart() self.horizontal_move.update(current_time_ms) if self.horizontal_move.is_finished: - self.horizontal_move = Animation.create_move(5000, start_position=0, total_distance=self.textures[self.name][2].width) + self.horizontal_move.restart() def draw(self): texture = self.textures[self.name][0] for i in range(0, self.screen_width + texture.width, texture.width): diff --git a/libs/utils.py b/libs/utils.py index 163abea..ceded3b 100644 --- a/libs/utils.py +++ b/libs/utils.py @@ -63,7 +63,6 @@ def load_all_textures_from_zip(zip_path: Path) -> dict[str, list[ray.Texture]]: result_dict[true_filename][index] = texture return result_dict - def rounded(num: float) -> int: sign = 1 if (num >= 0) else -1 num = abs(num) @@ -114,14 +113,16 @@ def save_config(config: dict[str, Any]) -> None: tomlkit.dump(config, f) def is_l_don_pressed() -> bool: - keys = global_data.config["keybinds"]["left_don"] + keys = global_data.config["keys"]["left_don"] + gamepad_buttons = global_data.config["gamepad"]["left_don"] for key in keys: if ray.is_key_pressed(ord(key)): return True if ray.is_gamepad_available(0): - if ray.is_gamepad_button_pressed(0, 16): - return True + for button in gamepad_buttons: + if ray.is_gamepad_button_pressed(0, button): + return True mid_x, mid_y = (1280//2, 720) allowed_gestures = {ray.Gesture.GESTURE_TAP, ray.Gesture.GESTURE_DOUBLETAP} @@ -134,14 +135,16 @@ def is_l_don_pressed() -> bool: return False def is_r_don_pressed() -> bool: - keys = global_data.config["keybinds"]["right_don"] + keys = global_data.config["keys"]["right_don"] + gamepad_buttons = global_data.config["gamepad"]["right_don"] for key in keys: if ray.is_key_pressed(ord(key)): return True if ray.is_gamepad_available(0): - if ray.is_gamepad_button_pressed(0, 17): - return True + for button in gamepad_buttons: + if ray.is_gamepad_button_pressed(0, button): + return True mid_x, mid_y = (1280//2, 720) allowed_gestures = {ray.Gesture.GESTURE_TAP, ray.Gesture.GESTURE_DOUBLETAP} @@ -154,14 +157,16 @@ def is_r_don_pressed() -> bool: return False def is_l_kat_pressed() -> bool: - keys = global_data.config["keybinds"]["left_kat"] + keys = global_data.config["keys"]["left_kat"] + gamepad_buttons = global_data.config["gamepad"]["left_kat"] for key in keys: if ray.is_key_pressed(ord(key)): return True if ray.is_gamepad_available(0): - if ray.is_gamepad_button_pressed(0, 10): - return True + for button in gamepad_buttons: + if ray.is_gamepad_button_pressed(0, button): + return True mid_x, mid_y = (1280//2, 720) allowed_gestures = {ray.Gesture.GESTURE_TAP, ray.Gesture.GESTURE_DOUBLETAP} @@ -174,14 +179,16 @@ def is_l_kat_pressed() -> bool: return False def is_r_kat_pressed() -> bool: - keys = global_data.config["keybinds"]["right_kat"] + keys = global_data.config["keys"]["right_kat"] + gamepad_buttons = global_data.config["gamepad"]["right_kat"] for key in keys: if ray.is_key_pressed(ord(key)): return True if ray.is_gamepad_available(0): - if ray.is_gamepad_button_pressed(0, 12): - return True + for button in gamepad_buttons: + if ray.is_gamepad_button_pressed(0, button): + return True mid_x, mid_y = (1280//2, 720) allowed_gestures = {ray.Gesture.GESTURE_TAP, ray.Gesture.GESTURE_DOUBLETAP} diff --git a/scenes/entry.py b/scenes/entry.py index 820c119..139118c 100644 --- a/scenes/entry.py +++ b/scenes/entry.py @@ -13,6 +13,7 @@ from libs.utils import ( is_r_don_pressed, is_r_kat_pressed, load_all_textures_from_zip, + load_texture_from_zip, ) @@ -32,6 +33,7 @@ class EntryScreen: def load_textures(self): self.textures = load_all_textures_from_zip(Path('Graphics/lumendata/entry.zip')) + self.texture_black = load_texture_from_zip(Path('Graphics/lumendata/attract/movie.zip'), 'movie_img00000.png') def unload_textures(self): for group in self.textures: @@ -42,6 +44,7 @@ class EntryScreen: sounds_dir = Path("Sounds") self.sound_don = audio.load_sound(sounds_dir / "inst_00_don.wav") self.sound_kat = audio.load_sound(sounds_dir / "inst_00_katsu.wav") + self.bgm = audio.load_sound(sounds_dir / "entry" / "JINGLE_ENTRY [1].ogg") def on_screen_start(self): if not self.screen_init: @@ -58,14 +61,21 @@ class EntryScreen: self.cloud_resize = None self.cloud_texture_change = None self.cloud_fade = None + self.fade_out = None self.cloud_resize_loop = Animation.create_texture_resize(200, initial_size=1.0, final_size=1.1, reverse_delay=200) + self.side_select_fade = Animation.create_fade(100, initial_opacity=0.0, final_opacity=1.0) + self.bg_flicker = Animation.create_fade(500, initial_opacity=0.5, final_opacity=0.4, reverse_delay=0) + audio.play_sound(self.bgm) def on_screen_end(self, next_screen: str): self.screen_init = False self.unload_textures() + audio.stop_sound(self.bgm) return next_screen def handle_input(self): + if self.fade_out is not None: + return if self.state == State.SELECT_SIDE: if is_l_don_pressed() or is_r_don_pressed(): if self.side == 1: @@ -89,10 +99,7 @@ class EntryScreen: elif self.state == State.SELECT_MODE: if is_l_don_pressed() or is_r_don_pressed(): audio.play_sound(self.sound_don) - if self.selected_box == 0: - return self.on_screen_end("SONG_SELECT") - elif self.selected_box == 1: - return self.on_screen_end("SETTINGS") + self.fade_out = Animation.create_fade(160) if is_l_kat_pressed(): audio.play_sound(self.sound_kat) self.selected_box = max(0, self.selected_box - 1) @@ -102,6 +109,10 @@ class EntryScreen: def update(self): self.on_screen_start() + self.side_select_fade.update(get_current_ms()) + self.bg_flicker.update(get_current_ms()) + if self.bg_flicker.is_finished: + self.bg_flicker.restart() if self.drum_move_1 is not None: self.drum_move_1.update(get_current_ms()) if self.drum_move_2 is not None: @@ -117,6 +128,13 @@ class EntryScreen: self.cloud_resize_loop.update(get_current_ms()) if self.cloud_resize_loop.is_finished: self.cloud_resize_loop = Animation.create_texture_resize(200, initial_size=1.0, final_size=1.1, reverse_delay=200) + if self.fade_out is not None: + self.fade_out.update(get_current_ms()) + if self.fade_out.is_finished: + if self.selected_box == 0: + return self.on_screen_end("SONG_SELECT") + elif self.selected_box == 1: + return self.on_screen_end("SETTINGS") return self.handle_input() def draw_background(self): @@ -130,7 +148,7 @@ class EntryScreen: ray.draw_texture(self.textures['entry'][371], (self.width // 2) - (self.textures['entry'][371].width // 2), (self.height // 2) - (self.textures['entry'][371].height // 2) + 10, ray.WHITE) ray.draw_texture(self.textures['entry'][372], 0, 0, ray.WHITE) ray.draw_texture(self.textures['entry'][373], self.width - self.textures['entry'][373].width, 0, ray.WHITE) - draw_scaled_texture(self.textures['entry'][374], -7, -15, 2.0, ray.fade(ray.WHITE, 0.50)) + draw_scaled_texture(self.textures['entry'][374], -7, -15, 2.0, ray.fade(ray.WHITE, self.bg_flicker.attribute)) def draw_footer(self): ray.draw_texture(self.textures['entry'][375], 1, self.height - self.textures['entry'][375].height + 7, ray.WHITE) @@ -139,37 +157,38 @@ class EntryScreen: if self.state == State.SELECT_SIDE or self.side != 2: ray.draw_texture(self.textures['entry'][377], 2 + self.textures['entry'][377].width, self.height - self.textures['entry'][376].height + 1, ray.WHITE) - def draw_side_select(self): + def draw_side_select(self, fade): + color = ray.fade(ray.WHITE, fade) left_x, top_y, right_x, bottom_y = 238, 108, 979, 520 - ray.draw_texture(self.textures['entry'][205], left_x, top_y, ray.WHITE) - ray.draw_texture(self.textures['entry'][208], right_x, top_y, ray.WHITE) - ray.draw_texture(self.textures['entry'][204], left_x, bottom_y, ray.WHITE) - ray.draw_texture(self.textures['entry'][207], right_x, bottom_y, ray.WHITE) + ray.draw_texture(self.textures['entry'][205], left_x, top_y, color) + ray.draw_texture(self.textures['entry'][208], right_x, top_y, color) + ray.draw_texture(self.textures['entry'][204], left_x, bottom_y, color) + ray.draw_texture(self.textures['entry'][207], right_x, bottom_y, color) texture = self.textures['entry'][209] src = ray.Rectangle(0, 0, texture.width, texture.height) dest = ray.Rectangle(left_x + self.textures['entry'][205].width, top_y, right_x - left_x - (self.textures['entry'][205].width), texture.height) - ray.draw_texture_pro(texture, src, dest, ray.Vector2(0, 0), 0, ray.WHITE) + ray.draw_texture_pro(texture, src, dest, ray.Vector2(0, 0), 0, color) texture = self.textures['entry'][210] src = ray.Rectangle(0, 0, texture.width, texture.height) dest = ray.Rectangle(left_x + self.textures['entry'][205].width, bottom_y, right_x - left_x - (self.textures['entry'][205].width), texture.height) - ray.draw_texture_pro(texture, src, dest, ray.Vector2(0, 0), 0, ray.WHITE) + ray.draw_texture_pro(texture, src, dest, ray.Vector2(0, 0), 0, color) texture = self.textures['entry'][203] src = ray.Rectangle(0, 0, texture.width, texture.height) dest = ray.Rectangle(left_x, top_y + self.textures['entry'][205].height, texture.width, bottom_y - top_y - (self.textures['entry'][205].height)) - ray.draw_texture_pro(texture, src, dest, ray.Vector2(0, 0), 0, ray.WHITE) + ray.draw_texture_pro(texture, src, dest, ray.Vector2(0, 0), 0, color) texture = self.textures['entry'][206] src = ray.Rectangle(0, 0, texture.width, texture.height) dest = ray.Rectangle(right_x, top_y + self.textures['entry'][205].height, texture.width, bottom_y - top_y - (self.textures['entry'][205].height)) - ray.draw_texture_pro(texture, src, dest, ray.Vector2(0, 0), 0, ray.WHITE) + ray.draw_texture_pro(texture, src, dest, ray.Vector2(0, 0), 0, color) texture = self.textures['entry'][202] src = ray.Rectangle(0, 0, texture.width, texture.height) dest = ray.Rectangle(left_x + self.textures['entry'][205].width, top_y + self.textures['entry'][205].height, right_x - left_x - (self.textures['entry'][205].width), bottom_y - top_y - (self.textures['entry'][205].height)) - ray.draw_texture_pro(texture, src, dest, ray.Vector2(0, 0), 0, ray.WHITE) + ray.draw_texture_pro(texture, src, dest, ray.Vector2(0, 0), 0, color) - ray.draw_texture(self.textures['entry'][226], 384, 144, ray.WHITE) + ray.draw_texture(self.textures['entry'][226], 384, 144, color) cursor_x = 261 cursor_texture = self.textures['entry'][230] @@ -179,7 +198,7 @@ class EntryScreen: flip = -1 else: texture = self.textures['entry'][232] - ray.draw_texture(texture, 261, 400, ray.WHITE) + ray.draw_texture(texture, 261, 400, color) if self.side == 1: texture = self.textures['entry'][76] @@ -187,19 +206,19 @@ class EntryScreen: cursor_x = 512 else: texture = self.textures['entry'][228] - ray.draw_texture(texture, 512, 400, ray.WHITE) - ray.draw_texture(self.textures['entry'][201], 512, 408, ray.WHITE) + ray.draw_texture(texture, 512, 400, color) + ray.draw_texture(self.textures['entry'][201], 512, 408, color) if self.side == 2: texture = self.textures['entry'][233] cursor_x = 762 else: texture = self.textures['entry'][227] - ray.draw_texture(texture, 762, 400, ray.WHITE) + ray.draw_texture(texture, 762, 400, color) src = ray.Rectangle(0, 0, cursor_texture.width * flip, cursor_texture.height) dest = ray.Rectangle(cursor_x, 400, cursor_texture.width, cursor_texture.height) - ray.draw_texture_pro(cursor_texture, src, dest, ray.Vector2(0, 0), 0, ray.WHITE) + ray.draw_texture_pro(cursor_texture, src, dest, ray.Vector2(0, 0), 0, color) def draw_player_drum(self): move_x = 0 @@ -230,8 +249,9 @@ class EntryScreen: color = ray.fade(ray.WHITE, self.cloud_fade.attribute) draw_scaled_texture(self.textures['entry'][texture_index], x + move_x - int(160 * (scale-1)), 720 + move_y - 200 - int(160 * (scale-1)), scale, color) - def draw_mode_select(self): + def draw_mode_select(self, fade): self.draw_player_drum() + color = ray.fade(ray.WHITE, fade) if self.cloud_fade is not None and self.cloud_fade.is_finished: box_width = self.textures['entry'][262].width spacing = 80 @@ -248,31 +268,39 @@ class EntryScreen: else: push_offset = push_distance final_x = x_pos + push_offset - ray.draw_texture(self.textures['entry'][262], final_x, y, ray.WHITE) + ray.draw_texture(self.textures['entry'][262], final_x, y, color) if i == self.selected_box: - ray.draw_texture(self.textures['entry'][302], final_x, y, ray.WHITE) + ray.draw_texture(self.textures['entry'][302], final_x, y, color) texture = self.textures['entry'][304] src = ray.Rectangle(0, 0, texture.width, texture.height) dest = ray.Rectangle(final_x + self.textures['entry'][302].width, y, 100 - self.textures['entry'][302].width, texture.height) - ray.draw_texture_pro(texture, src, dest, ray.Vector2(0, 0), 0, ray.WHITE) - ray.draw_texture(self.textures['entry'][303], final_x+100, y, ray.WHITE) + ray.draw_texture_pro(texture, src, dest, ray.Vector2(0, 0), 0, color) + ray.draw_texture(self.textures['entry'][303], final_x+100, y, color) box_title = self.box_titles[i][1] src = ray.Rectangle(0, 0, box_title.texture.width, box_title.texture.height) dest = ray.Rectangle(final_x + 12, y + 20, box_title.texture.width, box_title.texture.height) - box_title.draw(src, dest, ray.Vector2(0, 0), 0, ray.WHITE) + box_title.draw(src, dest, ray.Vector2(0, 0), 0, color) else: box_title = self.box_titles[i][0] src = ray.Rectangle(0, 0, box_title.texture.width, box_title.texture.height) dest = ray.Rectangle(final_x + 9, y + 20, box_title.texture.width, box_title.texture.height) - box_title.draw(src, dest, ray.Vector2(0, 0), 0, ray.WHITE) + box_title.draw(src, dest, ray.Vector2(0, 0), 0, color) def draw(self): self.draw_background() if self.state == State.SELECT_SIDE: - self.draw_side_select() + self.draw_side_select(self.side_select_fade.attribute) elif self.state == State.SELECT_MODE: - self.draw_mode_select() + if self.fade_out is not None: + self.draw_mode_select(self.fade_out.attribute) + else: + self.draw_mode_select(1.0) self.draw_footer() ray.draw_texture(self.textures['entry'][320], 0, 0, ray.WHITE) + + if self.fade_out is not None and self.fade_out.is_finished: + src = ray.Rectangle(0, 0, self.texture_black.width, self.texture_black.height) + dest = ray.Rectangle(0, 0, self.width, self.height) + ray.draw_texture_pro(self.texture_black, src, dest, ray.Vector2(0, 0), 0, ray.WHITE) diff --git a/scenes/game.py b/scenes/game.py index c137698..ac2af1e 100644 --- a/scenes/game.py +++ b/scenes/game.py @@ -28,22 +28,18 @@ from libs.video import VideoPlayer class GameScreen: JUDGE_X = 414 - SCREEN_WIDTH = 1280 - SCREEN_HEIGHT = 720 def __init__(self, width: int, height: int): self.width = width self.height = height self.current_ms = 0 self.result_transition = None + self.transition = None self.song_info = None self.screen_init = False self.movie = None self.end_ms = 0 self.start_delay = 1000 self.song_started = False - self.prev_touch_count = 0 - - self.background = Background(width, height) def load_textures(self): self.textures = load_all_textures_from_zip(Path('Graphics/lumendata/enso_system/common.zip')) @@ -124,13 +120,15 @@ class GameScreen: def on_screen_start(self): if not self.screen_init: self.screen_init = True + self.background = Background(self.width, self.height) self.load_textures() self.load_sounds() self.init_tja(global_data.selected_song, session_data.selected_difficulty) self.song_info = SongInfo(session_data.song_title, 'TEST') self.result_transition = None + self.transition = Transition(self.height) - def on_screen_end(self): + def on_screen_end(self, next_screen): self.screen_init = False for zip in self.textures: for texture in self.textures[zip]: @@ -140,7 +138,7 @@ class GameScreen: self.song_started = False self.end_ms = 0 self.movie = None - return 'RESULT' + return next_screen def write_score(self): if global_data.config['general']['autoplay']: @@ -167,6 +165,8 @@ class GameScreen: def update(self): self.on_screen_start() + if self.transition is not None: + self.transition.update(get_current_ms()) self.current_ms = get_current_ms() - self.start_ms if (self.current_ms >= self.tja.metadata.offset*1000 + self.start_delay - global_data.config["general"]["judge_offset"]) and not self.song_started: if self.song_music is not None: @@ -188,7 +188,7 @@ class GameScreen: if self.result_transition is not None: self.result_transition.update(get_current_ms()) if self.result_transition.is_finished: - return self.on_screen_end() + return self.on_screen_end('RESULT') elif len(self.player_1.play_notes) == 0: 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 @@ -206,6 +206,10 @@ class GameScreen: audio.play_sound(self.sound_restart) self.song_started = False + if ray.is_key_pressed(ray.KeyboardKey.KEY_ESCAPE): + audio.stop_sound(self.song_music) + return self.on_screen_end('SONG_SELECT') + def draw(self): if self.movie is not None: self.movie.draw() @@ -214,6 +218,8 @@ class GameScreen: self.player_1.draw(self) if self.song_info is not None: self.song_info.draw(self) + if self.transition is not None: + self.transition.draw(self.height) if self.result_transition is not None: self.result_transition.draw(self.width, self.height, global_data.textures['shutter'][0], global_data.textures['shutter'][1]) @@ -1215,6 +1221,32 @@ class SongInfo: dest = ray.Rectangle(text_x, text_y, self.song_title.texture.width, self.song_title.texture.height) self.song_title.draw(src, dest, ray.Vector2(0, 0), 0, self.song_name_fade) +class Transition: + def __init__(self, screen_height: int) -> None: + self.is_finished = False + self.rainbow_up = Animation.create_move(266, start_position=0, total_distance=screen_height + global_data.textures['scene_change_rainbow'][2].height, ease_in='cubic') + self.chara_down = None + def update(self, current_time_ms: float): + self.rainbow_up.update(current_time_ms) + if self.rainbow_up.is_finished and self.chara_down is None: + self.chara_down = Animation.create_move(33, start_position=0, total_distance=30) + + if self.chara_down is not None: + self.chara_down.update(current_time_ms) + self.is_finished = self.chara_down.is_finished + + def draw(self, screen_height: int): + ray.draw_texture(global_data.textures['scene_change_rainbow'][1], 0, screen_height - int(self.rainbow_up.attribute), ray.WHITE) + texture = global_data.textures['scene_change_rainbow'][0] + src = ray.Rectangle(0, 0, texture.width, texture.height) + dest = ray.Rectangle(0, -int(self.rainbow_up.attribute), texture.width, screen_height) + ray.draw_texture_pro(texture, src, dest, ray.Vector2(0, 0), 0, ray.WHITE) + texture = global_data.textures['scene_change_rainbow'][3] + offset = 0 + if self.chara_down is not None: + offset = int(self.chara_down.attribute) + ray.draw_texture(texture, 76, -int(self.rainbow_up.attribute*3) - offset, ray.WHITE) + class ResultTransition: def __init__(self, screen_height: int): self.move = Animation.create_move(983.33, start_position=0, total_distance=screen_height//2, ease_out='quadratic') diff --git a/scenes/settings.py b/scenes/settings.py index d2e9898..d5a685e 100644 --- a/scenes/settings.py +++ b/scenes/settings.py @@ -23,6 +23,7 @@ class SettingsScreen: self.setting_index = 0 self.in_setting_edit = False self.editing_key = False + self.editing_gamepad = False self.temp_key_input = "" def on_screen_start(self): @@ -122,6 +123,23 @@ class SettingsScreen: elif key_pressed == ray.KeyboardKey.KEY_ESCAPE: self.editing_key = False + def handle_gamepad_binding(self, section, key): + self.editing_gamepad = True + self.temp_key_input = "" + + def update_gamepad_binding(self): + """Update gamepad binding based on input""" + button_pressed = ray.get_gamepad_button_pressed() + if button_pressed != 0: + current_header = self.headers[self.header_index] + settings = self.get_current_settings() + if settings: + setting_key, _ = settings[self.setting_index] + self.config[current_header][setting_key] = [button_pressed] + self.editing_gamepad = False + if ray.is_key_pressed(ray.KeyboardKey.KEY_ESCAPE): + self.editing_gamepad = False + def update(self): self.on_screen_start() @@ -130,6 +148,10 @@ class SettingsScreen: self.update_key_binding() return + if self.editing_gamepad: + self.update_gamepad_binding() + return + current_header = self.headers[self.header_index] # Exit handling @@ -166,14 +188,18 @@ class SettingsScreen: elif isinstance(setting_value, (int, float)): self.handle_numeric_change(current_header, setting_key, 1) elif isinstance(setting_value, str): - if 'keybinds' in current_header: + if 'keys' in current_header: self.handle_key_binding(current_header, setting_key) + elif 'gamepad' in current_header: + self.handle_gamepad_binding(current_header, setting_key) else: self.handle_string_cycle(current_header, setting_key) elif isinstance(setting_value, list) and len(setting_value) > 0: if isinstance(setting_value[0], str) and len(setting_value[0]) == 1: # Key binding self.handle_key_binding(current_header, setting_key) + elif isinstance(setting_value[0], int): + self.handle_gamepad_binding(current_header, setting_key) elif is_l_don_pressed(): # Modify setting value (reverse direction for numeric) setting_key, setting_value = settings[self.setting_index] @@ -183,7 +209,7 @@ class SettingsScreen: elif isinstance(setting_value, (int, float)): self.handle_numeric_change(current_header, setting_key, -1) elif isinstance(setting_value, str): - if 'keybinds' not in current_header: + if ('keys' not in current_header) and ('gamepad' not in current_header): self.handle_string_cycle(current_header, setting_key) elif ray.is_key_pressed(ray.KeyboardKey.KEY_ESCAPE): diff --git a/scenes/song_select.py b/scenes/song_select.py index 15bbede..5045615 100644 --- a/scenes/song_select.py +++ b/scenes/song_select.py @@ -47,7 +47,7 @@ class SongSelectScreen: self.sound_skip = audio.load_sound(sounds_dir / 'song_select' / 'Skip.ogg') self.sound_ura_switch = audio.load_sound(sounds_dir / 'song_select' / 'SE_SELECT [4].ogg') audio.set_sound_volume(self.sound_ura_switch, 0.25) - #self.sound_select = audio.load_sound(sounds_dir / "song_select.wav") + self.sound_bgm = audio.load_sound(sounds_dir / "song_select" / "JINGLE_GENRE [1].ogg") #self.sound_cancel = audio.load_sound(sounds_dir / "cancel.wav") def on_screen_start(self): @@ -78,22 +78,25 @@ class SongSelectScreen: if str(global_data.selected_song) in self.navigator.all_song_files: self.navigator.mark_crowns_dirty_for_song(self.navigator.all_song_files[str(global_data.selected_song)]) - def on_screen_end(self): + def on_screen_end(self, next_screen): self.screen_init = False global_data.selected_song = self.navigator.get_current_item().path session_data.selected_difficulty = self.selected_difficulty + audio.unload_sound(self.sound_bgm) self.reset_demo_music() for zip in self.textures: for texture in self.textures[zip]: ray.unload_texture(texture) - return "GAME" + return next_screen def reset_demo_music(self): if self.demo_song is not None: audio.stop_music_stream(self.demo_song) audio.unload_music_stream(self.demo_song) + audio.play_sound(self.sound_bgm) self.demo_song = None self.navigator.get_current_item().box.wait = get_current_ms() + def handle_input(self): if self.state == State.BROWSING: if ray.is_key_pressed(ray.KeyboardKey.KEY_LEFT_CONTROL) or (is_l_kat_pressed() and get_current_ms() <= self.last_moved + 100): @@ -205,7 +208,7 @@ class SongSelectScreen: if self.game_transition is not None: self.game_transition.update(get_current_ms()) if self.game_transition.is_finished: - return self.on_screen_end() + return self.on_screen_end("GAME") else: self.handle_input() @@ -224,6 +227,7 @@ class SongSelectScreen: audio.normalize_music_stream(self.demo_song, 0.1935) audio.seek_music_stream(self.demo_song, song.tja.metadata.demostart) audio.play_music_stream(self.demo_song) + audio.stop_sound(self.sound_bgm) if song.box.is_open: current_box = song.box if current_box.texture_index != 552 and get_current_ms() >= song.box.wait + (83.33*3): @@ -255,6 +259,9 @@ class SongSelectScreen: if self.ura_switch_animation is not None: self.ura_switch_animation.update(get_current_ms()) + if ray.is_key_pressed(ray.KeyboardKey.KEY_ESCAPE): + return self.on_screen_end('ENTRY') + def draw_selector(self): if self.selected_difficulty == -1: ray.draw_texture(self.textures['song_select'][133], 314, 110, ray.WHITE) diff --git a/scenes/title.py b/scenes/title.py index b9519e6..8a96b2b 100644 --- a/scenes/title.py +++ b/scenes/title.py @@ -31,6 +31,7 @@ class TitleScreen: self.attract_video_list = [file for file in video_dir.glob("**/*.mp4")] self.load_sounds() self.screen_init = False + self.fade_out = None def get_videos(self): return self.op_video, self.attract_video @@ -38,7 +39,7 @@ class TitleScreen: def load_sounds(self): sounds_dir = Path("Sounds") title_dir = sounds_dir / "title" - + self.sound_don = audio.load_sound(sounds_dir / "inst_00_don.wav") self.sound_bachi_swipe = audio.load_sound(title_dir / "SE_ATTRACT_2.ogg") self.sound_bachi_hit = audio.load_sound(title_dir / "SE_ATTRACT_3.ogg") self.sound_warning_message = audio.load_sound(title_dir / "VO_ATTRACT_3.ogg") @@ -99,9 +100,15 @@ class TitleScreen: def update(self): self.on_screen_start() + if self.fade_out is not None: + self.fade_out.update(get_current_ms()) + if self.fade_out.is_finished: + return self.on_screen_end() + self.scene_manager() if is_l_don_pressed() or is_r_don_pressed(): - return self.on_screen_end() + self.fade_out = Animation.create_fade(1000, initial_opacity=0.0, final_opacity=1.0) + audio.play_sound(self.sound_don) def draw(self): if self.state == State.OP_VIDEO: @@ -114,6 +121,11 @@ class TitleScreen: elif self.state == State.ATTRACT_VIDEO: self.attract_video.draw() + if self.fade_out is not None: + src = ray.Rectangle(0, 0, self.texture_black.width, self.texture_black.height) + dest = ray.Rectangle(0, 0, self.width, self.height) + ray.draw_texture_pro(self.texture_black, src, dest, ray.Vector2(0, 0), 0, ray.fade(ray.WHITE, self.fade_out.attribute)) + class WarningScreen: class X: DELAY = 4250