mirror of
https://github.com/Yonokid/PyTaiko.git
synced 2026-02-04 11:40:13 +01:00
many optimizations and bug fixes
This commit is contained in:
@@ -68,7 +68,7 @@ def main():
|
|||||||
if global_data.config["video"]["target_fps"] != -1:
|
if global_data.config["video"]["target_fps"] != -1:
|
||||||
ray.set_target_fps(global_data.config["video"]["target_fps"])
|
ray.set_target_fps(global_data.config["video"]["target_fps"])
|
||||||
ray.set_config_flags(ray.ConfigFlags.FLAG_MSAA_4X_HINT)
|
ray.set_config_flags(ray.ConfigFlags.FLAG_MSAA_4X_HINT)
|
||||||
ray.set_trace_log_level(ray.TraceLogLevel.LOG_WARNING)
|
ray.set_trace_log_level(ray.TraceLogLevel.LOG_INFO)
|
||||||
|
|
||||||
camera = ray.Camera3D()
|
camera = ray.Camera3D()
|
||||||
camera.position = ray.Vector3(0.0, 0.0, 10.0) # Camera position
|
camera.position = ray.Vector3(0.0, 0.0, 10.0) # Camera position
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ judge_offset = 0
|
|||||||
visual_offset = 0
|
visual_offset = 0
|
||||||
language = "ja"
|
language = "ja"
|
||||||
hard_judge = 108
|
hard_judge = 108
|
||||||
|
touch_enabled = false
|
||||||
|
|
||||||
[nameplate]
|
[nameplate]
|
||||||
name = 'どんちゃん'
|
name = 'どんちゃん'
|
||||||
@@ -31,6 +32,7 @@ right_kat = [12]
|
|||||||
[audio]
|
[audio]
|
||||||
device_type = 0
|
device_type = 0
|
||||||
sample_rate = -1
|
sample_rate = -1
|
||||||
|
buffer_size = 0
|
||||||
exclusive = false
|
exclusive = false
|
||||||
|
|
||||||
[video]
|
[video]
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ ffi.cdef("""
|
|||||||
|
|
||||||
// Device management
|
// Device management
|
||||||
void list_host_apis(void);
|
void list_host_apis(void);
|
||||||
void init_audio_device(PaHostApiIndex host_api, double sample_rate);
|
void init_audio_device(PaHostApiIndex host_api, double sample_rate, unsigned long buffer_size);
|
||||||
void close_audio_device(void);
|
void close_audio_device(void);
|
||||||
bool is_audio_device_ready(void);
|
bool is_audio_device_ready(void);
|
||||||
void set_master_volume(float volume);
|
void set_master_volume(float volume);
|
||||||
@@ -128,11 +128,12 @@ except OSError as e:
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
class AudioEngine:
|
class AudioEngine:
|
||||||
def __init__(self, device_type: int, sample_rate: float):
|
def __init__(self, device_type: int, sample_rate: float, buffer_size: int):
|
||||||
self.device_type = device_type
|
self.device_type = device_type
|
||||||
if sample_rate == -1:
|
if sample_rate == -1:
|
||||||
sample_rate = 44100
|
sample_rate = 44100
|
||||||
self.target_sample_rate = sample_rate
|
self.target_sample_rate = sample_rate
|
||||||
|
self.buffer_size = buffer_size
|
||||||
self.sounds = {} # sound_id -> sound struct
|
self.sounds = {} # sound_id -> sound struct
|
||||||
self.music_streams = {} # music_id -> music struct
|
self.music_streams = {} # music_id -> music struct
|
||||||
self.sound_counter = 0
|
self.sound_counter = 0
|
||||||
@@ -145,7 +146,7 @@ class AudioEngine:
|
|||||||
def init_audio_device(self) -> bool:
|
def init_audio_device(self) -> bool:
|
||||||
"""Initialize the audio device"""
|
"""Initialize the audio device"""
|
||||||
try:
|
try:
|
||||||
lib.init_audio_device(self.device_type, self.target_sample_rate) # type: ignore
|
lib.init_audio_device(self.device_type, self.target_sample_rate, self.buffer_size) # type: ignore
|
||||||
self.audio_device_ready = lib.is_audio_device_ready() # type: ignore
|
self.audio_device_ready = lib.is_audio_device_ready() # type: ignore
|
||||||
if self.audio_device_ready:
|
if self.audio_device_ready:
|
||||||
print("Audio device initialized successfully")
|
print("Audio device initialized successfully")
|
||||||
@@ -361,5 +362,5 @@ class AudioEngine:
|
|||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
# Create the global audio instance
|
# Create the global audio instance
|
||||||
audio = AudioEngine(get_config()["audio"]["device_type"], get_config()["audio"]["sample_rate"])
|
audio = AudioEngine(get_config()["audio"]["device_type"], get_config()["audio"]["sample_rate"], get_config()["audio"]["buffer_size"])
|
||||||
audio.set_master_volume(0.75)
|
audio.set_master_volume(0.75)
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ typedef struct AudioData {
|
|||||||
// Function declarations
|
// Function declarations
|
||||||
// Device management
|
// Device management
|
||||||
void list_host_apis(void);
|
void list_host_apis(void);
|
||||||
void init_audio_device(PaHostApiIndex host_api, double sample_rate);
|
void init_audio_device(PaHostApiIndex host_api, double sample_rate, unsigned long buffer_size);
|
||||||
void close_audio_device(void);
|
void close_audio_device(void);
|
||||||
bool is_audio_device_ready(void);
|
bool is_audio_device_ready(void);
|
||||||
void set_master_volume(float volume);
|
void set_master_volume(float volume);
|
||||||
@@ -322,7 +322,7 @@ PaDeviceIndex get_best_output_device_for_host_api(PaHostApiIndex hostApi)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Device management implementations
|
// Device management implementations
|
||||||
void init_audio_device(PaHostApiIndex host_api, double sample_rate)
|
void init_audio_device(PaHostApiIndex host_api, double sample_rate, unsigned long buffer_size)
|
||||||
{
|
{
|
||||||
// Initialize PortAudio
|
// Initialize PortAudio
|
||||||
PaError err = Pa_Initialize();
|
PaError err = Pa_Initialize();
|
||||||
@@ -358,7 +358,7 @@ void init_audio_device(PaHostApiIndex host_api, double sample_rate)
|
|||||||
NULL, // No input
|
NULL, // No input
|
||||||
&AUDIO.System.outputParameters, // Output parameters
|
&AUDIO.System.outputParameters, // Output parameters
|
||||||
sample_rate, // Sample rate
|
sample_rate, // Sample rate
|
||||||
paFramesPerBufferUnspecified, // Frames per buffer (let PortAudio decide)
|
buffer_size, // Frames per buffer (let PortAudio decide)
|
||||||
paClipOff, // No clipping
|
paClipOff, // No clipping
|
||||||
port_audio_callback, // Callback function
|
port_audio_callback, // Callback function
|
||||||
NULL); // User data
|
NULL); // User data
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ void list_host_apis(void);
|
|||||||
* Initialize the audio device and system
|
* Initialize the audio device and system
|
||||||
* Must be called before using any other audio functions
|
* Must be called before using any other audio functions
|
||||||
*/
|
*/
|
||||||
void init_audio_device(PaHostApiIndex host_api, double sample_rate);
|
void init_audio_device(PaHostApiIndex host_api, double sample_rate, unsigned long buffer_size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the audio device and cleanup resources
|
* Close the audio device and cleanup resources
|
||||||
|
|||||||
@@ -51,13 +51,26 @@ class BGFever1(BGFeverBase):
|
|||||||
self.overlay_move_down.delay_saved = 300
|
self.overlay_move_down.delay_saved = 300
|
||||||
|
|
||||||
self.wave_spin = tex.get_animation(28)
|
self.wave_spin = tex.get_animation(28)
|
||||||
|
self.wave_origin = ray.Vector2(tex.textures[self.name]['wave'].width/2,tex.textures[self.name]['wave'].height/2)
|
||||||
self.circle = {
|
self.circle = {
|
||||||
"center_x": 100,
|
"center_x": 100,
|
||||||
"center_y": 130,
|
"center_y": 130,
|
||||||
"radius": 200,
|
"radius": 200,
|
||||||
}
|
}
|
||||||
|
self.lookup_table_size = 360 # One entry per degree
|
||||||
|
self._precomputed_positions: list[tuple[float, float]] = []
|
||||||
|
for i in range(self.lookup_table_size):
|
||||||
|
angle = (i / self.lookup_table_size) * 2 * math.pi
|
||||||
|
x = self.circle["center_x"] + math.cos(angle) * self.circle["radius"]
|
||||||
|
y = self.circle["center_y"] + math.sin(angle) * self.circle["radius"]
|
||||||
|
self._precomputed_positions.append((x, y))
|
||||||
self.bg_move = tex.get_animation(16)
|
self.bg_move = tex.get_animation(16)
|
||||||
|
|
||||||
|
def _get_wave_position(self):
|
||||||
|
normalized = (self.wave_spin.attribute % 180) / 180.0
|
||||||
|
index = int(normalized * (self.lookup_table_size - 1))
|
||||||
|
return self._precomputed_positions[index]
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.corner_move_up.start()
|
self.corner_move_up.start()
|
||||||
self.corner_move_down.start()
|
self.corner_move_down.start()
|
||||||
@@ -94,11 +107,8 @@ class BGFever1(BGFeverBase):
|
|||||||
for i, tile in enumerate(self.bg_tiles):
|
for i, tile in enumerate(self.bg_tiles):
|
||||||
tile.draw(tex, self.name, (i*128)-self.bg_move.attribute, i % 10)
|
tile.draw(tex, self.name, (i*128)-self.bg_move.attribute, i % 10)
|
||||||
tex.draw_texture(self.name, 'mountain', y=-self.mountain_move_up.attribute+self.mountain_move_down.attribute)
|
tex.draw_texture(self.name, 'mountain', y=-self.mountain_move_up.attribute+self.mountain_move_down.attribute)
|
||||||
angle = math.radians(self.wave_spin.attribute*2)
|
wave_x, wave_y = self._get_wave_position()
|
||||||
wave_x = self.circle["center_x"] + math.cos(angle) * self.circle["radius"]
|
tex.draw_texture(self.name, 'wave', x=wave_x, y=wave_y, origin=self.wave_origin)
|
||||||
wave_y = self.circle["center_y"] + math.sin(angle) * self.circle["radius"]
|
|
||||||
wave_origin = ray.Vector2(tex.textures[self.name]['wave'].width/2,tex.textures[self.name]['wave'].height/2)
|
|
||||||
tex.draw_texture(self.name, 'wave', x=wave_x, y=wave_y, origin=wave_origin)
|
|
||||||
tex.draw_texture(self.name, 'footer', y=-self.footer_move_up.attribute+self.footer_move_down.attribute)
|
tex.draw_texture(self.name, 'footer', y=-self.footer_move_up.attribute+self.footer_move_down.attribute)
|
||||||
tex.draw_texture(self.name, 'corner', y=-self.corner_move_up.attribute+self.corner_move_down.attribute)
|
tex.draw_texture(self.name, 'corner', y=-self.corner_move_up.attribute+self.corner_move_down.attribute)
|
||||||
tex.draw_texture(self.name, 'overlay', y=self.overlay_move_up.attribute-self.overlay_move_down.attribute)
|
tex.draw_texture(self.name, 'overlay', y=self.overlay_move_up.attribute-self.overlay_move_down.attribute)
|
||||||
@@ -112,6 +122,7 @@ class BGFever2(BGFeverBase):
|
|||||||
self.ship_rotation.start()
|
self.ship_rotation.start()
|
||||||
self.move_in = tex.get_animation(22)
|
self.move_in = tex.get_animation(22)
|
||||||
self.move_out = tex.get_animation(23)
|
self.move_out = tex.get_animation(23)
|
||||||
|
self.ship_origin = ray.Vector2(tex.textures[self.name]['ship'].width/2, tex.textures[self.name]['ship'].height/2)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.fadein.start()
|
self.fadein.start()
|
||||||
@@ -133,8 +144,7 @@ class BGFever2(BGFeverBase):
|
|||||||
tex.draw_texture(self.name, 'footer_2', y=self.move_in.attribute-self.move_out.attribute, fade=self.fadein.attribute)
|
tex.draw_texture(self.name, 'footer_2', y=self.move_in.attribute-self.move_out.attribute, fade=self.fadein.attribute)
|
||||||
tex.draw_texture(self.name, 'bird', index=0, x=self.move_in.attribute-self.move_out.attribute, mirror='horizontal', y=self.ship_rotation.attribute*180)
|
tex.draw_texture(self.name, 'bird', index=0, x=self.move_in.attribute-self.move_out.attribute, mirror='horizontal', y=self.ship_rotation.attribute*180)
|
||||||
tex.draw_texture(self.name, 'bird', index=1, x=-self.move_in.attribute+self.move_out.attribute, y=self.ship_rotation.attribute*180)
|
tex.draw_texture(self.name, 'bird', index=1, x=-self.move_in.attribute+self.move_out.attribute, y=self.ship_rotation.attribute*180)
|
||||||
origin = ray.Vector2(tex.textures[self.name]['ship'].width/2, tex.textures[self.name]['ship'].height/2)
|
tex.draw_texture(self.name, 'ship', x=self.ship_origin.x, y=self.ship_origin.y + self.move_in.attribute-self.move_out.attribute, origin=self.ship_origin, rotation=self.ship_rotation.attribute*100, center=True)
|
||||||
tex.draw_texture(self.name, 'ship', x=origin.x, y=origin.y + self.move_in.attribute-self.move_out.attribute, origin=origin, rotation=self.ship_rotation.attribute*100, center=True)
|
|
||||||
|
|
||||||
class BGFever3(BGFeverBase):
|
class BGFever3(BGFeverBase):
|
||||||
def __init__(self, tex: TextureWrapper, index: int, path: str):
|
def __init__(self, tex: TextureWrapper, index: int, path: str):
|
||||||
@@ -152,10 +162,53 @@ class BGFever3(BGFeverBase):
|
|||||||
"center_y": 300,
|
"center_y": 300,
|
||||||
"radius": 300,
|
"radius": 300,
|
||||||
}
|
}
|
||||||
|
|
||||||
self.num_fish = 8
|
self.num_fish = 8
|
||||||
self.fish_spacing = (2 * math.pi) / self.num_fish # 45 degrees in radians
|
self.fish_spacing = (2 * math.pi) / self.num_fish # 45 degrees in radians
|
||||||
|
|
||||||
|
self.wave_origin = ray.Vector2(tex.textures[self.name]['wave'].width/2, tex.textures[self.name]['wave'].height/2)
|
||||||
|
self.fish_origin = ray.Vector2(tex.textures[self.name]['fish'].width/2, tex.textures[self.name]['fish'].height/2)
|
||||||
|
self.circle_origin = ray.Vector2(tex.textures[self.name]['circle'].width/2,tex.textures[self.name]['circle'].height/2)
|
||||||
|
|
||||||
|
self.fish_phase_offsets = [i * self.fish_spacing for i in range(self.num_fish)]
|
||||||
|
self.lookup_table_size = 360 # One entry per degree
|
||||||
|
|
||||||
|
self._precomputed_main_positions: list[tuple[float, float]] = []
|
||||||
|
for i in range(self.lookup_table_size):
|
||||||
|
angle = (i / self.lookup_table_size) * 2 * math.pi
|
||||||
|
x = self.circle["center_x"] + math.cos(angle) * self.circle["radius"]
|
||||||
|
y = self.circle["center_y"] + math.sin(angle) * self.circle["radius"]
|
||||||
|
self._precomputed_main_positions.append((x, y))
|
||||||
|
|
||||||
|
self._precomputed_small_positions: list[tuple[float, float]] = []
|
||||||
|
for i in range(self.lookup_table_size):
|
||||||
|
angle = (i / self.lookup_table_size) * 2 * math.pi
|
||||||
|
x = self.circle["center_x"] + math.cos(angle) * 20
|
||||||
|
y = self.circle["center_y"] + math.sin(angle) * 20
|
||||||
|
self._precomputed_small_positions.append((x, y))
|
||||||
|
|
||||||
|
self._precomputed_rotations: list[float] = []
|
||||||
|
for i in range(self.lookup_table_size):
|
||||||
|
angle = (i / self.lookup_table_size) * 2 * math.pi
|
||||||
|
swimming_angle = angle + math.pi/2
|
||||||
|
swimming_rotation = math.degrees(swimming_angle)
|
||||||
|
self._precomputed_rotations.append(swimming_rotation)
|
||||||
|
|
||||||
|
def _get_main_circle_position(self, spin_value: float, multiplier: float = 1.0):
|
||||||
|
normalized = ((spin_value * multiplier) % 360) / 360.0
|
||||||
|
index = int(normalized * (self.lookup_table_size - 1))
|
||||||
|
return self._precomputed_main_positions[index]
|
||||||
|
|
||||||
|
def _get_small_circle_position(self, spin_value: float, multiplier: float = 1.0):
|
||||||
|
normalized = ((spin_value * multiplier) % 360) / 360.0
|
||||||
|
index = int(normalized * (self.lookup_table_size - 1))
|
||||||
|
return self._precomputed_small_positions[index]
|
||||||
|
|
||||||
|
def _get_fish_position_and_rotation(self, spin_value: float, phase_offset: float):
|
||||||
|
angle_degrees = spin_value + math.degrees(phase_offset)
|
||||||
|
normalized = (angle_degrees % 360) / 360.0
|
||||||
|
index = int(normalized * (self.lookup_table_size - 1))
|
||||||
|
return self._precomputed_main_positions[index], self._precomputed_rotations[index]
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.fadein.start()
|
self.fadein.start()
|
||||||
self.move_in.start()
|
self.move_in.start()
|
||||||
@@ -174,43 +227,26 @@ class BGFever3(BGFeverBase):
|
|||||||
def draw(self, tex: TextureWrapper):
|
def draw(self, tex: TextureWrapper):
|
||||||
tex.draw_texture(self.name, 'background', x=-self.move_in.attribute)
|
tex.draw_texture(self.name, 'background', x=-self.move_in.attribute)
|
||||||
tex.draw_texture(self.name, 'overlay', frame=self.overlay_tc.attribute, fade=self.fadein.attribute)
|
tex.draw_texture(self.name, 'overlay', frame=self.overlay_tc.attribute, fade=self.fadein.attribute)
|
||||||
origin = ray.Vector2(tex.textures[self.name]['circle'].width/2, tex.textures[self.name]['circle'].height/2)
|
tex.draw_texture(self.name, 'circle', x=self.circle_origin.x, y=self.circle_origin.y, fade=self.fadein.attribute, origin=self.circle_origin, rotation=self.circle_rotate.attribute)
|
||||||
tex.draw_texture(self.name, 'circle', x=origin.x, y=origin.y, fade=self.fadein.attribute, origin=origin, rotation=self.circle_rotate.attribute)
|
wave_x, wave_y = self._get_main_circle_position(self.fish_spin.attribute, 2.0)
|
||||||
|
tex.draw_texture(self.name, 'wave', x=wave_x, y=wave_y, fade=self.fadein.attribute, origin=self.wave_origin)
|
||||||
angle = math.radians(self.fish_spin.attribute*2)
|
|
||||||
wave_x = self.circle["center_x"] + math.cos(angle) * self.circle["radius"]
|
|
||||||
wave_y = self.circle["center_y"] + math.sin(angle) * self.circle["radius"]
|
|
||||||
wave_origin = ray.Vector2(tex.textures[self.name]['wave'].width/2,tex.textures[self.name]['wave'].height/2)
|
|
||||||
tex.draw_texture(self.name, 'wave', x=wave_x, y=wave_y, fade=self.fadein.attribute, origin=wave_origin)
|
|
||||||
|
|
||||||
for j in range(2):
|
for j in range(2):
|
||||||
for i in range(self.num_fish):
|
for i in range(self.num_fish):
|
||||||
fish_phase_offset = i * self.fish_spacing
|
fish_pos, fish_rotation = self._get_fish_position_and_rotation(
|
||||||
angle = math.radians(self.fish_spin.attribute) + fish_phase_offset
|
self.fish_spin.attribute, self.fish_phase_offsets[i])
|
||||||
fish_x = self.circle["center_x"] + math.cos(angle) * self.circle["radius"]
|
fish_x, fish_y = fish_pos
|
||||||
fish_y = self.circle["center_y"] + math.sin(angle) * self.circle["radius"]
|
|
||||||
|
|
||||||
# Fish should face the direction they're swimming (tangent to circle)
|
tex.draw_texture(self.name, 'fish', x=fish_x, y=fish_y, fade=self.fadein.attribute, origin=self.fish_origin, rotation=fish_rotation, index=j)
|
||||||
swimming_angle = angle + math.pi/2 # Perpendicular to radius
|
|
||||||
swimming_rotation = math.degrees(swimming_angle)
|
|
||||||
|
|
||||||
fish_origin = ray.Vector2(tex.textures[self.name]['fish'].width/2,tex.textures[self.name]['fish'].height/2)
|
footer_wave_x, footer_wave_y = self._get_small_circle_position(self.fish_spin.attribute, 3.0)
|
||||||
|
|
||||||
tex.draw_texture(self.name, 'fish', x=fish_x, y=fish_y, fade=self.fadein.attribute,
|
|
||||||
origin=fish_origin,
|
|
||||||
rotation=swimming_rotation,
|
|
||||||
index=j
|
|
||||||
)
|
|
||||||
|
|
||||||
angle = math.radians(self.fish_spin.attribute*3)
|
|
||||||
wave_x = self.circle["center_x"] + math.cos(angle) * 20
|
|
||||||
wave_y = self.circle["center_y"] + math.sin(angle) * 20
|
|
||||||
wave_origin = ray.Vector2(tex.textures[self.name]['wave'].width/2,tex.textures[self.name]['wave'].height/2)
|
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
tex.draw_texture(self.name, 'footer_2', x=wave_x + (i*600), y=wave_y, fade=self.fadein.attribute, origin=wave_origin)
|
tex.draw_texture(self.name, 'footer_2',
|
||||||
|
x=footer_wave_x + (i*600), y=footer_wave_y,
|
||||||
|
fade=self.fadein.attribute, origin=self.wave_origin)
|
||||||
|
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
tex.draw_texture(self.name, 'footer_1', x=i*450, y=-self.footer_move_up.attribute)
|
tex.draw_texture(self.name, 'footer_1', x=i*450, y=-self.footer_move_up.attribute)
|
||||||
|
|
||||||
tex.draw_texture(self.name, 'bird', frame=self.bird_tc.attribute, index=0, x=-self.move_in.attribute)
|
tex.draw_texture(self.name, 'bird', frame=self.bird_tc.attribute, index=0, x=-self.move_in.attribute)
|
||||||
tex.draw_texture(self.name, 'bird', frame=self.bird_tc.attribute, index=1, x=-self.move_in.attribute)
|
tex.draw_texture(self.name, 'bird', frame=self.bird_tc.attribute, index=1, x=-self.move_in.attribute)
|
||||||
|
|
||||||
|
|||||||
@@ -7,15 +7,14 @@ import pyray as ray
|
|||||||
class Renda:
|
class Renda:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create(tex: TextureWrapper, index: int, path: str):
|
def create(tex: TextureWrapper, index: int):
|
||||||
map = [Renda0, Renda1, Renda2]
|
map = [Renda0, Renda1, Renda2]
|
||||||
selected_obj = map[index]
|
selected_obj = map[index]
|
||||||
return selected_obj(tex, index, path)
|
return selected_obj(tex, index)
|
||||||
|
|
||||||
class BaseRenda:
|
class BaseRenda:
|
||||||
def __init__(self, tex: TextureWrapper, index: int, path: str):
|
def __init__(self, tex: TextureWrapper, index: int):
|
||||||
self.name = 'renda_' + str(index)
|
self.name = 'renda_' + str(index)
|
||||||
tex.load_zip(path, 'renda')
|
|
||||||
self.hori_move = Animation.create_move(1500, total_distance=1280)
|
self.hori_move = Animation.create_move(1500, total_distance=1280)
|
||||||
self.hori_move.start()
|
self.hori_move.start()
|
||||||
|
|
||||||
@@ -23,8 +22,8 @@ class BaseRenda:
|
|||||||
self.hori_move.update(current_time_ms)
|
self.hori_move.update(current_time_ms)
|
||||||
|
|
||||||
class Renda0(BaseRenda):
|
class Renda0(BaseRenda):
|
||||||
def __init__(self, tex: TextureWrapper, index: int, path: str):
|
def __init__(self, tex: TextureWrapper, index: int):
|
||||||
super().__init__(tex, index, path)
|
super().__init__(tex, index)
|
||||||
self.vert_move = Animation.create_move(1500, total_distance=800)
|
self.vert_move = Animation.create_move(1500, total_distance=800)
|
||||||
self.vert_move.start()
|
self.vert_move.start()
|
||||||
self.frame = random.randint(0, 5)
|
self.frame = random.randint(0, 5)
|
||||||
@@ -39,12 +38,13 @@ class Renda0(BaseRenda):
|
|||||||
tex.draw_texture('renda', self.name, frame=self.frame, x=self.hori_move.attribute+self.x, y=-self.vert_move.attribute+self.y)
|
tex.draw_texture('renda', self.name, frame=self.frame, x=self.hori_move.attribute+self.x, y=-self.vert_move.attribute+self.y)
|
||||||
|
|
||||||
class Renda1(BaseRenda):
|
class Renda1(BaseRenda):
|
||||||
def __init__(self, tex: TextureWrapper, index: int, path: str):
|
def __init__(self, tex: TextureWrapper, index: int):
|
||||||
super().__init__(tex, index, path)
|
super().__init__(tex, index)
|
||||||
self.frame = random.randint(0, 5)
|
self.frame = random.randint(0, 5)
|
||||||
self.y = random.randint(0, 200)
|
self.y = random.randint(0, 200)
|
||||||
self.rotate = Animation.create_move(800, total_distance=360)
|
self.rotate = Animation.create_move(800, total_distance=360)
|
||||||
self.rotate.start()
|
self.rotate.start()
|
||||||
|
self.origin = ray.Vector2(64, 64)
|
||||||
|
|
||||||
def update(self, current_time_ms: float):
|
def update(self, current_time_ms: float):
|
||||||
super().update(current_time_ms)
|
super().update(current_time_ms)
|
||||||
@@ -53,12 +53,11 @@ class Renda1(BaseRenda):
|
|||||||
self.rotate.restart()
|
self.rotate.restart()
|
||||||
|
|
||||||
def draw(self, tex: TextureWrapper):
|
def draw(self, tex: TextureWrapper):
|
||||||
origin = ray.Vector2(64, 64)
|
tex.draw_texture('renda', self.name, frame=self.frame, x=self.hori_move.attribute+self.origin.x, y=self.y+self.origin.y, origin=self.origin, rotation=self.rotate.attribute)
|
||||||
tex.draw_texture('renda', self.name, frame=self.frame, x=self.hori_move.attribute+origin.x, y=self.y+origin.y, origin=origin, rotation=self.rotate.attribute)
|
|
||||||
|
|
||||||
class Renda2(BaseRenda):
|
class Renda2(BaseRenda):
|
||||||
def __init__(self, tex: TextureWrapper, index: int, path: str):
|
def __init__(self, tex: TextureWrapper, index: int):
|
||||||
super().__init__(tex, index, path)
|
super().__init__(tex, index)
|
||||||
self.vert_move = Animation.create_move(1500, total_distance=800)
|
self.vert_move = Animation.create_move(1500, total_distance=800)
|
||||||
self.vert_move.start()
|
self.vert_move.start()
|
||||||
self.x = random.randint(0, 500)
|
self.x = random.randint(0, 500)
|
||||||
@@ -74,12 +73,13 @@ class Renda2(BaseRenda):
|
|||||||
class RendaController:
|
class RendaController:
|
||||||
def __init__(self, tex: TextureWrapper, index: int, path: str ='background'):
|
def __init__(self, tex: TextureWrapper, index: int, path: str ='background'):
|
||||||
self.rendas = set()
|
self.rendas = set()
|
||||||
|
tex.load_zip(path, 'renda')
|
||||||
self.tex = tex
|
self.tex = tex
|
||||||
self.index = index
|
self.index = index
|
||||||
self.path = path
|
self.path = path
|
||||||
|
|
||||||
def add_renda(self):
|
def add_renda(self):
|
||||||
self.rendas.add(Renda.create(self.tex, self.index, self.path))
|
self.rendas.add(Renda.create(self.tex, self.index))
|
||||||
|
|
||||||
def update(self, current_time_ms: float):
|
def update(self, current_time_ms: float):
|
||||||
remove = set()
|
remove = set()
|
||||||
|
|||||||
@@ -76,13 +76,6 @@ def build_song_hashes(output_dir=Path("cache")):
|
|||||||
all_tja_files: list[Path] = []
|
all_tja_files: list[Path] = []
|
||||||
for root_dir in tja_paths:
|
for root_dir in tja_paths:
|
||||||
root_path = Path(root_dir)
|
root_path = Path(root_dir)
|
||||||
'''
|
|
||||||
if (root_path / '.git').exists():
|
|
||||||
repo = Repo(root_path)
|
|
||||||
origin = repo.remotes.origin
|
|
||||||
origin.pull()
|
|
||||||
print('Pulled latest from', root_path)
|
|
||||||
'''
|
|
||||||
all_tja_files.extend(root_path.rglob("*.tja"))
|
all_tja_files.extend(root_path.rglob("*.tja"))
|
||||||
|
|
||||||
global_data.total_songs = len(all_tja_files)
|
global_data.total_songs = len(all_tja_files)
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ class TextureWrapper:
|
|||||||
self.load_zip(screen_name, zip.name)
|
self.load_zip(screen_name, zip.name)
|
||||||
|
|
||||||
def control(self, tex_object: Texture, index: int = 0):
|
def control(self, tex_object: Texture, index: int = 0):
|
||||||
|
'''debug function'''
|
||||||
distance = 1
|
distance = 1
|
||||||
if ray.is_key_down(ray.KeyboardKey.KEY_LEFT_SHIFT):
|
if ray.is_key_down(ray.KeyboardKey.KEY_LEFT_SHIFT):
|
||||||
distance = 10
|
distance = 10
|
||||||
|
|||||||
@@ -39,27 +39,6 @@ def force_dedicated_gpu():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
def load_all_textures_from_zip(zip_path: Path) -> dict[str, list[ray.Texture]]:
|
|
||||||
result_dict = dict()
|
|
||||||
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
|
|
||||||
files = zip_ref.namelist()
|
|
||||||
for file in files:
|
|
||||||
with zip_ref.open(file) as image_file:
|
|
||||||
with tempfile.NamedTemporaryFile(delete=False, suffix='.png') as temp_file:
|
|
||||||
temp_file.write(image_file.read())
|
|
||||||
temp_file_path = temp_file.name
|
|
||||||
texture = ray.load_texture(temp_file_path)
|
|
||||||
os.remove(temp_file_path)
|
|
||||||
|
|
||||||
true_filename, index = file.split('_img')
|
|
||||||
index = int(index.split('.')[0])
|
|
||||||
if true_filename not in result_dict:
|
|
||||||
result_dict[true_filename] = []
|
|
||||||
while len(result_dict[true_filename]) <= index:
|
|
||||||
result_dict[true_filename].append(None)
|
|
||||||
result_dict[true_filename][index] = texture
|
|
||||||
return result_dict
|
|
||||||
|
|
||||||
def rounded(num: float) -> int:
|
def rounded(num: float) -> int:
|
||||||
sign = 1 if (num >= 0) else -1
|
sign = 1 if (num >= 0) else -1
|
||||||
num = abs(num)
|
num = abs(num)
|
||||||
@@ -143,6 +122,8 @@ def is_r_don_pressed() -> bool:
|
|||||||
if ray.is_gamepad_button_pressed(0, button):
|
if ray.is_gamepad_button_pressed(0, button):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
if not global_data.config["general"]["touch_enabled"]:
|
||||||
|
return False
|
||||||
mid_x, mid_y = (1280//2, 720)
|
mid_x, mid_y = (1280//2, 720)
|
||||||
allowed_gestures = {ray.Gesture.GESTURE_TAP, ray.Gesture.GESTURE_DOUBLETAP}
|
allowed_gestures = {ray.Gesture.GESTURE_TAP, ray.Gesture.GESTURE_DOUBLETAP}
|
||||||
if ray.get_gesture_detected() in allowed_gestures and ray.is_gesture_detected(ray.get_gesture_detected()):
|
if ray.get_gesture_detected() in allowed_gestures and ray.is_gesture_detected(ray.get_gesture_detected()):
|
||||||
@@ -165,6 +146,8 @@ def is_l_kat_pressed() -> bool:
|
|||||||
if ray.is_gamepad_button_pressed(0, button):
|
if ray.is_gamepad_button_pressed(0, button):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
if not global_data.config["general"]["touch_enabled"]:
|
||||||
|
return False
|
||||||
mid_x, mid_y = (1280//2, 720)
|
mid_x, mid_y = (1280//2, 720)
|
||||||
allowed_gestures = {ray.Gesture.GESTURE_TAP, ray.Gesture.GESTURE_DOUBLETAP}
|
allowed_gestures = {ray.Gesture.GESTURE_TAP, ray.Gesture.GESTURE_DOUBLETAP}
|
||||||
if ray.get_gesture_detected() in allowed_gestures and ray.is_gesture_detected(ray.get_gesture_detected()):
|
if ray.get_gesture_detected() in allowed_gestures and ray.is_gesture_detected(ray.get_gesture_detected()):
|
||||||
@@ -187,6 +170,8 @@ def is_r_kat_pressed() -> bool:
|
|||||||
if ray.is_gamepad_button_pressed(0, button):
|
if ray.is_gamepad_button_pressed(0, button):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
if not global_data.config["general"]["touch_enabled"]:
|
||||||
|
return False
|
||||||
mid_x, mid_y = (1280//2, 720)
|
mid_x, mid_y = (1280//2, 720)
|
||||||
allowed_gestures = {ray.Gesture.GESTURE_TAP, ray.Gesture.GESTURE_DOUBLETAP}
|
allowed_gestures = {ray.Gesture.GESTURE_TAP, ray.Gesture.GESTURE_DOUBLETAP}
|
||||||
if ray.get_gesture_detected() in allowed_gestures and ray.is_gesture_detected(ray.get_gesture_detected()):
|
if ray.get_gesture_detected() in allowed_gestures and ray.is_gesture_detected(ray.get_gesture_detected()):
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ dependencies = [
|
|||||||
"moviepy>=2.1.2",
|
"moviepy>=2.1.2",
|
||||||
"raylib-sdl>=5.5.0.2",
|
"raylib-sdl>=5.5.0.2",
|
||||||
"tomlkit>=0.13.3",
|
"tomlkit>=0.13.3",
|
||||||
"dotenv>=0.9.9",
|
|
||||||
"gitpython>=3.1.44",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.vulture]
|
[tool.vulture]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import pyray as ray
|
|||||||
|
|
||||||
from libs.global_objects import Indicator
|
from libs.global_objects import Indicator
|
||||||
from libs.utils import get_current_ms
|
from libs.utils import get_current_ms
|
||||||
|
from libs.texture import tex
|
||||||
|
|
||||||
|
|
||||||
class DevScreen:
|
class DevScreen:
|
||||||
@@ -9,10 +10,12 @@ class DevScreen:
|
|||||||
self.width = 1280
|
self.width = 1280
|
||||||
self.height = 720
|
self.height = 720
|
||||||
self.screen_init = False
|
self.screen_init = False
|
||||||
|
self.length = 100
|
||||||
|
|
||||||
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
|
||||||
|
tex.load_screen_textures('game')
|
||||||
self.indicator = Indicator(Indicator.State.SELECT)
|
self.indicator = Indicator(Indicator.State.SELECT)
|
||||||
|
|
||||||
def on_screen_end(self, next_screen: str):
|
def on_screen_end(self, next_screen: str):
|
||||||
@@ -24,10 +27,27 @@ class DevScreen:
|
|||||||
self.indicator.update(get_current_ms())
|
self.indicator.update(get_current_ms())
|
||||||
if ray.is_key_pressed(ray.KeyboardKey.KEY_ENTER):
|
if ray.is_key_pressed(ray.KeyboardKey.KEY_ENTER):
|
||||||
return self.on_screen_end('GAME')
|
return self.on_screen_end('GAME')
|
||||||
|
elif ray.is_key_pressed(ray.KeyboardKey.KEY_RIGHT):
|
||||||
|
self.length += 1
|
||||||
|
print(self.length)
|
||||||
|
elif ray.is_key_pressed(ray.KeyboardKey.KEY_LEFT):
|
||||||
|
self.length -= 1
|
||||||
|
print(self.length)
|
||||||
|
elif ray.is_key_pressed(ray.KeyboardKey.KEY_UP):
|
||||||
|
self.length += 10
|
||||||
|
print(self.length)
|
||||||
|
elif ray.is_key_pressed(ray.KeyboardKey.KEY_DOWN):
|
||||||
|
self.length -= 10
|
||||||
|
print(self.length)
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
ray.draw_rectangle(0, 0, 1280, 720, ray.GREEN)
|
ray.draw_rectangle(0, 0, 1280, 720, ray.GREEN)
|
||||||
self.indicator.draw(430, 575)
|
start_position = 100
|
||||||
|
end_position = start_position + self.length
|
||||||
|
color = ray.WHITE
|
||||||
|
tex.draw_texture('notes', "8", frame=0, x=start_position+64, y=192, x2=self.length-47, color=color)
|
||||||
|
tex.draw_texture('notes', "drumroll_tail", x=end_position+64, y=192, color=color)
|
||||||
|
tex.draw_texture('notes', str(5), frame=0, x=start_position, y=192, color=color)
|
||||||
|
|
||||||
def draw_3d(self):
|
def draw_3d(self):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -282,16 +282,11 @@ class Box:
|
|||||||
|
|
||||||
def _draw_highlighted(self, color):
|
def _draw_highlighted(self, color):
|
||||||
texture_left = tex.textures['mode_select']['box_highlight_left'].texture
|
texture_left = tex.textures['mode_select']['box_highlight_left'].texture
|
||||||
texture_center = tex.textures['mode_select']['box_highlight_center'].texture
|
if isinstance(texture_left, list):
|
||||||
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")
|
raise Exception("highlight textures cannot be iterable")
|
||||||
center_src = ray.Rectangle(0, 0, texture_center.width, texture_center.height)
|
tex.draw_texture('mode_select', 'box_highlight_center', x=self.left_x + texture_left.width, y=self.y, x2=self.right_x - self.left_x -15, color=color)
|
||||||
center_dest = ray.Rectangle(self.left_x + texture_left.width, self.y, self.right_x - self.left_x, texture_center.height)
|
tex.draw_texture('mode_select', 'box_highlight_left', x=self.left_x, y=self.y, color=color)
|
||||||
ray.draw_texture_pro(texture_center, center_src, center_dest, ray.Vector2(0, 0), 0, color)
|
tex.draw_texture('mode_select', 'box_highlight_right', x=self.right_x, y=self.y, color=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):
|
def _draw_text(self, color):
|
||||||
text_x = self.x + (self.texture.width//2) - (self.text.texture.width//2)
|
text_x = self.x + (self.texture.width//2) - (self.text.texture.width//2)
|
||||||
|
|||||||
388
scenes/game.py
388
scenes/game.py
@@ -284,17 +284,10 @@ class Player:
|
|||||||
self.autoplay_hit_side = 'L'
|
self.autoplay_hit_side = 'L'
|
||||||
self.last_subdivision = -1
|
self.last_subdivision = -1
|
||||||
|
|
||||||
# Performance optimization: cache frequently used string conversions
|
|
||||||
self._cached_combo_str = ""
|
|
||||||
self._cached_combo = -1
|
|
||||||
self._cached_score_str = ""
|
|
||||||
self._cached_score = -1
|
|
||||||
|
|
||||||
def get_result_score(self):
|
def get_result_score(self):
|
||||||
return self.score, self.good_count, self.ok_count, self.bad_count, self.max_combo, self.total_drumroll
|
return self.score, self.good_count, self.ok_count, self.bad_count, self.max_combo, self.total_drumroll
|
||||||
|
|
||||||
def get_position_x(self, width: int, current_ms: float, load_ms: float, pixels_per_frame: float) -> int:
|
def get_position_x(self, width: int, current_ms: float, load_ms: float, pixels_per_frame: float) -> int:
|
||||||
# Cache frequently used calculations
|
|
||||||
time_diff = load_ms - current_ms
|
time_diff = load_ms - current_ms
|
||||||
return int(width + pixels_per_frame * 0.06 * time_diff - 64) - self.visual_offset
|
return int(width + pixels_per_frame * 0.06 * time_diff - 64) - self.visual_offset
|
||||||
|
|
||||||
@@ -306,16 +299,15 @@ class Player:
|
|||||||
if not animation_list:
|
if not animation_list:
|
||||||
return
|
return
|
||||||
|
|
||||||
# More efficient: collect finished animations and remove them in one pass
|
# More efficient: use list comprehension to filter out finished animations
|
||||||
finished_indices = []
|
remaining_animations = []
|
||||||
for i, animation in enumerate(animation_list):
|
for animation in animation_list:
|
||||||
animation.update(current_time)
|
animation.update(current_time)
|
||||||
if animation.is_finished:
|
if not animation.is_finished:
|
||||||
finished_indices.append(i)
|
remaining_animations.append(animation)
|
||||||
|
|
||||||
# Remove in reverse order to maintain indices
|
# Replace the original list contents
|
||||||
for i in reversed(finished_indices):
|
animation_list[:] = remaining_animations
|
||||||
animation_list.pop(i)
|
|
||||||
|
|
||||||
def bar_manager(self, current_ms: float):
|
def bar_manager(self, current_ms: float):
|
||||||
#Add bar to current_bars list if it is ready to be shown on screen
|
#Add bar to current_bars list if it is ready to be shown on screen
|
||||||
@@ -370,7 +362,7 @@ class Player:
|
|||||||
if 5 <= current_note.type <= 7:
|
if 5 <= current_note.type <= 7:
|
||||||
bisect.insort_left(self.current_notes_draw, current_note, key=lambda x: x.index)
|
bisect.insort_left(self.current_notes_draw, current_note, key=lambda x: x.index)
|
||||||
try:
|
try:
|
||||||
tail_note = next(note for note in self.draw_note_list if note.type == 8)
|
tail_note = next((note for note in self.draw_note_list if note.type == 8))
|
||||||
bisect.insort_left(self.current_notes_draw, tail_note, key=lambda x: x.index)
|
bisect.insort_left(self.current_notes_draw, tail_note, key=lambda x: x.index)
|
||||||
self.draw_note_list.remove(tail_note)
|
self.draw_note_list.remove(tail_note)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -648,11 +640,12 @@ class Player:
|
|||||||
length = end_position - start_position
|
length = end_position - start_position
|
||||||
color = ray.Color(255, head.color, head.color, 255)
|
color = ray.Color(255, head.color, head.color, 255)
|
||||||
if head.display:
|
if head.display:
|
||||||
tex.draw_texture('notes', "8", frame=is_big, x=start_position+64, y=192, x2=length-64-32, color=color)
|
if length > 0:
|
||||||
|
tex.draw_texture('notes', "8", frame=is_big, x=start_position+64, y=192, x2=length-47, color=color)
|
||||||
if is_big:
|
if is_big:
|
||||||
tex.draw_texture('notes', "drumroll_big_tail", x=end_position, y=192, color=color)
|
tex.draw_texture('notes', "drumroll_big_tail", x=end_position+64, y=192, color=color)
|
||||||
else:
|
else:
|
||||||
tex.draw_texture('notes', "drumroll_tail", x=end_position, y=192, color=color)
|
tex.draw_texture('notes', "drumroll_tail", x=end_position+64, y=192, color=color)
|
||||||
tex.draw_texture('notes', str(head.type), frame=current_eighth % 2, x=start_position, y=192, color=color)
|
tex.draw_texture('notes', str(head.type), frame=current_eighth % 2, x=start_position, y=192, color=color)
|
||||||
|
|
||||||
tex.draw_texture('notes', 'moji_drumroll_mid', x=start_position + 60, y=323, x2=length)
|
tex.draw_texture('notes', 'moji_drumroll_mid', x=start_position + 60, y=323, x2=length)
|
||||||
@@ -679,12 +672,18 @@ class Player:
|
|||||||
if not self.current_bars:
|
if not self.current_bars:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Batch bar draws by pre-calculating positions
|
||||||
|
bar_draws = []
|
||||||
for bar in reversed(self.current_bars):
|
for bar in reversed(self.current_bars):
|
||||||
if not bar.display:
|
if not bar.display:
|
||||||
continue
|
continue
|
||||||
x_position = self.get_position_x(SCREEN_WIDTH, current_ms, bar.load_ms, bar.pixels_per_frame_x)
|
x_position = self.get_position_x(SCREEN_WIDTH, current_ms, bar.load_ms, bar.pixels_per_frame_x)
|
||||||
y_position = self.get_position_y(current_ms, bar.load_ms, bar.pixels_per_frame_y, bar.pixels_per_frame_x)
|
y_position = self.get_position_y(current_ms, bar.load_ms, bar.pixels_per_frame_y, bar.pixels_per_frame_x)
|
||||||
tex.draw_texture('notes', str(bar.type), x=x_position+60, y=y_position+190)
|
bar_draws.append((str(bar.type), x_position+60, y_position+190))
|
||||||
|
|
||||||
|
# Draw all bars in one batch
|
||||||
|
for bar_type, x, y in bar_draws:
|
||||||
|
tex.draw_texture('notes', bar_type, x=x, y=y)
|
||||||
|
|
||||||
def draw_notes(self, current_ms: float, start_ms: float):
|
def draw_notes(self, current_ms: float, start_ms: float):
|
||||||
if not self.current_notes_draw:
|
if not self.current_notes_draw:
|
||||||
@@ -695,71 +694,116 @@ class Player:
|
|||||||
if self.combo >= 50 and eighth_in_ms != 0:
|
if self.combo >= 50 and eighth_in_ms != 0:
|
||||||
current_eighth = int((current_ms - start_ms) // eighth_in_ms)
|
current_eighth = int((current_ms - start_ms) // eighth_in_ms)
|
||||||
|
|
||||||
|
# Separate notes by type for better batching
|
||||||
|
regular_notes = []
|
||||||
|
drumrolls = []
|
||||||
|
balloons = []
|
||||||
|
|
||||||
for note in reversed(self.current_notes_draw):
|
for note in reversed(self.current_notes_draw):
|
||||||
if self.is_balloon and note == self.current_notes_draw[0]:
|
if self.is_balloon and note == self.current_notes_draw[0]:
|
||||||
continue
|
continue
|
||||||
if note.type == 8:
|
if note.type == 8:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if isinstance(note, Drumroll):
|
||||||
|
drumrolls.append(note)
|
||||||
|
elif isinstance(note, Balloon) and not note.is_kusudama:
|
||||||
|
balloons.append(note)
|
||||||
|
else:
|
||||||
|
regular_notes.append(note)
|
||||||
|
|
||||||
|
# Draw drumrolls first
|
||||||
|
for note in drumrolls:
|
||||||
|
self.draw_drumroll(current_ms, note, current_eighth)
|
||||||
|
|
||||||
|
# Draw balloons
|
||||||
|
for note in balloons:
|
||||||
x_position = self.get_position_x(SCREEN_WIDTH, current_ms, note.load_ms, note.pixels_per_frame_x)
|
x_position = self.get_position_x(SCREEN_WIDTH, current_ms, note.load_ms, note.pixels_per_frame_x)
|
||||||
y_position = self.get_position_y(current_ms, note.load_ms, note.pixels_per_frame_y, note.pixels_per_frame_x)
|
y_position = self.get_position_y(current_ms, note.load_ms, note.pixels_per_frame_y, note.pixels_per_frame_x)
|
||||||
if isinstance(note, Drumroll):
|
|
||||||
self.draw_drumroll(current_ms, note, current_eighth)
|
|
||||||
elif isinstance(note, Balloon) and not note.is_kusudama:
|
|
||||||
self.draw_balloon(current_ms, note, current_eighth)
|
self.draw_balloon(current_ms, note, current_eighth)
|
||||||
tex.draw_texture('notes', 'moji', frame=note.moji, x=x_position - (168//2) + 64, y=323 + y_position)
|
tex.draw_texture('notes', 'moji', frame=note.moji, x=x_position - (168//2) + 64, y=323 + y_position)
|
||||||
else:
|
|
||||||
|
# Draw regular notes in batches
|
||||||
|
for note in regular_notes:
|
||||||
|
x_position = self.get_position_x(SCREEN_WIDTH, current_ms, note.load_ms, note.pixels_per_frame_x)
|
||||||
|
y_position = self.get_position_y(current_ms, note.load_ms, note.pixels_per_frame_y, note.pixels_per_frame_x)
|
||||||
if note.display:
|
if note.display:
|
||||||
tex.draw_texture('notes', str(note.type), frame=current_eighth % 2, x=x_position, y=y_position+192, center=True)
|
tex.draw_texture('notes', str(note.type), frame=current_eighth % 2, x=x_position, y=y_position+192, center=True)
|
||||||
tex.draw_texture('notes', 'moji', frame=note.moji, x=x_position - (168//2) + 64, y=323 + y_position)
|
tex.draw_texture('notes', 'moji', frame=note.moji, x=x_position - (168//2) + 64, y=323 + y_position)
|
||||||
|
|
||||||
def draw_modifiers(self):
|
def draw_modifiers(self):
|
||||||
tex.draw_texture('lane', 'mod_shinuchi')
|
# Batch modifier texture draws to reduce state changes
|
||||||
|
modifiers_to_draw = ['mod_shinuchi']
|
||||||
|
|
||||||
|
# Speed modifiers
|
||||||
if global_data.modifiers.speed >= 4:
|
if global_data.modifiers.speed >= 4:
|
||||||
tex.draw_texture('lane', 'mod_yonbai')
|
modifiers_to_draw.append('mod_yonbai')
|
||||||
elif global_data.modifiers.speed >= 3:
|
elif global_data.modifiers.speed >= 3:
|
||||||
tex.draw_texture('lane', 'mod_sanbai')
|
modifiers_to_draw.append('mod_sanbai')
|
||||||
elif global_data.modifiers.speed > 1:
|
elif global_data.modifiers.speed > 1:
|
||||||
tex.draw_texture('lane', 'mod_baisaku')
|
modifiers_to_draw.append('mod_baisaku')
|
||||||
|
|
||||||
|
# Other modifiers
|
||||||
if global_data.modifiers.display:
|
if global_data.modifiers.display:
|
||||||
tex.draw_texture('lane', 'mod_doron')
|
modifiers_to_draw.append('mod_doron')
|
||||||
if global_data.modifiers.inverse:
|
if global_data.modifiers.inverse:
|
||||||
tex.draw_texture('lane', 'mod_abekobe')
|
modifiers_to_draw.append('mod_abekobe')
|
||||||
if global_data.modifiers.random == 2:
|
if global_data.modifiers.random == 2:
|
||||||
tex.draw_texture('lane', 'mod_detarame')
|
modifiers_to_draw.append('mod_detarame')
|
||||||
elif global_data.modifiers.random == 1:
|
elif global_data.modifiers.random == 1:
|
||||||
tex.draw_texture('lane', 'mod_kimagure')
|
modifiers_to_draw.append('mod_kimagure')
|
||||||
|
|
||||||
|
# Draw all modifiers in one batch
|
||||||
|
for modifier in modifiers_to_draw:
|
||||||
|
tex.draw_texture('lane', modifier)
|
||||||
|
|
||||||
def draw(self, game_screen: GameScreen):
|
def draw(self, game_screen: GameScreen):
|
||||||
|
current_ms = game_screen.current_ms
|
||||||
|
|
||||||
|
# Group 1: Background and lane elements
|
||||||
tex.draw_texture('lane', 'lane_background')
|
tex.draw_texture('lane', 'lane_background')
|
||||||
self.gauge.draw()
|
self.gauge.draw()
|
||||||
if self.lane_hit_effect is not None:
|
if self.lane_hit_effect is not None:
|
||||||
self.lane_hit_effect.draw()
|
self.lane_hit_effect.draw()
|
||||||
tex.draw_texture('lane', 'lane_hit_circle')
|
tex.draw_texture('lane', 'lane_hit_circle')
|
||||||
|
|
||||||
|
# Group 2: Judgement and hit effects
|
||||||
for anim in self.draw_judge_list:
|
for anim in self.draw_judge_list:
|
||||||
anim.draw()
|
anim.draw()
|
||||||
current_ms = game_screen.current_ms
|
|
||||||
|
# Group 3: Notes and bars (game content)
|
||||||
self.draw_bars(current_ms)
|
self.draw_bars(current_ms)
|
||||||
self.draw_notes(current_ms, game_screen.start_ms)
|
self.draw_notes(current_ms, game_screen.start_ms)
|
||||||
|
|
||||||
|
# Group 4: Lane covers and UI elements (batch similar textures)
|
||||||
tex.draw_texture('lane', f'{self.player_number}p_lane_cover')
|
tex.draw_texture('lane', f'{self.player_number}p_lane_cover')
|
||||||
tex.draw_texture('lane', 'drum')
|
tex.draw_texture('lane', 'drum')
|
||||||
if global_data.modifiers.auto:
|
if global_data.modifiers.auto:
|
||||||
tex.draw_texture('lane', 'auto_icon')
|
tex.draw_texture('lane', 'auto_icon')
|
||||||
|
|
||||||
|
# Group 5: Hit effects and animations
|
||||||
for anim in self.draw_drum_hit_list:
|
for anim in self.draw_drum_hit_list:
|
||||||
anim.draw()
|
anim.draw()
|
||||||
self.combo_display.draw()
|
|
||||||
tex.draw_texture('lane', 'lane_score_cover')
|
|
||||||
tex.draw_texture('lane', f'{self.player_number}p_icon')
|
|
||||||
tex.draw_texture('lane', 'lane_difficulty', frame=self.difficulty)
|
|
||||||
if not global_data.modifiers.auto:
|
|
||||||
self.nameplate.draw(-62, 285)
|
|
||||||
self.draw_modifiers()
|
|
||||||
self.chara.draw()
|
|
||||||
if self.drumroll_counter is not None:
|
|
||||||
self.drumroll_counter.draw()
|
|
||||||
for anim in self.draw_arc_list:
|
for anim in self.draw_arc_list:
|
||||||
anim.draw(game_screen)
|
anim.draw(game_screen)
|
||||||
for anim in self.gauge_hit_effect:
|
for anim in self.gauge_hit_effect:
|
||||||
anim.draw()
|
anim.draw()
|
||||||
|
|
||||||
|
# Group 6: UI overlays
|
||||||
|
self.combo_display.draw()
|
||||||
|
tex.draw_texture('lane', 'lane_score_cover')
|
||||||
|
tex.draw_texture('lane', f'{self.player_number}p_icon')
|
||||||
|
tex.draw_texture('lane', 'lane_difficulty', frame=self.difficulty)
|
||||||
|
|
||||||
|
# Group 7: Player-specific elements
|
||||||
|
if not global_data.modifiers.auto:
|
||||||
|
self.nameplate.draw(-62, 285)
|
||||||
|
self.draw_modifiers()
|
||||||
|
self.chara.draw()
|
||||||
|
|
||||||
|
# Group 8: Special animations and counters
|
||||||
|
if self.drumroll_counter is not None:
|
||||||
|
self.drumroll_counter.draw()
|
||||||
if self.balloon_anim is not None:
|
if self.balloon_anim is not None:
|
||||||
self.balloon_anim.draw()
|
self.balloon_anim.draw()
|
||||||
if self.kusudama_anim is not None:
|
if self.kusudama_anim is not None:
|
||||||
@@ -791,7 +835,6 @@ class Judgement:
|
|||||||
self.texture_animation.start()
|
self.texture_animation.start()
|
||||||
|
|
||||||
def update(self, current_ms):
|
def update(self, current_ms):
|
||||||
# Batch animation updates for better performance
|
|
||||||
animations = [self.fade_animation_1, self.fade_animation_2, self.move_animation, self.texture_animation]
|
animations = [self.fade_animation_1, self.fade_animation_2, self.move_animation, self.texture_animation]
|
||||||
for anim in animations:
|
for anim in animations:
|
||||||
anim.update(current_ms)
|
anim.update(current_ms)
|
||||||
@@ -801,32 +844,27 @@ class Judgement:
|
|||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
y = self.move_animation.attribute
|
y = self.move_animation.attribute
|
||||||
index = int(self.texture_animation.attribute)
|
index = self.texture_animation.attribute
|
||||||
hit_color = ray.fade(ray.WHITE, self.fade_animation_1.attribute)
|
hit_fade = self.fade_animation_1.attribute
|
||||||
color = ray.fade(ray.WHITE, self.fade_animation_2.attribute)
|
fade = self.fade_animation_2.attribute
|
||||||
if self.curr_hit_ms is not None:
|
|
||||||
if float(self.curr_hit_ms) < -(global_data.config['general']['hard_judge']):
|
|
||||||
color = ray.fade(ray.BLUE, self.fade_animation_2.attribute)
|
|
||||||
elif float(self.curr_hit_ms) > (global_data.config['general']['hard_judge']):
|
|
||||||
color = ray.fade(ray.RED, self.fade_animation_2.attribute)
|
|
||||||
if self.type == 'GOOD':
|
if self.type == 'GOOD':
|
||||||
if self.big:
|
if self.big:
|
||||||
tex.draw_texture('hit_effect', 'hit_effect_good_big', color=color)
|
tex.draw_texture('hit_effect', 'hit_effect_good_big', fade=fade)
|
||||||
tex.draw_texture('hit_effect', 'outer_good_big', frame=index, color=hit_color)
|
tex.draw_texture('hit_effect', 'outer_good_big', frame=index, fade=hit_fade)
|
||||||
else:
|
else:
|
||||||
tex.draw_texture('hit_effect', 'hit_effect_good', color=color)
|
tex.draw_texture('hit_effect', 'hit_effect_good', fade=fade)
|
||||||
tex.draw_texture('hit_effect', 'outer_good', frame=index, color=hit_color)
|
tex.draw_texture('hit_effect', 'outer_good', frame=index, fade=hit_fade)
|
||||||
tex.draw_texture('hit_effect', 'judge_good', y=y, color=color)
|
tex.draw_texture('hit_effect', 'judge_good', y=y, fade=fade)
|
||||||
elif self.type == 'OK':
|
elif self.type == 'OK':
|
||||||
if self.big:
|
if self.big:
|
||||||
tex.draw_texture('hit_effect', 'hit_effect_ok_big', color=color)
|
tex.draw_texture('hit_effect', 'hit_effect_ok_big', fade=fade)
|
||||||
tex.draw_texture('hit_effect', 'outer_ok_big', frame=index, color=hit_color)
|
tex.draw_texture('hit_effect', 'outer_ok_big', frame=index, fade=hit_fade)
|
||||||
else:
|
else:
|
||||||
tex.draw_texture('hit_effect', 'hit_effect_ok', color=color)
|
tex.draw_texture('hit_effect', 'hit_effect_ok', fade=fade)
|
||||||
tex.draw_texture('hit_effect', 'outer_ok', frame=index, color=hit_color)
|
tex.draw_texture('hit_effect', 'outer_ok', frame=index, fade=hit_fade)
|
||||||
tex.draw_texture('hit_effect', 'judge_ok', y=y, color=color)
|
tex.draw_texture('hit_effect', 'judge_ok', y=y, fade=fade)
|
||||||
elif self.type == 'BAD':
|
elif self.type == 'BAD':
|
||||||
tex.draw_texture('hit_effect', 'judge_bad', y=y, color=color)
|
tex.draw_texture('hit_effect', 'judge_bad', y=y, fade=fade)
|
||||||
|
|
||||||
class LaneHitEffect:
|
class LaneHitEffect:
|
||||||
def __init__(self, type: str):
|
def __init__(self, type: str):
|
||||||
@@ -874,6 +912,9 @@ class DrumHitEffect:
|
|||||||
tex.draw_texture('lane', 'drum_kat_r', fade=self.fade.attribute)
|
tex.draw_texture('lane', 'drum_kat_r', fade=self.fade.attribute)
|
||||||
|
|
||||||
class GaugeHitEffect:
|
class GaugeHitEffect:
|
||||||
|
# Pre-define color thresholds for better performance
|
||||||
|
_COLOR_THRESHOLDS = [(0.70, ray.WHITE), (0.80, ray.YELLOW), (0.90, ray.ORANGE), (1.00, ray.RED)]
|
||||||
|
|
||||||
def __init__(self, note_type: int, big: bool):
|
def __init__(self, note_type: int, big: bool):
|
||||||
self.note_type = note_type
|
self.note_type = note_type
|
||||||
self.is_big = big
|
self.is_big = big
|
||||||
@@ -889,53 +930,104 @@ class GaugeHitEffect:
|
|||||||
self.rotation.start()
|
self.rotation.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
|
||||||
|
|
||||||
|
self.texture_color = ray.WHITE
|
||||||
|
self.dest_width = 152
|
||||||
|
self.dest_height = 152
|
||||||
|
self.origin = ray.Vector2(76, 76) # 152/2
|
||||||
|
self.rotation_angle = 0
|
||||||
|
self.x2_pos = -152
|
||||||
|
self.y2_pos = -152
|
||||||
|
|
||||||
|
# Cache for texture selection
|
||||||
|
self.circle_texture = 'hit_effect_circle_big' if self.is_big else 'hit_effect_circle'
|
||||||
|
self._last_resize_value = -1
|
||||||
|
self._cached_texture_color = ray.WHITE
|
||||||
|
|
||||||
|
def _get_texture_color_for_resize(self, resize_value):
|
||||||
|
"""Calculate texture color based on resize attribute value with caching"""
|
||||||
|
# Use cached value if resize hasn't changed significantly
|
||||||
|
if abs(resize_value - self._last_resize_value) < 0.01:
|
||||||
|
return self._cached_texture_color
|
||||||
|
|
||||||
|
self._last_resize_value = resize_value
|
||||||
|
|
||||||
|
if resize_value >= 1.00:
|
||||||
|
self._cached_texture_color = ray.RED
|
||||||
|
else:
|
||||||
|
# Use pre-defined thresholds for faster lookup
|
||||||
|
self._cached_texture_color = ray.WHITE
|
||||||
|
for threshold, color in self._COLOR_THRESHOLDS:
|
||||||
|
if resize_value <= threshold:
|
||||||
|
self._cached_texture_color = color
|
||||||
|
break
|
||||||
|
|
||||||
|
return self._cached_texture_color
|
||||||
|
|
||||||
def update(self, current_ms):
|
def update(self, current_ms):
|
||||||
|
# Update all animations
|
||||||
self.texture_change.update(current_ms)
|
self.texture_change.update(current_ms)
|
||||||
self.circle_fadein.update(current_ms)
|
self.circle_fadein.update(current_ms)
|
||||||
self.fade_out.update(current_ms)
|
self.fade_out.update(current_ms)
|
||||||
self.resize.update(current_ms)
|
self.resize.update(current_ms)
|
||||||
self.rotation.update(current_ms)
|
self.rotation.update(current_ms)
|
||||||
color = ray.YELLOW
|
|
||||||
if self.circle_fadein.is_finished:
|
# Update circle color with optimized calculation
|
||||||
color = ray.WHITE
|
base_color = ray.WHITE if self.circle_fadein.is_finished else ray.YELLOW
|
||||||
self.color = ray.fade(color, min(self.fade_out.attribute, self.circle_fadein.attribute))
|
fade_value = min(self.fade_out.attribute, self.circle_fadein.attribute)
|
||||||
|
self.color = ray.fade(base_color, fade_value)
|
||||||
|
|
||||||
|
# Pre-compute drawing values only when resize changes significantly
|
||||||
|
resize_val = self.resize.attribute
|
||||||
|
if abs(resize_val - getattr(self, '_last_resize_calc', -1)) > 0.005:
|
||||||
|
self._last_resize_calc = resize_val
|
||||||
|
self.texture_color = self._get_texture_color_for_resize(resize_val)
|
||||||
|
self.dest_width = 152 * resize_val
|
||||||
|
self.dest_height = 152 * resize_val
|
||||||
|
self.origin = ray.Vector2(self.dest_width / 2, self.dest_height / 2)
|
||||||
|
self.x2_pos = -152 + (152 * resize_val)
|
||||||
|
self.y2_pos = -152 + (152 * resize_val)
|
||||||
|
|
||||||
|
self.rotation_angle = self.rotation.attribute * 100
|
||||||
|
|
||||||
|
# Check if finished
|
||||||
if self.fade_out.is_finished:
|
if self.fade_out.is_finished:
|
||||||
self.is_finished = True
|
self.is_finished = True
|
||||||
def draw(self):
|
|
||||||
color_map = {0.70: ray.WHITE, 0.80: ray.YELLOW, 0.90: ray.ORANGE, 1.00: ray.RED}
|
|
||||||
texture_color = ray.WHITE
|
|
||||||
for upper_bound, color in color_map.items():
|
|
||||||
lower_bound = list(color_map.keys())[list(color_map.keys()).index(upper_bound) - 1] if list(color_map.keys()).index(upper_bound) > 0 else 0.70
|
|
||||||
|
|
||||||
if lower_bound <= self.resize.attribute <= upper_bound:
|
def draw(self):
|
||||||
texture_color = color
|
fade_value = self.fade_out.attribute
|
||||||
elif self.resize.attribute >= upper_bound:
|
|
||||||
texture_color = ray.RED
|
# Main hit effect texture
|
||||||
dest_width = 152 * self.resize.attribute
|
tex.draw_texture('gauge', 'hit_effect',
|
||||||
dest_height = 152 * self.resize.attribute
|
frame=self.texture_change.attribute,
|
||||||
origin = ray.Vector2(dest_width / 2, dest_height / 2)
|
x2=self.x2_pos,
|
||||||
rotation = self.rotation.attribute*100
|
y2=self.y2_pos,
|
||||||
tex.draw_texture('gauge', 'hit_effect', frame=self.texture_change.attribute, x2=-152 + (152 * self.resize.attribute), y2=-152 + (152 * self.resize.attribute), color=ray.fade(texture_color, self.fade_out.attribute), origin=origin, rotation=rotation, center=True)
|
color=ray.fade(self.texture_color, fade_value),
|
||||||
tex.draw_texture('notes', str(self.note_type), x=1158, y=101, fade=self.fade_out.attribute)
|
origin=self.origin,
|
||||||
if self.is_big:
|
rotation=self.rotation_angle,
|
||||||
tex.draw_texture('gauge', 'hit_effect_circle_big', color=self.color)
|
center=True)
|
||||||
else:
|
|
||||||
tex.draw_texture('gauge', 'hit_effect_circle', color=self.color)
|
# Note type texture
|
||||||
|
tex.draw_texture('notes', str(self.note_type),
|
||||||
|
x=1158, y=101,
|
||||||
|
fade=fade_value)
|
||||||
|
|
||||||
|
# Circle effect texture (use cached texture name)
|
||||||
|
tex.draw_texture('gauge', self.circle_texture, color=self.color)
|
||||||
|
|
||||||
class NoteArc:
|
class NoteArc:
|
||||||
def __init__(self, note_type: int, current_ms: float, player_number: int, big: bool, is_balloon: bool):
|
def __init__(self, note_type: int, current_ms: float, player_number: int, big: bool, is_balloon: bool):
|
||||||
self.note_type = note_type
|
self.note_type = note_type
|
||||||
self.is_big = big
|
self.is_big = big
|
||||||
self.is_balloon = is_balloon
|
self.is_balloon = is_balloon
|
||||||
self.arc_points = 22
|
self.arc_points = 100
|
||||||
|
self.arc_duration = 22
|
||||||
self.current_progress = 0
|
self.current_progress = 0
|
||||||
self.create_ms = current_ms
|
self.create_ms = current_ms
|
||||||
self.player_number = player_number
|
self.player_number = player_number
|
||||||
curve_height = 425
|
curve_height = 425
|
||||||
|
|
||||||
self.start_x, self.start_y = 350, 192
|
self.start_x, self.start_y = 350, 192
|
||||||
self.end_x, self.end_y = 1158, 101
|
self.end_x, self.end_y = 1158, 101
|
||||||
|
|
||||||
if self.player_number == 1:
|
if self.player_number == 1:
|
||||||
# Control point influences the curve shape
|
# Control point influences the curve shape
|
||||||
self.control_x = (self.start_x + self.end_x) // 2
|
self.control_x = (self.start_x + self.end_x) // 2
|
||||||
@@ -944,34 +1036,31 @@ class NoteArc:
|
|||||||
# For player 2 (assumed to be a downward arc)
|
# For player 2 (assumed to be a downward arc)
|
||||||
self.control_x = (self.start_x + self.end_x) // 2
|
self.control_x = (self.start_x + self.end_x) // 2
|
||||||
self.control_y = max(self.start_y, self.end_y) + curve_height # Arc downward
|
self.control_y = max(self.start_y, self.end_y) + curve_height # Arc downward
|
||||||
|
|
||||||
self.x_i = self.start_x
|
|
||||||
self.y_i = self.start_y
|
|
||||||
self.is_finished = False
|
|
||||||
|
|
||||||
self.total_path_length = math.sqrt((self.end_x - self.start_x)**2 + (self.end_y - self.start_y)**2) * 1.2 # Approximate arc length
|
|
||||||
self.x_i = self.start_x
|
self.x_i = self.start_x
|
||||||
self.y_i = self.start_y
|
self.y_i = self.start_y
|
||||||
self.is_finished = False
|
self.is_finished = False
|
||||||
|
# Pre-calculate all arc points for better performance
|
||||||
|
self.arc_points_cache = []
|
||||||
|
for i in range(self.arc_points + 1):
|
||||||
|
t = i / self.arc_points
|
||||||
|
t_inv = 1.0 - t
|
||||||
|
x = int(t_inv * t_inv * self.start_x + 2 * t_inv * t * self.control_x + t * t * self.end_x)
|
||||||
|
y = int(t_inv * t_inv * self.start_y + 2 * t_inv * t * self.control_y + t * t * self.end_y)
|
||||||
|
self.arc_points_cache.append((x, y))
|
||||||
|
|
||||||
def update(self, current_ms: float):
|
def update(self, current_ms: float):
|
||||||
if self.x_i >= self.end_x:
|
|
||||||
self.is_finished = True
|
|
||||||
self.x_i = self.end_x
|
|
||||||
self.y_i = self.end_y
|
|
||||||
return
|
|
||||||
|
|
||||||
ms_since_call = (current_ms - self.create_ms) / 16.67
|
ms_since_call = (current_ms - self.create_ms) / 16.67
|
||||||
ms_since_call = max(0, min(ms_since_call, self.arc_points))
|
ms_since_call = max(0, min(ms_since_call, self.arc_duration))
|
||||||
|
self.current_progress = ms_since_call / self.arc_duration
|
||||||
self.current_progress = ms_since_call / self.arc_points
|
if self.current_progress >= 1.0:
|
||||||
|
self.is_finished = True
|
||||||
t = self.current_progress
|
self.x_i, self.y_i = self.arc_points_cache[-1]
|
||||||
t_inv = 1.0 - t
|
return
|
||||||
|
point_index = int(self.current_progress * self.arc_points)
|
||||||
# Quadratic Bezier: B(t) = (1-t)²P₀ + 2(1-t)tP₁ + t²P₂
|
if point_index < len(self.arc_points_cache):
|
||||||
self.x_i = int(t_inv * t_inv * self.start_x + 2 * t_inv * t * self.control_x + t * t * self.end_x)
|
self.x_i, self.y_i = self.arc_points_cache[point_index]
|
||||||
self.y_i = int(t_inv * t_inv * self.start_y + 2 * t_inv * t * self.control_y + t * t * self.end_y)
|
else:
|
||||||
|
self.x_i, self.y_i = self.arc_points_cache[-1]
|
||||||
|
|
||||||
def draw(self, game_screen: GameScreen):
|
def draw(self, game_screen: GameScreen):
|
||||||
if self.is_balloon:
|
if self.is_balloon:
|
||||||
@@ -1250,10 +1339,16 @@ class ScoreCounterAnimation:
|
|||||||
self.move_animation_4.start()
|
self.move_animation_4.start()
|
||||||
|
|
||||||
if player_num == '2':
|
if player_num == '2':
|
||||||
self.color = ray.fade(ray.Color(84, 250, 238, 255), 1.0)
|
self.base_color = ray.Color(84, 250, 238, 255)
|
||||||
else:
|
else:
|
||||||
self.color = ray.fade(ray.Color(254, 102, 0, 255), 1.0)
|
self.base_color = ray.Color(254, 102, 0, 255)
|
||||||
|
self.color = ray.fade(self.base_color, 1.0)
|
||||||
self.is_finished = False
|
self.is_finished = False
|
||||||
|
|
||||||
|
# Cache string and layout calculations
|
||||||
|
self.counter_str = str(counter)
|
||||||
|
self.margin = 20
|
||||||
|
self.total_width = len(self.counter_str) * self.margin
|
||||||
self.y_pos_list = []
|
self.y_pos_list = []
|
||||||
|
|
||||||
def update(self, current_ms: float):
|
def update(self, current_ms: float):
|
||||||
@@ -1264,35 +1359,34 @@ class ScoreCounterAnimation:
|
|||||||
self.move_animation_4.update(current_ms)
|
self.move_animation_4.update(current_ms)
|
||||||
self.fade_animation_2.update(current_ms)
|
self.fade_animation_2.update(current_ms)
|
||||||
|
|
||||||
if self.fade_animation_1.is_finished:
|
fade_value = self.fade_animation_2.attribute if self.fade_animation_1.is_finished else self.fade_animation_1.attribute
|
||||||
self.color = ray.fade(self.color, self.fade_animation_2.attribute)
|
self.color = ray.fade(self.base_color, fade_value)
|
||||||
else:
|
|
||||||
self.color = ray.fade(self.color, self.fade_animation_1.attribute)
|
|
||||||
if self.fade_animation_2.is_finished:
|
if self.fade_animation_2.is_finished:
|
||||||
self.is_finished = True
|
self.is_finished = True
|
||||||
self.y_pos_list = []
|
|
||||||
for i in range(1, len(str(self.counter))+1):
|
# Cache y positions
|
||||||
self.y_pos_list.append(self.move_animation_4.attribute + i*5)
|
self.y_pos_list = [self.move_animation_4.attribute + i*5 for i in range(1, len(self.counter_str)+1)]
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
if self.move_animation_1.is_finished:
|
x = self.move_animation_2.attribute if self.move_animation_1.is_finished else self.move_animation_1.attribute
|
||||||
x = self.move_animation_2.attribute
|
|
||||||
else:
|
|
||||||
x = self.move_animation_1.attribute
|
|
||||||
if x == 0:
|
if x == 0:
|
||||||
return
|
return
|
||||||
counter = str(self.counter)
|
|
||||||
margin = 20
|
start_x = x - self.total_width
|
||||||
total_width = len(counter) * margin
|
|
||||||
start_x = x - total_width
|
for i, digit in enumerate(self.counter_str):
|
||||||
for i, digit in enumerate(counter):
|
|
||||||
if self.move_animation_3.is_finished:
|
if self.move_animation_3.is_finished:
|
||||||
y = self.y_pos_list[i]
|
y = self.y_pos_list[i]
|
||||||
elif self.move_animation_2.is_finished:
|
elif self.move_animation_2.is_finished:
|
||||||
y = self.move_animation_3.attribute
|
y = self.move_animation_3.attribute
|
||||||
else:
|
else:
|
||||||
y = 148
|
y = 148
|
||||||
tex.draw_texture('lane', 'score_number', frame=int(digit), x=start_x + (i * margin), y=y, color=self.color)
|
tex.draw_texture('lane', 'score_number',
|
||||||
|
frame=int(digit),
|
||||||
|
x=start_x + (i * self.margin),
|
||||||
|
y=y,
|
||||||
|
color=self.color)
|
||||||
|
|
||||||
class SongInfo:
|
class SongInfo:
|
||||||
def __init__(self, song_name: str, genre: int):
|
def __init__(self, song_name: str, genre: int):
|
||||||
@@ -1451,14 +1545,30 @@ class Gauge:
|
|||||||
tex.draw_texture('gauge', 'border' + self.string_diff)
|
tex.draw_texture('gauge', 'border' + self.string_diff)
|
||||||
tex.draw_texture('gauge', f'{self.player_num}p_unfilled' + self.string_diff)
|
tex.draw_texture('gauge', f'{self.player_num}p_unfilled' + self.string_diff)
|
||||||
gauge_length = int(self.gauge_length)
|
gauge_length = int(self.gauge_length)
|
||||||
for i in range(gauge_length):
|
clear_point = self.clear_start[self.difficulty]
|
||||||
if i == self.clear_start[self.difficulty] - 1:
|
|
||||||
tex.draw_texture('gauge', 'bar_clear_transition', x=i*8)
|
# Batch draw gauge bars by type instead of individual draws
|
||||||
elif i > self.clear_start[self.difficulty] - 1:
|
if gauge_length > 0:
|
||||||
tex.draw_texture('gauge', 'bar_clear_top', x=i*8)
|
# Draw pre-clear bars as a batch
|
||||||
tex.draw_texture('gauge', 'bar_clear_bottom', x=i*8)
|
pre_clear_length = min(gauge_length, clear_point - 1)
|
||||||
else:
|
if pre_clear_length > 0:
|
||||||
|
for i in range(pre_clear_length):
|
||||||
tex.draw_texture('gauge', f'{self.player_num}p_bar', x=i*8)
|
tex.draw_texture('gauge', f'{self.player_num}p_bar', x=i*8)
|
||||||
|
|
||||||
|
# Draw clear transition bar if applicable
|
||||||
|
if gauge_length >= clear_point - 1:
|
||||||
|
tex.draw_texture('gauge', 'bar_clear_transition', x=(clear_point - 1)*8)
|
||||||
|
|
||||||
|
# Draw post-clear bars as a batch
|
||||||
|
if gauge_length > clear_point:
|
||||||
|
post_clear_start = clear_point
|
||||||
|
post_clear_length = gauge_length - post_clear_start
|
||||||
|
for i in range(post_clear_length):
|
||||||
|
x_pos = (post_clear_start + i) * 8
|
||||||
|
tex.draw_texture('gauge', 'bar_clear_top', x=x_pos)
|
||||||
|
tex.draw_texture('gauge', 'bar_clear_bottom', x=x_pos)
|
||||||
|
|
||||||
|
# Rainbow effect for full gauge
|
||||||
if gauge_length == self.gauge_max and self.rainbow_fade_in is not None and self.rainbow_animation is not None:
|
if gauge_length == self.gauge_max and self.rainbow_fade_in is not None and self.rainbow_animation is not None:
|
||||||
if 0 < self.rainbow_animation.attribute < 8:
|
if 0 < self.rainbow_animation.attribute < 8:
|
||||||
tex.draw_texture('gauge', 'rainbow' + self.string_diff, frame=self.rainbow_animation.attribute-1, fade=self.rainbow_fade_in.attribute)
|
tex.draw_texture('gauge', 'rainbow' + self.string_diff, frame=self.rainbow_animation.attribute-1, fade=self.rainbow_fade_in.attribute)
|
||||||
@@ -1471,7 +1581,9 @@ class Gauge:
|
|||||||
else:
|
else:
|
||||||
tex.draw_texture('gauge', f'{self.player_num}p_bar_fade', x=gauge_length*8, fade=self.gauge_update_anim.attribute)
|
tex.draw_texture('gauge', f'{self.player_num}p_bar_fade', x=gauge_length*8, fade=self.gauge_update_anim.attribute)
|
||||||
tex.draw_texture('gauge', 'overlay' + self.string_diff, fade=0.15)
|
tex.draw_texture('gauge', 'overlay' + self.string_diff, fade=0.15)
|
||||||
if gauge_length >= self.clear_start[self.difficulty]:
|
|
||||||
|
# Draw clear status indicators
|
||||||
|
if gauge_length >= clear_point:
|
||||||
tex.draw_texture('gauge', 'clear', index=min(2, self.difficulty))
|
tex.draw_texture('gauge', 'clear', index=min(2, self.difficulty))
|
||||||
tex.draw_texture('gauge', 'tamashii')
|
tex.draw_texture('gauge', 'tamashii')
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ class SettingsScreen:
|
|||||||
audio.close_audio_device()
|
audio.close_audio_device()
|
||||||
audio.device_type = global_data.config["audio"]["device_type"]
|
audio.device_type = global_data.config["audio"]["device_type"]
|
||||||
audio.target_sample_rate = global_data.config["audio"]["sample_rate"]
|
audio.target_sample_rate = global_data.config["audio"]["sample_rate"]
|
||||||
|
audio.buffer_size = global_data.config["audio"]["buffer_size"]
|
||||||
audio.init_audio_device()
|
audio.init_audio_device()
|
||||||
return "ENTRY"
|
return "ENTRY"
|
||||||
|
|
||||||
|
|||||||
@@ -601,7 +601,7 @@ class SongBox:
|
|||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
# Batch database query for all diffs at once
|
# Batch database query for all diffs at once
|
||||||
if self.tja.metadata.course_data:
|
if self.tja.metadata.course_data:
|
||||||
hash_values = [self.hash[diff] for diff in self.tja.metadata.course_data]
|
hash_values = [self.hash[diff] for diff in self.tja.metadata.course_data if diff in self.hash]
|
||||||
placeholders = ','.join('?' * len(hash_values))
|
placeholders = ','.join('?' * len(hash_values))
|
||||||
|
|
||||||
batch_query = f"""
|
batch_query = f"""
|
||||||
@@ -614,6 +614,8 @@ class SongBox:
|
|||||||
hash_to_score = {row[0]: row[1:] for row in cursor.fetchall()}
|
hash_to_score = {row[0]: row[1:] for row in cursor.fetchall()}
|
||||||
|
|
||||||
for diff in self.tja.metadata.course_data:
|
for diff in self.tja.metadata.course_data:
|
||||||
|
if diff not in self.hash:
|
||||||
|
continue
|
||||||
diff_hash = self.hash[diff]
|
diff_hash = self.hash[diff]
|
||||||
self.scores[diff] = hash_to_score.get(diff_hash)
|
self.scores[diff] = hash_to_score.get(diff_hash)
|
||||||
|
|
||||||
@@ -707,19 +709,19 @@ class SongBox:
|
|||||||
highest_key = max(valid_scores.keys())
|
highest_key = max(valid_scores.keys())
|
||||||
score = self.scores[highest_key]
|
score = self.scores[highest_key]
|
||||||
if score and score[2] == 0 and score[3] == 0:
|
if score and score[2] == 0 and score[3] == 0:
|
||||||
tex.draw_texture('yellow_box', 'crown_dfc', x=x, y=y, frame=highest_key)
|
tex.draw_texture('yellow_box', 'crown_dfc', x=x, y=y, frame=min(4, highest_key))
|
||||||
elif score and score[3] == 0:
|
elif score and score[3] == 0:
|
||||||
tex.draw_texture('yellow_box', 'crown_fc', x=x, y=y, frame=highest_key)
|
tex.draw_texture('yellow_box', 'crown_fc', x=x, y=y, frame=min(4, highest_key))
|
||||||
elif score and score[4] == 1:
|
elif score and score[4] == 1:
|
||||||
tex.draw_texture('yellow_box', 'crown_clear', x=x, y=y, frame=highest_key)
|
tex.draw_texture('yellow_box', 'crown_clear', x=x, y=y, frame=min(4, highest_key))
|
||||||
if self.crown: #Folder lamp
|
if self.crown: #Folder lamp
|
||||||
highest_crown = max(self.crown)
|
highest_crown = max(self.crown)
|
||||||
if self.crown[highest_crown] == 'DFC':
|
if self.crown[highest_crown] == 'DFC':
|
||||||
tex.draw_texture('yellow_box', 'crown_dfc', x=x, y=y, frame=highest_crown)
|
tex.draw_texture('yellow_box', 'crown_dfc', x=x, y=y, frame=min(4, highest_crown))
|
||||||
elif self.crown[highest_crown] == 'FC':
|
elif self.crown[highest_crown] == 'FC':
|
||||||
tex.draw_texture('yellow_box', 'crown_fc', x=x, y=y, frame=highest_crown)
|
tex.draw_texture('yellow_box', 'crown_fc', x=x, y=y, frame=min(4, highest_crown))
|
||||||
else:
|
else:
|
||||||
tex.draw_texture('yellow_box', 'crown_clear', x=x, y=y, frame=highest_crown)
|
tex.draw_texture('yellow_box', 'crown_clear', x=x, y=y, frame=min(4, highest_crown))
|
||||||
|
|
||||||
def _draw_open(self, x: int, y: int, fade_override: Optional[float]):
|
def _draw_open(self, x: int, y: int, fade_override: Optional[float]):
|
||||||
color = ray.WHITE
|
color = ray.WHITE
|
||||||
|
|||||||
48
uv.lock
generated
48
uv.lock
generated
@@ -69,41 +69,6 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" },
|
{ url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dotenv"
|
|
||||||
version = "0.9.9"
|
|
||||||
source = { registry = "https://pypi.org/simple" }
|
|
||||||
dependencies = [
|
|
||||||
{ name = "python-dotenv" },
|
|
||||||
]
|
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b2/b7/545d2c10c1fc15e48653c91efde329a790f2eecfbbf2bd16003b5db2bab0/dotenv-0.9.9-py2.py3-none-any.whl", hash = "sha256:29cf74a087b31dafdb5a446b6d7e11cbce8ed2741540e2339c69fbef92c94ce9", size = 1892, upload-time = "2025-02-19T22:15:01.647Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gitdb"
|
|
||||||
version = "4.0.12"
|
|
||||||
source = { registry = "https://pypi.org/simple" }
|
|
||||||
dependencies = [
|
|
||||||
{ name = "smmap" },
|
|
||||||
]
|
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" }
|
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gitpython"
|
|
||||||
version = "3.1.44"
|
|
||||||
source = { registry = "https://pypi.org/simple" }
|
|
||||||
dependencies = [
|
|
||||||
{ name = "gitdb" },
|
|
||||||
]
|
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/c0/89/37df0b71473153574a5cdef8f242de422a0f5d26d7a9e231e6f169b4ad14/gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269", size = 214196, upload-time = "2025-01-02T07:32:43.59Z" }
|
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/1d/9a/4114a9057db2f1462d5c8f8390ab7383925fe1ac012eaa42402ad65c2963/GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110", size = 207599, upload-time = "2025-01-02T07:32:40.731Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "imageio"
|
name = "imageio"
|
||||||
version = "2.37.0"
|
version = "2.37.0"
|
||||||
@@ -292,8 +257,6 @@ name = "pytaiko"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = { virtual = "." }
|
source = { virtual = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "dotenv" },
|
|
||||||
{ name = "gitpython" },
|
|
||||||
{ name = "moviepy" },
|
{ name = "moviepy" },
|
||||||
{ name = "raylib-sdl" },
|
{ name = "raylib-sdl" },
|
||||||
{ name = "tomlkit" },
|
{ name = "tomlkit" },
|
||||||
@@ -301,8 +264,6 @@ dependencies = [
|
|||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "dotenv", specifier = ">=0.9.9" },
|
|
||||||
{ name = "gitpython", specifier = ">=3.1.44" },
|
|
||||||
{ name = "moviepy", specifier = ">=2.1.2" },
|
{ name = "moviepy", specifier = ">=2.1.2" },
|
||||||
{ name = "raylib-sdl", specifier = ">=5.5.0.2" },
|
{ name = "raylib-sdl", specifier = ">=5.5.0.2" },
|
||||||
{ name = "tomlkit", specifier = ">=0.13.3" },
|
{ name = "tomlkit", specifier = ">=0.13.3" },
|
||||||
@@ -344,15 +305,6 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/b0/4f/8ba71611c74d6e3ff5e95d2935c1f5f98fc61183ebf70d4dfb09547a5767/raylib_sdl-5.5.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:fd3841ab8d8d6ca24e4fbffc9514c58c0cf5bd29e3f2406006eba872416325b3", size = 1499920, upload-time = "2025-02-12T04:22:16.996Z" },
|
{ url = "https://files.pythonhosted.org/packages/b0/4f/8ba71611c74d6e3ff5e95d2935c1f5f98fc61183ebf70d4dfb09547a5767/raylib_sdl-5.5.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:fd3841ab8d8d6ca24e4fbffc9514c58c0cf5bd29e3f2406006eba872416325b3", size = 1499920, upload-time = "2025-02-12T04:22:16.996Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "smmap"
|
|
||||||
version = "5.0.2"
|
|
||||||
source = { registry = "https://pypi.org/simple" }
|
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329, upload-time = "2025-01-02T07:14:40.909Z" }
|
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303, upload-time = "2025-01-02T07:14:38.724Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tomlkit"
|
name = "tomlkit"
|
||||||
version = "0.13.3"
|
version = "0.13.3"
|
||||||
|
|||||||
Reference in New Issue
Block a user