Contributions are now open

This commit is contained in:
Yonokid
2025-05-08 15:49:09 -04:00
parent bc08f24c9d
commit 209a98cd73
7 changed files with 227 additions and 261 deletions

View File

@@ -10,12 +10,13 @@ Download for OS of choice on releases page
How to run: How to run:
Windows: Windows:
```bash ```
PyTaiko.exe Run PyTaiko.exe
``` ```
MacOS/Linux: MacOS/Linux:
```bash ```
PyTaiko.bin Good luck, would suggest running with python directly
```
## Roadmap ## Roadmap
@@ -68,4 +69,4 @@ I like it
## Contributing ## Contributing
I would highly suggest not contributing to this until sizeable progress has been made. The fabric of this code is ever changing and anything you write could disappear at any time Contributions are now open. I don't have any particular contribution guidelines other than be mindful of what builtin functions already exist in this project (ie, for animations, videos, etc)

View File

@@ -3,113 +3,6 @@ from typing import Optional
from libs.utils import get_current_ms from libs.utils import get_current_ms
class Animation:
def __init__(self, current_ms: float, duration: float, type: str):
self.type = type
self.start_ms = current_ms
self.attribute = 0
self.duration = duration
self.params = dict()
self.is_finished = False
def update(self, current_ms: float):
if self.type == 'move':
self.move(current_ms,
self.duration,
self.params['total_distance'],
self.params['start_position'],
delay=self.params.get('delay', 0.0),
ease_in=self.params.get('ease_in', None),
ease_out=self.params.get('ease_out', None))
elif self.type == 'texture_change':
self.texture_change(current_ms,
self.duration,
self.params['textures'])
elif self.type == 'text_stretch':
self.text_stretch(current_ms,
self.duration)
elif self.type == 'texture_resize':
self.texture_resize(current_ms,
self.duration,
initial_size=self.params.get('initial_size', 1.0),
final_size=self.params.get('final_size', 1.0),
delay=self.params.get('delay', 0.0))
if self.params.get('reverse', None) is not None and current_ms - self.start_ms >= self.duration + self.params.get('delay', 0.0):
self.texture_resize(current_ms,
self.duration,
final_size=self.params.get('initial_size', 1.0),
initial_size=self.params.get('final_size', 1.0),
delay=self.params.get('delay', 0.0) + self.duration)
def _ease_out_progress(self, progress: float, ease: str | None) -> float:
if ease == 'quadratic':
return progress * (2 - progress)
elif ease == 'cubic':
return 1 - pow(1 - progress, 3)
elif ease == 'exponential':
return 1 - pow(2, -10 * progress)
else:
return progress
def _ease_in_progress(self, progress: float, ease: str | None) -> float:
if ease == 'quadratic':
return progress * progress
elif ease == 'cubic':
return progress * progress * progress
elif ease == 'exponential':
return pow(2, 10 * (progress - 1))
else:
return progress
def move(self, current_ms: float, duration: float, total_distance: float, start_position: float, delay: float, ease_in: str | None, ease_out: str | None) -> None:
elapsed_time = current_ms - self.start_ms
if elapsed_time < delay:
self.attribute = start_position
elapsed_time -= delay
if elapsed_time <= duration:
if ease_in is not None:
progress = self._ease_in_progress(elapsed_time / duration, ease_in)
elif ease_out is not None:
progress = self._ease_out_progress(elapsed_time / duration, ease_out)
else:
progress = elapsed_time / duration
self.attribute = start_position + (total_distance * progress)
else:
self.attribute = start_position + total_distance
self.is_finished = True
def texture_change(self, current_ms: float, duration: float, textures: list[tuple[float, float, int]]) -> None:
elapsed_time = current_ms - self.start_ms
if elapsed_time <= duration:
for start, end, index in textures:
if start < elapsed_time <= end:
self.attribute = index
else:
self.is_finished = True
def text_stretch(self, current_ms: float, duration: float):
elapsed_time = current_ms - self.start_ms
if elapsed_time <= duration:
self.attribute = 2 + 5 * (elapsed_time // 25)
elif elapsed_time <= duration + 116:
frame_time = (elapsed_time - duration) // 16.57
self.attribute = 2 + 10 - (2 * (frame_time + 1))
else:
self.attribute = 0
self.is_finished = True
def texture_resize(self, current_ms: float, duration: float, initial_size: float, final_size: float, delay: float):
elapsed_time = current_ms - self.start_ms
if elapsed_time < delay:
self.attribute = initial_size
elapsed_time -= delay
if elapsed_time >= duration:
self.attribute = final_size
self.is_finished = True
elif elapsed_time < duration:
progress = elapsed_time / duration
self.attribute = initial_size + ((final_size - initial_size) * progress)
else:
self.attribute = final_size
self.is_finished = True
class BaseAnimation(): class BaseAnimation():
def __init__(self, duration: float, delay: float = 0.0): def __init__(self, duration: float, delay: float = 0.0):
""" """
@@ -213,33 +106,134 @@ class MoveAnimation(BaseAnimation):
progress = self._apply_easing(progress, self.ease_in, self.ease_out) progress = self._apply_easing(progress, self.ease_in, self.ease_out)
self.attribute = self.start_position + (self.total_distance * progress) self.attribute = self.start_position + (self.total_distance * progress)
class TextureChangeAnimation(BaseAnimation):
def __init__(self, duration: float, textures: list[tuple[float, float, int]]):
super().__init__(duration)
self.textures = textures
def update(self, current_time_ms: float):
elapsed_time = current_time_ms - self.start_ms
if elapsed_time <= self.duration:
for start, end, index in self.textures:
if start < elapsed_time <= end:
self.attribute = index
else:
self.is_finished = True
class Animation2: class TextStretchAnimation(BaseAnimation):
def __init__(self, duration: float):
super().__init__(duration)
def update(self, current_time_ms: float):
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
self.attribute = 2 + 10 - (2 * (frame_time + 1))
else:
self.attribute = 0
self.is_finished = True
class TextureResizeAnimation(BaseAnimation):
def __init__(self, duration: float, initial_size: float = 1.0,
final_size: float = 0.0, delay: float = 0.0,
reverse_delay: Optional[float] = None):
super().__init__(duration, delay)
self.initial_size = initial_size
self.final_size = final_size
self.reverse_delay = reverse_delay
def update(self, current_time_ms: float):
elapsed_time = current_time_ms - self.start_ms
if elapsed_time <= self.delay:
self.attribute = self.initial_size
elif elapsed_time >= self.delay + self.duration:
self.attribute = self.final_size
if self.reverse_delay is not None:
self.start_ms = current_time_ms
self.delay = self.reverse_delay
self.initial_size, self.final_size = self.final_size, self.initial_size
self.reverse_delay = None
else:
self.is_finished = True
else:
animation_time = elapsed_time - self.delay
progress = animation_time / self.duration
self.attribute = self.initial_size + ((self.final_size - self.initial_size) * progress)
class Animation:
"""Factory for creating different types of animations.""" """Factory for creating different types of animations."""
@staticmethod @staticmethod
def create_fade(duration: float, **kwargs) -> FadeAnimation: def create_fade(duration: float, **kwargs) -> FadeAnimation:
"""Create a fade animation.""" """Create a fade animation.
Args:
duration: Length of the fade in milliseconds
delay: Time to wait before starting the fade
initial_opacity: Default is 1.0
final_opacity: Default is 0.0
reverse_delay: If provided, fade will play in reverse after this delay
ease_in: Control ease into the fade
ease_out: Control ease out of the fade
Easing options:
quadratic,
cubic,
exponential
"""
return FadeAnimation(duration, **kwargs) return FadeAnimation(duration, **kwargs)
@staticmethod @staticmethod
def create_move(duration: float, **kwargs) -> MoveAnimation: def create_move(duration: float, **kwargs) -> MoveAnimation:
"""Create a movement animation.""" """Create a movement animation.
Args:
duration: Length of the move in milliseconds
start_position: The coordinates of the object before the move
total_distance: The distance travelled from the start to end position
delay: Time to wait before starting the move
ease_in: Control ease into the move
ease_out: Control ease out of the move
Easing options:
quadratic,
cubic,
exponential
"""
return MoveAnimation(duration, **kwargs) return MoveAnimation(duration, **kwargs)
'''
@staticmethod @staticmethod
def create_texture_change(duration: float, **kwargs) -> TextureChangeAnimation: def create_texture_change(duration: float, **kwargs) -> TextureChangeAnimation:
"""Create a texture change animation.""" """Create a texture change animation
Args:
duration: Length of the change in milliseconds
textures: Passed in as a tuple of the starting millisecond, ending millisecond, and texture index
"""
return TextureChangeAnimation(duration, **kwargs) return TextureChangeAnimation(duration, **kwargs)
@staticmethod @staticmethod
def create_text_stretch(duration: float) -> TextStretchAnimation: def create_text_stretch(duration: float) -> TextStretchAnimation:
"""Create a text stretch animation.""" """Create a text stretch animation.
Args:
duration: Length of the stretch in milliseconds
"""
return TextStretchAnimation(duration) return TextStretchAnimation(duration)
@staticmethod @staticmethod
def create_texture_resize(duration: float, **kwargs) -> TextureResizeAnimation: def create_texture_resize(duration: float, **kwargs) -> TextureResizeAnimation:
"""Create a texture resize animation.""" """Create a texture resize animation.
Args:
duration: Length of the change in milliseconds
initial_size: Default is 1.0
final_size: Default is 0.0
delay: Time to wait before starting the resize
reverse_delay: If provided, resize will play in reverse after this delay
"""
return TextureResizeAnimation(duration, **kwargs) return TextureResizeAnimation(duration, **kwargs)
'''

View File

@@ -95,11 +95,9 @@ def get_config() -> dict[str, Any]:
config_file = tomllib.load(f) config_file = tomllib.load(f)
return config_file return config_file
def draw_scaled_texture(texture, x: int, y: int, scale: float, color: ray.Color) -> None: def draw_scaled_texture(texture: ray.Texture, x: int, y: int, scale: float, color: ray.Color) -> None:
width = texture.width src_rect = ray.Rectangle(0, 0, texture.width, texture.height)
height = texture.height dst_rect = ray.Rectangle(x, y, texture.width*scale, texture.height*scale)
src_rect = ray.Rectangle(0, 0, width, height)
dst_rect = ray.Rectangle(x, y, width*scale, height*scale)
ray.draw_texture_pro(texture, src_rect, dst_rect, ray.Vector2(0, 0), 0, color) ray.draw_texture_pro(texture, src_rect, dst_rect, ray.Vector2(0, 0), 0, color)
@dataclass @dataclass

View File

@@ -6,7 +6,10 @@ from libs.utils import get_current_ms
class VideoPlayer: class VideoPlayer:
def __init__(self, path): def __init__(self, path: str):
"""Initialize a video player instance. Audio must have the same name and an ogg extension.
Todo: extract audio from video directly
"""
self.is_finished_list = [False, False] self.is_finished_list = [False, False]
self.video_path = path self.video_path = path
self.video = moviepy.VideoFileClip(path) self.video = moviepy.VideoFileClip(path)
@@ -32,7 +35,7 @@ class VideoPlayer:
if time_played > ending_lenience: if time_played > ending_lenience:
self.is_finished_list[1] = True self.is_finished_list[1] = True
def _load_frame(self, index): def _load_frame(self, index: int):
"""Load a specific frame into the buffer""" """Load a specific frame into the buffer"""
if index >= len(self.frame_timestamps) or index < 0: if index >= len(self.frame_timestamps) or index < 0:
return None return None
@@ -75,20 +78,26 @@ class VideoPlayer:
texture = self.frame_buffer.pop(ts) texture = self.frame_buffer.pop(ts)
ray.unload_texture(texture) ray.unload_texture(texture)
def is_started(self): def is_started(self) -> bool:
"""Returns boolean value if the video has begun"""
return self.start_ms is not None return self.start_ms is not None
def start(self, current_ms): def start(self, current_ms: float) -> None:
"""Start video playback at call time"""
self.start_ms = current_ms self.start_ms = current_ms
for i in range(min(self.buffer_size, len(self.frame_timestamps))): for i in range(min(self.buffer_size, len(self.frame_timestamps))):
self._load_frame(i) self._load_frame(i)
def is_finished(self): def is_finished(self) -> bool:
"""Check if video is finished playing"""
return all(self.is_finished_list) return all(self.is_finished_list)
def set_volume(self, volume): def set_volume(self, volume: float) -> None:
"""Set video volume, takes float value from 0.0 to 1.0"""
audio.set_music_volume(self.audio, volume) audio.set_music_volume(self.audio, volume)
def update(self): def update(self):
"""Updates video playback, advancing frames and audio"""
self._audio_manager() self._audio_manager()
if self.frame_index >= len(self.frame_timestamps) - 1: if self.frame_index >= len(self.frame_timestamps) - 1:
@@ -113,10 +122,12 @@ class VideoPlayer:
self._load_frame(current_index + i) self._load_frame(current_index + i)
def draw(self): def draw(self):
"""Draw video frames to the raylib canvas"""
if self.current_frame is not None: if self.current_frame is not None:
ray.draw_texture(self.current_frame, 0, 0, ray.WHITE) ray.draw_texture(self.current_frame, 0, 0, ray.WHITE)
def stop(self): def stop(self):
"""Stops the video, audio, and clears its buffer"""
for timestamp, texture in self.frame_buffer.items(): for timestamp, texture in self.frame_buffer.items():
ray.unload_texture(texture) ray.unload_texture(texture)
self.frame_buffer.clear() self.frame_buffer.clear()

View File

@@ -5,7 +5,7 @@ from typing import Optional
import pyray as ray import pyray as ray
from libs.animation import Animation, Animation2 from libs.animation import Animation
from libs.audio import audio from libs.audio import audio
from libs.tja import Balloon, Drumroll, Note, TJAParser, calculate_base_score from libs.tja import Balloon, Drumroll, Note, TJAParser, calculate_base_score
from libs.utils import ( from libs.utils import (
@@ -121,7 +121,7 @@ class GameScreen:
self.screen_init = True self.screen_init = True
self.init_tja(session_data.selected_song, session_data.selected_difficulty) self.init_tja(session_data.selected_song, session_data.selected_difficulty)
self.current_ms = get_current_ms() - self.start_ms self.current_ms = get_current_ms() - self.start_ms
self.song_info = SongInfo(self.current_ms, self.tja.title, 'TEST') self.song_info = SongInfo(self.tja.title, 'TEST')
self.result_transition = None self.result_transition = None
if self.movie is not None: if self.movie is not None:
self.movie.start(get_current_ms()) self.movie.start(get_current_ms())
@@ -150,7 +150,7 @@ class GameScreen:
elif len(self.player_1.play_notes) == 0: 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_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 session_data.result_gauge_length = self.player_1.gauge.gauge_length
self.result_transition = ResultTransition(get_current_ms(), self.height) self.result_transition = ResultTransition(self.height)
audio.play_sound(self.sound_result_transition) audio.play_sound(self.sound_result_transition)
def draw(self): def draw(self):
@@ -207,11 +207,11 @@ class Player:
self.balloon_anim: Optional[BalloonAnimation] = None self.balloon_anim: Optional[BalloonAnimation] = None
self.base_score_list: list[ScoreCounterAnimation] = [] self.base_score_list: list[ScoreCounterAnimation] = []
self.combo_display = Combo(self.combo, get_current_ms()) self.combo_display = Combo(self.combo, get_current_ms())
self.score_counter = ScoreCounter(self.score, get_current_ms()) self.score_counter = ScoreCounter(self.score)
self.input_log: dict[float, tuple] = dict() self.input_log: dict[float, tuple] = dict()
self.gauge = Gauge(get_current_ms(), self.difficulty, metadata[-1][self.difficulty][0]) self.gauge = Gauge(self.difficulty, metadata[-1][self.difficulty][0])
def get_result_score(self): def get_result_score(self):
return self.score, self.good_count, self.ok_count, self.bad_count, self.total_drumroll, self.max_combo return self.score, self.good_count, self.ok_count, self.bad_count, self.total_drumroll, self.max_combo
@@ -326,7 +326,7 @@ class Player:
self.curr_drumroll_count += 1 self.curr_drumroll_count += 1
self.total_drumroll += 1 self.total_drumroll += 1
self.score += 100 self.score += 100
self.base_score_list.append(ScoreCounterAnimation(get_current_ms(), 100)) self.base_score_list.append(ScoreCounterAnimation(100))
if not isinstance(self.current_notes_draw[0], Drumroll): if not isinstance(self.current_notes_draw[0], Drumroll):
return return
self.current_notes_draw[0].color = max(0, 255 - (self.curr_drumroll_count * 10)) self.current_notes_draw[0].color = max(0, 255 - (self.curr_drumroll_count * 10))
@@ -339,7 +339,7 @@ class Player:
self.curr_balloon_count += 1 self.curr_balloon_count += 1
self.total_drumroll += 1 self.total_drumroll += 1
self.score += 100 self.score += 100
self.base_score_list.append(ScoreCounterAnimation(get_current_ms(), 100)) self.base_score_list.append(ScoreCounterAnimation(100))
if self.curr_balloon_count == note.count: if self.curr_balloon_count == note.count:
self.is_balloon = False self.is_balloon = False
note.popped = True note.popped = True
@@ -377,22 +377,22 @@ class Player:
return return
big = curr_note.type == 3 or curr_note.type == 4 big = curr_note.type == 3 or curr_note.type == 4
if (curr_note.hit_ms - Player.TIMING_GOOD) + self.judge_offset <= game_screen.current_ms <= (curr_note.hit_ms + Player.TIMING_GOOD) + self.judge_offset: if (curr_note.hit_ms - Player.TIMING_GOOD) + self.judge_offset <= game_screen.current_ms <= (curr_note.hit_ms + Player.TIMING_GOOD) + self.judge_offset:
self.draw_judge_list.append(Judgement(get_current_ms(), 'GOOD', big)) self.draw_judge_list.append(Judgement('GOOD', big))
self.lane_hit_effect = LaneHitEffect(get_current_ms(), 'GOOD') self.lane_hit_effect = LaneHitEffect('GOOD')
self.good_count += 1 self.good_count += 1
self.score += self.base_score self.score += self.base_score
self.base_score_list.append(ScoreCounterAnimation(get_current_ms(), self.base_score)) self.base_score_list.append(ScoreCounterAnimation(self.base_score))
self.note_correct(game_screen, curr_note) self.note_correct(game_screen, curr_note)
elif (curr_note.hit_ms - Player.TIMING_OK) + self.judge_offset <= game_screen.current_ms <= (curr_note.hit_ms + Player.TIMING_OK) + self.judge_offset: elif (curr_note.hit_ms - Player.TIMING_OK) + self.judge_offset <= game_screen.current_ms <= (curr_note.hit_ms + Player.TIMING_OK) + self.judge_offset:
self.draw_judge_list.append(Judgement(get_current_ms(), 'OK', big)) self.draw_judge_list.append(Judgement('OK', big))
self.ok_count += 1 self.ok_count += 1
self.score += 10 * math.floor(self.base_score / 2 / 10) self.score += 10 * math.floor(self.base_score / 2 / 10)
self.base_score_list.append(ScoreCounterAnimation(get_current_ms(), 10 * math.floor(self.base_score / 2 / 10))) self.base_score_list.append(ScoreCounterAnimation(10 * math.floor(self.base_score / 2 / 10)))
self.note_correct(game_screen, curr_note) self.note_correct(game_screen, curr_note)
elif (curr_note.hit_ms - Player.TIMING_BAD) + self.judge_offset <= game_screen.current_ms <= (curr_note.hit_ms + Player.TIMING_BAD) + self.judge_offset: elif (curr_note.hit_ms - Player.TIMING_BAD) + self.judge_offset <= game_screen.current_ms <= (curr_note.hit_ms + Player.TIMING_BAD) + self.judge_offset:
self.draw_judge_list.append(Judgement(get_current_ms(), 'BAD', big)) self.draw_judge_list.append(Judgement('BAD', big))
self.bad_count += 1 self.bad_count += 1
self.combo = 0 self.combo = 0
self.play_notes.popleft() self.play_notes.popleft()
@@ -424,8 +424,8 @@ class Player:
for key in config["keys"]: for key in config["keys"]:
if ray.is_key_pressed(ord(key)): if ray.is_key_pressed(ord(key)):
hit_type = config["type"] hit_type = config["type"]
self.lane_hit_effect = LaneHitEffect(get_current_ms(), hit_type) self.lane_hit_effect = LaneHitEffect(hit_type)
self.draw_drum_hit_list.append(DrumHitEffect(get_current_ms(), hit_type, config["side"])) self.draw_drum_hit_list.append(DrumHitEffect(hit_type, config["side"]))
sound = game_screen.sound_don if hit_type == "DON" else game_screen.sound_kat sound = game_screen.sound_don if hit_type == "DON" else game_screen.sound_kat
audio.play_sound(sound) audio.play_sound(sound)
@@ -560,23 +560,17 @@ class Player:
anim.draw(game_screen) anim.draw(game_screen)
class Judgement: class Judgement:
def __init__(self, game_screen, type: str, big: bool): def __init__(self, type: str, big: bool):
self.type = type self.type = type
self.big = big self.big = big
self.is_finished = False self.is_finished = False
current_ms = get_current_ms()
self.fade_animation_1 = Animation2.create_fade(132, initial_opacity=0.5, delay=100) self.fade_animation_1 = Animation.create_fade(132, initial_opacity=0.5, delay=100)
self.fade_animation_2 = Animation.create_fade(316 - 233.3, delay=233.3)
self.move_animation = Animation.create_move(83, total_distance=15, start_position=144)
self.texture_animation = Animation.create_texture_change(100, textures=[(33, 50, 1), (50, 83, 2), (83, 100, 3), (100, float('inf'), 4)])
self.fade_animation_2 = Animation2.create_fade(316 - 233.3, delay=233.3) def update(self, current_ms):
self.move_animation = Animation2.create_move(83, total_distance=15, start_position=144)
self.texture_animation = Animation(current_ms, 100, 'texture_change')
self.texture_animation.params['textures'] = [(33, 50, 1), (50, 83, 2), (83, 100, 3), (100, float('inf'), 4)]
def update(self, game_screen):
current_ms = get_current_ms()
self.fade_animation_1.update(current_ms) self.fade_animation_1.update(current_ms)
self.fade_animation_2.update(current_ms) self.fade_animation_2.update(current_ms)
self.move_animation.update(current_ms) self.move_animation.update(current_ms)
@@ -610,10 +604,10 @@ class Judgement:
ray.draw_texture(textures_2[10], 370, int(y), color) ray.draw_texture(textures_2[10], 370, int(y), color)
class LaneHitEffect: class LaneHitEffect:
def __init__(self, current_ms: float, type: str): def __init__(self, type: str):
self.type = type self.type = type
self.color = ray.fade(ray.WHITE, 0.5) self.color = ray.fade(ray.WHITE, 0.5)
self.fade = Animation2.create_fade(150, delay=83, initial_opacity=0.5) self.fade = Animation.create_fade(150, delay=83, initial_opacity=0.5)
self.is_finished = False self.is_finished = False
def update(self, current_ms: float): def update(self, current_ms: float):
@@ -632,12 +626,12 @@ class LaneHitEffect:
ray.draw_texture(textures[6], 328, 192, self.color) ray.draw_texture(textures[6], 328, 192, self.color)
class DrumHitEffect: class DrumHitEffect:
def __init__(self, current_ms: float, type: str, side: str): def __init__(self, type: str, side: str):
self.type = type self.type = type
self.side = side self.side = side
self.color = ray.fade(ray.WHITE, 1) self.color = ray.fade(ray.WHITE, 1)
self.is_finished = False self.is_finished = False
self.fade = Animation2.create_fade(100, delay=67) self.fade = Animation.create_fade(100, delay=67)
def update(self, current_ms: float): def update(self, current_ms: float):
self.fade.update(current_ms) self.fade.update(current_ms)
@@ -753,15 +747,15 @@ class DrumrollCounter:
self.is_finished = False self.is_finished = False
self.total_duration = 1349 self.total_duration = 1349
self.drumroll_count = 0 self.drumroll_count = 0
self.fade_animation = Animation2.create_fade(166, delay=self.total_duration - 166) self.fade_animation = Animation.create_fade(166, delay=self.total_duration - 166)
self.stretch_animation = Animation(current_ms, 0, 'text_stretch') self.stretch_animation = Animation.create_text_stretch(0)
def update_count(self, current_ms: float, count: int, elapsed_time: float): def update_count(self, count: int, elapsed_time: float):
self.total_duration = elapsed_time + 1349 self.total_duration = elapsed_time + 1349
self.fade_animation.delay = self.total_duration - 166 self.fade_animation.delay = self.total_duration - 166
if self.drumroll_count != count: if self.drumroll_count != count:
self.drumroll_count = count self.drumroll_count = count
self.stretch_animation = Animation(current_ms, 50, 'text_stretch') self.stretch_animation = Animation.create_text_stretch(50)
def update(self, game_screen: GameScreen, current_ms: float, drumroll_count: int): def update(self, game_screen: GameScreen, current_ms: float, drumroll_count: int):
self.stretch_animation.update(current_ms) self.stretch_animation.update(current_ms)
@@ -769,7 +763,7 @@ class DrumrollCounter:
elapsed_time = current_ms - self.create_ms elapsed_time = current_ms - self.create_ms
if drumroll_count != 0: if drumroll_count != 0:
self.update_count(current_ms, drumroll_count, elapsed_time) self.update_count(drumroll_count, elapsed_time)
if self.fade_animation.is_finished: if self.fade_animation.is_finished:
self.is_finished = True self.is_finished = True
@@ -793,16 +787,16 @@ class BalloonAnimation:
self.balloon_count = 0 self.balloon_count = 0
self.balloon_total = balloon_total self.balloon_total = balloon_total
self.is_popped = False self.is_popped = False
self.fade_animation = Animation2.create_fade(166) self.fade_animation = Animation.create_fade(166)
self.stretch_animation = Animation(current_ms, 0, 'text_stretch') self.stretch_animation = Animation.create_text_stretch(0)
def update_count(self, current_ms: float, balloon_count: int): def update_count(self, balloon_count: int):
if self.balloon_count != balloon_count: if self.balloon_count != balloon_count:
self.balloon_count = balloon_count self.balloon_count = balloon_count
self.stretch_animation = Animation(current_ms, 50, 'text_stretch') self.stretch_animation = Animation.create_text_stretch(50)
def update(self, game_screen: GameScreen, current_ms: float, balloon_count: int, is_popped: bool): def update(self, game_screen: GameScreen, current_ms: float, balloon_count: int, is_popped: bool):
self.update_count(current_ms, balloon_count) self.update_count(balloon_count)
self.stretch_animation.update(current_ms) self.stretch_animation.update(current_ms)
self.is_popped = is_popped self.is_popped = is_popped
@@ -837,7 +831,7 @@ class BalloonAnimation:
class Combo: class Combo:
def __init__(self, combo: int, current_ms: float): def __init__(self, combo: int, current_ms: float):
self.combo = combo self.combo = combo
self.stretch_animation = Animation(current_ms, 0, 'text_stretch') self.stretch_animation = Animation.create_text_stretch(0)
self.color = [ray.fade(ray.WHITE, 1), ray.fade(ray.WHITE, 1), ray.fade(ray.WHITE, 1)] self.color = [ray.fade(ray.WHITE, 1), ray.fade(ray.WHITE, 1), ray.fade(ray.WHITE, 1)]
self.glimmer_dict = {0: 0, 1: 0, 2: 0} self.glimmer_dict = {0: 0, 1: 0, 2: 0}
self.total_time = 250 self.total_time = 250
@@ -851,7 +845,7 @@ class Combo:
def update_count(self, current_ms: float, combo: int): def update_count(self, current_ms: float, combo: int):
if self.combo != combo: if self.combo != combo:
self.combo = combo self.combo = combo
self.stretch_animation = Animation(current_ms, 50, 'text_stretch') self.stretch_animation = Animation.create_text_stretch(50)
def update(self, game_screen: GameScreen, current_ms: float, combo: int): def update(self, game_screen: GameScreen, current_ms: float, combo: int):
self.update_count(current_ms, combo) self.update_count(current_ms, combo)
@@ -900,15 +894,14 @@ class Combo:
ray.draw_texture(game_screen.texture_combo_glimmer, x + (i * 30), y + self.glimmer_dict[j], self.color[j]) ray.draw_texture(game_screen.texture_combo_glimmer, x + (i * 30), y + self.glimmer_dict[j], self.color[j])
class ScoreCounter: class ScoreCounter:
def __init__(self, score: int, current_ms: float): def __init__(self, score: int):
self.score = score self.score = score
self.create_ms = current_ms self.stretch_animation = Animation.create_text_stretch(0)
self.stretch_animation = Animation(current_ms, 0, 'text_stretch')
def update_count(self, current_ms: float, score: int): def update_count(self, current_ms: float, score: int):
if self.score != score: if self.score != score:
self.score = score self.score = score
self.stretch_animation = Animation(current_ms, 50, 'text_stretch') self.stretch_animation = Animation.create_text_stretch(50)
def update(self, current_ms: float, score: int): def update(self, current_ms: float, score: int):
self.update_count(current_ms, score) self.update_count(current_ms, score)
@@ -927,14 +920,14 @@ class ScoreCounter:
ray.draw_texture_pro(game_screen.textures['lane_obi'][int(counter[i])+4], source_rect, dest_rect, ray.Vector2(0,0), 0, ray.WHITE) ray.draw_texture_pro(game_screen.textures['lane_obi'][int(counter[i])+4], source_rect, dest_rect, ray.Vector2(0,0), 0, ray.WHITE)
class ScoreCounterAnimation: class ScoreCounterAnimation:
def __init__(self, current_ms: float, counter: int): def __init__(self, counter: int):
self.counter = counter self.counter = counter
self.fade_animation_1 = Animation2.create_fade(50, initial_opacity=0.0, final_opacity=1.0) self.fade_animation_1 = Animation.create_fade(50, initial_opacity=0.0, final_opacity=1.0)
self.move_animation_1 = Animation2.create_move(80, total_distance=-20, start_position=175) self.move_animation_1 = Animation.create_move(80, total_distance=-20, start_position=175)
self.fade_animation_2 = Animation2.create_fade(80, delay=366.74) self.fade_animation_2 = Animation.create_fade(80, delay=366.74)
self.move_animation_2 = Animation2.create_move(66, total_distance=5, start_position=145, delay=80) self.move_animation_2 = Animation.create_move(66, total_distance=5, start_position=145, delay=80)
self.move_animation_3 = Animation2.create_move(66, delay=279.36, total_distance=-2, start_position=146) self.move_animation_3 = Animation.create_move(66, delay=279.36, total_distance=-2, start_position=146)
self.move_animation_4 = Animation2.create_move(80, delay=366.74, total_distance=10, start_position=148) self.move_animation_4 = Animation.create_move(80, delay=366.74, total_distance=10, start_position=148)
self.color = ray.fade(ray.Color(254, 102, 0, 255), 1.0) self.color = ray.fade(ray.Color(254, 102, 0, 255), 1.0)
self.is_finished = False self.is_finished = False
@@ -984,9 +977,9 @@ class SongInfo:
FADE_DURATION = 366 FADE_DURATION = 366
DISPLAY_DURATION = 1666 DISPLAY_DURATION = 1666
def __init__(self, current_ms: float, song_name: str, genre: str): def __init__(self, song_name: str, genre: str):
self.fade_in = Animation2.create_fade(self.FADE_DURATION, initial_opacity=0.0, final_opacity=1.0) self.fade_in = Animation.create_fade(self.FADE_DURATION, initial_opacity=0.0, final_opacity=1.0)
self.fade_out = Animation2.create_fade(self.FADE_DURATION, delay=self.FADE_DURATION + self.DISPLAY_DURATION) self.fade_out = Animation.create_fade(self.FADE_DURATION, delay=self.FADE_DURATION + self.DISPLAY_DURATION)
self.song_name = song_name self.song_name = song_name
self.genre = genre self.genre = genre
@@ -1014,11 +1007,11 @@ class SongInfo:
self.song_name_fade = ray.fade(ray.WHITE, 1 - self.fade_out.attribute) self.song_name_fade = ray.fade(ray.WHITE, 1 - self.fade_out.attribute)
if self.fade_out.is_finished: if self.fade_out.is_finished:
self._reset_animations(current_ms) self._reset_animations()
def _reset_animations(self, current_ms: float): def _reset_animations(self):
self.fade_in = Animation2.create_fade(self.FADE_DURATION, initial_opacity=0.0, final_opacity=1.0) self.fade_in = Animation.create_fade(self.FADE_DURATION, initial_opacity=0.0, final_opacity=1.0)
self.fade_out = Animation2.create_fade(self.FADE_DURATION, delay=self.FADE_DURATION + self.DISPLAY_DURATION) self.fade_out = Animation.create_fade(self.FADE_DURATION, delay=self.FADE_DURATION + self.DISPLAY_DURATION)
def draw(self, game_screen: GameScreen): def draw(self, game_screen: GameScreen):
song_texture_index = (global_data.songs_played % 4) + 8 song_texture_index = (global_data.songs_played % 4) + 8
@@ -1033,8 +1026,8 @@ class SongInfo:
self.song_title.draw(text_x, text_y, self.song_name_fade) self.song_title.draw(text_x, text_y, self.song_name_fade)
class ResultTransition: class ResultTransition:
def __init__(self, current_ms: float, screen_height: int): def __init__(self, screen_height: int):
self.move = Animation2.create_move(983.33, start_position=0, total_distance=screen_height//2, ease_out='quadratic') self.move = Animation.create_move(983.33, start_position=0, total_distance=screen_height//2, ease_out='quadratic')
self.is_finished = False self.is_finished = False
@@ -1057,7 +1050,7 @@ class ResultTransition:
x += texture_2.width x += texture_2.width
class Gauge: class Gauge:
def __init__(self, current_ms: float, difficulty: int, level: int): def __init__(self, difficulty: int, level: int):
self.gauge_length = 0 self.gauge_length = 0
self.difficulty = min(3, difficulty) self.difficulty = min(3, difficulty)
self.clear_start = [0, 0, 68, 68] self.clear_start = [0, 0, 68, 68]
@@ -1110,18 +1103,6 @@ class Gauge:
self.rainbow_fade_in = None self.rainbow_fade_in = None
self.rainbow_animation = None self.rainbow_animation = None
def _create_rainbow_anim(self, current_ms):
anim = Animation(current_ms, (16.67*8) * 3, 'texture_change')
anim.params['textures'] = []
for i in range(8):
anim.params['textures'].append(((16.67* 3)*i, (16.67 * 3)*(i+1), i))
anim.params['textures'] = tuple(anim.params['textures'])
return anim
def _create_anim(self, current_ms: float, init: float, final: float):
anim = Animation2.create_fade(450, initial_opacity=init, final_opacity=final)
return anim
def update(self, current_ms: float, good_count: int, ok_count: int, bad_count: int, total_notes: int): def update(self, current_ms: float, good_count: int, ok_count: int, bad_count: int, total_notes: int):
gauge_length = int(((good_count + gauge_length = int(((good_count +
(ok_count * self.table[self.difficulty][self.level]["ok_multiplier"] + (ok_count * self.table[self.difficulty][self.level]["ok_multiplier"] +
@@ -1129,9 +1110,9 @@ class Gauge:
previous_length = self.gauge_length previous_length = self.gauge_length
self.gauge_length = min(87, gauge_length) self.gauge_length = min(87, gauge_length)
if self.gauge_length == 87 and self.rainbow_fade_in is None: if self.gauge_length == 87 and self.rainbow_fade_in is None:
self.rainbow_fade_in = self._create_anim(current_ms, 0.0, 1.0) self.rainbow_fade_in = Animation.create_fade(450, initial_opacity=0.0, final_opacity=1.0)
if self.gauge_length > previous_length: if self.gauge_length > previous_length:
self.gauge_update_anim = self._create_anim(current_ms, 1.0, 0.0) self.gauge_update_anim = Animation.create_fade(450)
if self.gauge_update_anim is not None: if self.gauge_update_anim is not None:
self.gauge_update_anim.update(current_ms) self.gauge_update_anim.update(current_ms)
@@ -1142,7 +1123,7 @@ class Gauge:
self.rainbow_fade_in.update(current_ms) self.rainbow_fade_in.update(current_ms)
if self.rainbow_animation is None: if self.rainbow_animation is None:
self.rainbow_animation = self._create_rainbow_anim(current_ms) self.rainbow_animation = Animation.create_texture_change((16.67*8) * 3, textures=[((16.67 * 3) * i, (16.67 * 3) * (i + 1), i) for i in range(8)])
else: else:
self.rainbow_animation.update(current_ms) self.rainbow_animation.update(current_ms)
if self.rainbow_animation.is_finished or self.gauge_length < 87: if self.rainbow_animation.is_finished or self.gauge_length < 87:

View File

@@ -3,7 +3,7 @@ from pathlib import Path
import pyray as ray import pyray as ray
from libs import utils from libs import utils
from libs.animation import Animation, Animation2 from libs.animation import Animation
from libs.audio import audio from libs.audio import audio
from libs.utils import ( from libs.utils import (
OutlinedText, OutlinedText,
@@ -178,7 +178,7 @@ class ResultScreen:
class FadeIn: class FadeIn:
def __init__(self, current_ms: float): def __init__(self, current_ms: float):
self.fadein = Animation2.create_fade(450, initial_opacity=1.0, final_opacity=0.0, delay=100) self.fadein = Animation.create_fade(450, initial_opacity=1.0, final_opacity=0.0, delay=100)
self.fade = ray.fade(ray.WHITE, self.fadein.attribute) self.fade = ray.fade(ray.WHITE, self.fadein.attribute)
self.is_finished = False self.is_finished = False
@@ -235,18 +235,15 @@ class Gauge:
def __init__(self, current_ms: float, gauge_length): def __init__(self, current_ms: float, gauge_length):
self.gauge_length = gauge_length self.gauge_length = gauge_length
self.rainbow_animation = None self.rainbow_animation = None
self.gauge_fade_in = Animation2.create_fade(366, initial_opacity=0.0, final_opacity=1.0) self.gauge_fade_in = Animation.create_fade(366, initial_opacity=0.0, final_opacity=1.0)
self.is_finished = self.gauge_fade_in.is_finished self.is_finished = self.gauge_fade_in.is_finished
def _create_rainbow_anim(self, current_ms): def _create_rainbow_anim(self, current_ms):
anim = Animation(current_ms, (16.67*8) * 3, 'texture_change') anim = Animation.create_texture_change((16.67*8) * 3, textures=[((16.67 * 3) * i, (16.67 * 3) * (i + 1), i) for i in range(8)])
anim.params['textures'] = []
for i in range(8):
anim.params['textures'].append(((16.67* 3)*i, (16.67 * 3)*(i+1), i))
return anim return anim
def _create_anim(self, current_ms: float, init: float, final: float): def _create_anim(self, current_ms: float, init: float, final: float):
anim = Animation2.create_fade(450, initial_opacity=init, final_opacity=final) anim = Animation.create_fade(450, initial_opacity=init, final_opacity=final)
return anim return anim
def update(self, current_ms: float): def update(self, current_ms: float):

View File

@@ -3,7 +3,7 @@ from pathlib import Path
import pyray as ray import pyray as ray
from libs.animation import Animation, Animation2 from libs.animation import Animation
from libs.audio import audio from libs.audio import audio
from libs.utils import ( from libs.utils import (
get_config, get_config,
@@ -46,6 +46,7 @@ class TitleScreen:
if not self.screen_init: if not self.screen_init:
self.screen_init = True self.screen_init = True
self.load_textures() self.load_textures()
self.scene = 'Opening Video' self.scene = 'Opening Video'
self.op_video = VideoPlayer(random.choice(self.op_video_list)) self.op_video = VideoPlayer(random.choice(self.op_video_list))
self.attract_video = VideoPlayer(random.choice(self.attract_video_list)) self.attract_video = VideoPlayer(random.choice(self.attract_video_list))
@@ -107,25 +108,17 @@ class TitleScreen:
elif self.scene == 'Attract Video': elif self.scene == 'Attract Video':
self.attract_video.draw() self.attract_video.draw()
ray.draw_text(f"Scene: {self.scene}", 20, 40, 20, ray.BLUE)
class WarningScreen: class WarningScreen:
class X: class X:
DELAY = 4250 DELAY = 4250
def __init__(self, current_ms: float): def __init__(self):
self.resize = Animation(current_ms, 166.67, 'texture_resize') self.resize = Animation.create_texture_resize(166.67, initial_size=1.0, final_size=1.5, delay=self.DELAY, reverse_delay=0)
self.resize.params['initial_size'] = 1.0 self.fadein = Animation.create_fade(166.67, delay=self.DELAY, initial_opacity=0.0, final_opacity=1.0, reverse_delay=166.67)
self.resize.params['final_size'] = 1.5 self.fadein_2 = Animation.create_fade(166.67, delay=self.DELAY + self.fadein.duration, initial_opacity=0.0, final_opacity=1.0)
self.resize.params['delay'] = self.DELAY
self.resize.params['reverse'] = 0
self.fadein = Animation2.create_fade(166.67, delay=self.DELAY, initial_opacity=0.0, final_opacity=1.0, reverse_delay=166.67)
self.fadein_2 = Animation2.create_fade(166.67, delay=self.DELAY + self.fadein.duration, initial_opacity=0.0, final_opacity=1.0)
self.sound_played = False self.sound_played = False
def update(self, current_ms: float, sound, elapsed_time): def update(self, current_ms: float, sound, elapsed_time):
self.fadein.update(current_ms) self.fadein.update(current_ms)
self.fadein_2.update(current_ms) self.fadein_2.update(current_ms)
self.resize.update(current_ms) self.resize.update(current_ms)
@@ -136,21 +129,16 @@ class WarningScreen:
def draw(self, texture): def draw(self, texture):
scale = self.resize.attribute scale = self.resize.attribute
width = texture.width x_x = 150 + (texture.width//2) - ((texture.width * scale)//2)
height = texture.height x_y = 200 + (texture.height//2) - ((texture.height * scale)//2)
x_x = 150 + (width//2) - ((width * scale)//2) x_source = ray.Rectangle(0, 0, texture.width, texture.height)
x_y = 200 + (height//2) - ((height * scale)//2) x_dest = ray.Rectangle(x_x, x_y, texture.width*scale, texture.height*scale)
x_source = ray.Rectangle(0, 0, width, height)
x_dest = ray.Rectangle(x_x, x_y, width*scale, height*scale)
ray.draw_texture_pro(texture, x_source, x_dest, ray.Vector2(0,0), 0, ray.fade(ray.WHITE, self.fadein.attribute)) ray.draw_texture_pro(texture, x_source, x_dest, ray.Vector2(0,0), 0, ray.fade(ray.WHITE, self.fadein.attribute))
class BachiHit: class BachiHit:
def __init__(self, current_ms: float): def __init__(self):
self.resize = Animation(current_ms, 233.34, 'texture_resize') self.resize = Animation.create_texture_resize(233.34, initial_size=0.5, final_size=1.5)
self.resize.params['initial_size'] = 0.5 self.fadein = Animation.create_fade(116.67, initial_opacity=0.0, final_opacity=1.0, reverse_delay=0)
self.resize.params['final_size'] = 1.5
self.fadein = Animation2.create_fade(116.67, initial_opacity=0.0, final_opacity=1.0, reverse_delay=0)
self.sound_played = False self.sound_played = False
@@ -165,19 +153,17 @@ class WarningScreen:
def draw(self, texture): def draw(self, texture):
scale = self.resize.attribute scale = self.resize.attribute
width = texture.width hit_x = 350 + (texture.width//2) - ((texture.width * scale)//2)
height = texture.height hit_y = 225 + (texture.height//2) - ((texture.height * scale)//2)
hit_x = 350 + (width//2) - ((width * scale)//2) hit_source = ray.Rectangle(0, 0, texture.width, texture.height)
hit_y = 225 + (height//2) - ((height * scale)//2) hit_dest = ray.Rectangle(hit_x, hit_y, texture.width*scale, texture.height*scale)
hit_source = ray.Rectangle(0, 0, width, height)
hit_dest = ray.Rectangle(hit_x, hit_y, width*scale, height*scale)
ray.draw_texture_pro(texture, hit_source, hit_dest, ray.Vector2(0,0), 0, ray.fade(ray.WHITE, self.fadein.attribute)) ray.draw_texture_pro(texture, hit_source, hit_dest, ray.Vector2(0,0), 0, ray.fade(ray.WHITE, self.fadein.attribute))
class Characters: class Characters:
def __init__(self, current_ms: float, start_ms: float): def __init__(self, current_ms: float, start_ms: float):
self.start_ms = start_ms self.start_ms = start_ms
self.current_ms = current_ms self.current_ms = current_ms
self.shadow_fade = Animation2.create_fade(50, delay=16.67, initial_opacity=0.75) self.shadow_fade = Animation.create_fade(50, delay=16.67, initial_opacity=0.75)
self.animation_sequence = [(300.00, 5, 4), (183.33, 6, 4), (166.67, 7, 4), (166.67, 8, 9), (166.67, 11, 9), (166.67, 12, 9), (166.67, 13, 9), self.animation_sequence = [(300.00, 5, 4), (183.33, 6, 4), (166.67, 7, 4), (166.67, 8, 9), (166.67, 11, 9), (166.67, 12, 9), (166.67, 13, 9),
(166.67, 5, 4), (150.00, 5, 4), (133.34, 6, 4), (133.34, 7, 4), (133.34, 8, 9), (133.34, 11, 9), (133.34, 12, 9), (133.34, 13, 9), (166.67, 5, 4), (150.00, 5, 4), (133.34, 6, 4), (133.34, 7, 4), (133.34, 8, 9), (133.34, 11, 9), (133.34, 12, 9), (133.34, 13, 9),
@@ -228,15 +214,15 @@ class WarningScreen:
ray.draw_texture(textures['keikoku'][19], 350, y+135, ray.WHITE) ray.draw_texture(textures['keikoku'][19], 350, y+135, ray.WHITE)
class Board: class Board:
def __init__(self, current_ms: float, screen_width, screen_height, texture): def __init__(self, screen_width, screen_height, texture):
#Move warning board down from top of screen #Move warning board down from top of screen
self.move_down = Animation2.create_move(266.67, total_distance=screen_height + ((screen_height - texture.height)//2) + 20, start_position=-720) self.move_down = Animation.create_move(266.67, total_distance=screen_height + ((screen_height - texture.height)//2) + 20, start_position=-720)
#Move warning board up a little bit #Move warning board up a little bit
self.move_up = Animation2.create_move(116.67, start_position=92 + 20, delay=self.move_down.duration, total_distance =-30) self.move_up = Animation.create_move(116.67, start_position=92 + 20, delay=self.move_down.duration, total_distance =-30)
#And finally into its correct position #And finally into its correct position
self.move_center = Animation2.create_move(116.67, start_position=82, delay=self.move_down.duration + self.move_up.duration, total_distance=10) self.move_center = Animation.create_move(116.67, start_position=82, delay=self.move_down.duration + self.move_up.duration, total_distance=10)
self.y_pos = 0 self.y_pos = 0
def update(self, current_ms): def update(self, current_ms):
@@ -257,14 +243,12 @@ class WarningScreen:
def __init__(self, current_ms: float, title_screen: TitleScreen): def __init__(self, current_ms: float, title_screen: TitleScreen):
self.start_ms = current_ms self.start_ms = current_ms
self.fade_in = Animation2.create_fade(300, delay=266.67, initial_opacity=0.0, final_opacity=1.0) self.fade_in = Animation.create_fade(300, delay=266.67, initial_opacity=0.0, final_opacity=1.0)
self.fade_out = Animation.create_fade(500, delay=1000, initial_opacity=0.0, final_opacity=1.0)
#Fade to black self.board = self.Board(title_screen.width, title_screen.height, title_screen.textures['keikoku'][1])
self.fade_out = Animation2.create_fade(500, delay=1000, initial_opacity=0.0, final_opacity=1.0) self.warning_x = self.X()
self.warning_bachi_hit = self.BachiHit()
self.board = self.Board(current_ms, title_screen.width, title_screen.height, title_screen.textures['keikoku'][1])
self.warning_x = self.X(current_ms)
self.warning_bachi_hit = self.BachiHit(current_ms)
self.characters = self.Characters(current_ms, self.start_ms) self.characters = self.Characters(current_ms, self.start_ms)
self.source_rect = ray.Rectangle(0, 0, title_screen.texture_black.width, title_screen.texture_black.height) self.source_rect = ray.Rectangle(0, 0, title_screen.texture_black.width, title_screen.texture_black.height)