add new texture wrapper

This commit is contained in:
Yonokid
2025-08-05 16:48:50 -04:00
parent 57383339cc
commit bb3a2a1a38
15 changed files with 1522 additions and 1209 deletions

View File

@@ -12,6 +12,7 @@ from raylib.defines import (
from libs.audio import audio from libs.audio import audio
from libs.utils import ( from libs.utils import (
force_dedicated_gpu,
get_config, get_config,
global_data, global_data,
load_all_textures_from_zip, load_all_textures_from_zip,
@@ -59,6 +60,7 @@ def create_song_db():
print("Scores database created successfully") print("Scores database created successfully")
def main(): def main():
force_dedicated_gpu()
global_data.config = get_config() global_data.config = get_config()
screen_width: int = global_data.config["video"]["screen_width"] screen_width: int = global_data.config["video"]["screen_width"]
screen_height: int = global_data.config["video"]["screen_height"] screen_height: int = global_data.config["video"]["screen_height"]
@@ -77,6 +79,7 @@ def main():
ray.init_window(screen_width, screen_height, "PyTaiko") ray.init_window(screen_width, screen_height, "PyTaiko")
global_data.textures = load_all_textures_from_zip(Path('Graphics/lumendata/intermission.zip')) global_data.textures = load_all_textures_from_zip(Path('Graphics/lumendata/intermission.zip'))
global_data.tex.load_screen_textures('transition')
if global_data.config["video"]["borderless"]: if global_data.config["video"]["borderless"]:
ray.toggle_borderless_windowed() ray.toggle_borderless_windowed()
if global_data.config["video"]["fullscreen"]: if global_data.config["video"]["fullscreen"]:
@@ -114,21 +117,22 @@ def main():
ray.set_exit_key(ray.KeyboardKey.KEY_A) ray.set_exit_key(ray.KeyboardKey.KEY_A)
while not ray.window_should_close(): while not ray.window_should_close():
if ray.is_key_pressed(ray.KeyboardKey.KEY_F11):
ray.toggle_fullscreen()
elif ray.is_key_pressed(ray.KeyboardKey.KEY_F10):
ray.toggle_borderless_windowed()
ray.begin_texture_mode(target) ray.begin_texture_mode(target)
ray.begin_blend_mode(ray.BlendMode.BLEND_CUSTOM_SEPARATE) ray.begin_blend_mode(ray.BlendMode.BLEND_CUSTOM_SEPARATE)
screen = screen_mapping[current_screen] screen = screen_mapping[current_screen]
# Begin 3D mode with orthographic camera
if ray.is_key_pressed(ray.KeyboardKey.KEY_F11):
ray.toggle_borderless_windowed()
next_screen = screen.update() next_screen = screen.update()
ray.clear_background(ray.BLACK) ray.clear_background(ray.BLACK)
screen.draw() screen.draw()
ray.begin_mode_3d(camera) #ray.begin_mode_3d(camera)
screen.draw_3d() #screen.draw_3d()
ray.end_mode_3d() # ray.end_mode_3d()
if next_screen is not None: if next_screen is not None:
current_screen = next_screen current_screen = next_screen

View File

@@ -16,14 +16,18 @@ Windows:
``` ```
Run PyTaiko.exe Run PyTaiko.exe
``` ```
MacOS/Linux: MacOS:
``` ```
Good luck, would suggest running with python directly Good luck, would suggest running with python directly
``` ```
Linux:
```
Run PyTaiko.bin for Debian based systems, otherwise run python
```
## Roadmap ## Roadmap
https://linear.app/pytaiko Ask me
## Known Issues ## Known Issues

View File

@@ -1,6 +1,17 @@
from typing import Optional import time
from typing import Any, Optional
from libs.utils import get_current_ms
def rounded(num: float) -> int:
sign = 1 if (num >= 0) else -1
num = abs(num)
result = int(num)
if (num - result >= 0.5):
result += 1
return sign * result
def get_current_ms() -> int:
return rounded(time.time() * 1000)
class BaseAnimation(): class BaseAnimation():
@@ -15,17 +26,39 @@ class BaseAnimation():
""" """
self.duration = duration self.duration = duration
self.delay = delay self.delay = delay
self.delay_saved = delay
self.start_ms = get_current_ms() self.start_ms = get_current_ms()
self.is_finished = False self.is_finished = False
self.attribute = 0 self.attribute = 0
self.is_started = False
def __repr__(self):
return str(self.__dict__)
def __str__(self):
return str(self.__dict__)
def update(self, current_time_ms: float) -> None: def update(self, current_time_ms: float) -> None:
"""Update the animation based on the current time.""" """Update the animation based on the current time."""
pass
def restart(self) -> None: def restart(self) -> None:
self.start_ms = get_current_ms() self.start_ms = get_current_ms()
self.is_finished = False self.is_finished = False
self.delay = self.delay_saved
def start(self) -> None:
self.is_started = True
self.restart()
def pause(self):
self.is_started = False
def unpause(self):
self.is_started = True
def reset(self):
self.restart()
self.pause()
def _ease_in(self, progress: float, ease_type: str) -> float: def _ease_in(self, progress: float, ease_type: str) -> float:
if ease_type == "quadratic": if ease_type == "quadratic":
@@ -60,6 +93,7 @@ class FadeAnimation(BaseAnimation):
reverse_delay: Optional[float] = None) -> None: reverse_delay: Optional[float] = None) -> None:
super().__init__(duration, delay) super().__init__(duration, delay)
self.initial_opacity = initial_opacity self.initial_opacity = initial_opacity
self.attribute = initial_opacity
self.final_opacity = final_opacity self.final_opacity = final_opacity
self.initial_opacity_saved = initial_opacity self.initial_opacity_saved = initial_opacity
self.final_opacity_saved = final_opacity self.final_opacity_saved = final_opacity
@@ -73,8 +107,13 @@ class FadeAnimation(BaseAnimation):
self.reverse_delay = self.reverse_delay_saved self.reverse_delay = self.reverse_delay_saved
self.initial_opacity = self.initial_opacity_saved self.initial_opacity = self.initial_opacity_saved
self.final_opacity = self.final_opacity_saved self.final_opacity = self.final_opacity_saved
self.attribute = self.initial_opacity
def update(self, current_time_ms: float) -> None: def update(self, current_time_ms: float) -> None:
if not self.is_started:
return
else:
self.is_started = not self.is_finished
elapsed_time = current_time_ms - self.start_ms elapsed_time = current_time_ms - self.start_ms
if elapsed_time <= self.delay: if elapsed_time <= self.delay:
@@ -116,8 +155,13 @@ class MoveAnimation(BaseAnimation):
self.reverse_delay = self.reverse_delay_saved self.reverse_delay = self.reverse_delay_saved
self.total_distance = self.total_distance_saved self.total_distance = self.total_distance_saved
self.start_position = self.start_position_saved self.start_position = self.start_position_saved
self.attribute = self.start_position
def update(self, current_time_ms: float) -> None: def update(self, current_time_ms: float) -> None:
if not self.is_started:
return
else:
self.is_started = not self.is_finished
elapsed_time = current_time_ms - self.start_ms elapsed_time = current_time_ms - self.start_ms
if elapsed_time < self.delay: if elapsed_time < self.delay:
self.attribute = self.start_position self.attribute = self.start_position
@@ -142,12 +186,22 @@ class TextureChangeAnimation(BaseAnimation):
super().__init__(duration) super().__init__(duration)
self.textures = textures self.textures = textures
self.delay = delay self.delay = delay
self.delay_saved = delay
def update(self, current_time_ms: float) -> None: def update(self, current_time_ms: float) -> None:
elapsed_time = current_time_ms - self.start_ms - self.delay super().update(current_time_ms)
if elapsed_time <= self.duration: if not self.is_started:
return
else:
self.is_started = not self.is_finished
elapsed_time = current_time_ms - self.start_ms
if elapsed_time < self.delay:
return
animation_time = elapsed_time - self.delay
if animation_time <= self.duration:
for start, end, index in self.textures: for start, end, index in self.textures:
if start < elapsed_time <= end: if start < animation_time <= end:
self.attribute = index self.attribute = index
else: else:
self.is_finished = True self.is_finished = True
@@ -156,6 +210,10 @@ class TextStretchAnimation(BaseAnimation):
def __init__(self, duration: float) -> None: def __init__(self, duration: float) -> None:
super().__init__(duration) super().__init__(duration)
def update(self, current_time_ms: float) -> None: def update(self, current_time_ms: float) -> None:
if not self.is_started:
return
else:
self.is_started = not self.is_finished
elapsed_time = current_time_ms - self.start_ms elapsed_time = current_time_ms - self.start_ms
if elapsed_time <= self.duration: if elapsed_time <= self.duration:
self.attribute = 2 + 5 * (elapsed_time // 25) self.attribute = 2 + 5 * (elapsed_time // 25)
@@ -189,6 +247,10 @@ class TextureResizeAnimation(BaseAnimation):
def update(self, current_time_ms: float) -> None: def update(self, current_time_ms: float) -> None:
if not self.is_started:
return
else:
self.is_started = not self.is_finished
elapsed_time = current_time_ms - self.start_ms elapsed_time = current_time_ms - self.start_ms
if elapsed_time <= self.delay: if elapsed_time <= self.delay:
@@ -286,3 +348,86 @@ class Animation:
reverse_delay: If provided, resize will play in reverse after this delay reverse_delay: If provided, resize will play in reverse after this delay
""" """
return TextureResizeAnimation(duration, **kwargs) return TextureResizeAnimation(duration, **kwargs)
ANIMATION_CLASSES = {
"fade": FadeAnimation,
"move": MoveAnimation,
"texture_change": TextureChangeAnimation,
"text_stretch": TextStretchAnimation,
"texture_resize": TextureResizeAnimation
}
def parse_animations(animation_json):
raw_anims = {}
for item in animation_json:
if "id" not in item:
raise Exception("Animation requires id")
if "type" not in item:
raise Exception("Animation requires type")
raw_anims[item["id"]] = item.copy()
def find_refs(anim_id: int, visited: Optional[set] = None):
if visited is None:
visited = set()
if anim_id in visited:
raise Exception(f"Circular reference detected involving animation {anim_id}")
visited.add(anim_id)
animation = raw_anims[anim_id].copy()
for key, value in animation.items():
if isinstance(value, dict) and "reference_id" in value:
animation[key] = resolve_value(value, visited.copy())
visited.remove(anim_id)
return animation
def resolve_value(ref_obj: dict[str, Any], visited: set):
if "property" not in ref_obj:
raise Exception("Reference requires 'property' field")
ref_id = ref_obj["reference_id"]
ref_property = ref_obj["property"]
if ref_id not in raw_anims:
raise Exception(f"Referenced animation {ref_id} not found")
resolved_ref_animation = find_refs(ref_id, visited)
if ref_property not in resolved_ref_animation:
raise Exception(f"Property '{ref_property}' not found in animation {ref_id}")
base_value = resolved_ref_animation[ref_property]
if "init_val" in ref_obj:
init_val = ref_obj["init_val"]
if isinstance(init_val, dict) and "reference_id" in init_val:
init_val = resolve_value(init_val, visited)
try:
return base_value + init_val
except TypeError:
raise Exception(f"Cannot add init_val {init_val} to referenced value {base_value}")
return base_value
anim_dict = dict()
for id in raw_anims:
absolute_anim = find_refs(id)
type = absolute_anim.pop("type")
id_val = absolute_anim.pop("id")
if "comment" in absolute_anim:
absolute_anim.pop("comment")
if type not in ANIMATION_CLASSES:
raise Exception(f"Unknown Animation type: {type}")
anim_class = ANIMATION_CLASSES[type]
anim_object = anim_class(**absolute_anim)
anim_dict[id_val] = anim_object
return anim_dict

View File

@@ -17,6 +17,7 @@ from numpy import (
interp, interp,
mean, mean,
ndarray, ndarray,
ones,
sqrt, sqrt,
uint8, uint8,
zeros, zeros,
@@ -570,23 +571,58 @@ class AudioEngine:
return True return True
def _audio_callback(self, outdata: ndarray, frames: int, time: int, status: str) -> None: def _audio_callback(self, outdata: ndarray, frames: int, time: int, status: str) -> None:
"""Callback function for the sounddevice stream""" """callback function for the sounddevice stream"""
if status: if status:
print(f"Status: {status}") print(f"Status: {status}")
# Process any new sound play requests self._process_sound_queue()
while not self.sound_queue.empty(): self._process_music_queue()
# Pre-allocate output buffer (reuse if possible)
if not hasattr(self, '_output_buffer') or self._output_buffer.shape != (frames, self.output_channels):
self._output_buffer = zeros((frames, self.output_channels), dtype=float32)
else:
self._output_buffer.fill(0.0) # Clear previous data
self._mix_sounds(self._output_buffer, frames)
self._mix_music(self._output_buffer, frames)
# Apply master volume in-place
if self.master_volume != 1.0:
self._output_buffer *= self.master_volume
# Apply limiter only if needed
max_val = np_max(np_abs(self._output_buffer))
if max_val > 1.0:
self._output_buffer /= max_val
outdata[:] = self._output_buffer
def _process_sound_queue(self) -> None:
"""Process sound queue"""
sounds_to_play = []
try: try:
sound_name = self.sound_queue.get_nowait() while True:
sounds_to_play.append(self.sound_queue.get_nowait())
except queue.Empty:
pass
for sound_name in sounds_to_play:
if sound_name in self.sounds: if sound_name in self.sounds:
self.sounds[sound_name].play() self.sounds[sound_name].play()
except queue.Empty:
break
# Process any new music play requests def _process_music_queue(self) -> None:
while not self.music_queue.empty(): """Process music queue"""
music_commands = []
try: try:
music_name, action, *args = self.music_queue.get_nowait() while True:
music_commands.append(self.music_queue.get_nowait())
except queue.Empty:
pass
for command in music_commands:
music_name, action, *args = command
if music_name in self.music_streams: if music_name in self.music_streams:
music = self.music_streams[music_name] music = self.music_streams[music_name]
if action == 'play': if action == 'play':
@@ -599,64 +635,52 @@ class AudioEngine:
music.resume() music.resume()
elif action == 'seek' and args: elif action == 'seek' and args:
music.seek(args[0]) music.seek(args[0])
except queue.Empty:
break
# Mix all playing sounds and music def _mix_sounds(self, output: ndarray, frames: int) -> None:
output = zeros((frames, self.output_channels), dtype=float32) """sound mixing"""
for sound in self.sounds.values():
if not sound.is_playing:
continue
# Mix sounds
for sound_name, sound in self.sounds.items():
if sound.is_playing:
sound_data = sound.get_frames(frames) sound_data = sound.get_frames(frames)
if sound_data is None or not isinstance(sound_data, ndarray):
continue
# If mono sound but stereo output, duplicate to both channels # Handle channel mismatch
if isinstance(sound_data, ndarray): if sound.channels != self.output_channels:
if sound.channels == 1 and self.output_channels > 1: sound_data = self._convert_channels(sound_data, sound.channels)
sound_data = column_stack([sound_data] * self.output_channels)
# Ensure sound_data matches the output format
if sound.channels > self.output_channels:
# Down-mix if needed
if self.output_channels == 1:
sound_data = mean(sound_data, axis=1)
else:
# Keep only the first output_channels
sound_data = sound_data[:, :self.output_channels]
# Add to the mix (simple additive mixing)
output += sound_data output += sound_data
# Mix music streams def _mix_music(self, output: ndarray, frames: int) -> None:
for music_name, music in self.music_streams.items(): """music mixing"""
if music.is_playing: for music in self.music_streams.values():
if not music.is_playing:
continue
music_data = music.get_frames(frames) music_data = music.get_frames(frames)
if music_data is None:
continue
# If mono music but stereo output, duplicate to both channels if music.channels != self.output_channels:
if music.channels == 1 and self.output_channels > 1: music_data = self._convert_channels(music_data, music.channels)
music_data = column_stack([music_data] * self.output_channels)
# Ensure music_data matches the output format
if music.channels > self.output_channels:
# Down-mix if needed
if self.output_channels == 1:
music_data = mean(music_data, axis=1)
else:
# Keep only the first output_channels
music_data = music_data[:, :self.output_channels]
# Add to the mix
output += music_data output += music_data
# Apply master volume def _convert_channels(self, data: ndarray, input_channels: int) -> ndarray:
output *= self.master_volume """channel conversion with caching"""
if input_channels == self.output_channels:
return data
# Apply simple limiter to prevent clipping if input_channels == 1 and self.output_channels > 1:
max_val = np_max(np_abs(output)) return data[:, None] * ones((1, self.output_channels), dtype=float32)
if max_val > 1.0: elif input_channels > self.output_channels:
output = output / max_val if self.output_channels == 1:
return mean(data, axis=1, keepdims=True)
else:
return data[:, :self.output_channels]
outdata[:] = output return data
def _start_update_thread(self) -> None: def _start_update_thread(self) -> None:
"""Start a thread to update music streams""" """Start a thread to update music streams"""
@@ -671,16 +695,13 @@ class AudioEngine:
active_streams = [music for music in self.music_streams.values() if music.is_playing] active_streams = [music for music in self.music_streams.values() if music.is_playing]
if not active_streams: if not active_streams:
# Sleep longer when no streams are active
time.sleep(0.5) time.sleep(0.5)
continue continue
for music in active_streams: for music in active_streams:
music.update() music.update()
# Adjust sleep based on number of active streams time.sleep(0.1)
sleep_time = max(0.05, 0.1 / len(active_streams))
time.sleep(sleep_time)
def init_audio_device(self): def init_audio_device(self):
if self.audio_device_ready: if self.audio_device_ready:
@@ -770,6 +791,9 @@ class AudioEngine:
if sound in self.sounds: if sound in self.sounds:
del self.sounds[sound] del self.sounds[sound]
def unload_all_sounds(self) -> None:
self.sounds.clear()
def normalize_sound(self, sound: str, rms: float) -> None: def normalize_sound(self, sound: str, rms: float) -> None:
if sound in self.sounds: if sound in self.sounds:
self.sounds[sound].normalize_vol(rms) self.sounds[sound].normalize_vol(rms)

View File

@@ -52,12 +52,14 @@ class DonBGBase:
self.name = 'donbg_a_' + str(index).zfill(2) self.name = 'donbg_a_' + str(index).zfill(2)
self.textures = (load_all_textures_from_zip(Path(f'Graphics/lumendata/enso_original/{self.name}_{self.player_num}p.zip'))) self.textures = (load_all_textures_from_zip(Path(f'Graphics/lumendata/enso_original/{self.name}_{self.player_num}p.zip')))
self.move = Animation.create_move(3000, start_position=0, total_distance=-self.textures[self.name + f'_{self.player_num}p'][0].width) self.move = Animation.create_move(3000, start_position=0, total_distance=-self.textures[self.name + f'_{self.player_num}p'][0].width)
self.move.start()
self.is_clear = False self.is_clear = False
self.clear_fade = None self.clear_fade = None
def update(self, current_time_ms: float, is_clear: bool): def update(self, current_time_ms: float, is_clear: bool):
if not self.is_clear and is_clear: if not self.is_clear and is_clear:
self.clear_fade = Animation.create_fade(150, initial_opacity=0.0, final_opacity=1.0) self.clear_fade = Animation.create_fade(150, initial_opacity=0.0, final_opacity=1.0)
self.clear_fade.start()
self.is_clear = is_clear self.is_clear = is_clear
self.move.update(current_time_ms) self.move.update(current_time_ms)
if self.clear_fade is not None: if self.clear_fade is not None:
@@ -74,6 +76,7 @@ class DonBG1(DonBGBase):
def __init__(self, index: int, screen_width: int, screen_height: int, player_num: int): def __init__(self, index: int, screen_width: int, screen_height: int, player_num: int):
super().__init__(index, screen_width, screen_height, player_num) super().__init__(index, screen_width, screen_height, player_num)
self.overlay_move = Animation.create_move(1000, start_position=0, total_distance=20, reverse_delay=0) self.overlay_move = Animation.create_move(1000, start_position=0, total_distance=20, reverse_delay=0)
self.overlay_move.start()
def update(self, current_time_ms: float, is_clear: bool): def update(self, current_time_ms: float, is_clear: bool):
super().update(current_time_ms, is_clear) super().update(current_time_ms, is_clear)
self.overlay_move.update(current_time_ms) self.overlay_move.update(current_time_ms)
@@ -100,6 +103,7 @@ class DonBG2(DonBGBase):
def __init__(self, index: int, screen_width: int, screen_height: int, player_num: int): def __init__(self, index: int, screen_width: int, screen_height: int, player_num: int):
super().__init__(index, screen_width, screen_height, player_num) super().__init__(index, screen_width, screen_height, player_num)
self.overlay_move = Animation.create_move(1500, start_position=0, total_distance=20, reverse_delay=0) self.overlay_move = Animation.create_move(1500, start_position=0, total_distance=20, reverse_delay=0)
self.overlay_move.start()
def update(self, current_time_ms: float, is_clear: bool): def update(self, current_time_ms: float, is_clear: bool):
super().update(current_time_ms, is_clear) super().update(current_time_ms, is_clear)
self.overlay_move.update(current_time_ms) self.overlay_move.update(current_time_ms)
@@ -124,9 +128,13 @@ class DonBG3(DonBGBase):
duration = 266 duration = 266
bounce_distance = 40 bounce_distance = 40
self.bounce_up = Animation.create_move(duration, total_distance=-bounce_distance, ease_out='quadratic') self.bounce_up = Animation.create_move(duration, total_distance=-bounce_distance, ease_out='quadratic')
self.bounce_up.start()
self.bounce_down = Animation.create_move(duration, total_distance=-bounce_distance, ease_in='quadratic', delay=self.bounce_up.duration) self.bounce_down = Animation.create_move(duration, total_distance=-bounce_distance, ease_in='quadratic', delay=self.bounce_up.duration)
self.bounce_down.start()
self.overlay_move = Animation.create_move(duration*3, total_distance=20, reverse_delay=0, ease_in='quadratic', ease_out='quadratic', delay=self.bounce_up.duration+self.bounce_down.duration) self.overlay_move = Animation.create_move(duration*3, total_distance=20, reverse_delay=0, ease_in='quadratic', ease_out='quadratic', delay=self.bounce_up.duration+self.bounce_down.duration)
self.overlay_move.start()
self.overlay_move_2 = Animation.create_move(duration*3, total_distance=20, reverse_delay=0, ease_in='quadratic', ease_out='quadratic', delay=self.bounce_up.duration+self.bounce_down.duration+self.overlay_move.duration) self.overlay_move_2 = Animation.create_move(duration*3, total_distance=20, reverse_delay=0, ease_in='quadratic', ease_out='quadratic', delay=self.bounce_up.duration+self.bounce_down.duration+self.overlay_move.duration)
self.overlay_move_2.start()
def update(self, current_time_ms: float, is_clear: bool): def update(self, current_time_ms: float, is_clear: bool):
super().update(current_time_ms, is_clear) super().update(current_time_ms, is_clear)
@@ -157,6 +165,7 @@ class DonBG4(DonBGBase):
def __init__(self, index: int, screen_width: int, screen_height: int, player_num: int): def __init__(self, index: int, screen_width: int, screen_height: int, player_num: int):
super().__init__(index, screen_width, screen_height, player_num) super().__init__(index, screen_width, screen_height, player_num)
self.overlay_move = Animation.create_move(1500, start_position=0, total_distance=20, reverse_delay=0) self.overlay_move = Animation.create_move(1500, start_position=0, total_distance=20, reverse_delay=0)
self.overlay_move.start()
def update(self, current_time_ms: float, is_clear: bool): def update(self, current_time_ms: float, is_clear: bool):
super().update(current_time_ms, is_clear) super().update(current_time_ms, is_clear)
self.overlay_move.update(current_time_ms) self.overlay_move.update(current_time_ms)
@@ -182,8 +191,11 @@ class DonBG5(DonBGBase):
duration = 266 duration = 266
bounce_distance = 40 bounce_distance = 40
self.bounce_up = Animation.create_move(duration, total_distance=-bounce_distance, ease_out='quadratic') self.bounce_up = Animation.create_move(duration, total_distance=-bounce_distance, ease_out='quadratic')
self.bounce_up.start()
self.bounce_down = Animation.create_move(duration, total_distance=-bounce_distance, ease_in='quadratic', delay=self.bounce_up.duration) self.bounce_down = Animation.create_move(duration, total_distance=-bounce_distance, ease_in='quadratic', delay=self.bounce_up.duration)
self.bounce_down.start()
self.adjust = Animation.create_move(1000, total_distance=10, reverse_delay=0, delay=self.bounce_up.duration+self.bounce_down.duration) self.adjust = Animation.create_move(1000, total_distance=10, reverse_delay=0, delay=self.bounce_up.duration+self.bounce_down.duration)
self.adjust.start()
def update(self, current_time_ms: float, is_clear: bool): def update(self, current_time_ms: float, is_clear: bool):
super().update(current_time_ms, is_clear) super().update(current_time_ms, is_clear)
@@ -212,6 +224,7 @@ class DonBG6(DonBGBase):
def __init__(self, index: int, screen_width: int, screen_height: int, player_num: int): def __init__(self, index: int, screen_width: int, screen_height: int, player_num: int):
super().__init__(index, screen_width, screen_height, player_num) super().__init__(index, screen_width, screen_height, player_num)
self.overlay_move = Animation.create_move(1000, start_position=0, total_distance=20, reverse_delay=0) self.overlay_move = Animation.create_move(1000, start_position=0, total_distance=20, reverse_delay=0)
self.overlay_move.start()
def update(self, current_time_ms: float, is_clear: bool): def update(self, current_time_ms: float, is_clear: bool):
super().update(current_time_ms, is_clear) super().update(current_time_ms, is_clear)
self.overlay_move.update(current_time_ms) self.overlay_move.update(current_time_ms)
@@ -260,6 +273,7 @@ class BGNormal1(BGNormalBase):
def __init__(self, index: int, screen_width: int, screen_height: int): def __init__(self, index: int, screen_width: int, screen_height: int):
super().__init__(index, screen_width, screen_height) 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) self.flicker = Animation.create_fade(16.67*4, initial_opacity=0.5, final_opacity=0.4, reverse_delay=0)
self.flicker.start()
def update(self, current_time_ms: float): def update(self, current_time_ms: float):
self.flicker.update(current_time_ms) self.flicker.update(current_time_ms)
if self.flicker.is_finished: if self.flicker.is_finished:
@@ -272,6 +286,7 @@ class BGNormal2(BGNormalBase):
def __init__(self, index: int, screen_width: int, screen_height: int): def __init__(self, index: int, screen_width: int, screen_height: int):
super().__init__(index, screen_width, screen_height) 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) self.flicker = Animation.create_fade(16.67*4, initial_opacity=0.5, final_opacity=0.4, reverse_delay=0)
self.flicker.start()
def update(self, current_time_ms: float): def update(self, current_time_ms: float):
self.flicker.update(current_time_ms) self.flicker.update(current_time_ms)
if self.flicker.is_finished: if self.flicker.is_finished:
@@ -284,6 +299,7 @@ class BGNormal3(BGNormalBase):
def __init__(self, index: int, screen_width: int, screen_height: int): def __init__(self, index: int, screen_width: int, screen_height: int):
super().__init__(index, screen_width, screen_height) 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) self.flicker = Animation.create_fade(16.67*10, initial_opacity=0.5, final_opacity=0.4, reverse_delay=0)
self.flicker.start()
def update(self, current_time_ms): def update(self, current_time_ms):
self.flicker.update(current_time_ms) self.flicker.update(current_time_ms)
if self.flicker.is_finished: if self.flicker.is_finished:
@@ -325,7 +341,9 @@ class BGNormal4(BGNormalBase):
self.spawn_point = self.random_excluding_range() self.spawn_point = self.random_excluding_range()
duration = random.randint(1400, 2000) duration = random.randint(1400, 2000)
self.move_y = Animation.create_move(duration, total_distance=360) self.move_y = Animation.create_move(duration, total_distance=360)
self.move_y.start()
self.move_x = Animation.create_move(duration, total_distance=random.randint(-300, 300)) self.move_x = Animation.create_move(duration, total_distance=random.randint(-300, 300))
self.move_x.start()
def random_excluding_range(self): def random_excluding_range(self):
while True: while True:
num = random.randint(0, 1280) num = random.randint(0, 1280)
@@ -339,9 +357,12 @@ class BGNormal4(BGNormalBase):
def __init__(self, index: int, screen_width: int, screen_height: int): def __init__(self, index: int, screen_width: int, screen_height: int):
super().__init__(index, screen_width, screen_height) 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.flicker = Animation.create_fade(16.67*3, initial_opacity=0.5, final_opacity=0.4, reverse_delay=0)
self.flicker.start()
self.turtle_move = Animation.create_move(3333*2, start_position=screen_width+112, total_distance=-(screen_width+(112*4))) self.turtle_move = Animation.create_move(3333*2, start_position=screen_width+112, total_distance=-(screen_width+(112*4)))
self.turtle_move.start()
textures = ((0, 100, 3), (100, 200, 4), (200, 300, 5), (300, 400, 6), (400, 500, 7), (500, 600, 8)) 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.turtle_change = Animation.create_texture_change(600, textures=textures)
self.turtle_change.start()
self.petals = {self.Petal(), self.Petal(), self.Petal(), self.Petal(), self.Petal()} self.petals = {self.Petal(), self.Petal(), self.Petal(), self.Petal(), self.Petal()}
def update(self, current_time_ms: float): def update(self, current_time_ms: float):
self.flicker.update(current_time_ms) self.flicker.update(current_time_ms)
@@ -372,6 +393,7 @@ class BGNormal5(BGNormalBase):
def __init__(self, index: int, screen_width: int, screen_height: int): def __init__(self, index: int, screen_width: int, screen_height: int):
super().__init__(index, screen_width, screen_height) 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) self.flicker = Animation.create_fade(16.67*10, initial_opacity=0.75, final_opacity=0.4, reverse_delay=0)
self.flicker.start()
def update(self, current_time_ms: float): def update(self, current_time_ms: float):
self.flicker.update(current_time_ms) self.flicker.update(current_time_ms)
if self.flicker.is_finished: if self.flicker.is_finished:
@@ -434,13 +456,17 @@ class BGFever4(BGFeverBase):
def __init__(self, index: int, screen_width: int, screen_height: int): def __init__(self, index: int, screen_width: int, screen_height: int):
super().__init__(index, screen_width, screen_height) super().__init__(index, screen_width, screen_height)
self.vertical_move = Animation.create_move(1300, start_position=0, total_distance=50, reverse_delay=0) self.vertical_move = Animation.create_move(1300, start_position=0, total_distance=50, reverse_delay=0)
self.vertical_move.start()
self.horizontal_move = Animation.create_move(5000, start_position=0, total_distance=self.textures[self.name][2].width) self.horizontal_move = Animation.create_move(5000, start_position=0, total_distance=self.textures[self.name][2].width)
self.horizontal_move.start()
self.bg_texture_move_down = None self.bg_texture_move_down = None
self.bg_texture_move_up = None self.bg_texture_move_up = None
def start(self): def start(self):
self.bg_texture_move_down = Animation.create_move(516, total_distance=400, ease_in='cubic') self.bg_texture_move_down = Animation.create_move(516, total_distance=400, ease_in='cubic')
self.bg_texture_move_down.start()
self.bg_texture_move_up = Animation.create_move(200, total_distance=40, delay=self.bg_texture_move_down.duration, ease_out='quadratic') self.bg_texture_move_up = Animation.create_move(200, total_distance=40, delay=self.bg_texture_move_down.duration, ease_out='quadratic')
self.bg_texture_move_up.start()
def update(self, current_time_ms: float): def update(self, current_time_ms: float):
if self.bg_texture_move_down is not None: if self.bg_texture_move_down is not None:

View File

@@ -1,10 +1,130 @@
import json
import os
import tempfile
import zipfile
from pathlib import Path from pathlib import Path
from typing import Union
import pyray as ray import pyray as ray
from libs.animation import BaseAnimation, parse_animations
class Texture:
def __init__(self, name: str, texture: Union[ray.Texture, list[ray.Texture]], init_vals: dict[str, int]):
self.name = name
self.texture = texture
self.init_vals = init_vals
if isinstance(self.texture, list):
self.width = self.texture[0].width
self.height = self.texture[0].height
else:
self.width = self.texture.width
self.height = self.texture.height
self.is_frames = isinstance(self.texture, list)
self.x = 0
self.y = 0
self.x2 = self.width
self.y2 = self.height
class TextureWrapper: class TextureWrapper:
def __init__(self): def __init__(self):
pass self.textures: dict[str, dict[str, Texture]] = dict()
def load_texture(self, texture: Path) -> ray.Texture: self.animations: dict[int, BaseAnimation] = dict()
return ray.load_texture(str(texture)) self.graphics_path = Path("Graphics")
def unload_textures(self):
for zip in self.textures:
for file in self.textures[zip]:
tex_object = self.textures[zip][file]
if isinstance(tex_object.texture, list):
for texture in tex_object.texture:
ray.unload_texture(texture)
else:
ray.unload_texture(tex_object.texture)
def get_animation(self, index: int):
return self.animations[index]
def update_attr(self, subset: str, texture: str, attr: str, value: float | int):
tex_object = self.textures[subset][texture]
if hasattr(tex_object, attr):
setattr(tex_object, attr, tex_object.init_vals[attr] + value)
def _read_tex_obj_data(self, tex_mapping: dict, tex_object: Texture):
tex_object.x = tex_mapping.get("x", 0)
tex_object.y = tex_mapping.get("y", 0)
tex_object.x2 = tex_mapping.get("x2", tex_object.width)
tex_object.y2 = tex_mapping.get("y2", tex_object.height)
def load_screen_textures(self, screen_name: str) -> None:
self.unload_textures()
screen_path = self.graphics_path / screen_name
if (screen_path / 'animation.json').exists():
with open(screen_path / 'animation.json') as json_file:
self.animations = parse_animations(json.loads(json_file.read()))
for zip in screen_path.iterdir():
if zip.is_dir() or zip.suffix != ".zip":
continue
with zipfile.ZipFile(zip, 'r') as zip_ref:
if 'texture.json' not in zip_ref.namelist():
raise Exception(f"texture.json file missing from {zip}")
with zip_ref.open('texture.json') as json_file:
tex_mapping_data = json.loads(json_file.read().decode('utf-8'))
self.textures[zip.stem] = dict()
for tex_name in tex_mapping_data:
if f"{tex_name}/" in zip_ref.namelist():
tex_mapping = tex_mapping_data[tex_name]
with tempfile.TemporaryDirectory() as temp_dir:
zip_ref.extractall(temp_dir, members=[name for name in zip_ref.namelist()
if name.startswith(tex_name)])
extracted_path = Path(temp_dir) / tex_name
if extracted_path.is_dir():
frames = [ray.load_texture(str(frame)) for frame in sorted(extracted_path.iterdir(),
key=lambda x: int(x.stem)) if frame.is_file()]
else:
frames = [ray.load_texture(str(extracted_path))]
self.textures[zip.stem][tex_name] = Texture(tex_name, frames, tex_mapping)
self._read_tex_obj_data(tex_mapping, self.textures[zip.stem][tex_name])
elif f"{tex_name}.png" in zip_ref.namelist():
tex_mapping = tex_mapping_data[tex_name]
png_filename = f"{tex_name}.png"
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as temp_file:
temp_file.write(zip_ref.read(png_filename))
temp_path = temp_file.name
try:
tex = ray.load_texture(temp_path)
self.textures[zip.stem][tex_name] = Texture(tex_name, tex, tex_mapping)
self._read_tex_obj_data(tex_mapping, self.textures[zip.stem][tex_name])
finally:
os.unlink(temp_path)
else:
raise Exception(f"Texture {tex_name} was not found in {zip}")
def draw_texture(self, subset: str, texture: str, color: ray.Color=ray.WHITE, frame: int = 0, scale: float = 1.0, center: bool = False, mirror: str = '', x: int | float = 0, y: int | float = 0, x2: int | float = 0, y2: int | float = 0) -> None:
mirror_x = -1 if mirror == 'horizontal' else 1
mirror_y = -1 if mirror == 'vertical' else 1
tex_object = self.textures[subset][texture]
source_rect = ray.Rectangle(0, 0, tex_object.width * mirror_x, tex_object.height * mirror_y)
if center:
dest_rect = ray.Rectangle(tex_object.x + (tex_object.width//2) - ((tex_object.width * scale)//2) + x, tex_object.y + (tex_object.height//2) - ((tex_object.height * scale)//2) + y, tex_object.x2*scale + x2, tex_object.y2*scale + y2)
else:
dest_rect = ray.Rectangle(tex_object.x + x, tex_object.y + y, tex_object.x2*scale + x2, tex_object.y2*scale + y2)
if tex_object.is_frames:
if not isinstance(tex_object.texture, list):
raise Exception("Texture was marked as multiframe but is only 1 texture")
ray.draw_texture_pro(tex_object.texture[frame], source_rect, dest_rect, ray.Vector2(0, 0), 0, color)
else:
if isinstance(tex_object.texture, list):
raise Exception("Texture is multiframe but was called as 1 texture")
ray.draw_texture_pro(tex_object.texture, source_rect, dest_rect, ray.Vector2(0, 0), 0, color)
tex = TextureWrapper()

125
libs/transition.py Normal file
View File

@@ -0,0 +1,125 @@
import pyray as ray
from libs.animation import Animation
from libs.utils import OutlinedText, global_data
class Transition:
def __init__(self, title: str, subtitle: str) -> None:
self.is_finished = False
self.rainbow_up = global_data.tex.get_animation(0)
self.mini_up = global_data.tex.get_animation(1)
self.chara_down = global_data.tex.get_animation(2)
self.song_info_fade = global_data.tex.get_animation(3)
self.song_info_fade_out = global_data.tex.get_animation(4)
self.title = OutlinedText(title, 40, ray.WHITE, ray.BLACK, outline_thickness=5)
self.subtitle = OutlinedText(subtitle, 30, ray.WHITE, ray.BLACK, outline_thickness=5)
self.is_second = False
def start(self):
self.rainbow_up.start()
self.mini_up.start()
self.chara_down.start()
self.song_info_fade.start()
self.song_info_fade_out.start()
def update(self, current_time_ms: float):
self.rainbow_up.update(current_time_ms)
self.chara_down.update(current_time_ms)
self.mini_up.update(current_time_ms)
self.song_info_fade.update(current_time_ms)
self.song_info_fade_out.update(current_time_ms)
self.is_finished = self.song_info_fade.is_finished
def draw_song_info(self):
color_1 = ray.fade(ray.WHITE, self.song_info_fade.attribute)
color_2 = ray.fade(ray.WHITE, min(0.70, self.song_info_fade.attribute))
offset = 0
if self.is_second:
color_1 = ray.fade(ray.WHITE, self.song_info_fade_out.attribute)
color_2 = ray.fade(ray.WHITE, min(0.70, self.song_info_fade_out.attribute))
offset = 816 - self.rainbow_up.attribute
global_data.tex.draw_texture('rainbow_transition', 'text_bg', y=-self.rainbow_up.attribute - offset, color=color_2)
texture = self.title.texture
y = 1176 - texture.height//2 - int(self.rainbow_up.attribute) - offset
dest = ray.Rectangle(1280//2 - texture.width//2, y - 20, texture.width, texture.height)
self.title.draw(self.title.default_src, dest, ray.Vector2(0, 0), 0, color_1)
texture = self.subtitle.texture
dest = ray.Rectangle(1280//2 - texture.width//2, y + 30, texture.width, texture.height)
self.subtitle.draw(self.subtitle.default_src, dest, ray.Vector2(0, 0), 0, color_1)
def draw(self):
total_offset = 0
if self.is_second:
total_offset = 816
global_data.tex.draw_texture('rainbow_transition', 'rainbow_bg_bottom', y=-self.rainbow_up.attribute - total_offset)
global_data.tex.draw_texture('rainbow_transition', 'rainbow_bg_top', y=-self.rainbow_up.attribute - total_offset)
global_data.tex.draw_texture('rainbow_transition', 'rainbow_bg', y=-self.rainbow_up.attribute - total_offset)
offset = self.chara_down.attribute
chara_offset = 0
if self.is_second:
offset = self.chara_down.attribute - self.mini_up.attribute//3
chara_offset = 408
global_data.tex.draw_texture('rainbow_transition', 'chara_left', x=-self.mini_up.attribute//2 - chara_offset, y=-self.mini_up.attribute + offset - total_offset)
global_data.tex.draw_texture('rainbow_transition', 'chara_right', x=self.mini_up.attribute//2 + chara_offset, y=-self.mini_up.attribute + offset - total_offset)
global_data.tex.draw_texture('rainbow_transition', 'chara_center', y=-self.rainbow_up.attribute + offset - total_offset)
self.draw_song_info()
class Transition2:
def __init__(self, screen_height: int, title: str, subtitle: str) -> None:
duration = 266
self.is_finished = False
self.rainbow_up = Animation.create_move(duration, start_position=0, total_distance=screen_height + global_data.textures['scene_change_rainbow'][2].height, ease_in='cubic')
self.rainbow_up.start()
self.chara_down = None
self.title = OutlinedText(title, 40, ray.WHITE, ray.BLACK, outline_thickness=5)
self.subtitle = OutlinedText(subtitle, 30, ray.WHITE, ray.BLACK, outline_thickness=5)
self.song_info_fade = Animation.create_fade(duration/2)
self.song_info_fade.start()
def update(self, current_time_ms: float):
self.rainbow_up.update(current_time_ms)
self.song_info_fade.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)
self.chara_down.start()
if self.chara_down is not None:
self.chara_down.update(current_time_ms)
self.is_finished = self.chara_down.is_finished
def draw_song_info(self):
texture = global_data.textures['scene_change_rainbow'][6]
y = 720//2 - texture.height
src = ray.Rectangle(0, 0, texture.width, texture.height)
dest = ray.Rectangle(1280//2 - (texture.width*3)//2, y, texture.width*3, texture.height*2)
ray.draw_texture_pro(texture, src, dest, ray.Vector2(0, 0), 0, ray.fade(ray.WHITE, min(0.70, self.song_info_fade.attribute)))
texture = self.title.texture
y = 720//2 - texture.height//2 - 20
src = ray.Rectangle(0, 0, texture.width, texture.height)
dest = ray.Rectangle(1280//2 - texture.width//2, y, texture.width, texture.height)
self.title.draw(src, dest, ray.Vector2(0, 0), 0, ray.fade(ray.WHITE, self.song_info_fade.attribute))
texture = self.subtitle.texture
src = ray.Rectangle(0, 0, texture.width, texture.height)
dest = ray.Rectangle(1280//2 - texture.width//2, y + 50, texture.width, texture.height)
self.subtitle.draw(src, dest, ray.Vector2(0, 0), 0, ray.fade(ray.WHITE, self.song_info_fade.attribute))
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(global_data.textures['scene_change_rainbow'][4], 142, 14 -int(self.rainbow_up.attribute*3) - offset, ray.WHITE)
ray.draw_texture(global_data.textures['scene_change_rainbow'][5], 958, 144 -int(self.rainbow_up.attribute*3) - offset, ray.WHITE)
ray.draw_texture(texture, 76, -int(self.rainbow_up.attribute*3) - offset, ray.WHITE)
self.draw_song_info()

View File

@@ -1,5 +1,8 @@
import ctypes
import hashlib
import math import math
import os import os
import sys
import tempfile import tempfile
import time import time
import zipfile import zipfile
@@ -16,7 +19,25 @@ from raylib import (
SHADER_UNIFORM_VEC4, SHADER_UNIFORM_VEC4,
) )
#TJA Format creator is unknown. I did not create the format, but I did write the parser though. from libs.texture import TextureWrapper
def force_dedicated_gpu():
"""Force Windows to use dedicated GPU for this application"""
if sys.platform == "win32":
try:
# NVIDIA Optimus
nvapi = ctypes.windll.kernel32.LoadLibraryW("nvapi64.dll")
if nvapi:
ctypes.windll.kernel32.SetEnvironmentVariableW("SHIM_MCCOMPAT", "0x800000001")
except Exception as e:
print(e)
try:
# AMD PowerXpress
ctypes.windll.kernel32.SetEnvironmentVariableW("AMD_VULKAN_ICD", "DISABLE")
except Exception as e:
print(e)
def get_zip_filenames(zip_path: Path) -> list[str]: def get_zip_filenames(zip_path: Path) -> list[str]:
result = [] result = []
@@ -230,6 +251,7 @@ def reset_session():
class GlobalData: class GlobalData:
selected_song: Path = Path() selected_song: Path = Path()
textures: dict[str, list[ray.Texture]] = field(default_factory=lambda: dict()) textures: dict[str, list[ray.Texture]] = field(default_factory=lambda: dict())
tex: TextureWrapper = field(default_factory=lambda: TextureWrapper())
songs_played: int = 0 songs_played: int = 0
config: dict = field(default_factory=lambda: dict()) config: dict = field(default_factory=lambda: dict())
song_hashes: dict[str, list[dict]] = field(default_factory=lambda: dict()) #Hash to path song_hashes: dict[str, list[dict]] = field(default_factory=lambda: dict()) #Hash to path
@@ -239,8 +261,19 @@ class GlobalData:
global_data = GlobalData() global_data = GlobalData()
text_cache = set()
if not Path('cache/image').exists():
Path('cache/image').mkdir()
for file in Path('cache/image').iterdir():
text_cache.add(file.stem)
class OutlinedText: class OutlinedText:
def __init__(self, text: str, font_size: int, color: ray.Color, outline_color: ray.Color, outline_thickness=5.0, vertical=False): def __init__(self, text: str, font_size: int, color: ray.Color, outline_color: ray.Color, outline_thickness=5.0, vertical=False):
self.hash = self._hash_text(text, font_size, color, vertical)
if self.hash in text_cache:
self.texture = ray.load_texture(f'cache/image/{self.hash}.png')
else:
self.font = self._load_font_for_text(text) self.font = self._load_font_for_text(text)
if vertical: if vertical:
self.texture = self._create_text_vertical(text, font_size, color, ray.BLANK, self.font) self.texture = self._create_text_vertical(text, font_size, color, ray.BLANK, self.font)
@@ -272,11 +305,31 @@ class OutlinedText:
ray.set_shader_value(self.shader, outline_color_loc, outline_color_alloc, SHADER_UNIFORM_VEC4) ray.set_shader_value(self.shader, outline_color_loc, outline_color_alloc, SHADER_UNIFORM_VEC4)
ray.set_shader_value(self.shader, texture_size_loc, texture_size, SHADER_UNIFORM_VEC2) ray.set_shader_value(self.shader, texture_size_loc, texture_size, SHADER_UNIFORM_VEC2)
self.default_src = ray.Rectangle(0, 0, self.texture.width, self.texture.height)
def _hash_text(self, text: str, font_size: int, color: ray.Color, vertical: bool):
n = hashlib.sha256()
n.update(text.encode('utf-8'))
n.update(str(font_size).encode('utf-8'))
if isinstance(color, tuple):
n.update(str(color[0]).encode('utf-8'))
n.update(str(color[1]).encode('utf-8'))
n.update(str(color[2]).encode('utf-8'))
n.update(str(color[3]).encode('utf-8'))
else:
n.update(str(color.r).encode('utf-8'))
n.update(str(color.g).encode('utf-8'))
n.update(str(color.b).encode('utf-8'))
n.update(str(color.a).encode('utf-8'))
n.update(str(vertical).encode('utf-8'))
return n.hexdigest()
def _load_font_for_text(self, text: str) -> ray.Font: def _load_font_for_text(self, text: str) -> ray.Font:
codepoint_count = ray.ffi.new('int *', 0) codepoint_count = ray.ffi.new('int *', 0)
unique_codepoints = set(text) unique_codepoints = set(text)
codepoints = ray.load_codepoints(''.join(unique_codepoints), codepoint_count) codepoints = ray.load_codepoints(''.join(unique_codepoints), codepoint_count)
return ray.load_font_ex(str(Path('Graphics/Modified-DFPKanteiryu-XB.ttf')), 40, codepoints, 0) font = ray.load_font_ex(str(Path('Graphics/Modified-DFPKanteiryu-XB.ttf')), 40, codepoints, 0)
return font
def _create_text_vertical(self, text: str, font_size: int, color: ray.Color, bg_color: ray.Color, font: Optional[ray.Font]=None, padding: int=10): def _create_text_vertical(self, text: str, font_size: int, color: ray.Color, bg_color: ray.Color, font: Optional[ray.Font]=None, padding: int=10):
rotate_chars = {'-', '', '|', '/', '\\', '', '', '~', '', '', '(', ')', rotate_chars = {'-', '', '|', '/', '\\', '', '', '~', '', '', '(', ')',
@@ -427,6 +480,7 @@ class OutlinedText:
ray.WHITE) ray.WHITE)
ray.unload_image(char_image) ray.unload_image(char_image)
ray.export_image(image, f'cache/image/{self.hash}.png')
texture = ray.load_texture_from_image(image) texture = ray.load_texture_from_image(image)
ray.unload_image(image) ray.unload_image(image)
return texture return texture
@@ -452,6 +506,7 @@ class OutlinedText:
ray.WHITE) ray.WHITE)
ray.unload_image(text_image) ray.unload_image(text_image)
ray.export_image(image, f'cache/image/{self.hash}.png')
texture = ray.load_texture_from_image(image) texture = ray.load_texture_from_image(image)
ray.unload_image(image) ray.unload_image(image)
return texture return texture

View File

@@ -2,18 +2,15 @@ from pathlib import Path
import pyray as ray import pyray as ray
from libs.animation import Animation
from libs.audio import audio from libs.audio import audio
from libs.texture import tex
from libs.utils import ( from libs.utils import (
OutlinedText, OutlinedText,
draw_scaled_texture,
get_current_ms, get_current_ms,
is_l_don_pressed, is_l_don_pressed,
is_l_kat_pressed, is_l_kat_pressed,
is_r_don_pressed, is_r_don_pressed,
is_r_kat_pressed, is_r_kat_pressed,
load_all_textures_from_zip,
load_texture_from_zip,
) )
@@ -26,19 +23,9 @@ class EntryScreen:
self.width = width self.width = width
self.height = height self.height = height
self.screen_init = False self.screen_init = False
self.box_titles: list[tuple[OutlinedText, OutlinedText]] = [(OutlinedText('演奏ゲーム', 50, ray.WHITE, ray.Color(109, 68, 24, 255), outline_thickness=5, vertical=True),
OutlinedText('演奏ゲーム', 50, ray.WHITE, ray.BLACK, outline_thickness=5, vertical=True)),
(OutlinedText('ゲーム設定', 50, ray.WHITE, ray.Color(109, 68, 24, 255), outline_thickness=5, vertical=True),
OutlinedText('ゲーム設定', 50, ray.WHITE, ray.BLACK, outline_thickness=5, vertical=True))]
def load_textures(self): def load_textures(self):
self.textures = load_all_textures_from_zip(Path('Graphics/lumendata/entry.zip')) tex.load_screen_textures('entry')
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:
for texture in self.textures[group]:
ray.unload_texture(texture)
def load_sounds(self): def load_sounds(self):
sounds_dir = Path("Sounds") sounds_dir = Path("Sounds")
@@ -51,43 +38,44 @@ class EntryScreen:
self.load_textures() self.load_textures()
self.load_sounds() self.load_sounds()
self.side = 1 self.side = 1
self.selected_box = 0 self.box_manager = BoxManager()
self.num_boxes = 2
self.state = State.SELECT_SIDE self.state = State.SELECT_SIDE
self.screen_init = True self.screen_init = True
self.drum_move_1 = None self.side_select_fade = tex.get_animation(0)
self.drum_move_2 = None self.bg_flicker = tex.get_animation(1)
self.drum_move_3 = None self.drum_move_1 = tex.get_animation(2)
self.cloud_resize = None self.drum_move_2 = tex.get_animation(3)
self.cloud_texture_change = None self.drum_move_3 = tex.get_animation(4)
self.cloud_fade = None self.cloud_resize = tex.get_animation(5)
self.fade_out = None self.cloud_resize_loop = tex.get_animation(6)
self.cloud_resize_loop = Animation.create_texture_resize(200, initial_size=1.0, final_size=1.1, reverse_delay=200) self.cloud_texture_change = tex.get_animation(7)
self.side_select_fade = Animation.create_fade(100, initial_opacity=0.0, final_opacity=1.0) self.cloud_fade = tex.get_animation(8)
self.bg_flicker = Animation.create_fade(500, initial_opacity=0.5, final_opacity=0.4, reverse_delay=0) self.cloud_resize_loop.start()
self.side_select_fade.start()
self.bg_flicker.start()
audio.play_sound(self.bgm) audio.play_sound(self.bgm)
def on_screen_end(self, next_screen: str): def on_screen_end(self, next_screen: str):
self.screen_init = False self.screen_init = False
self.unload_textures()
audio.stop_sound(self.bgm) audio.stop_sound(self.bgm)
tex.unload_textures()
audio.unload_all_sounds()
return next_screen return next_screen
def handle_input(self): def handle_input(self):
if self.fade_out is not None: if self.box_manager.is_box_selected():
return return
if self.state == State.SELECT_SIDE: if self.state == State.SELECT_SIDE:
if is_l_don_pressed() or is_r_don_pressed(): if is_l_don_pressed() or is_r_don_pressed():
if self.side == 1: if self.side == 1:
return self.on_screen_end("TITLE") return self.on_screen_end("TITLE")
self.drum_move_1 = Animation.create_move(350, total_distance=-295, ease_out='quadratic') self.drum_move_1.start()
self.drum_move_2 = Animation.create_move(200, total_distance=50, delay=self.drum_move_1.duration, ease_in='quadratic') self.drum_move_2.start()
self.drum_move_3 = Animation.create_move(350, total_distance=-170, delay=self.drum_move_1.duration+self.drum_move_2.duration, ease_out='quadratic') self.drum_move_3.start()
self.cloud_resize = Animation.create_texture_resize(350, initial_size=0.75, final_size=1.0) self.cloud_resize.start()
self.cloud_resize_loop = Animation.create_texture_resize(200, initial_size=1.0, final_size=1.2, reverse_delay=200, delay=self.cloud_resize.duration) self.cloud_resize_loop.start()
textures = ((0, 83.35, 45), (83.35, 166.7, 48), (166.7, 250, 49), (250, 333, 50)) self.cloud_texture_change.start()
self.cloud_texture_change = Animation.create_texture_change(333, textures=textures, delay=self.drum_move_1.duration+self.drum_move_2.duration+self.drum_move_3.duration) self.cloud_fade.start()
self.cloud_fade = Animation.create_fade(83.35, delay=self.drum_move_1.duration+self.drum_move_2.duration+self.drum_move_3.duration+self.cloud_texture_change.duration)
self.state = State.SELECT_MODE self.state = State.SELECT_MODE
audio.play_sound(self.sound_don) audio.play_sound(self.sound_don)
if is_l_kat_pressed(): if is_l_kat_pressed():
@@ -99,13 +87,13 @@ class EntryScreen:
elif self.state == State.SELECT_MODE: elif self.state == State.SELECT_MODE:
if is_l_don_pressed() or is_r_don_pressed(): if is_l_don_pressed() or is_r_don_pressed():
audio.play_sound(self.sound_don) audio.play_sound(self.sound_don)
self.fade_out = Animation.create_fade(160) self.box_manager.select_box()
if is_l_kat_pressed(): if is_l_kat_pressed():
audio.play_sound(self.sound_kat) audio.play_sound(self.sound_kat)
self.selected_box = max(0, self.selected_box - 1) self.box_manager.move_left()
if is_r_kat_pressed(): if is_r_kat_pressed():
audio.play_sound(self.sound_kat) audio.play_sound(self.sound_kat)
self.selected_box = min(self.num_boxes - 1, self.selected_box + 1) self.box_manager.move_right()
def update(self): def update(self):
self.on_screen_start() self.on_screen_start()
@@ -113,197 +101,255 @@ class EntryScreen:
self.bg_flicker.update(get_current_ms()) self.bg_flicker.update(get_current_ms())
if self.bg_flicker.is_finished: if self.bg_flicker.is_finished:
self.bg_flicker.restart() self.bg_flicker.restart()
if self.drum_move_1 is not None:
self.drum_move_1.update(get_current_ms()) self.drum_move_1.update(get_current_ms())
if self.drum_move_2 is not None:
self.drum_move_2.update(get_current_ms()) self.drum_move_2.update(get_current_ms())
if self.drum_move_3 is not None:
self.drum_move_3.update(get_current_ms()) self.drum_move_3.update(get_current_ms())
if self.cloud_resize is not None:
self.cloud_resize.update(get_current_ms()) self.cloud_resize.update(get_current_ms())
if self.cloud_texture_change is not None:
self.cloud_texture_change.update(get_current_ms()) self.cloud_texture_change.update(get_current_ms())
if self.cloud_fade is not None:
self.cloud_fade.update(get_current_ms()) self.cloud_fade.update(get_current_ms())
self.cloud_resize_loop.update(get_current_ms()) self.cloud_resize_loop.update(get_current_ms())
if self.cloud_resize_loop.is_finished: 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) self.cloud_resize_loop.restart()
if self.fade_out is not None: self.box_manager.update(get_current_ms())
self.fade_out.update(get_current_ms()) if self.box_manager.is_finished():
if self.fade_out.is_finished: return self.on_screen_end(self.box_manager.selected_box())
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() return self.handle_input()
def draw_background(self): def draw_background(self):
bg_texture = self.textures['entry'][368] tex.draw_texture('background', 'bg')
src = ray.Rectangle(0, 0, bg_texture.width, bg_texture.height) tex.draw_texture('background', 'tower')
dest = ray.Rectangle(0, 0, self.width, bg_texture.height) tex.draw_texture('background', 'shops_center')
ray.draw_texture_pro(bg_texture, src, dest, ray.Vector2(0, 0), 0, ray.WHITE) tex.draw_texture('background', 'people')
tex.draw_texture('background', 'shops_left')
ray.draw_texture(self.textures['entry'][369], (self.width // 2) - (self.textures['entry'][369].width // 2), (self.height // 2) - self.textures['entry'][369].height, ray.WHITE) tex.draw_texture('background', 'shops_right')
ray.draw_texture(self.textures['entry'][370], 0, (self.height // 2) - (self.textures['entry'][370].height // 2), ray.WHITE) tex.draw_texture('background', 'lights', scale=2.0, color=ray.fade(ray.WHITE, self.bg_flicker.attribute))
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, self.bg_flicker.attribute))
def draw_footer(self): def draw_footer(self):
ray.draw_texture(self.textures['entry'][375], 1, self.height - self.textures['entry'][375].height + 7, ray.WHITE) tex.draw_texture('side_select', 'footer')
if self.state == State.SELECT_SIDE or self.side != 0: if self.state == State.SELECT_SIDE or self.side != 0:
ray.draw_texture(self.textures['entry'][376], 1, self.height - self.textures['entry'][376].height + 1, ray.WHITE) tex.draw_texture('side_select', 'footer_left')
if self.state == State.SELECT_SIDE or self.side != 2: 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) tex.draw_texture('side_select', 'footer_right')
def draw_side_select(self, fade): def draw_side_select(self, fade):
color = ray.fade(ray.WHITE, fade) color = ray.fade(ray.WHITE, fade)
left_x, top_y, right_x, bottom_y = 238, 108, 979, 520 tex.draw_texture('side_select', 'box_top_left', color=color)
ray.draw_texture(self.textures['entry'][205], left_x, top_y, color) tex.draw_texture('side_select', 'box_top_right', color=color)
ray.draw_texture(self.textures['entry'][208], right_x, top_y, color) tex.draw_texture('side_select', 'box_bottom_left', color=color)
ray.draw_texture(self.textures['entry'][204], left_x, bottom_y, color) tex.draw_texture('side_select', 'box_bottom_right', color=color)
ray.draw_texture(self.textures['entry'][207], right_x, bottom_y, color)
texture = self.textures['entry'][209] tex.draw_texture('side_select', 'box_top', color=color)
src = ray.Rectangle(0, 0, texture.width, texture.height) tex.draw_texture('side_select', 'box_bottom', color=color)
dest = ray.Rectangle(left_x + self.textures['entry'][205].width, top_y, right_x - left_x - (self.textures['entry'][205].width), texture.height) tex.draw_texture('side_select', 'box_left', color=color)
ray.draw_texture_pro(texture, src, dest, ray.Vector2(0, 0), 0, color) tex.draw_texture('side_select', 'box_right', color=color)
texture = self.textures['entry'][210] tex.draw_texture('side_select', 'box_center', color=color)
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, color)
texture = self.textures['entry'][203] tex.draw_texture('side_select', 'question', color=color)
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, 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, color)
texture = self.textures['entry'][202] tex.draw_texture('side_select', '1P', color=color)
src = ray.Rectangle(0, 0, texture.width, texture.height) tex.draw_texture('side_select', 'cancel', color=color)
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)) tex.draw_texture('side_select', '2P', color=color)
ray.draw_texture_pro(texture, src, dest, ray.Vector2(0, 0), 0, color)
ray.draw_texture(self.textures['entry'][226], 384, 144, color)
cursor_x = 261
cursor_texture = self.textures['entry'][230]
flip = 1
if self.side == 0: if self.side == 0:
texture = self.textures['entry'][229] tex.draw_texture('side_select', '1P_highlight', color=color)
flip = -1 tex.textures['side_select']['1P2P_outline'].x = 261
tex.draw_texture('side_select', '1P2P_outline', color=color, mirror='horizontal')
elif self.side == 1:
tex.draw_texture('side_select', 'cancel_highlight', color=color)
tex.draw_texture('side_select', 'cancel_outline', color=color)
else: else:
texture = self.textures['entry'][232] tex.draw_texture('side_select', '2P_highlight', color=color)
ray.draw_texture(texture, 261, 400, color) tex.textures['side_select']['1P2P_outline'].x = 762
tex.draw_texture('side_select', '1P2P_outline', color=color)
if self.side == 1: tex.draw_texture('side_select', 'cancel_text', color=color)
texture = self.textures['entry'][76]
cursor_texture = self.textures['entry'][77]
cursor_x = 512
else:
texture = self.textures['entry'][228]
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, 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, color)
def draw_player_drum(self): def draw_player_drum(self):
move_x = 0 move_x = self.drum_move_3.attribute
if self.drum_move_3 is not None: move_y = self.drum_move_1.attribute + self.drum_move_2.attribute
move_x = int(self.drum_move_3.attribute) tex.update_attr('side_select', 'red_drum', 'x', move_x)
tex.update_attr('side_select', 'red_drum', 'y', move_y)
tex.update_attr('side_select', 'blue_drum', 'y', move_y)
if self.side == 0: if self.side == 0:
drum_texture = self.textures['entry'][366] tex.draw_texture('side_select', 'red_drum')
x = 160
else: else:
drum_texture = self.textures['entry'][367] move_x *= -1
x = 780 tex.textures['side_select']['cloud'].init_vals['x'] = tex.textures['side_select']['blue_drum'].init_vals['x']
move_x = move_x * -1 tex.update_attr('side_select', 'blue_drum', 'x', move_x)
move_y = 0 tex.draw_texture('side_select', 'blue_drum')
if self.drum_move_1 is not None:
move_y = int(self.drum_move_1.attribute)
if self.drum_move_2 is not None:
move_y += int(self.drum_move_2.attribute)
ray.draw_texture(drum_texture, x + move_x, 720 + move_y, ray.WHITE)
if self.cloud_resize is not None and not self.cloud_resize.is_finished:
scale = self.cloud_resize.attribute scale = self.cloud_resize.attribute
else: if self.cloud_resize.is_finished:
scale = max(1, self.cloud_resize_loop.attribute) scale = max(1, self.cloud_resize_loop.attribute)
texture_index = 45
if self.cloud_texture_change is not None and self.cloud_texture_change.attribute != 0:
texture_index = self.cloud_texture_change.attribute
color = ray.fade(ray.WHITE, 1.0)
if self.cloud_fade is not None:
color = ray.fade(ray.WHITE, self.cloud_fade.attribute) 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) tex.update_attr('side_select', 'cloud', 'x', move_x)
tex.update_attr('side_select', 'cloud', 'y', move_y)
tex.draw_texture('side_select', 'cloud', frame=self.cloud_texture_change.attribute, color=color, scale=scale, center=True)
def draw_mode_select(self, fade): def draw_mode_select(self):
self.draw_player_drum() self.draw_player_drum()
color = ray.fade(ray.WHITE, fade) if not self.cloud_texture_change.is_finished:
if self.cloud_fade is not None and self.cloud_fade.is_finished: return
box_width = self.textures['entry'][262].width self.box_manager.draw()
spacing = 80
push_distance = 50
total_width = self.num_boxes * box_width + (self.num_boxes - 1) * spacing
start_x = self.width//2 - total_width//2
y = self.height//2 - (self.textures['entry'][262].height//2) - 15
for i in range(self.num_boxes):
x_pos = start_x + i * (box_width + spacing)
push_offset = 0
if i != self.selected_box:
if i < self.selected_box:
push_offset = -push_distance
else:
push_offset = push_distance
final_x = x_pos + push_offset
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, 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, 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 + 25, y + 20, box_title.texture.width, box_title.texture.height)
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 + 20, y + 20, box_title.texture.width, box_title.texture.height)
box_title.draw(src, dest, ray.Vector2(0, 0), 0, color)
def draw(self): def draw(self):
self.draw_background() self.draw_background()
if self.state == State.SELECT_SIDE: if self.state == State.SELECT_SIDE:
self.draw_side_select(self.side_select_fade.attribute) self.draw_side_select(self.side_select_fade.attribute)
elif self.state == State.SELECT_MODE: elif self.state == State.SELECT_MODE:
if self.fade_out is not None: self.draw_mode_select()
self.draw_mode_select(self.fade_out.attribute)
else:
self.draw_mode_select(1.0)
self.draw_footer() self.draw_footer()
ray.draw_texture(self.textures['entry'][320], 0, 0, ray.WHITE) tex.draw_texture('global', 'player_entry')
if self.fade_out is not None and self.fade_out.is_finished: if self.box_manager.is_finished():
src = ray.Rectangle(0, 0, self.texture_black.width, self.texture_black.height) ray.draw_rectangle(0, 0, self.width, self.height, ray.BLACK)
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)
def draw_3d(self): def draw_3d(self):
pass pass
class Box:
def __init__(self, text: tuple[OutlinedText, OutlinedText], location: str):
self.text, self.text_highlight = text
self.location = location
self.box_tex_obj = tex.textures['mode_select']['box']
if isinstance(self.box_tex_obj.texture, list):
raise Exception("Box texture cannot be iterable")
self.texture = self.box_tex_obj.texture
self.x = self.box_tex_obj.x
self.y = self.box_tex_obj.y
self.move = tex.get_animation(10)
self.open = tex.get_animation(11)
self.is_selected = False
self.moving_left = False
self.moving_right = False
def set_positions(self, x: int):
self.x = x
self.static_x = self.x
self.left_x = self.x
self.static_left = self.left_x
self.right_x = self.left_x + tex.textures['mode_select']['box'].width - tex.textures['mode_select']['box_highlight_right'].width
self.static_right = self.right_x
def update(self, current_time_ms: float, is_selected: bool):
self.move.update(current_time_ms)
if self.moving_left:
self.x = self.static_x - int(self.move.attribute)
elif self.moving_right:
self.x = self.static_x + int(self.move.attribute)
if self.move.is_finished:
self.moving_left = False
self.moving_right = False
self.static_x = self.x
if is_selected and not self.is_selected:
self.open.start()
self.is_selected = is_selected
if self.is_selected:
self.left_x = self.static_left - int(self.open.attribute)
self.right_x = self.static_right + int(self.open.attribute)
self.open.update(current_time_ms)
def move_left(self):
if not self.move.is_started:
self.move.start()
self.moving_left = True
def move_right(self):
if not self.move.is_started:
self.move.start()
self.moving_right = True
def _draw_highlighted(self, color):
texture_left = tex.textures['mode_select']['box_highlight_left'].texture
texture_center = tex.textures['mode_select']['box_highlight_center'].texture
texture_right = tex.textures['mode_select']['box_highlight_right'].texture
if isinstance(texture_center, list) or isinstance(texture_left, list):
raise Exception("highlight textures cannot be iterable")
center_src = ray.Rectangle(0, 0, texture_center.width, texture_center.height)
center_dest = ray.Rectangle(self.left_x + texture_left.width, self.y, self.right_x - self.left_x, texture_center.height)
ray.draw_texture_pro(texture_center, center_src, center_dest, ray.Vector2(0, 0), 0, color)
ray.draw_texture(texture_center, self.left_x, self.y, color)
ray.draw_texture(texture_left, self.left_x, self.y, color)
ray.draw_texture(texture_right, self.right_x, self.y, color)
def _draw_text(self, color):
text_x = self.x + (self.texture.width//2) - (self.text.texture.width//2)
text_y = self.y + 20
text_dest = ray.Rectangle(text_x, text_y, self.text.texture.width, self.text.texture.height)
if self.is_selected:
self.text_highlight.draw(self.text.default_src, text_dest, ray.Vector2(0, 0), 0, color)
else:
self.text.draw(self.text.default_src, text_dest, ray.Vector2(0, 0), 0, color)
def draw(self, fade: float):
color = ray.fade(ray.WHITE, fade)
ray.draw_texture(self.texture, self.x, self.y, color)
if self.is_selected and self.move.is_finished:
self._draw_highlighted(color)
self._draw_text(color)
class BoxManager:
def __init__(self):
self.box_titles: list[tuple[OutlinedText, OutlinedText]] = [
(OutlinedText('演奏ゲーム', 50, ray.WHITE, ray.Color(109, 68, 24, 255), outline_thickness=5, vertical=True),
OutlinedText('演奏ゲーム', 50, ray.WHITE, ray.BLACK, outline_thickness=5, vertical=True)),
(OutlinedText('ゲーム設定', 50, ray.WHITE, ray.Color(109, 68, 24, 255), outline_thickness=5, vertical=True),
OutlinedText('ゲーム設定', 50, ray.WHITE, ray.BLACK, outline_thickness=5, vertical=True))]
self.box_locations = ["SONG_SELECT", "SETTINGS"]
self.num_boxes = len(self.box_titles)
self.boxes = [Box(self.box_titles[i], self.box_locations[i]) for i in range(len(self.box_titles))]
self.selected_box_index = 0
self.fade_out = tex.get_animation(9)
spacing = 80
box_width = self.boxes[0].texture.width
total_width = self.num_boxes * box_width + (self.num_boxes - 1) * spacing
start_x = 640 - total_width//2
for i, box in enumerate(self.boxes):
box.set_positions(start_x + i * (box_width + spacing))
if i > 0:
box.move_right()
def select_box(self):
self.fade_out.start()
def is_box_selected(self):
return self.fade_out.is_started
def is_finished(self):
return self.fade_out.is_finished
def selected_box(self):
return self.boxes[self.selected_box_index].location
def move_left(self):
prev_selection = self.selected_box_index
if self.boxes[prev_selection].move.is_started and not self.boxes[prev_selection].move.is_finished:
return
self.selected_box_index = max(0, self.selected_box_index - 1)
if prev_selection == self.selected_box_index:
return
if self.selected_box_index != self.selected_box_index - 1:
self.boxes[self.selected_box_index+1].move_right()
self.boxes[self.selected_box_index].move_right()
def move_right(self):
prev_selection = self.selected_box_index
if self.boxes[prev_selection].move.is_started and not self.boxes[prev_selection].move.is_finished:
return
self.selected_box_index = min(self.num_boxes - 1, self.selected_box_index + 1)
if prev_selection == self.selected_box_index:
return
if self.selected_box_index != 0:
self.boxes[self.selected_box_index-1].move_left()
self.boxes[self.selected_box_index].move_left()
def update(self, current_time_ms: float):
self.fade_out.update(current_time_ms)
for i, box in enumerate(self.boxes):
is_selected = i == self.selected_box_index
box.update(current_time_ms, is_selected)
def draw(self):
for box in self.boxes:
box.draw(self.fade_out.attribute)

View File

@@ -11,6 +11,7 @@ from libs.animation import Animation
from libs.audio import audio from libs.audio import audio
from libs.backgrounds import Background from libs.backgrounds import Background
from libs.tja import Balloon, Drumroll, Note, TJAParser, calculate_base_score from libs.tja import Balloon, Drumroll, Note, TJAParser, calculate_base_score
from libs.transition import Transition
from libs.utils import ( from libs.utils import (
OutlinedText, OutlinedText,
get_current_ms, get_current_ms,
@@ -138,7 +139,9 @@ class GameScreen:
subtitle = self.tja.metadata.subtitle.get(global_data.config['general']['language'].lower(), '') subtitle = self.tja.metadata.subtitle.get(global_data.config['general']['language'].lower(), '')
else: else:
subtitle = '' subtitle = ''
self.transition = Transition(self.height, session_data.song_title, subtitle) self.transition = Transition(session_data.song_title, subtitle)
self.transition.is_second = True
self.transition.start()
def on_screen_end(self, next_screen): def on_screen_end(self, next_screen):
self.screen_init = False self.screen_init = False
@@ -236,7 +239,7 @@ class GameScreen:
if self.song_info is not None: if self.song_info is not None:
self.song_info.draw(self) self.song_info.draw(self)
if self.transition is not None: if self.transition is not None:
self.transition.draw(self.height) self.transition.draw()
if self.result_transition is not None: 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]) self.result_transition.draw(self.width, self.height, global_data.textures['shutter'][0], global_data.textures['shutter'][1])
@@ -764,9 +767,13 @@ class Judgement:
self.curr_hit_ms = str(round(ms_display, 2)) self.curr_hit_ms = str(round(ms_display, 2))
self.fade_animation_1 = Animation.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_1.start()
self.fade_animation_2 = Animation.create_fade(316 - 233.3, delay=233.3) self.fade_animation_2 = Animation.create_fade(316 - 233.3, delay=233.3)
self.fade_animation_2.start()
self.move_animation = Animation.create_move(83, total_distance=15, start_position=144) self.move_animation = Animation.create_move(83, total_distance=15, start_position=144)
self.move_animation.start()
self.texture_animation = Animation.create_texture_change(100, textures=[(33, 50, 1), (50, 83, 2), (83, 100, 3), (100, float('inf'), 4)]) self.texture_animation = Animation.create_texture_change(100, textures=[(33, 50, 1), (50, 83, 2), (83, 100, 3), (100, float('inf'), 4)])
self.texture_animation.start()
def update(self, current_ms): def update(self, current_ms):
self.fade_animation_1.update(current_ms) self.fade_animation_1.update(current_ms)
@@ -811,6 +818,7 @@ class LaneHitEffect:
self.type = type self.type = type
self.color = ray.fade(ray.WHITE, 0.5) self.color = ray.fade(ray.WHITE, 0.5)
self.fade = Animation.create_fade(150, delay=83, initial_opacity=0.5) self.fade = Animation.create_fade(150, delay=83, initial_opacity=0.5)
self.fade.start()
self.is_finished = False self.is_finished = False
def update(self, current_ms: float): def update(self, current_ms: float):
@@ -835,6 +843,7 @@ class DrumHitEffect:
self.color = ray.fade(ray.WHITE, 1) self.color = ray.fade(ray.WHITE, 1)
self.is_finished = False self.is_finished = False
self.fade = Animation.create_fade(100, delay=67) self.fade = Animation.create_fade(100, delay=67)
self.fade.start()
def update(self, current_ms: float): def update(self, current_ms: float):
self.fade.update(current_ms) self.fade.update(current_ms)
@@ -861,10 +870,15 @@ class GaugeHitEffect:
self.note_texture = note_texture self.note_texture = note_texture
self.is_big = big self.is_big = big
self.texture_change = Animation.create_texture_change(116.67, textures=[(0, 33.33, 1), (33.33, 66.66, 2), (66.66, float('inf'), 3)]) self.texture_change = Animation.create_texture_change(116.67, textures=[(0, 33.33, 1), (33.33, 66.66, 2), (66.66, float('inf'), 3)])
self.texture_change.start()
self.circle_fadein = Animation.create_fade(133, initial_opacity=0.0, final_opacity=1.0, delay=16.67) self.circle_fadein = Animation.create_fade(133, initial_opacity=0.0, final_opacity=1.0, delay=16.67)
self.circle_fadein.start()
self.resize = Animation.create_texture_resize(233, delay=self.texture_change.duration, initial_size=0.75, final_size=1.15) self.resize = Animation.create_texture_resize(233, delay=self.texture_change.duration, initial_size=0.75, final_size=1.15)
self.resize.start()
self.fade_out = Animation.create_fade(66, delay=233) self.fade_out = Animation.create_fade(66, delay=233)
self.fade_out.start()
self.test = Animation.create_fade(300, delay=116.67, initial_opacity=0.0, final_opacity=1.0) self.test = Animation.create_fade(300, delay=116.67, initial_opacity=0.0, final_opacity=1.0)
self.test.start()
self.color = ray.fade(ray.YELLOW, self.circle_fadein.attribute) self.color = ray.fade(ray.YELLOW, self.circle_fadein.attribute)
self.is_finished = False self.is_finished = False
def update(self, current_ms): def update(self, current_ms):
@@ -1000,7 +1014,9 @@ class DrumrollCounter:
self.total_duration = 1349 self.total_duration = 1349
self.drumroll_count = 0 self.drumroll_count = 0
self.fade_animation = Animation.create_fade(166, delay=self.total_duration - 166) self.fade_animation = Animation.create_fade(166, delay=self.total_duration - 166)
self.fade_animation.start()
self.stretch_animation = Animation.create_text_stretch(0) self.stretch_animation = Animation.create_text_stretch(0)
self.stretch_animation.start()
def update_count(self, 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
@@ -1008,6 +1024,7 @@ class DrumrollCounter:
if self.drumroll_count != count: if self.drumroll_count != count:
self.drumroll_count = count self.drumroll_count = count
self.stretch_animation = Animation.create_text_stretch(50) self.stretch_animation = Animation.create_text_stretch(50)
self.stretch_animation.start()
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)
@@ -1040,12 +1057,15 @@ class BalloonAnimation:
self.balloon_total = balloon_total self.balloon_total = balloon_total
self.is_popped = False self.is_popped = False
self.fade_animation = Animation.create_fade(166) self.fade_animation = Animation.create_fade(166)
self.fade_animation.start()
self.stretch_animation = Animation.create_text_stretch(0) self.stretch_animation = Animation.create_text_stretch(0)
self.stretch_animation.start()
def update_count(self, 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.create_text_stretch(50) self.stretch_animation = Animation.create_text_stretch(50)
self.stretch_animation.start()
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(balloon_count) self.update_count(balloon_count)
@@ -1084,6 +1104,7 @@ 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.create_text_stretch(0) self.stretch_animation = Animation.create_text_stretch(0)
self.stretch_animation.start()
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
@@ -1098,6 +1119,7 @@ class Combo:
if self.combo != combo: if self.combo != combo:
self.combo = combo self.combo = combo
self.stretch_animation = Animation.create_text_stretch(50) self.stretch_animation = Animation.create_text_stretch(50)
self.stretch_animation.start()
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)
@@ -1149,11 +1171,13 @@ class ScoreCounter:
def __init__(self, score: int): def __init__(self, score: int):
self.score = score self.score = score
self.stretch_animation = Animation.create_text_stretch(0) self.stretch_animation = Animation.create_text_stretch(0)
self.stretch_animation.start()
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.create_text_stretch(50) self.stretch_animation = Animation.create_text_stretch(50)
self.stretch_animation.start()
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)
@@ -1175,11 +1199,17 @@ class ScoreCounterAnimation:
def __init__(self, counter: int): def __init__(self, counter: int):
self.counter = counter self.counter = counter
self.fade_animation_1 = Animation.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.fade_animation_1.start()
self.move_animation_1 = Animation.create_move(80, total_distance=-20, start_position=175) self.move_animation_1 = Animation.create_move(80, total_distance=-20, start_position=175)
self.move_animation_1.start()
self.fade_animation_2 = Animation.create_fade(80, delay=366.74) self.fade_animation_2 = Animation.create_fade(80, delay=366.74)
self.fade_animation_2.start()
self.move_animation_2 = Animation.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_2.start()
self.move_animation_3 = Animation.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_3.start()
self.move_animation_4 = Animation.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.move_animation_4.start()
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
@@ -1236,8 +1266,11 @@ class SongInfo:
song_name, 40, ray.Color(255, 255, 255, 255), ray.Color(0, 0, 0, 255), outline_thickness=5 song_name, 40, ray.Color(255, 255, 255, 255), ray.Color(0, 0, 0, 255), outline_thickness=5
) )
self.fade_in = Animation.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_in.start()
self.fade_out = Animation.create_fade(self.FADE_DURATION, delay=self.DISPLAY_DURATION) self.fade_out = Animation.create_fade(self.FADE_DURATION, delay=self.DISPLAY_DURATION)
self.fade_out.start()
self.fade_fake = Animation.create_fade(0, delay=self.DISPLAY_DURATION*2 + self.FADE_DURATION) self.fade_fake = Animation.create_fade(0, delay=self.DISPLAY_DURATION*2 + self.FADE_DURATION)
self.fade_fake.start()
def update(self, current_ms: float): def update(self, current_ms: float):
self.fade_in.update(current_ms) self.fade_in.update(current_ms)
@@ -1256,8 +1289,11 @@ class SongInfo:
def _reset_animations(self, current_ms: float): def _reset_animations(self, current_ms: float):
self.fade_in = Animation.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_in.start()
self.fade_out = Animation.create_fade(self.FADE_DURATION, delay=self.DISPLAY_DURATION) self.fade_out = Animation.create_fade(self.FADE_DURATION, delay=self.DISPLAY_DURATION)
self.fade_out.start()
self.fade_fake = Animation.create_fade(0, delay=self.DISPLAY_DURATION*2 + self.FADE_DURATION) self.fade_fake = Animation.create_fade(0, delay=self.DISPLAY_DURATION*2 + self.FADE_DURATION)
self.fade_fake.start()
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
@@ -1273,62 +1309,10 @@ class SongInfo:
dest = ray.Rectangle(text_x, text_y, self.song_title.texture.width, self.song_title.texture.height) 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) self.song_title.draw(src, dest, ray.Vector2(0, 0), 0, self.song_name_fade)
class Transition:
def __init__(self, screen_height: int, title: str, subtitle: str) -> None:
duration = 266
self.is_finished = False
self.rainbow_up = Animation.create_move(duration, start_position=0, total_distance=screen_height + global_data.textures['scene_change_rainbow'][2].height, ease_in='cubic')
self.chara_down = None
self.title = OutlinedText(title, 40, ray.WHITE, ray.BLACK, outline_thickness=5)
self.subtitle = OutlinedText(subtitle, 30, ray.WHITE, ray.BLACK, outline_thickness=5)
self.song_info_fade = Animation.create_fade(duration/2)
def update(self, current_time_ms: float):
self.rainbow_up.update(current_time_ms)
self.song_info_fade.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_song_info(self):
texture = global_data.textures['scene_change_rainbow'][6]
y = 720//2 - texture.height
src = ray.Rectangle(0, 0, texture.width, texture.height)
dest = ray.Rectangle(1280//2 - (texture.width*3)//2, y, texture.width*3, texture.height*2)
ray.draw_texture_pro(texture, src, dest, ray.Vector2(0, 0), 0, ray.fade(ray.WHITE, min(0.70, self.song_info_fade.attribute)))
texture = self.title.texture
y = 720//2 - texture.height//2 - 20
src = ray.Rectangle(0, 0, texture.width, texture.height)
dest = ray.Rectangle(1280//2 - texture.width//2, y, texture.width, texture.height)
self.title.draw(src, dest, ray.Vector2(0, 0), 0, ray.fade(ray.WHITE, self.song_info_fade.attribute))
texture = self.subtitle.texture
src = ray.Rectangle(0, 0, texture.width, texture.height)
dest = ray.Rectangle(1280//2 - texture.width//2, y + 50, texture.width, texture.height)
self.subtitle.draw(src, dest, ray.Vector2(0, 0), 0, ray.fade(ray.WHITE, self.song_info_fade.attribute))
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(global_data.textures['scene_change_rainbow'][4], 142, 14 -int(self.rainbow_up.attribute*3) - offset, ray.WHITE)
ray.draw_texture(global_data.textures['scene_change_rainbow'][5], 958, 144 -int(self.rainbow_up.attribute*3) - offset, ray.WHITE)
ray.draw_texture(texture, 76, -int(self.rainbow_up.attribute*3) - offset, ray.WHITE)
self.draw_song_info()
class ResultTransition: class ResultTransition:
def __init__(self, screen_height: int): def __init__(self, screen_height: int):
self.move = Animation.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.move.start()
self.is_finished = False self.is_finished = False
@@ -1409,6 +1393,7 @@ class Gauge:
def add_good(self): def add_good(self):
self.gauge_update_anim = Animation.create_fade(450) self.gauge_update_anim = Animation.create_fade(450)
self.gauge_update_anim.start()
self.previous_length = int(self.gauge_length) self.previous_length = int(self.gauge_length)
self.gauge_length += (1 / self.total_notes) * (100 * (self.clear_start[self.difficulty] / self.table[self.difficulty][self.level]["clear_rate"])) self.gauge_length += (1 / self.total_notes) * (100 * (self.clear_start[self.difficulty] / self.table[self.difficulty][self.level]["clear_rate"]))
if self.gauge_length > 87: if self.gauge_length > 87:
@@ -1416,6 +1401,7 @@ class Gauge:
def add_ok(self): def add_ok(self):
self.gauge_update_anim = Animation.create_fade(450) self.gauge_update_anim = Animation.create_fade(450)
self.gauge_update_anim.start()
self.previous_length = int(self.gauge_length) self.previous_length = int(self.gauge_length)
self.gauge_length += ((1 * self.table[self.difficulty][self.level]["ok_multiplier"]) / self.total_notes) * (100 * (self.clear_start[self.difficulty] / self.table[self.difficulty][self.level]["clear_rate"])) self.gauge_length += ((1 * self.table[self.difficulty][self.level]["ok_multiplier"]) / self.total_notes) * (100 * (self.clear_start[self.difficulty] / self.table[self.difficulty][self.level]["clear_rate"]))
if self.gauge_length > 87: if self.gauge_length > 87:
@@ -1430,6 +1416,7 @@ class Gauge:
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):
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 = Animation.create_fade(450, initial_opacity=0.0, final_opacity=1.0) self.rainbow_fade_in = Animation.create_fade(450, initial_opacity=0.0, final_opacity=1.0)
self.rainbow_fade_in.start()
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)
@@ -1441,6 +1428,7 @@ class Gauge:
if self.rainbow_animation is None: if self.rainbow_animation is None:
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)]) 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)])
self.rainbow_animation.start()
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

@@ -1,11 +1,11 @@
import threading import threading
from pathlib import Path
import pyray as ray import pyray as ray
from libs.animation import Animation from libs.animation import Animation
from libs.song_hash import build_song_hashes from libs.song_hash import build_song_hashes
from libs.utils import get_current_ms, global_data, load_all_textures_from_zip from libs.texture import tex
from libs.utils import get_current_ms, global_data
from scenes.song_select import SongSelectScreen from scenes.song_select import SongSelectScreen
@@ -29,8 +29,6 @@ class LoadScreen:
self.loading_thread = None self.loading_thread = None
self.navigator_thread = None self.navigator_thread = None
self.textures = load_all_textures_from_zip(Path('Graphics/lumendata/attract/kidou.zip'))
self.fade_in = None self.fade_in = None
def _load_song_hashes(self): def _load_song_hashes(self):
@@ -53,6 +51,7 @@ class LoadScreen:
def on_screen_start(self): def on_screen_start(self):
if not self.screen_init: if not self.screen_init:
tex.load_screen_textures('loading')
self.loading_thread = threading.Thread(target=self._load_song_hashes) self.loading_thread = threading.Thread(target=self._load_song_hashes)
self.loading_thread.daemon = True self.loading_thread.daemon = True
self.loading_thread.start() self.loading_thread.start()
@@ -60,7 +59,7 @@ class LoadScreen:
def on_screen_end(self, next_screen: str): def on_screen_end(self, next_screen: str):
self.screen_init = False self.screen_init = False
tex.unload_textures()
if self.loading_thread and self.loading_thread.is_alive(): if self.loading_thread and self.loading_thread.is_alive():
self.loading_thread.join(timeout=1.0) self.loading_thread.join(timeout=1.0)
if self.navigator_thread and self.navigator_thread.is_alive(): if self.navigator_thread and self.navigator_thread.is_alive():
@@ -79,6 +78,7 @@ class LoadScreen:
if self.loading_complete and self.fade_in is None: if self.loading_complete and self.fade_in is None:
self.fade_in = Animation.create_fade(1000, initial_opacity=0.0, final_opacity=1.0, ease_in='cubic') self.fade_in = Animation.create_fade(1000, initial_opacity=0.0, final_opacity=1.0, ease_in='cubic')
self.fade_in.start()
if self.fade_in is not None: if self.fade_in is not None:
self.fade_in.update(get_current_ms()) self.fade_in.update(get_current_ms())
@@ -87,7 +87,7 @@ class LoadScreen:
def draw(self): def draw(self):
ray.draw_rectangle(0, 0, self.width, self.height, ray.BLACK) ray.draw_rectangle(0, 0, self.width, self.height, ray.BLACK)
ray.draw_texture(self.textures['kidou'][1], self.width//2 - self.textures['kidou'][1].width//2, 50, ray.WHITE) tex.draw_texture('kidou', 'warning')
# Draw progress bar background # Draw progress bar background
ray.draw_rectangle( ray.draw_rectangle(

View File

@@ -142,6 +142,7 @@ class ResultScreen:
if self.score_delay is not None: if self.score_delay is not None:
if get_current_ms() > self.score_delay and self.fade_in_bottom is None: if get_current_ms() > self.score_delay and self.fade_in_bottom is None:
self.fade_in_bottom = Animation.create_fade(333, initial_opacity=0.0, final_opacity=1.0) self.fade_in_bottom = Animation.create_fade(333, initial_opacity=0.0, final_opacity=1.0)
self.fade_in_bottom.start()
if self.gauge is not None: if self.gauge is not None:
self.state = self.gauge.state self.state = self.gauge.state
@@ -258,10 +259,15 @@ class Crown:
def __init__(self): def __init__(self):
duration = 466 duration = 466
self.resize = Animation.create_texture_resize(duration, initial_size=3.5, final_size=0.90, ease_in='quadratic') self.resize = Animation.create_texture_resize(duration, initial_size=3.5, final_size=0.90, ease_in='quadratic')
self.resize.start()
self.resize_fix = Animation.create_texture_resize(216, initial_size=self.resize.final_size, final_size=1.0, delay=self.resize.duration) self.resize_fix = Animation.create_texture_resize(216, initial_size=self.resize.final_size, final_size=1.0, delay=self.resize.duration)
self.resize_fix.start()
self.white_fadein = Animation.create_fade(133, initial_opacity=0.0, final_opacity=1.0, delay=self.resize.duration + self.resize_fix.duration, reverse_delay=0) self.white_fadein = Animation.create_fade(133, initial_opacity=0.0, final_opacity=1.0, delay=self.resize.duration + self.resize_fix.duration, reverse_delay=0)
self.white_fadein.start()
self.gleam = Animation.create_texture_change(400, textures=[(0, 200, 0), (200, 250, 127), (250, 300, 128), (300, 350, 129), (350, 400, 0)], delay=self.resize.duration + self.resize_fix.duration + self.white_fadein.duration) self.gleam = Animation.create_texture_change(400, textures=[(0, 200, 0), (200, 250, 127), (250, 300, 128), (300, 350, 129), (350, 400, 0)], delay=self.resize.duration + self.resize_fix.duration + self.white_fadein.duration)
self.gleam.start()
self.fadein = Animation.create_fade(duration, initial_opacity=0.0, final_opacity=1.0, ease_in='quadratic') self.fadein = Animation.create_fade(duration, initial_opacity=0.0, final_opacity=1.0, ease_in='quadratic')
self.fadein.start()
self.sound = audio.load_sound(Path('Sounds/result/SE_RESULT [1].ogg')) self.sound = audio.load_sound(Path('Sounds/result/SE_RESULT [1].ogg'))
self.sound_played = False self.sound_played = False
@@ -303,12 +309,16 @@ class BottomCharacters:
self.char_1_index = 339 self.char_1_index = 339
self.char_2_index = 340 self.char_2_index = 340
self.c_bounce_up = Animation.create_move(266, total_distance=40, ease_in='quadratic') self.c_bounce_up = Animation.create_move(266, total_distance=40, ease_in='quadratic')
self.c_bounce_up.start()
self.c_bounce_down = Animation.create_move(266, total_distance=40, ease_out='quadratic', delay=self.c_bounce_up.duration) self.c_bounce_down = Animation.create_move(266, total_distance=40, ease_out='quadratic', delay=self.c_bounce_up.duration)
self.c_bounce_down.start()
self.is_finished = False self.is_finished = False
def start(self): def start(self):
self.move_up = Animation.create_move(366, total_distance=380, ease_out='cubic') self.move_up = Animation.create_move(366, total_distance=380, ease_out='cubic')
self.move_up.start()
self.move_down = Animation.create_move(133, total_distance=30, ease_out='cubic', delay=self.move_up.duration-5) self.move_down = Animation.create_move(133, total_distance=30, ease_out='cubic', delay=self.move_up.duration-5)
self.move_down.start()
def update(self, state): def update(self, state):
self.state = state self.state = state
@@ -317,10 +327,14 @@ class BottomCharacters:
self.char_2_index = 346 self.char_2_index = 346
if self.bounce_up is None: if self.bounce_up is None:
self.bounce_up = Animation.create_move(266, total_distance=40, ease_in='quadratic') self.bounce_up = Animation.create_move(266, total_distance=40, ease_in='quadratic')
self.bounce_up.start()
self.bounce_down = Animation.create_move(266, total_distance=40, ease_out='quadratic', delay=self.bounce_up.duration) self.bounce_down = Animation.create_move(266, total_distance=40, ease_out='quadratic', delay=self.bounce_up.duration)
self.bounce_down.start()
self.move_center = Animation.create_move(266, total_distance=450, ease_out='quadratic', delay=self.bounce_down.duration+self.bounce_up.duration) self.move_center = Animation.create_move(266, total_distance=450, ease_out='quadratic', delay=self.bounce_down.duration+self.bounce_up.duration)
self.move_center.start()
if self.flower_up is None: if self.flower_up is None:
self.flower_up = Animation.create_move(333, total_distance=365, ease_out='quadratic') self.flower_up = Animation.create_move(333, total_distance=365, ease_out='quadratic')
self.flower_up.start()
self.flower_start = get_current_ms() self.flower_start = get_current_ms()
elif self.state == State.FAIL: elif self.state == State.FAIL:
self.char_1_index = 347 self.char_1_index = 347
@@ -384,6 +398,7 @@ class BottomCharacters:
class FadeIn: class FadeIn:
def __init__(self): def __init__(self):
self.fadein = Animation.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.fadein.start()
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
@@ -430,6 +445,7 @@ class Gauge:
self.gauge_length = gauge_length self.gauge_length = gauge_length
self.rainbow_animation = None self.rainbow_animation = None
self.gauge_fade_in = Animation.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.gauge_fade_in.start()
self.is_finished = self.gauge_fade_in.is_finished self.is_finished = self.gauge_fade_in.is_finished
if self.gauge_length == 87: if self.gauge_length == 87:
self.state = State.RAINBOW self.state = State.RAINBOW
@@ -440,10 +456,12 @@ class Gauge:
def _create_rainbow_anim(self, current_ms): def _create_rainbow_anim(self, current_ms):
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 = 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.start()
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 = Animation.create_fade(450, initial_opacity=init, final_opacity=final) anim = Animation.create_fade(450, initial_opacity=init, final_opacity=final)
anim.start()
return anim return anim
def update(self, current_ms: float): def update(self, current_ms: float):
@@ -493,6 +511,7 @@ class FadeOut:
def __init__(self) -> None: def __init__(self) -> None:
self.texture = global_data.textures['scene_change_fade'][0] self.texture = global_data.textures['scene_change_fade'][0]
self.fade_out = Animation.create_fade(1000, initial_opacity=0.0, final_opacity=1.0) self.fade_out = Animation.create_fade(1000, initial_opacity=0.0, final_opacity=1.0)
self.fade_out.start()
self.is_finished = False self.is_finished = False
def update(self, current_time_ms: float): def update(self, current_time_ms: float):
self.fade_out.update(current_time_ms) self.fade_out.update(current_time_ms)

File diff suppressed because it is too large Load Diff

View File

@@ -3,15 +3,13 @@ from pathlib import Path
import pyray as ray import pyray as ray
from libs.animation import Animation
from libs.audio import audio from libs.audio import audio
from libs.texture import tex
from libs.utils import ( from libs.utils import (
get_current_ms, get_current_ms,
global_data, global_data,
is_l_don_pressed, is_l_don_pressed,
is_r_don_pressed, is_r_don_pressed,
load_all_textures_from_zip,
load_texture_from_zip,
) )
from libs.video import VideoPlayer from libs.video import VideoPlayer
@@ -29,9 +27,7 @@ class TitleScreen:
self.op_video_list = [file for file in video_dir.glob("**/*.mp4")] self.op_video_list = [file for file in video_dir.glob("**/*.mp4")]
video_dir = Path(global_data.config["paths"]["video_path"]) / "attract_videos" video_dir = Path(global_data.config["paths"]["video_path"]) / "attract_videos"
self.attract_video_list = [file for file in video_dir.glob("**/*.mp4")] self.attract_video_list = [file for file in video_dir.glob("**/*.mp4")]
self.load_sounds()
self.screen_init = False self.screen_init = False
self.fade_out = None
def load_sounds(self): def load_sounds(self):
sounds_dir = Path("Sounds") sounds_dir = Path("Sounds")
@@ -41,32 +37,25 @@ class TitleScreen:
self.sound_bachi_hit = audio.load_sound(title_dir / "SE_ATTRACT_3.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") self.sound_warning_message = audio.load_sound(title_dir / "VO_ATTRACT_3.ogg")
self.sound_warning_error = audio.load_sound(title_dir / "SE_ATTRACT_1.ogg") self.sound_warning_error = audio.load_sound(title_dir / "SE_ATTRACT_1.ogg")
self.sounds = [self.sound_bachi_swipe, self.sound_bachi_hit, self.sound_warning_message, self.sound_warning_error]
def load_textures(self):
self.textures = load_all_textures_from_zip(Path('Graphics/lumendata/attract/keikoku.zip'))
self.texture_black = load_texture_from_zip(Path('Graphics/lumendata/attract/movie.zip'), 'movie_img00000.png')
def on_screen_start(self): def on_screen_start(self):
if not self.screen_init: if not self.screen_init:
self.screen_init = True self.screen_init = True
self.load_textures() tex.load_screen_textures('title')
self.load_sounds()
self.state = State.OP_VIDEO self.state = State.OP_VIDEO
self.op_video = None self.op_video = None
self.attract_video = None self.attract_video = None
self.warning_board = None self.warning_board = None
self.fade_out = tex.get_animation(13)
def on_screen_end(self) -> str: def on_screen_end(self) -> str:
if self.op_video is not None: if self.op_video is not None:
self.op_video.stop() self.op_video.stop()
if self.attract_video is not None: if self.attract_video is not None:
self.attract_video.stop() self.attract_video.stop()
for sound in self.sounds: audio.unload_all_sounds()
if audio.is_sound_playing(sound): tex.unload_textures()
audio.stop_sound(sound)
for zip in self.textures:
for texture in self.textures[zip]:
ray.unload_texture(texture)
self.screen_init = False self.screen_init = False
return "ENTRY" return "ENTRY"
@@ -82,7 +71,7 @@ class TitleScreen:
self.state = State.WARNING self.state = State.WARNING
elif self.state == State.WARNING: elif self.state == State.WARNING:
if self.warning_board is None: if self.warning_board is None:
self.warning_board = WarningScreen(get_current_ms(), self) self.warning_board = WarningScreen(get_current_ms())
self.warning_board.update(get_current_ms(), self) self.warning_board.update(get_current_ms(), self)
if self.warning_board.is_finished: if self.warning_board.is_finished:
self.state = State.ATTRACT_VIDEO self.state = State.ATTRACT_VIDEO
@@ -101,66 +90,59 @@ class TitleScreen:
def update(self): def update(self):
self.on_screen_start() self.on_screen_start()
if self.fade_out is not None:
self.fade_out.update(get_current_ms()) self.fade_out.update(get_current_ms())
if self.fade_out.is_finished: if self.fade_out.is_finished:
return self.on_screen_end() return self.on_screen_end()
self.scene_manager() self.scene_manager()
if is_l_don_pressed() or is_r_don_pressed(): if is_l_don_pressed() or is_r_don_pressed():
self.fade_out = Animation.create_fade(1000, initial_opacity=0.0, final_opacity=1.0) self.fade_out.start()
audio.play_sound(self.sound_don) audio.play_sound(self.sound_don)
def draw(self): def draw(self):
if self.state == State.OP_VIDEO and self.op_video is not None: if self.state == State.OP_VIDEO and self.op_video is not None:
self.op_video.draw() self.op_video.draw()
elif self.state == State.WARNING and self.warning_board is not None: elif self.state == State.WARNING and self.warning_board is not None:
bg_source = ray.Rectangle(0, 0, self.textures['keikoku'][0].width, self.textures['keikoku'][0].height) tex.draw_texture('warning', 'background')
bg_dest = ray.Rectangle(0, 0, self.width, self.height) self.warning_board.draw()
ray.draw_texture_pro(self.textures['keikoku'][0], bg_source, bg_dest, ray.Vector2(0,0), 0, ray.WHITE)
self.warning_board.draw(self)
elif self.state == State.ATTRACT_VIDEO and self.attract_video is not None: elif self.state == State.ATTRACT_VIDEO and self.attract_video is not None:
self.attract_video.draw() self.attract_video.draw()
if self.fade_out is not None: tex.draw_texture('movie', 'background', color=ray.fade(ray.WHITE, self.fade_out.attribute))
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))
def draw_3d(self): def draw_3d(self):
pass pass
class WarningScreen: class WarningScreen:
class X: class X:
DELAY = 4250
def __init__(self): def __init__(self):
self.resize = Animation.create_texture_resize(166.67, initial_size=1.0, final_size=1.5, delay=self.DELAY, reverse_delay=0) self.resize = tex.get_animation(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.start()
self.fadein_2 = Animation.create_fade(166.67, delay=self.DELAY + self.fadein.duration, initial_opacity=0.0, final_opacity=1.0) self.fadein = tex.get_animation(1)
self.fadein.start()
self.fadein_2 = tex.get_animation(2)
self.fadein_2.start()
self.sound_played = False self.sound_played = False
def update(self, current_ms: float, sound, elapsed_time): def update(self, current_ms: float, sound):
self.resize.update(current_ms)
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)
if self.DELAY + self.fadein.duration <= elapsed_time and not self.sound_played: if self.resize.attribute > 1 and not self.sound_played:
audio.play_sound(sound) audio.play_sound(sound)
self.sound_played = True self.sound_played = True
def draw(self, texture): def draw_bg(self):
scale = self.resize.attribute tex.draw_texture('warning', 'x_lightred', color=ray.fade(ray.WHITE, self.fadein_2.attribute))
x_x = 150 + (texture.width//2) - ((texture.width * scale)//2)
x_y = 200 + (texture.height//2) - ((texture.height * scale)//2) def draw_fg(self):
x_source = ray.Rectangle(0, 0, texture.width, texture.height) tex.draw_texture('warning', 'x_red', color=ray.fade(ray.WHITE, self.fadein.attribute), scale=self.resize.attribute, center=True)
x_dest = ray.Rectangle(x_x, x_y, texture.width*scale, texture.height*scale)
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): def __init__(self):
self.resize = Animation.create_texture_resize(233.34, initial_size=0.5, final_size=1.5) self.resize = tex.get_animation(3)
self.fadein = Animation.create_fade(116.67, initial_opacity=0.0, final_opacity=1.0, reverse_delay=0) self.fadein = tex.get_animation(4)
self.sound_played = False self.sound_played = False
@@ -168,83 +150,55 @@ class WarningScreen:
if not self.sound_played: if not self.sound_played:
audio.play_sound(sound) audio.play_sound(sound)
self.sound_played = True self.sound_played = True
self.resize.start_ms = current_ms self.fadein.start()
self.fadein.start_ms = current_ms self.resize.start()
self.resize.update(current_ms) self.resize.update(current_ms)
self.fadein.update(current_ms) self.fadein.update(current_ms)
def draw(self, texture): def draw(self):
scale = self.resize.attribute tex.draw_texture('warning', 'bachi_hit', color=ray.fade(ray.WHITE, self.fadein.attribute), scale=self.resize.attribute, center=True)
hit_x = 350 + (texture.width//2) - ((texture.width * scale)//2) if self.resize.attribute > 0 and self.sound_played:
hit_y = 225 + (texture.height//2) - ((texture.height * scale)//2) tex.draw_texture('warning', 'bachi')
hit_source = ray.Rectangle(0, 0, texture.width, texture.height)
hit_dest = ray.Rectangle(hit_x, hit_y, texture.width*scale, texture.height*scale)
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):
self.start_ms = start_ms self.shadow_fade = tex.get_animation(5)
self.current_ms = current_ms self.chara_0_frame = tex.get_animation(7)
self.shadow_fade = Animation.create_fade(50, delay=16.67, initial_opacity=0.75) self.chara_1_frame = tex.get_animation(6)
self.chara_0_frame.start()
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.chara_1_frame.start()
(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), self.saved_frame = 0
(133.34, 5, 4), (116.67, 5, 4), (100.00, 6, 4), (100.00, 7, 4), (100.00, 8, 9), (100.00, 11, 9), (100.00, 12, 9), (100.00, 13, 9),
(100.00, 5, 4), (100.00, 5, 4), (83.330, 6, 4), (83.330, 7, 4), (83.330, 8, 9), (83.330, 11, 9), (83.330, 12, 9), (83.330, 13, 9),
(83.330, 5, 4), (83.330, 5, 4), (66.670, 6, 4), (66.670, 7, 4), (66.670, 8, 9), (66.670, 11, 9), (66.670, 12, 9), (66.670, 13, 9),
(66.670, 5, 4), (66.670, 5, 4), (66.670, 6, 4), (66.670, 7, 4), (66.670, 8, 9), (66.670, 11, 9), (66.670, 12, 9), (66.670, 13, 9),
(66.670, 5, 4), (66.670, 5, 4), (66.670, 6, 4), (66.670, 7, 4), (66.670, 8, 9), (66.670, 11, 9), (66.670, 12, 9), (66.670, 13, 9),
(66.670, 17, 16)]
self.time = 0
self.index_val = 0
self.is_finished = False self.is_finished = False
def character_index(self, index: int) -> int:
elapsed_time = self.current_ms - self.start_ms
delay = 566.67
if self.index_val == len(self.animation_sequence)-1:
return int(self.animation_sequence[len(self.animation_sequence)-1][index])
elif elapsed_time <= delay:
return int(self.animation_sequence[0][index])
elif elapsed_time >= delay + self.time:
new_index = self.animation_sequence[self.index_val][index]
self.index_val += 1
self.shadow_fade.start_ms = self.current_ms
self.shadow_fade.duration = int(self.animation_sequence[self.index_val][0])
self.time += self.animation_sequence[self.index_val][0]
return int(new_index)
else:
return int(self.animation_sequence[self.index_val][index])
def update(self, current_ms: float): def update(self, current_ms: float):
self.shadow_fade.update(current_ms) self.shadow_fade.update(current_ms)
self.chara_1_frame.update(current_ms)
self.chara_0_frame.update(current_ms)
self.current_ms = current_ms self.current_ms = current_ms
self.is_finished = True if self.character_index(1) == self.animation_sequence[-1][1] else False if self.chara_1_frame.attribute != self.saved_frame:
self.saved_frame = self.chara_1_frame.attribute
if not self.shadow_fade.is_started:
self.shadow_fade.start()
else:
self.shadow_fade.restart()
self.is_finished = self.chara_1_frame.is_finished
def draw(self, fade: ray.Color, fade_2: ray.Color):
tex.draw_texture('warning', 'chara_0_shadow', color=fade_2)
tex.draw_texture('warning', 'chara_0', frame=self.chara_0_frame.attribute, color=fade)
def draw(self, textures, fade: ray.Color, fade_2: ray.Color, y: int): tex.draw_texture('warning', 'chara_1_shadow', color=fade_2)
ray.draw_texture(textures['keikoku'][2], 135, y+textures['keikoku'][4].height+110, fade_2) if -1 < self.chara_1_frame.attribute-1 < 7:
ray.draw_texture(textures['keikoku'][self.character_index(2)], 115, y+150, fade) tex.draw_texture('warning', 'chara_1', frame=self.chara_1_frame.attribute-1, color=ray.fade(ray.WHITE, self.shadow_fade.attribute))
tex.draw_texture('warning', 'chara_1', frame=self.chara_1_frame.attribute, color=fade)
ray.draw_texture(textures['keikoku'][3], 360, y+textures['keikoku'][5].height+60, fade_2)
if 6 < self.character_index(1) < 17:
ray.draw_texture(textures['keikoku'][self.character_index(1) - 1], 315, y+100, ray.fade(ray.WHITE, self.shadow_fade.attribute))
ray.draw_texture(textures['keikoku'][self.character_index(1)], 315, y+100, fade)
if self.character_index(1) == 17:
ray.draw_texture(textures['keikoku'][19], 350, y+135, ray.WHITE)
class Board: class Board:
def __init__(self, screen_width, screen_height, texture): def __init__(self):
#Move warning board down from top of screen self.move_down = tex.get_animation(10)
self.move_down = Animation.create_move(266.67, total_distance=screen_height + ((screen_height - texture.height)//2) + 20, start_position=-720) self.move_down.start()
self.move_up = tex.get_animation(11)
#Move warning board up a little bit self.move_up.start()
self.move_up = Animation.create_move(116.67, start_position=92 + 20, delay=self.move_down.duration, total_distance =-30) self.move_center = tex.get_animation(12)
self.move_center.start()
#And finally into its correct position
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):
@@ -252,29 +206,29 @@ class WarningScreen:
self.move_up.update(current_ms) self.move_up.update(current_ms)
self.move_center.update(current_ms) self.move_center.update(current_ms)
if self.move_up.is_finished: if self.move_up.is_finished:
self.y_pos = int(self.move_center.attribute) self.y_pos = self.move_center.attribute
elif self.move_down.is_finished: elif self.move_down.is_finished:
self.y_pos = int(self.move_up.attribute) self.y_pos = self.move_up.attribute
else: else:
self.y_pos = int(self.move_down.attribute) self.y_pos = self.move_down.attribute
tex.update_attr('warning', 'warning_box', 'y', self.y_pos)
def draw(self, texture): def draw(self):
ray.draw_texture(texture, 0, self.y_pos, ray.WHITE) tex.draw_texture('warning', 'warning_box')
def __init__(self, current_ms: float, title_screen: TitleScreen): def __init__(self, current_ms: float):
self.start_ms = current_ms self.start_ms = current_ms
self.fade_in = Animation.create_fade(300, delay=266.67, initial_opacity=0.0, final_opacity=1.0) self.fade_in = tex.get_animation(8)
self.fade_out = Animation.create_fade(500, delay=1000, initial_opacity=0.0, final_opacity=1.0) self.fade_in.start()
self.fade_out = tex.get_animation(9)
self.fade_out.start()
self.board = self.Board(title_screen.width, title_screen.height, title_screen.textures['keikoku'][1]) self.board = self.Board()
self.warning_x = self.X() self.warning_x = self.X()
self.warning_bachi_hit = self.BachiHit() self.warning_bachi_hit = self.BachiHit()
self.characters = self.Characters(current_ms, self.start_ms) self.characters = self.Characters()
self.source_rect = ray.Rectangle(0, 0, title_screen.texture_black.width, title_screen.texture_black.height)
self.dest_rect = ray.Rectangle(0, 0, title_screen.width, title_screen.height)
self.is_finished = False self.is_finished = False
@@ -284,8 +238,12 @@ class WarningScreen:
self.fade_out.update(current_ms) self.fade_out.update(current_ms)
delay = 566.67 delay = 566.67
elapsed_time = current_ms - self.start_ms elapsed_time = current_ms - self.start_ms
self.warning_x.update(current_ms, title_screen.sound_warning_error, elapsed_time) self.warning_x.update(current_ms, title_screen.sound_warning_error)
self.characters.update(current_ms) self.characters.update(current_ms)
tex.update_attr('warning', 'chara_0', 'y', self.board.y_pos)
tex.update_attr('warning', 'chara_0_shadow', 'y', self.board.y_pos)
tex.update_attr('warning', 'chara_1_shadow', 'y', self.board.y_pos)
tex.update_attr('warning', 'chara_1', 'y', self.board.y_pos)
if self.characters.is_finished: if self.characters.is_finished:
self.warning_bachi_hit.update(current_ms, title_screen.sound_bachi_hit) self.warning_bachi_hit.update(current_ms, title_screen.sound_bachi_hit)
@@ -297,16 +255,14 @@ class WarningScreen:
self.is_finished = self.fade_out.is_finished self.is_finished = self.fade_out.is_finished
def draw(self, title_screen: TitleScreen): def draw(self):
fade = ray.fade(ray.WHITE, self.fade_in.attribute) fade = ray.fade(ray.WHITE, self.fade_in.attribute)
fade_2 = ray.fade(ray.WHITE, self.fade_in.attribute if self.fade_in.attribute < 0.75 else 0.75) fade_2 = ray.fade(ray.WHITE, min(self.fade_in.attribute, 0.75))
self.board.draw(title_screen.textures['keikoku'][1])
ray.draw_texture(title_screen.textures['keikoku'][15], 150, 200, ray.fade(ray.WHITE, self.warning_x.fadein_2.attribute))
self.characters.draw(title_screen.textures, fade, fade_2, self.board.y_pos) self.board.draw()
self.warning_x.draw_bg()
self.characters.draw(fade, fade_2)
self.warning_x.draw_fg()
self.warning_bachi_hit.draw()
self.warning_x.draw(title_screen.textures['keikoku'][14]) tex.draw_texture('movie', 'background', color=ray.fade(ray.WHITE, self.fade_out.attribute))
self.warning_bachi_hit.draw(title_screen.textures['keikoku'][18])
ray.draw_texture_pro(title_screen.texture_black, self.source_rect, self.dest_rect, ray.Vector2(0,0), 0, ray.fade(ray.WHITE, self.fade_out.attribute))