mirror of
https://github.com/Yonokid/PyTaiko.git
synced 2026-02-04 11:40:13 +01:00
Add doc strings
This commit is contained in:
@@ -37,6 +37,8 @@ class Screens:
|
|||||||
LOADING = "LOADING"
|
LOADING = "LOADING"
|
||||||
|
|
||||||
def create_song_db():
|
def create_song_db():
|
||||||
|
"""Create the scores database if it doesn't exist
|
||||||
|
The migration will eventually be removed"""
|
||||||
with sqlite3.connect('scores.db') as con:
|
with sqlite3.connect('scores.db') as con:
|
||||||
cursor = con.cursor()
|
cursor = con.cursor()
|
||||||
create_table_query = '''
|
create_table_query = '''
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ except OSError as e:
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
class AudioEngine:
|
class AudioEngine:
|
||||||
|
"""Initialize an audio engine for playing sounds and music."""
|
||||||
def __init__(self, device_type: int, sample_rate: float, buffer_size: int, volume_presets: dict[str, float]):
|
def __init__(self, device_type: int, sample_rate: float, buffer_size: int, volume_presets: dict[str, float]):
|
||||||
self.device_type = device_type
|
self.device_type = device_type
|
||||||
if sample_rate == -1:
|
if sample_rate == -1:
|
||||||
|
|||||||
@@ -12,17 +12,52 @@ from libs.bg_objects.renda import RendaController
|
|||||||
from libs.texture import TextureWrapper
|
from libs.texture import TextureWrapper
|
||||||
|
|
||||||
class Background:
|
class Background:
|
||||||
|
"""The background class for the game."""
|
||||||
COLLABS = {
|
COLLABS = {
|
||||||
"A3": libs.bg_collabs.a3.Background,
|
"A3": libs.bg_collabs.a3.Background,
|
||||||
"ANIMAL": libs.bg_collabs.animal.Background,
|
"ANIMAL": libs.bg_collabs.animal.Background,
|
||||||
"BUTTOBURST": libs.bg_collabs.buttoburst.Background
|
"BUTTOBURST": libs.bg_collabs.buttoburst.Background
|
||||||
}
|
}
|
||||||
def __init__(self, player_num: int, bpm: float, scene_preset: str = ''):
|
def __init__(self, player_num: int, bpm: float, scene_preset: str = ''):
|
||||||
|
"""
|
||||||
|
Initialize the background class.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
player_num (int): The player number.
|
||||||
|
bpm (float): The beats per minute.
|
||||||
|
scene_preset (str): The scene preset.
|
||||||
|
"""
|
||||||
self.tex_wrapper = TextureWrapper()
|
self.tex_wrapper = TextureWrapper()
|
||||||
self.tex_wrapper.load_animations('background')
|
self.tex_wrapper.load_animations('background')
|
||||||
|
if player_num == 3:
|
||||||
if scene_preset == '':
|
if scene_preset == '':
|
||||||
|
self.max_dancers = 5
|
||||||
|
don_bg_num = random.randint(0, 5)
|
||||||
|
self.don_bg = DonBG.create(self.tex_wrapper, don_bg_num, 1)
|
||||||
|
self.don_bg_2 = DonBG.create(self.tex_wrapper, don_bg_num, 2)
|
||||||
|
self.renda = RendaController(self.tex_wrapper, random.randint(0, 2))
|
||||||
|
self.chibi = ChibiController(self.tex_wrapper, random.randint(0, 13), bpm)
|
||||||
|
self.bg_normal = None
|
||||||
|
self.bg_fever = None
|
||||||
|
self.footer = None
|
||||||
|
self.fever = None
|
||||||
|
self.dancer = None
|
||||||
|
else:
|
||||||
|
collab_bg = Background.COLLABS[scene_preset](self.tex_wrapper, 1, bpm)
|
||||||
|
self.max_dancers = 5
|
||||||
|
self.don_bg = collab_bg.don_bg
|
||||||
|
self.don_bg_2 = collab_bg.don_bg
|
||||||
|
self.bg_normal = None
|
||||||
|
self.bg_fever = None
|
||||||
|
self.footer = None
|
||||||
|
self.fever = None
|
||||||
|
self.dancer = None
|
||||||
|
self.renda = collab_bg.renda
|
||||||
|
self.chibi = collab_bg.chibi
|
||||||
|
elif scene_preset == '':
|
||||||
self.max_dancers = 5
|
self.max_dancers = 5
|
||||||
self.don_bg = DonBG.create(self.tex_wrapper, random.randint(0, 5), player_num)
|
self.don_bg = DonBG.create(self.tex_wrapper, random.randint(0, 5), player_num)
|
||||||
|
self.don_bg_2 = None
|
||||||
self.bg_normal = BGNormal.create(self.tex_wrapper, random.randint(0, 4))
|
self.bg_normal = BGNormal.create(self.tex_wrapper, random.randint(0, 4))
|
||||||
self.bg_fever = BGFever.create(self.tex_wrapper, random.randint(0, 3))
|
self.bg_fever = BGFever.create(self.tex_wrapper, random.randint(0, 3))
|
||||||
self.footer = Footer(self.tex_wrapper, random.randint(0, 2))
|
self.footer = Footer(self.tex_wrapper, random.randint(0, 2))
|
||||||
@@ -34,6 +69,7 @@ class Background:
|
|||||||
collab_bg = Background.COLLABS[scene_preset](self.tex_wrapper, player_num, bpm)
|
collab_bg = Background.COLLABS[scene_preset](self.tex_wrapper, player_num, bpm)
|
||||||
self.max_dancers = collab_bg.max_dancers
|
self.max_dancers = collab_bg.max_dancers
|
||||||
self.don_bg = collab_bg.don_bg
|
self.don_bg = collab_bg.don_bg
|
||||||
|
self.don_bg_2 = None
|
||||||
self.bg_normal = collab_bg.bg_normal
|
self.bg_normal = collab_bg.bg_normal
|
||||||
self.bg_fever = collab_bg.bg_fever
|
self.bg_fever = collab_bg.bg_fever
|
||||||
self.footer = collab_bg.footer
|
self.footer = collab_bg.footer
|
||||||
@@ -45,13 +81,32 @@ class Background:
|
|||||||
self.is_rainbow = False
|
self.is_rainbow = False
|
||||||
self.last_milestone = 0
|
self.last_milestone = 0
|
||||||
|
|
||||||
def add_chibi(self, bad: bool):
|
def add_chibi(self, bad: bool, player_num: int):
|
||||||
self.chibi.add_chibi(bad)
|
"""
|
||||||
|
Add a chibi to the background.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
player_num (int): The player number.
|
||||||
|
bad (bool): Whether the chibi is bad.
|
||||||
|
"""
|
||||||
|
self.chibi.add_chibi(player_num, bad)
|
||||||
|
|
||||||
def add_renda(self):
|
def add_renda(self):
|
||||||
|
"""
|
||||||
|
Add a renda to the background.
|
||||||
|
"""
|
||||||
self.renda.add_renda()
|
self.renda.add_renda()
|
||||||
|
|
||||||
def update(self, current_time_ms: float, bpm: float, gauge):
|
def update(self, current_time_ms: float, bpm: float, gauge):
|
||||||
|
"""
|
||||||
|
Update the background.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
current_time_ms (float): The current time in milliseconds.
|
||||||
|
bpm (float): The beats per minute.
|
||||||
|
gauge (Gauge): The gauge object.
|
||||||
|
"""
|
||||||
|
if self.dancer is not None:
|
||||||
clear_threshold = gauge.clear_start[min(gauge.difficulty, 3)]
|
clear_threshold = gauge.clear_start[min(gauge.difficulty, 3)]
|
||||||
if gauge.gauge_length < clear_threshold:
|
if gauge.gauge_length < clear_threshold:
|
||||||
current_milestone = min(self.max_dancers - 1, int(gauge.gauge_length / (clear_threshold / self.max_dancers)))
|
current_milestone = min(self.max_dancers - 1, int(gauge.gauge_length / (clear_threshold / self.max_dancers)))
|
||||||
@@ -60,6 +115,7 @@ class Background:
|
|||||||
if current_milestone > self.last_milestone and current_milestone < self.max_dancers:
|
if current_milestone > self.last_milestone and current_milestone < self.max_dancers:
|
||||||
self.dancer.add_dancer()
|
self.dancer.add_dancer()
|
||||||
self.last_milestone = current_milestone
|
self.last_milestone = current_milestone
|
||||||
|
if self.bg_fever is not None:
|
||||||
if not self.is_clear and gauge.is_clear:
|
if not self.is_clear and gauge.is_clear:
|
||||||
self.bg_fever.start()
|
self.bg_fever.start()
|
||||||
if not self.is_rainbow and gauge.is_rainbow and self.fever is not None:
|
if not self.is_rainbow and gauge.is_rainbow and self.fever is not None:
|
||||||
@@ -67,14 +123,24 @@ class Background:
|
|||||||
self.is_clear = gauge.is_clear
|
self.is_clear = gauge.is_clear
|
||||||
self.is_rainbow = gauge.is_rainbow
|
self.is_rainbow = gauge.is_rainbow
|
||||||
self.don_bg.update(current_time_ms, self.is_clear)
|
self.don_bg.update(current_time_ms, self.is_clear)
|
||||||
|
if self.don_bg_2 is not None:
|
||||||
|
self.don_bg_2.update(current_time_ms, self.is_clear)
|
||||||
|
if self.bg_normal is not None:
|
||||||
self.bg_normal.update(current_time_ms)
|
self.bg_normal.update(current_time_ms)
|
||||||
|
if self.bg_fever is not None:
|
||||||
self.bg_fever.update(current_time_ms)
|
self.bg_fever.update(current_time_ms)
|
||||||
if self.fever is not None:
|
if self.fever is not None:
|
||||||
self.fever.update(current_time_ms, bpm)
|
self.fever.update(current_time_ms, bpm)
|
||||||
|
if self.dancer is not None:
|
||||||
self.dancer.update(current_time_ms, bpm)
|
self.dancer.update(current_time_ms, bpm)
|
||||||
self.renda.update(current_time_ms)
|
self.renda.update(current_time_ms)
|
||||||
self.chibi.update(current_time_ms, bpm)
|
self.chibi.update(current_time_ms, bpm)
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
|
"""
|
||||||
|
Draw the background.
|
||||||
|
"""
|
||||||
|
if self.bg_normal is not None:
|
||||||
if self.is_clear and not self.bg_fever.transitioned:
|
if self.is_clear and not self.bg_fever.transitioned:
|
||||||
self.bg_normal.draw(self.tex_wrapper)
|
self.bg_normal.draw(self.tex_wrapper)
|
||||||
self.bg_fever.draw(self.tex_wrapper)
|
self.bg_fever.draw(self.tex_wrapper)
|
||||||
@@ -83,12 +149,19 @@ class Background:
|
|||||||
else:
|
else:
|
||||||
self.bg_normal.draw(self.tex_wrapper)
|
self.bg_normal.draw(self.tex_wrapper)
|
||||||
self.don_bg.draw(self.tex_wrapper)
|
self.don_bg.draw(self.tex_wrapper)
|
||||||
|
if self.don_bg_2 is not None:
|
||||||
|
self.don_bg_2.draw(self.tex_wrapper, y=536)
|
||||||
self.renda.draw()
|
self.renda.draw()
|
||||||
|
if self.dancer is not None:
|
||||||
self.dancer.draw(self.tex_wrapper)
|
self.dancer.draw(self.tex_wrapper)
|
||||||
if self.footer is not None:
|
if self.footer is not None:
|
||||||
self.footer.draw(self.tex_wrapper)
|
self.footer.draw(self.tex_wrapper)
|
||||||
if self.is_rainbow and self.fever is not None:
|
if self.is_rainbow and self.fever is not None:
|
||||||
self.fever.draw(self.tex_wrapper)
|
self.fever.draw(self.tex_wrapper)
|
||||||
self.chibi.draw()
|
self.chibi.draw()
|
||||||
|
|
||||||
def unload(self):
|
def unload(self):
|
||||||
|
"""
|
||||||
|
Unload the background.
|
||||||
|
"""
|
||||||
self.tex_wrapper.unload_textures()
|
self.tex_wrapper.unload_textures()
|
||||||
|
|||||||
@@ -7,18 +7,19 @@ import pyray as ray
|
|||||||
class Chibi:
|
class Chibi:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create(index: int, bpm: float, bad: bool, tex: TextureWrapper):
|
def create(index: int, bpm: float, bad: bool, tex: TextureWrapper, is_2p: bool):
|
||||||
if bad:
|
if bad:
|
||||||
return ChibiBad(index, bpm, tex)
|
return ChibiBad(index, bpm, tex, is_2p)
|
||||||
map = [Chibi0, BaseChibi, Chibi2, BaseChibi, Chibi4, Chibi5, BaseChibi,
|
map = [Chibi0, BaseChibi, Chibi2, BaseChibi, Chibi4, Chibi5, BaseChibi,
|
||||||
BaseChibi, Chibi8, BaseChibi, BaseChibi, BaseChibi, BaseChibi, Chibi13]
|
BaseChibi, Chibi8, BaseChibi, BaseChibi, BaseChibi, BaseChibi, Chibi13]
|
||||||
selected_obj = map[index]
|
selected_obj = map[index]
|
||||||
return selected_obj(index, bpm, tex)
|
return selected_obj(index, bpm, tex, is_2p)
|
||||||
|
|
||||||
class BaseChibi:
|
class BaseChibi:
|
||||||
def __init__(self, index: int, bpm: float, tex: TextureWrapper):
|
def __init__(self, index: int, bpm: float, tex: TextureWrapper, is_2p: bool):
|
||||||
self.name = 'chibi_' + str(index)
|
self.name = 'chibi_' + str(index)
|
||||||
self.bpm = bpm
|
self.bpm = bpm
|
||||||
|
self.is_2p = is_2p
|
||||||
self.hori_move = Animation.create_move(60000 / self.bpm * 5, total_distance=1280)
|
self.hori_move = Animation.create_move(60000 / self.bpm * 5, total_distance=1280)
|
||||||
self.hori_move.start()
|
self.hori_move.start()
|
||||||
self.vert_move = Animation.create_move(60000 / self.bpm / 2, total_distance=50, reverse_delay=0)
|
self.vert_move = Animation.create_move(60000 / self.bpm / 2, total_distance=50, reverse_delay=0)
|
||||||
@@ -42,10 +43,11 @@ class BaseChibi:
|
|||||||
self.texture_change.restart()
|
self.texture_change.restart()
|
||||||
|
|
||||||
def draw(self, tex: TextureWrapper):
|
def draw(self, tex: TextureWrapper):
|
||||||
tex.draw_texture(self.name, str(self.index), frame=self.texture_change.attribute, x=self.hori_move.attribute, y=-self.vert_move.attribute)
|
tex.draw_texture(self.name, str(self.index), frame=self.texture_change.attribute, x=self.hori_move.attribute, y=-self.vert_move.attribute+(self.is_2p*535))
|
||||||
|
|
||||||
class ChibiBad(BaseChibi):
|
class ChibiBad(BaseChibi):
|
||||||
def __init__(self, index: int, bpm: float, tex: TextureWrapper):
|
def __init__(self, index: int, bpm: float, tex: TextureWrapper, is_2p: bool):
|
||||||
|
self.is_2p = is_2p
|
||||||
self.bpm = bpm
|
self.bpm = bpm
|
||||||
self.index = random.randint(0, 2)
|
self.index = random.randint(0, 2)
|
||||||
self.keyframes = [3, 4]
|
self.keyframes = [3, 4]
|
||||||
@@ -72,17 +74,17 @@ class ChibiBad(BaseChibi):
|
|||||||
|
|
||||||
def draw(self, tex: TextureWrapper):
|
def draw(self, tex: TextureWrapper):
|
||||||
if not self.s_texture_change.is_finished:
|
if not self.s_texture_change.is_finished:
|
||||||
tex.draw_texture('chibi_bad', '0', frame=self.s_texture_change.attribute, x=self.hori_move.attribute, y=self.vert_move.attribute, fade=self.fade_in.attribute)
|
tex.draw_texture('chibi_bad', '0', frame=self.s_texture_change.attribute, x=self.hori_move.attribute, y=self.vert_move.attribute+(self.is_2p*535), fade=self.fade_in.attribute)
|
||||||
else:
|
else:
|
||||||
tex.draw_texture('chibi_bad', '0', frame=self.texture_change.attribute, x=self.hori_move.attribute, y=self.vert_move.attribute)
|
tex.draw_texture('chibi_bad', '0', frame=self.texture_change.attribute, x=self.hori_move.attribute, y=self.vert_move.attribute+(self.is_2p*535))
|
||||||
|
|
||||||
class Chibi0(BaseChibi):
|
class Chibi0(BaseChibi):
|
||||||
def draw(self, tex: TextureWrapper):
|
def draw(self, tex: TextureWrapper):
|
||||||
tex.draw_texture(self.name, str(self.index), frame=self.texture_change.attribute, x=self.hori_move.attribute, y=self.vert_move.attribute)
|
tex.draw_texture(self.name, str(self.index), frame=self.texture_change.attribute, x=self.hori_move.attribute, y=self.vert_move.attribute+(self.is_2p*535))
|
||||||
|
|
||||||
class Chibi2(BaseChibi):
|
class Chibi2(BaseChibi):
|
||||||
def __init__(self, index: int, bpm: float, tex: TextureWrapper):
|
def __init__(self, index: int, bpm: float, tex: TextureWrapper, is_2p: bool):
|
||||||
super().__init__(index, bpm, tex)
|
super().__init__(index, bpm, tex, is_2p)
|
||||||
self.rotate = Animation.create_move(60000 / self.bpm / 2, total_distance=360, reverse_delay=0)
|
self.rotate = Animation.create_move(60000 / self.bpm / 2, total_distance=360, reverse_delay=0)
|
||||||
self.rotate.start()
|
self.rotate.start()
|
||||||
|
|
||||||
@@ -94,23 +96,23 @@ class Chibi2(BaseChibi):
|
|||||||
|
|
||||||
def draw(self, tex: TextureWrapper):
|
def draw(self, tex: TextureWrapper):
|
||||||
origin = ray.Vector2(64, 64)
|
origin = ray.Vector2(64, 64)
|
||||||
tex.draw_texture(self.name, str(self.index), x=self.hori_move.attribute+origin.x, y=origin.y, origin=origin, rotation=self.rotate.attribute)
|
tex.draw_texture(self.name, str(self.index), x=self.hori_move.attribute+origin.x, y=origin.y+(self.is_2p*535), origin=origin, rotation=self.rotate.attribute)
|
||||||
|
|
||||||
class Chibi4(BaseChibi):
|
class Chibi4(BaseChibi):
|
||||||
def draw(self, tex: TextureWrapper):
|
def draw(self, tex: TextureWrapper):
|
||||||
tex.draw_texture(self.name, str(self.index), frame=self.texture_change.attribute, x=self.hori_move.attribute)
|
tex.draw_texture(self.name, str(self.index), frame=self.texture_change.attribute, x=self.hori_move.attribute, y=(self.is_2p*535))
|
||||||
|
|
||||||
class Chibi5(BaseChibi):
|
class Chibi5(BaseChibi):
|
||||||
def draw(self, tex: TextureWrapper):
|
def draw(self, tex: TextureWrapper):
|
||||||
tex.draw_texture(self.name, str(self.index), frame=self.texture_change.attribute, x=self.hori_move.attribute)
|
tex.draw_texture(self.name, str(self.index), frame=self.texture_change.attribute, x=self.hori_move.attribute, y=(self.is_2p*535))
|
||||||
|
|
||||||
class Chibi8(BaseChibi):
|
class Chibi8(BaseChibi):
|
||||||
def draw(self, tex: TextureWrapper):
|
def draw(self, tex: TextureWrapper):
|
||||||
tex.draw_texture(self.name, str(self.index), frame=self.texture_change.attribute, x=self.hori_move.attribute)
|
tex.draw_texture(self.name, str(self.index), frame=self.texture_change.attribute, x=self.hori_move.attribute, y=(self.is_2p*535))
|
||||||
|
|
||||||
class Chibi13(BaseChibi):
|
class Chibi13(BaseChibi):
|
||||||
def __init__(self, index: int, bpm: float, tex: TextureWrapper):
|
def __init__(self, index: int, bpm: float, tex: TextureWrapper, is_2p: bool):
|
||||||
super().__init__(index, bpm, tex)
|
super().__init__(index, bpm, tex, is_2p)
|
||||||
duration = (60000 / self.bpm)
|
duration = (60000 / self.bpm)
|
||||||
self.scale = Animation.create_fade(duration, initial_opacity=1.0, final_opacity=0.75, delay=duration, reverse_delay=duration)
|
self.scale = Animation.create_fade(duration, initial_opacity=1.0, final_opacity=0.75, delay=duration, reverse_delay=duration)
|
||||||
self.scale.start()
|
self.scale.start()
|
||||||
@@ -129,9 +131,9 @@ class Chibi13(BaseChibi):
|
|||||||
def draw(self, tex: TextureWrapper):
|
def draw(self, tex: TextureWrapper):
|
||||||
tex.draw_texture(self.name, 'tail', frame=self.frame, x=self.hori_move.attribute, y=-self.vert_move.attribute)
|
tex.draw_texture(self.name, 'tail', frame=self.frame, x=self.hori_move.attribute, y=-self.vert_move.attribute)
|
||||||
if self.scale.attribute == 0.75:
|
if self.scale.attribute == 0.75:
|
||||||
tex.draw_texture(self.name, str(self.index), frame=self.frame, x=self.hori_move.attribute, y=-self.vert_move.attribute)
|
tex.draw_texture(self.name, str(self.index), frame=self.frame, x=self.hori_move.attribute, y=-self.vert_move.attribute+(self.is_2p*535))
|
||||||
else:
|
else:
|
||||||
tex.draw_texture(self.name, str(self.index), scale=self.scale.attribute, center=True, frame=self.frame, x=self.hori_move.attribute, y=-self.vert_move.attribute)
|
tex.draw_texture(self.name, str(self.index), scale=self.scale.attribute, center=True, frame=self.frame, x=self.hori_move.attribute, y=-self.vert_move.attribute+(self.is_2p*535))
|
||||||
|
|
||||||
|
|
||||||
class ChibiController:
|
class ChibiController:
|
||||||
@@ -144,8 +146,8 @@ class ChibiController:
|
|||||||
tex.load_zip(path, f'chibi/{self.name}')
|
tex.load_zip(path, f'chibi/{self.name}')
|
||||||
tex.load_zip('background', 'chibi/chibi_bad')
|
tex.load_zip('background', 'chibi/chibi_bad')
|
||||||
|
|
||||||
def add_chibi(self, bad=False):
|
def add_chibi(self, player_num: int, bad=False):
|
||||||
self.chibis.append(Chibi.create(self.index, self.bpm, bad, self.tex))
|
self.chibis.append(Chibi.create(self.index, self.bpm, bad, self.tex, player_num == 2))
|
||||||
|
|
||||||
def update(self, current_time_ms: float, bpm: float):
|
def update(self, current_time_ms: float, bpm: float):
|
||||||
self.bpm = bpm
|
self.bpm = bpm
|
||||||
|
|||||||
@@ -31,17 +31,17 @@ class DonBG1(DonBGBase):
|
|||||||
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)
|
||||||
def draw(self, tex: TextureWrapper):
|
def draw(self, tex: TextureWrapper, y: float=0):
|
||||||
self._draw_textures(tex, 1.0)
|
self._draw_textures(tex, 1.0, y)
|
||||||
if self.is_clear:
|
if self.is_clear:
|
||||||
self._draw_textures(tex, self.clear_fade.attribute)
|
self._draw_textures(tex, self.clear_fade.attribute, y)
|
||||||
def _draw_textures(self, tex: TextureWrapper, fade: float):
|
def _draw_textures(self, tex: TextureWrapper, fade: float, y: float):
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
tex.draw_texture(self.name, 'background', frame=self.is_clear, fade=fade, x=(i*328)+self.move.attribute)
|
tex.draw_texture(self.name, 'background', frame=self.is_clear, fade=fade, x=(i*328)+self.move.attribute, y=y)
|
||||||
for i in range(6):
|
for i in range(6):
|
||||||
tex.draw_texture(self.name, 'overlay', frame=self.is_clear, fade=fade, x=(i*347)+self.move.attribute*(347/328), y=self.overlay_move.attribute)
|
tex.draw_texture(self.name, 'overlay', frame=self.is_clear, fade=fade, x=(i*347)+self.move.attribute*(347/328), y=self.overlay_move.attribute+y)
|
||||||
for i in range(30):
|
for i in range(30):
|
||||||
tex.draw_texture(self.name, 'footer', frame=self.is_clear, fade=fade, x=(i*56)+self.move.attribute*((56/328)*3), y=self.overlay_move.attribute)
|
tex.draw_texture(self.name, 'footer', frame=self.is_clear, fade=fade, x=(i*56)+self.move.attribute*((56/328)*3), y=self.overlay_move.attribute+y)
|
||||||
|
|
||||||
class DonBG2(DonBGBase):
|
class DonBG2(DonBGBase):
|
||||||
def __init__(self, tex: TextureWrapper, index: int, player_num: int, path: str):
|
def __init__(self, tex: TextureWrapper, index: int, player_num: int, path: str):
|
||||||
@@ -50,14 +50,14 @@ class DonBG2(DonBGBase):
|
|||||||
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)
|
||||||
def draw(self, tex: TextureWrapper):
|
def draw(self, tex: TextureWrapper, y: float = 0):
|
||||||
self._draw_textures(tex, 1.0)
|
self._draw_textures(tex, 1.0, y)
|
||||||
if self.is_clear:
|
if self.is_clear:
|
||||||
self._draw_textures(tex, self.clear_fade.attribute)
|
self._draw_textures(tex, self.clear_fade.attribute, y)
|
||||||
def _draw_textures(self, tex: TextureWrapper, fade: float):
|
def _draw_textures(self, tex: TextureWrapper, fade: float, y: float):
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
tex.draw_texture(self.name, 'background', frame=self.is_clear, fade=fade, x=(i*328)+self.move.attribute)
|
tex.draw_texture(self.name, 'background', frame=self.is_clear, fade=fade, x=(i*328)+self.move.attribute, y=y)
|
||||||
tex.draw_texture(self.name, 'overlay', frame=self.is_clear, fade=fade, x=(i*328)+self.move.attribute, y=self.overlay_move.attribute)
|
tex.draw_texture(self.name, 'overlay', frame=self.is_clear, fade=fade, x=(i*328)+self.move.attribute, y=self.overlay_move.attribute+y)
|
||||||
|
|
||||||
class DonBG3(DonBGBase):
|
class DonBG3(DonBGBase):
|
||||||
def __init__(self, tex: TextureWrapper, index: int, player_num: int, path: str):
|
def __init__(self, tex: TextureWrapper, index: int, player_num: int, path: str):
|
||||||
@@ -79,17 +79,17 @@ class DonBG3(DonBGBase):
|
|||||||
self.overlay_move.update(current_time_ms)
|
self.overlay_move.update(current_time_ms)
|
||||||
self.overlay_move_2.update(current_time_ms)
|
self.overlay_move_2.update(current_time_ms)
|
||||||
|
|
||||||
def draw(self, tex: TextureWrapper):
|
def draw(self, tex: TextureWrapper, y: float = 0):
|
||||||
self._draw_textures(tex, 1.0)
|
self._draw_textures(tex, 1.0, y)
|
||||||
if self.is_clear:
|
if self.is_clear:
|
||||||
self._draw_textures(tex, self.clear_fade.attribute)
|
self._draw_textures(tex, self.clear_fade.attribute, y)
|
||||||
|
|
||||||
def _draw_textures(self, tex: TextureWrapper, fade: float):
|
def _draw_textures(self, tex: TextureWrapper, fade: float, y: float):
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
tex.draw_texture(self.name, 'background', frame=self.is_clear, fade=fade, x=(i*164)+self.move.attribute)
|
tex.draw_texture(self.name, 'background', frame=self.is_clear, fade=fade, x=(i*164)+self.move.attribute, y=y)
|
||||||
y = self.bounce_up.attribute - self.bounce_down.attribute + self.overlay_move.attribute + self.overlay_move_2.attribute
|
y_pos = self.bounce_up.attribute - self.bounce_down.attribute + self.overlay_move.attribute + self.overlay_move_2.attribute
|
||||||
for i in range(6):
|
for i in range(6):
|
||||||
tex.draw_texture(self.name, 'overlay', frame=self.is_clear, fade=fade, x=(i*328)+(self.move.attribute*2), y=y)
|
tex.draw_texture(self.name, 'overlay', frame=self.is_clear, fade=fade, x=(i*328)+(self.move.attribute*2), y=y_pos+y)
|
||||||
|
|
||||||
class DonBG4(DonBGBase):
|
class DonBG4(DonBGBase):
|
||||||
def __init__(self, tex: TextureWrapper, index: int, player_num: int, path: str):
|
def __init__(self, tex: TextureWrapper, index: int, player_num: int, path: str):
|
||||||
@@ -98,15 +98,15 @@ class DonBG4(DonBGBase):
|
|||||||
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)
|
||||||
def draw(self, tex: TextureWrapper):
|
def draw(self, tex: TextureWrapper, y: float = 0):
|
||||||
self._draw_textures(tex, 1.0)
|
self._draw_textures(tex, 1.0, y)
|
||||||
if self.is_clear:
|
if self.is_clear:
|
||||||
self._draw_textures(tex, self.clear_fade.attribute)
|
self._draw_textures(tex, self.clear_fade.attribute, y)
|
||||||
|
|
||||||
def _draw_textures(self, tex: TextureWrapper, fade: float):
|
def _draw_textures(self, tex: TextureWrapper, fade: float, y: float):
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
tex.draw_texture(self.name, 'background', frame=self.is_clear, fade=fade, x=(i*328)+self.move.attribute)
|
tex.draw_texture(self.name, 'background', frame=self.is_clear, fade=fade, x=(i*328)+self.move.attribute, y=y)
|
||||||
tex.draw_texture(self.name, 'overlay', frame=self.is_clear, fade=fade, x=(i*328)+self.move.attribute, y=self.overlay_move.attribute)
|
tex.draw_texture(self.name, 'overlay', frame=self.is_clear, fade=fade, x=(i*328)+self.move.attribute, y=self.overlay_move.attribute+y)
|
||||||
|
|
||||||
class DonBG5(DonBGBase):
|
class DonBG5(DonBGBase):
|
||||||
def __init__(self, tex: TextureWrapper, index: int, player_num: int, path: str):
|
def __init__(self, tex: TextureWrapper, index: int, player_num: int, path: str):
|
||||||
@@ -126,16 +126,16 @@ class DonBG5(DonBGBase):
|
|||||||
self.bounce_down.restart()
|
self.bounce_down.restart()
|
||||||
self.adjust.update(current_time_ms)
|
self.adjust.update(current_time_ms)
|
||||||
|
|
||||||
def draw(self, tex: TextureWrapper):
|
def draw(self, tex: TextureWrapper, y: float = 0):
|
||||||
self._draw_textures(tex, 1.0)
|
self._draw_textures(tex, 1.0, y)
|
||||||
if self.is_clear:
|
if self.is_clear:
|
||||||
self._draw_textures(tex, self.clear_fade.attribute)
|
self._draw_textures(tex, self.clear_fade.attribute, y)
|
||||||
|
|
||||||
def _draw_textures(self, tex: TextureWrapper, fade: float):
|
def _draw_textures(self, tex: TextureWrapper, fade: float, y: float):
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
tex.draw_texture(self.name, 'background', frame=self.is_clear, fade=fade, x=(i*328)+self.move.attribute)
|
tex.draw_texture(self.name, 'background', frame=self.is_clear, fade=fade, x=(i*328)+self.move.attribute, y=y)
|
||||||
for i in range(6):
|
for i in range(6):
|
||||||
tex.draw_texture(self.name, 'overlay', frame=self.is_clear, fade=fade, x=(i*368)+(self.move.attribute * ((184/328)*2)), y=self.bounce_up.attribute - self.bounce_down.attribute - self.adjust.attribute)
|
tex.draw_texture(self.name, 'overlay', frame=self.is_clear, fade=fade, x=(i*368)+(self.move.attribute * ((184/328)*2)), y=self.bounce_up.attribute - self.bounce_down.attribute - self.adjust.attribute + y)
|
||||||
|
|
||||||
class DonBG6(DonBGBase):
|
class DonBG6(DonBGBase):
|
||||||
def __init__(self, tex: TextureWrapper, index: int, player_num: int, path: str):
|
def __init__(self, tex: TextureWrapper, index: int, player_num: int, path: str):
|
||||||
@@ -144,15 +144,15 @@ class DonBG6(DonBGBase):
|
|||||||
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)
|
||||||
def draw(self, tex: TextureWrapper):
|
def draw(self, tex: TextureWrapper, y: float = 0):
|
||||||
self._draw_textures(tex, 1.0)
|
self._draw_textures(tex, 1.0, y)
|
||||||
if self.is_clear:
|
if self.is_clear:
|
||||||
self._draw_textures(tex, self.clear_fade.attribute)
|
self._draw_textures(tex, self.clear_fade.attribute, y)
|
||||||
|
|
||||||
def _draw_textures(self, tex: TextureWrapper, fade: float):
|
def _draw_textures(self, tex: TextureWrapper, fade: float, y: float):
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
tex.draw_texture(self.name, 'background', frame=self.is_clear, fade=fade, x=(i*328)+self.move.attribute)
|
tex.draw_texture(self.name, 'background', frame=self.is_clear, fade=fade, x=(i*328)+self.move.attribute, y=y)
|
||||||
for i in range(0, 6, 2):
|
for i in range(0, 6, 2):
|
||||||
tex.draw_texture(self.name, 'overlay_1', frame=self.is_clear, fade=fade, x=(i*264) + self.move.attribute*3, y=-self.move.attribute*0.85)
|
tex.draw_texture(self.name, 'overlay_1', frame=self.is_clear, fade=fade, x=(i*264) + self.move.attribute*3, y=-self.move.attribute*0.85+y)
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
tex.draw_texture(self.name, 'overlay_2', frame=self.is_clear, fade=fade, x=(i*328)+self.move.attribute, y=self.overlay_move.attribute)
|
tex.draw_texture(self.name, 'overlay_2', frame=self.is_clear, fade=fade, x=(i*328)+self.move.attribute, y=self.overlay_move.attribute+y)
|
||||||
|
|||||||
@@ -3,6 +3,14 @@ from libs.utils import global_tex
|
|||||||
|
|
||||||
class Chara2D:
|
class Chara2D:
|
||||||
def __init__(self, index: int, bpm: float, path: str = 'chara'):
|
def __init__(self, index: int, bpm: float, path: str = 'chara'):
|
||||||
|
"""
|
||||||
|
Initialize a Chara2D object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
index (int): The index of the character.
|
||||||
|
bpm (float): The beats per minute.
|
||||||
|
path (str, optional): The path to the character's textures. Defaults to 'chara'.
|
||||||
|
"""
|
||||||
self.name = "chara_" + str(index)
|
self.name = "chara_" + str(index)
|
||||||
self.tex = global_tex
|
self.tex = global_tex
|
||||||
self.anims = dict()
|
self.anims = dict()
|
||||||
@@ -27,6 +35,12 @@ class Chara2D:
|
|||||||
self.anims[name].start()
|
self.anims[name].start()
|
||||||
|
|
||||||
def set_animation(self, name: str):
|
def set_animation(self, name: str):
|
||||||
|
"""
|
||||||
|
Set the current animation for the character.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The name of the animation to set.
|
||||||
|
"""
|
||||||
if name == self.current_anim:
|
if name == self.current_anim:
|
||||||
return
|
return
|
||||||
if self.current_anim in self.temp_anims:
|
if self.current_anim in self.temp_anims:
|
||||||
@@ -49,6 +63,15 @@ class Chara2D:
|
|||||||
self.current_anim = name
|
self.current_anim = name
|
||||||
self.anims[name].start()
|
self.anims[name].start()
|
||||||
def update(self, current_time_ms: float, bpm: float, is_clear: bool, is_rainbow: bool):
|
def update(self, current_time_ms: float, bpm: float, is_clear: bool, is_rainbow: bool):
|
||||||
|
"""
|
||||||
|
Update the character's animation state and appearance.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
current_time_ms (float): The current time in milliseconds.
|
||||||
|
bpm (float): The beats per minute.
|
||||||
|
is_clear (bool): Whether the gauge is in clear mode.
|
||||||
|
is_rainbow (bool): Whether the gauge is in rainbow mode.
|
||||||
|
"""
|
||||||
if is_rainbow and not self.is_rainbow:
|
if is_rainbow and not self.is_rainbow:
|
||||||
self.is_rainbow = True
|
self.is_rainbow = True
|
||||||
self.set_animation('soul_in')
|
self.set_animation('soul_in')
|
||||||
@@ -76,6 +99,14 @@ class Chara2D:
|
|||||||
self.anims[self.current_anim].restart()
|
self.anims[self.current_anim].restart()
|
||||||
|
|
||||||
def draw(self, x: float = 0, y: float = 0, mirror=False):
|
def draw(self, x: float = 0, y: float = 0, mirror=False):
|
||||||
|
"""
|
||||||
|
Draw the character on the screen.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
x (float): The x-coordinate of the character's position.
|
||||||
|
y (float): The y-coordinate of the character's position.
|
||||||
|
mirror (bool): Whether to mirror the character horizontally.
|
||||||
|
"""
|
||||||
if self.is_rainbow and self.current_anim not in {'soul_in', 'balloon_pop', 'balloon_popping'}:
|
if self.is_rainbow and self.current_anim not in {'soul_in', 'balloon_pop', 'balloon_popping'}:
|
||||||
self.tex.draw_texture(self.name, self.current_anim + '_max', frame=self.anims[self.current_anim].attribute, x=x, y=y)
|
self.tex.draw_texture(self.name, self.current_anim + '_max', frame=self.anims[self.current_anim].attribute, x=x, y=y)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Modifiers:
|
class Modifiers:
|
||||||
|
"""
|
||||||
|
Modifiers for the game.
|
||||||
|
"""
|
||||||
auto: bool = False
|
auto: bool = False
|
||||||
speed: float = 1.0
|
speed: float = 1.0
|
||||||
display: bool = False
|
display: bool = False
|
||||||
@@ -11,9 +15,25 @@ class Modifiers:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class GlobalData:
|
class GlobalData:
|
||||||
|
"""
|
||||||
|
Global data for the game. Should be accessed via the global_data variable.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
selected_song (Path): The currently selected song.
|
||||||
|
songs_played (int): The number of songs played.
|
||||||
|
config (dict): The configuration settings.
|
||||||
|
song_hashes (dict[str, list[dict]]): A dictionary mapping song hashes to their metadata.
|
||||||
|
song_paths (dict[Path, str]): A dictionary mapping song paths to their hashes.
|
||||||
|
song_progress (float): The progress of the loading bar.
|
||||||
|
total_songs (int): The total number of songs.
|
||||||
|
hit_sound (int): The index of the hit sound currently used.
|
||||||
|
player_num (int): The player number. Either 1 or 2.
|
||||||
|
input_locked (int): The input lock status. 0 means unlocked, 1 or greater means locked.
|
||||||
|
modifiers (Modifiers): The modifiers for the game.
|
||||||
|
"""
|
||||||
selected_song: Path = Path()
|
selected_song: Path = Path()
|
||||||
songs_played: int = 0
|
songs_played: int = 0
|
||||||
config: dict = field(default_factory=lambda: dict())
|
config: dict[str, Any] = 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
|
||||||
song_paths: dict[Path, str] = field(default_factory=lambda: dict()) #path to hash
|
song_paths: dict[Path, str] = field(default_factory=lambda: dict()) #path to hash
|
||||||
song_progress: float = 0.0
|
song_progress: float = 0.0
|
||||||
|
|||||||
@@ -6,19 +6,43 @@ from libs.audio import audio
|
|||||||
|
|
||||||
|
|
||||||
class Nameplate:
|
class Nameplate:
|
||||||
|
"""Nameplate for displaying player information."""
|
||||||
def __init__(self, name: str, title: str, player_num: int, dan: int, is_gold: bool):
|
def __init__(self, name: str, title: str, player_num: int, dan: int, is_gold: bool):
|
||||||
|
"""Initialize a Nameplate object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The player's name.
|
||||||
|
title (str): The player's title.
|
||||||
|
player_num (int): The player's number.
|
||||||
|
dan (int): The player's dan level.
|
||||||
|
is_gold (bool): Whether the player's dan is gold.
|
||||||
|
"""
|
||||||
self.name = OutlinedText(name, 22, ray.WHITE, ray.BLACK, outline_thickness=3.0)
|
self.name = OutlinedText(name, 22, ray.WHITE, ray.BLACK, outline_thickness=3.0)
|
||||||
self.title = OutlinedText(title, 20, ray.BLACK, ray.WHITE, outline_thickness=0)
|
self.title = OutlinedText(title, 20, ray.BLACK, ray.WHITE, outline_thickness=0)
|
||||||
self.dan_index = dan
|
self.dan_index = dan
|
||||||
self.player_num = player_num
|
self.player_num = player_num
|
||||||
self.is_gold = is_gold
|
self.is_gold = is_gold
|
||||||
def update(self, current_time_ms: float):
|
def update(self, current_time_ms: float):
|
||||||
|
"""Update the Nameplate object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
current_time_ms (float): The current time in milliseconds.
|
||||||
|
Currently unused as rainbow nameplates are not implemented.
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def unload(self):
|
def unload(self):
|
||||||
|
"""Unload the Nameplate object."""
|
||||||
self.name.unload()
|
self.name.unload()
|
||||||
self.title.unload()
|
self.title.unload()
|
||||||
def draw(self, x: int, y: int, fade: float = 1.0):
|
def draw(self, x: int, y: int, fade: float = 1.0):
|
||||||
|
"""Draw the Nameplate object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
x (int): The x-coordinate of the Nameplate object.
|
||||||
|
y (int): The y-coordinate of the Nameplate object.
|
||||||
|
fade (float): The fade value of the Nameplate object.
|
||||||
|
"""
|
||||||
tex = global_tex
|
tex = global_tex
|
||||||
tex.draw_texture('nameplate', 'shadow', x=x, y=y, fade=min(0.5, fade))
|
tex.draw_texture('nameplate', 'shadow', x=x, y=y, fade=min(0.5, fade))
|
||||||
if self.player_num == -1:
|
if self.player_num == -1:
|
||||||
@@ -46,23 +70,28 @@ class Nameplate:
|
|||||||
self.title.draw(self.title.default_src, dest, ray.Vector2(0, 0), 0, ray.fade(ray.WHITE, fade))
|
self.title.draw(self.title.default_src, dest, ray.Vector2(0, 0), 0, ray.fade(ray.WHITE, fade))
|
||||||
|
|
||||||
class Indicator:
|
class Indicator:
|
||||||
|
"""Indicator class for displaying drum navigation."""
|
||||||
class State(Enum):
|
class State(Enum):
|
||||||
|
"""Enum representing the different states of the indicator."""
|
||||||
SKIP = 0
|
SKIP = 0
|
||||||
SIDE = 1
|
SIDE = 1
|
||||||
SELECT = 2
|
SELECT = 2
|
||||||
WAIT = 3
|
WAIT = 3
|
||||||
def __init__(self, state: State):
|
def __init__(self, state: State):
|
||||||
|
"""Initialize the indicator with the given state."""
|
||||||
self.state = state
|
self.state = state
|
||||||
self.don_fade = global_tex.get_animation(6)
|
self.don_fade = global_tex.get_animation(6)
|
||||||
self.blue_arrow_move = global_tex.get_animation(7)
|
self.blue_arrow_move = global_tex.get_animation(7)
|
||||||
self.blue_arrow_fade = global_tex.get_animation(8)
|
self.blue_arrow_fade = global_tex.get_animation(8)
|
||||||
|
|
||||||
def update(self, current_time_ms: float):
|
def update(self, current_time_ms: float):
|
||||||
|
"""Update the indicator's animations."""
|
||||||
self.don_fade.update(current_time_ms)
|
self.don_fade.update(current_time_ms)
|
||||||
self.blue_arrow_move.update(current_time_ms)
|
self.blue_arrow_move.update(current_time_ms)
|
||||||
self.blue_arrow_fade.update(current_time_ms)
|
self.blue_arrow_fade.update(current_time_ms)
|
||||||
|
|
||||||
def draw(self, x: int, y: int, fade=1.0):
|
def draw(self, x: int, y: int, fade=1.0):
|
||||||
|
"""Draw the indicator at the given position with the given fade."""
|
||||||
tex = global_tex
|
tex = global_tex
|
||||||
tex.draw_texture('indicator', 'background', x=x, y=y, fade=fade)
|
tex.draw_texture('indicator', 'background', x=x, y=y, fade=fade)
|
||||||
tex.draw_texture('indicator', 'text', frame=self.state.value, x=x, y=y, fade=fade)
|
tex.draw_texture('indicator', 'text', frame=self.state.value, x=x, y=y, fade=fade)
|
||||||
@@ -80,29 +109,42 @@ class Indicator:
|
|||||||
tex.draw_texture('indicator', 'drum_don', fade=min(fade, self.don_fade.attribute), index=self.state.value, x=x, y=y)
|
tex.draw_texture('indicator', 'drum_don', fade=min(fade, self.don_fade.attribute), index=self.state.value, x=x, y=y)
|
||||||
|
|
||||||
class CoinOverlay:
|
class CoinOverlay:
|
||||||
|
"""Coin overlay for the game."""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
"""Initialize the coin overlay."""
|
||||||
pass
|
pass
|
||||||
def update(self, current_time_ms: float):
|
def update(self, current_time_ms: float):
|
||||||
|
"""Update the coin overlay. Unimplemented"""
|
||||||
pass
|
pass
|
||||||
def draw(self, x: int = 0, y: int = 0):
|
def draw(self, x: int = 0, y: int = 0):
|
||||||
|
"""Draw the coin overlay.
|
||||||
|
Only draws free play for now."""
|
||||||
tex = global_tex
|
tex = global_tex
|
||||||
tex.draw_texture('overlay', 'free_play', x=x, y=y)
|
tex.draw_texture('overlay', 'free_play', x=x, y=y)
|
||||||
|
|
||||||
class AllNetIcon:
|
class AllNetIcon:
|
||||||
|
"""All.Net status icon for the game."""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
"""Initialize the All.Net status icon."""
|
||||||
pass
|
pass
|
||||||
def update(self, current_time_ms: float):
|
def update(self, current_time_ms: float):
|
||||||
|
"""Update the All.Net status icon."""
|
||||||
pass
|
pass
|
||||||
def draw(self, x: int = 0, y: int = 0):
|
def draw(self, x: int = 0, y: int = 0):
|
||||||
|
"""Draw the All.Net status icon. Only drawn offline for now"""
|
||||||
tex = global_tex
|
tex = global_tex
|
||||||
tex.draw_texture('overlay', 'allnet_indicator', x=x, y=y, frame=0)
|
tex.draw_texture('overlay', 'allnet_indicator', x=x, y=y, frame=0)
|
||||||
|
|
||||||
class EntryOverlay:
|
class EntryOverlay:
|
||||||
|
"""Banapass and Camera status icons"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
"""Initialize the Banapass and Camera status icons."""
|
||||||
self.online = False
|
self.online = False
|
||||||
def update(self, current_time_ms: float):
|
def update(self, current_time_ms: float):
|
||||||
|
"""Update the Banapass and Camera status icons."""
|
||||||
pass
|
pass
|
||||||
def draw(self, x: int = 0, y: int = 0):
|
def draw(self, x: int = 0, y: int = 0):
|
||||||
|
"""Draw the Banapass and Camera status icons."""
|
||||||
tex = global_tex
|
tex = global_tex
|
||||||
tex.draw_texture('overlay', 'banapass_or', x=x, y=y, frame=self.online)
|
tex.draw_texture('overlay', 'banapass_or', x=x, y=y, frame=self.online)
|
||||||
tex.draw_texture('overlay', 'banapass_card', x=x, y=y, frame=self.online)
|
tex.draw_texture('overlay', 'banapass_card', x=x, y=y, frame=self.online)
|
||||||
@@ -113,7 +155,16 @@ class EntryOverlay:
|
|||||||
tex.draw_texture('overlay', 'camera', x=x, y=y, frame=0)
|
tex.draw_texture('overlay', 'camera', x=x, y=y, frame=0)
|
||||||
|
|
||||||
class Timer:
|
class Timer:
|
||||||
|
"""Timer class for displaying countdown timers."""
|
||||||
def __init__(self, time: int, current_time_ms: float, confirm_func):
|
def __init__(self, time: int, current_time_ms: float, confirm_func):
|
||||||
|
"""
|
||||||
|
Initialize a Timer object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
time (int): The value to start counting down from.
|
||||||
|
current_time_ms (float): The current time in milliseconds.
|
||||||
|
confirm_func (function): The function to call when the timer finishes.
|
||||||
|
"""
|
||||||
self.time = time
|
self.time = time
|
||||||
self.last_time = current_time_ms
|
self.last_time = current_time_ms
|
||||||
self.counter = str(self.time)
|
self.counter = str(self.time)
|
||||||
@@ -124,6 +175,7 @@ class Timer:
|
|||||||
self.is_finished = False
|
self.is_finished = False
|
||||||
self.is_frozen = get_config()["general"]["timer_frozen"]
|
self.is_frozen = get_config()["general"]["timer_frozen"]
|
||||||
def update(self, current_time_ms: float):
|
def update(self, current_time_ms: float):
|
||||||
|
"""Update the timer's state."""
|
||||||
if self.time == 0 and not self.is_finished and not audio.is_sound_playing('voice_timer_0'):
|
if self.time == 0 and not self.is_finished and not audio.is_sound_playing('voice_timer_0'):
|
||||||
self.is_finished = True
|
self.is_finished = True
|
||||||
self.confirm_func()
|
self.confirm_func()
|
||||||
@@ -148,6 +200,7 @@ class Timer:
|
|||||||
elif self.time == 0:
|
elif self.time == 0:
|
||||||
audio.play_sound('voice_timer_0', 'voice')
|
audio.play_sound('voice_timer_0', 'voice')
|
||||||
def draw(self, x: int = 0, y: int = 0):
|
def draw(self, x: int = 0, y: int = 0):
|
||||||
|
"""Draw the timer on the screen."""
|
||||||
tex = global_tex
|
tex = global_tex
|
||||||
if self.time < 10:
|
if self.time < 10:
|
||||||
tex.draw_texture('timer', 'bg_red')
|
tex.draw_texture('timer', 'bg_red')
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ class DiffHashesDecoder(json.JSONDecoder):
|
|||||||
super().__init__(object_hook=diff_hashes_object_hook, *args, **kwargs)
|
super().__init__(object_hook=diff_hashes_object_hook, *args, **kwargs)
|
||||||
|
|
||||||
def read_tjap3_score(input_file: Path):
|
def read_tjap3_score(input_file: Path):
|
||||||
|
"""Read a TJAPlayer3 score.ini file and return the scores and clears."""
|
||||||
score_ini = configparser.ConfigParser()
|
score_ini = configparser.ConfigParser()
|
||||||
score_ini.read(input_file)
|
score_ini.read(input_file)
|
||||||
scores = [int(score_ini['HiScore.Drums']['HiScore1']),
|
scores = [int(score_ini['HiScore.Drums']['HiScore1']),
|
||||||
@@ -50,6 +51,7 @@ def read_tjap3_score(input_file: Path):
|
|||||||
return scores, clears, None
|
return scores, clears, None
|
||||||
|
|
||||||
def build_song_hashes(output_dir=Path("cache")):
|
def build_song_hashes(output_dir=Path("cache")):
|
||||||
|
"""Build a dictionary of song hashes and save it to a file."""
|
||||||
if not output_dir.exists():
|
if not output_dir.exists():
|
||||||
output_dir.mkdir()
|
output_dir.mkdir()
|
||||||
song_hashes: dict[str, list[dict]] = dict()
|
song_hashes: dict[str, list[dict]] = dict()
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ SCREEN_WIDTH = 1280
|
|||||||
SCREEN_HEIGHT = 720
|
SCREEN_HEIGHT = 720
|
||||||
|
|
||||||
class Texture:
|
class Texture:
|
||||||
|
"""Texture class for managing textures and animations."""
|
||||||
def __init__(self, name: str, texture: Union[ray.Texture, list[ray.Texture]], init_vals: dict[str, int]):
|
def __init__(self, name: str, texture: Union[ray.Texture, list[ray.Texture]], init_vals: dict[str, int]):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.texture = texture
|
self.texture = texture
|
||||||
@@ -33,12 +34,14 @@ class Texture:
|
|||||||
self.controllable: list[bool] = [False]
|
self.controllable: list[bool] = [False]
|
||||||
|
|
||||||
class TextureWrapper:
|
class TextureWrapper:
|
||||||
|
"""Texture wrapper class for managing textures and animations."""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.textures: dict[str, dict[str, Texture]] = dict()
|
self.textures: dict[str, dict[str, Texture]] = dict()
|
||||||
self.animations: dict[int, BaseAnimation] = dict()
|
self.animations: dict[int, BaseAnimation] = dict()
|
||||||
self.graphics_path = Path("Graphics")
|
self.graphics_path = Path("Graphics")
|
||||||
|
|
||||||
def unload_textures(self):
|
def unload_textures(self):
|
||||||
|
"""Unload all textures and animations."""
|
||||||
for zip in self.textures:
|
for zip in self.textures:
|
||||||
for file in self.textures[zip]:
|
for file in self.textures[zip]:
|
||||||
tex_object = self.textures[zip][file]
|
tex_object = self.textures[zip][file]
|
||||||
@@ -49,6 +52,8 @@ class TextureWrapper:
|
|||||||
ray.unload_texture(tex_object.texture)
|
ray.unload_texture(tex_object.texture)
|
||||||
|
|
||||||
def get_animation(self, index: int, is_copy: bool = False):
|
def get_animation(self, index: int, is_copy: bool = False):
|
||||||
|
"""Get an animation by ID and returns a reference.
|
||||||
|
Returns a copy of the animation if is_copy is True."""
|
||||||
if index not in self.animations:
|
if index not in self.animations:
|
||||||
raise Exception(f"Unable to find id {index} in loaded animations")
|
raise Exception(f"Unable to find id {index} in loaded animations")
|
||||||
if is_copy:
|
if is_copy:
|
||||||
@@ -83,12 +88,14 @@ class TextureWrapper:
|
|||||||
tex_object.controllable = [tex_mapping.get("controllable", False)]
|
tex_object.controllable = [tex_mapping.get("controllable", False)]
|
||||||
|
|
||||||
def load_animations(self, screen_name: str):
|
def load_animations(self, screen_name: str):
|
||||||
|
"""Load animations for a screen."""
|
||||||
screen_path = self.graphics_path / screen_name
|
screen_path = self.graphics_path / screen_name
|
||||||
if (screen_path / 'animation.json').exists():
|
if (screen_path / 'animation.json').exists():
|
||||||
with open(screen_path / 'animation.json') as json_file:
|
with open(screen_path / 'animation.json') as json_file:
|
||||||
self.animations = parse_animations(json.loads(json_file.read()))
|
self.animations = parse_animations(json.loads(json_file.read()))
|
||||||
|
|
||||||
def load_zip(self, screen_name: str, subset: str):
|
def load_zip(self, screen_name: str, subset: str):
|
||||||
|
"""Load textures from a zip file."""
|
||||||
zip = (self.graphics_path / screen_name / subset).with_suffix('.zip')
|
zip = (self.graphics_path / screen_name / subset).with_suffix('.zip')
|
||||||
if screen_name in self.textures and subset in self.textures[screen_name]:
|
if screen_name in self.textures and subset in self.textures[screen_name]:
|
||||||
return
|
return
|
||||||
@@ -134,6 +141,7 @@ class TextureWrapper:
|
|||||||
raise Exception(f"Texture {tex_name} was not found in {zip}")
|
raise Exception(f"Texture {tex_name} was not found in {zip}")
|
||||||
|
|
||||||
def load_screen_textures(self, screen_name: str) -> None:
|
def load_screen_textures(self, screen_name: str) -> None:
|
||||||
|
"""Load textures for a screen."""
|
||||||
screen_path = self.graphics_path / screen_name
|
screen_path = self.graphics_path / screen_name
|
||||||
if (screen_path / 'animation.json').exists():
|
if (screen_path / 'animation.json').exists():
|
||||||
with open(screen_path / 'animation.json') as json_file:
|
with open(screen_path / 'animation.json') as json_file:
|
||||||
@@ -165,6 +173,26 @@ class TextureWrapper:
|
|||||||
mirror: str = '', x: float = 0, y: float = 0, x2: float = 0, y2: float = 0,
|
mirror: str = '', x: float = 0, y: float = 0, x2: float = 0, y2: float = 0,
|
||||||
origin: ray.Vector2 = ray.Vector2(0,0), rotation: float = 0, fade: float = 1.1,
|
origin: ray.Vector2 = ray.Vector2(0,0), rotation: float = 0, fade: float = 1.1,
|
||||||
index: int = 0, src: Optional[ray.Rectangle] = None) -> None:
|
index: int = 0, src: Optional[ray.Rectangle] = None) -> None:
|
||||||
|
"""
|
||||||
|
Wrapper function for raylib's draw_texture_pro().
|
||||||
|
Parameters:
|
||||||
|
subset (str): The subset of textures to use.
|
||||||
|
texture (str): The name of the texture to draw.
|
||||||
|
color (ray.Color): The color to tint the texture.
|
||||||
|
frame (int): The frame of the texture to draw. Only used if the texture is animated.
|
||||||
|
scale (float): The scale factor to apply to the texture.
|
||||||
|
center (bool): Whether to center the texture.
|
||||||
|
mirror (str): The direction to mirror the texture, either 'horizontal' or 'vertical'.
|
||||||
|
x (float): An x-value added to the top-left corner of the texture.
|
||||||
|
y (float): The y-value added to the top-left corner of the texture.
|
||||||
|
x2 (float): The x-value added to the bottom-right corner of the texture.
|
||||||
|
y2 (float): The y-value added to the bottom-right corner of the texture.
|
||||||
|
origin (ray.Vector2): The origin point of the texture.
|
||||||
|
rotation (float): The rotation angle of the texture.
|
||||||
|
fade (float): The fade factor to apply to the texture.
|
||||||
|
index (int): The index of the position data for the texture. Only used if the texture has multiple positions.
|
||||||
|
src (Optional[ray.Rectangle]): The source rectangle of the texture.
|
||||||
|
"""
|
||||||
mirror_x = -1 if mirror == 'horizontal' else 1
|
mirror_x = -1 if mirror == 'horizontal' else 1
|
||||||
mirror_y = -1 if mirror == 'vertical' else 1
|
mirror_y = -1 if mirror == 'vertical' else 1
|
||||||
if fade != 1.1:
|
if fade != 1.1:
|
||||||
|
|||||||
131
libs/tja.py
131
libs/tja.py
@@ -11,18 +11,36 @@ from libs.utils import get_pixels_per_frame, global_data, strip_comments
|
|||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=64)
|
@lru_cache(maxsize=64)
|
||||||
def get_ms_per_measure(bpm_val, time_sig):
|
def get_ms_per_measure(bpm_val: float, time_sig: float):
|
||||||
|
"""Calculate the number of milliseconds per measure."""
|
||||||
#https://gist.github.com/KatieFrogs/e000f406bbc70a12f3c34a07303eec8b#measure
|
#https://gist.github.com/KatieFrogs/e000f406bbc70a12f3c34a07303eec8b#measure
|
||||||
if bpm_val == 0:
|
if bpm_val == 0:
|
||||||
return 0
|
return 0
|
||||||
return 60000 * (time_sig * 4) / bpm_val
|
return 60000 * (time_sig * 4) / bpm_val
|
||||||
|
|
||||||
@lru_cache(maxsize=64)
|
@lru_cache(maxsize=64)
|
||||||
def get_pixels_per_ms(pixels_per_frame):
|
def get_pixels_per_ms(pixels_per_frame: float):
|
||||||
|
"""Calculate the number of pixels per millisecond."""
|
||||||
return pixels_per_frame / (1000 / 60)
|
return pixels_per_frame / (1000 / 60)
|
||||||
|
|
||||||
@dataclass()
|
@dataclass()
|
||||||
class Note:
|
class Note:
|
||||||
|
"""A note in a TJA file.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
type (int): The type (color) of the note.
|
||||||
|
hit_ms (float): The time at which the note should be hit.
|
||||||
|
load_ms (float): The time at which the note should be loaded.
|
||||||
|
pixels_per_frame_x (float): The number of pixels per frame in the x direction.
|
||||||
|
pixels_per_frame_y (float): The number of pixels per frame in the y direction.
|
||||||
|
display (bool): Whether the note should be displayed.
|
||||||
|
index (int): The index of the note.
|
||||||
|
bpm (float): The beats per minute of the song.
|
||||||
|
gogo_time (bool): Whether the note is a gogo time note.
|
||||||
|
moji (int): The text drawn below the note.
|
||||||
|
is_branch_start (bool): Whether the note is the start of a branch.
|
||||||
|
branch_params (str): The parameters (requirements) of the branch.
|
||||||
|
"""
|
||||||
type: int = field(init=False)
|
type: int = field(init=False)
|
||||||
hit_ms: float = field(init=False)
|
hit_ms: float = field(init=False)
|
||||||
load_ms: float = field(init=False)
|
load_ms: float = field(init=False)
|
||||||
@@ -78,6 +96,12 @@ class Note:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Drumroll(Note):
|
class Drumroll(Note):
|
||||||
|
"""A drumroll note in a TJA file.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
_source_note (Note): The source note.
|
||||||
|
color (int): The color of the drumroll. (0-255 where 255 is red)
|
||||||
|
"""
|
||||||
_source_note: Note
|
_source_note: Note
|
||||||
color: int = field(init=False)
|
color: int = field(init=False)
|
||||||
|
|
||||||
@@ -94,6 +118,14 @@ class Drumroll(Note):
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Balloon(Note):
|
class Balloon(Note):
|
||||||
|
"""A balloon note in a TJA file.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
_source_note (Note): The source note.
|
||||||
|
count (int): The number of hits it takes to pop.
|
||||||
|
popped (bool): Whether the balloon has been popped.
|
||||||
|
is_kusudama (bool): Whether the balloon is a kusudama.
|
||||||
|
"""
|
||||||
_source_note: Note
|
_source_note: Note
|
||||||
count: int = field(init=False)
|
count: int = field(init=False)
|
||||||
popped: bool = False
|
popped: bool = False
|
||||||
@@ -125,6 +157,10 @@ class Balloon(Note):
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class NoteList:
|
class NoteList:
|
||||||
|
"""A collection of notes
|
||||||
|
play_notes: A list of notes, drumrolls, and balloons that are played by the player
|
||||||
|
draw_notes: A list of notes, drumrolls, and balloons that are drawn by the player
|
||||||
|
bars: A list of bars"""
|
||||||
play_notes: list[Note | Drumroll | Balloon] = field(default_factory=lambda: [])
|
play_notes: list[Note | Drumroll | Balloon] = field(default_factory=lambda: [])
|
||||||
draw_notes: list[Note | Drumroll | Balloon] = field(default_factory=lambda: [])
|
draw_notes: list[Note | Drumroll | Balloon] = field(default_factory=lambda: [])
|
||||||
bars: list[Note] = field(default_factory=lambda: [])
|
bars: list[Note] = field(default_factory=lambda: [])
|
||||||
@@ -144,6 +180,13 @@ class NoteList:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class CourseData:
|
class CourseData:
|
||||||
|
"""A collection of course metadata
|
||||||
|
level: number of stars
|
||||||
|
balloon: list of balloon counts
|
||||||
|
scoreinit: Unused
|
||||||
|
scorediff: Unused
|
||||||
|
is_branching: whether the course has branches
|
||||||
|
"""
|
||||||
level: int = 0
|
level: int = 0
|
||||||
balloon: list[int] = field(default_factory=lambda: [])
|
balloon: list[int] = field(default_factory=lambda: [])
|
||||||
scoreinit: list[int] = field(default_factory=lambda: [])
|
scoreinit: list[int] = field(default_factory=lambda: [])
|
||||||
@@ -152,6 +195,19 @@ class CourseData:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TJAMetadata:
|
class TJAMetadata:
|
||||||
|
"""Metadata for a TJA file
|
||||||
|
title: dictionary for song titles, accessed by language code
|
||||||
|
subtitle: dictionary for song subtitles, accessed by language code
|
||||||
|
genre: genre of the song
|
||||||
|
wave: path to the song's audio file
|
||||||
|
demostart: start time of the preview
|
||||||
|
offset: offset of the song's audio file
|
||||||
|
bpm: beats per minute of the song
|
||||||
|
bgmovie: path to the song's background movie file
|
||||||
|
movieoffset: offset of the song's background movie file
|
||||||
|
scene_preset: background for the song
|
||||||
|
course_data: dictionary of course metadata, accessed by diff number
|
||||||
|
"""
|
||||||
title: dict[str, str] = field(default_factory= lambda: {'en': ''})
|
title: dict[str, str] = field(default_factory= lambda: {'en': ''})
|
||||||
subtitle: dict[str, str] = field(default_factory= lambda: {'en': ''})
|
subtitle: dict[str, str] = field(default_factory= lambda: {'en': ''})
|
||||||
genre: str = ''
|
genre: str = ''
|
||||||
@@ -166,6 +222,11 @@ class TJAMetadata:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TJAEXData:
|
class TJAEXData:
|
||||||
|
"""Extra data for TJA files
|
||||||
|
new_audio: Contains the word "-New Audio-" in any song title
|
||||||
|
old_audio: Contains the word "-Old Audio-" in any song title
|
||||||
|
limited_time: Contains the word "限定" in any song title or subtitle
|
||||||
|
new: If the TJA file has been created or modified in the last week"""
|
||||||
new_audio: bool = False
|
new_audio: bool = False
|
||||||
old_audio: bool = False
|
old_audio: bool = False
|
||||||
limited_time: bool = False
|
limited_time: bool = False
|
||||||
@@ -173,6 +234,14 @@ class TJAEXData:
|
|||||||
|
|
||||||
|
|
||||||
def calculate_base_score(notes: NoteList) -> int:
|
def calculate_base_score(notes: NoteList) -> int:
|
||||||
|
"""Calculate the base score for a song based on the number of notes, balloons, and drumrolls.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
notes (NoteList): The list of notes in the song.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: The base score for the song.
|
||||||
|
"""
|
||||||
total_notes = 0
|
total_notes = 0
|
||||||
balloon_count = 0
|
balloon_count = 0
|
||||||
drumroll_msec = 0
|
drumroll_msec = 0
|
||||||
@@ -195,6 +264,14 @@ def calculate_base_score(notes: NoteList) -> int:
|
|||||||
return math.ceil((1000000 - (balloon_count * 100) - (16.920079999994086 * drumroll_msec / 1000 * 100)) / total_notes / 10) * 10
|
return math.ceil((1000000 - (balloon_count * 100) - (16.920079999994086 * drumroll_msec / 1000 * 100)) / total_notes / 10) * 10
|
||||||
|
|
||||||
def test_encodings(file_path):
|
def test_encodings(file_path):
|
||||||
|
"""Test the encoding of a file by trying different encodings.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file_path (Path): The path to the file to test.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The encoding that successfully decoded the file.
|
||||||
|
"""
|
||||||
encodings = ['utf-8-sig', 'shift-jis', 'utf-8']
|
encodings = ['utf-8-sig', 'shift-jis', 'utf-8']
|
||||||
final_encoding = None
|
final_encoding = None
|
||||||
|
|
||||||
@@ -209,8 +286,28 @@ def test_encodings(file_path):
|
|||||||
|
|
||||||
|
|
||||||
class TJAParser:
|
class TJAParser:
|
||||||
|
"""Parse a TJA file and extract metadata and data.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path (Path): The path to the TJA file.
|
||||||
|
start_delay (int): The delay in milliseconds before the first note.
|
||||||
|
distance (int): The distance between notes.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
metadata (TJAMetadata): The metadata extracted from the TJA file.
|
||||||
|
ex_data (TJAEXData): The extended data extracted from the TJA file.
|
||||||
|
data (list): The data extracted from the TJA file.
|
||||||
|
"""
|
||||||
DIFFS = {0: "easy", 1: "normal", 2: "hard", 3: "oni", 4: "edit", 5: "tower", 6: "dan"}
|
DIFFS = {0: "easy", 1: "normal", 2: "hard", 3: "oni", 4: "edit", 5: "tower", 6: "dan"}
|
||||||
def __init__(self, path: Path, start_delay: int = 0, distance: int = 866):
|
def __init__(self, path: Path, start_delay: int = 0, distance: int = 866):
|
||||||
|
"""
|
||||||
|
Initialize a TJA object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path (Path): The path to the TJA file.
|
||||||
|
start_delay (int): The delay in milliseconds before the first note.
|
||||||
|
distance (int): The distance between notes.
|
||||||
|
"""
|
||||||
self.file_path: Path = path
|
self.file_path: Path = path
|
||||||
|
|
||||||
encoding = test_encodings(self.file_path)
|
encoding = test_encodings(self.file_path)
|
||||||
@@ -226,6 +323,9 @@ class TJAParser:
|
|||||||
self.current_ms: float = start_delay
|
self.current_ms: float = start_delay
|
||||||
|
|
||||||
def get_metadata(self):
|
def get_metadata(self):
|
||||||
|
"""
|
||||||
|
Extract metadata from the TJA file.
|
||||||
|
"""
|
||||||
current_diff = None # Track which difficulty we're currently processing
|
current_diff = None # Track which difficulty we're currently processing
|
||||||
|
|
||||||
for item in self.data:
|
for item in self.data:
|
||||||
@@ -332,6 +432,15 @@ class TJAParser:
|
|||||||
self.ex_data.limited_time = True
|
self.ex_data.limited_time = True
|
||||||
|
|
||||||
def data_to_notes(self, diff) -> list[list[str]]:
|
def data_to_notes(self, diff) -> list[list[str]]:
|
||||||
|
"""
|
||||||
|
Convert the data to notes.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
diff (int): The difficulty level.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[list[str]]: The notes.
|
||||||
|
"""
|
||||||
diff_name = self.DIFFS.get(diff, "").lower()
|
diff_name = self.DIFFS.get(diff, "").lower()
|
||||||
|
|
||||||
# Use enumerate for single iteration
|
# Use enumerate for single iteration
|
||||||
@@ -380,6 +489,16 @@ class TJAParser:
|
|||||||
return notes
|
return notes
|
||||||
|
|
||||||
def get_moji(self, play_note_list: list[Note], ms_per_measure: float) -> None:
|
def get_moji(self, play_note_list: list[Note], ms_per_measure: float) -> None:
|
||||||
|
"""
|
||||||
|
Assign 口唱歌 (note phoneticization) to notes.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
play_note_list (list[Note]): The list of notes to process.
|
||||||
|
ms_per_measure (float): The duration of a measure in milliseconds.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
se_notes = {
|
se_notes = {
|
||||||
1: [0, 1, 2], # Note '1' has three possible sound effects
|
1: [0, 1, 2], # Note '1' has three possible sound effects
|
||||||
2: [3, 4], # Note '2' has two possible sound effects
|
2: [3, 4], # Note '2' has two possible sound effects
|
||||||
@@ -440,6 +559,7 @@ class TJAParser:
|
|||||||
play_note_list[-3].moji = se_notes[1][2]
|
play_note_list[-3].moji = se_notes[1][2]
|
||||||
|
|
||||||
def notes_to_position(self, diff: int):
|
def notes_to_position(self, diff: int):
|
||||||
|
"""Parse a TJA's notes into a NoteList."""
|
||||||
master_notes = NoteList()
|
master_notes = NoteList()
|
||||||
branch_m: list[NoteList] = []
|
branch_m: list[NoteList] = []
|
||||||
branch_e: list[NoteList] = []
|
branch_e: list[NoteList] = []
|
||||||
@@ -705,6 +825,7 @@ class TJAParser:
|
|||||||
return master_notes, branch_m, branch_e, branch_n
|
return master_notes, branch_m, branch_e, branch_n
|
||||||
|
|
||||||
def hash_note_data(self, notes: NoteList):
|
def hash_note_data(self, notes: NoteList):
|
||||||
|
"""Hashes the note data for the given NoteList."""
|
||||||
n = hashlib.sha256()
|
n = hashlib.sha256()
|
||||||
list1 = notes.play_notes
|
list1 = notes.play_notes
|
||||||
list2 = notes.bars
|
list2 = notes.bars
|
||||||
@@ -726,6 +847,7 @@ class TJAParser:
|
|||||||
return n.hexdigest()
|
return n.hexdigest()
|
||||||
|
|
||||||
def modifier_speed(notes: NoteList, value: float):
|
def modifier_speed(notes: NoteList, value: float):
|
||||||
|
"""Modifies the speed of the notes in the given NoteList."""
|
||||||
modded_notes = notes.draw_notes.copy()
|
modded_notes = notes.draw_notes.copy()
|
||||||
modded_bars = notes.bars.copy()
|
modded_bars = notes.bars.copy()
|
||||||
for note in modded_notes:
|
for note in modded_notes:
|
||||||
@@ -737,12 +859,14 @@ def modifier_speed(notes: NoteList, value: float):
|
|||||||
return modded_notes, modded_bars
|
return modded_notes, modded_bars
|
||||||
|
|
||||||
def modifier_display(notes: NoteList):
|
def modifier_display(notes: NoteList):
|
||||||
|
"""Modifies the display of the notes in the given NoteList."""
|
||||||
modded_notes = notes.draw_notes.copy()
|
modded_notes = notes.draw_notes.copy()
|
||||||
for note in modded_notes:
|
for note in modded_notes:
|
||||||
note.display = False
|
note.display = False
|
||||||
return modded_notes
|
return modded_notes
|
||||||
|
|
||||||
def modifier_inverse(notes: NoteList):
|
def modifier_inverse(notes: NoteList):
|
||||||
|
"""Inverts the type of the notes in the given NoteList."""
|
||||||
modded_notes = notes.play_notes.copy()
|
modded_notes = notes.play_notes.copy()
|
||||||
type_mapping = {1: 2, 2: 1, 3: 4, 4: 3}
|
type_mapping = {1: 2, 2: 1, 3: 4, 4: 3}
|
||||||
for note in modded_notes:
|
for note in modded_notes:
|
||||||
@@ -751,6 +875,8 @@ def modifier_inverse(notes: NoteList):
|
|||||||
return modded_notes
|
return modded_notes
|
||||||
|
|
||||||
def modifier_random(notes: NoteList, value: int):
|
def modifier_random(notes: NoteList, value: int):
|
||||||
|
"""Randomly modifies the type of the notes in the given NoteList.
|
||||||
|
value: 1 == kimagure, 2 == detarame"""
|
||||||
#value: 1 == kimagure, 2 == detarame
|
#value: 1 == kimagure, 2 == detarame
|
||||||
modded_notes = notes.play_notes.copy()
|
modded_notes = notes.play_notes.copy()
|
||||||
percentage = int(len(modded_notes) / 5) * value
|
percentage = int(len(modded_notes) / 5) * value
|
||||||
@@ -762,6 +888,7 @@ def modifier_random(notes: NoteList, value: int):
|
|||||||
return modded_notes
|
return modded_notes
|
||||||
|
|
||||||
def apply_modifiers(notes: NoteList):
|
def apply_modifiers(notes: NoteList):
|
||||||
|
"""Applies all selected modifiers from global_data to the given NoteList."""
|
||||||
if global_data.modifiers.display:
|
if global_data.modifiers.display:
|
||||||
draw_notes = modifier_display(notes)
|
draw_notes = modifier_display(notes)
|
||||||
if global_data.modifiers.inverse:
|
if global_data.modifiers.inverse:
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ from libs.utils import OutlinedText, global_tex
|
|||||||
|
|
||||||
|
|
||||||
class Transition:
|
class Transition:
|
||||||
|
"""Transition class for the game."""
|
||||||
def __init__(self, title: str, subtitle: str, is_second: bool = False) -> None:
|
def __init__(self, title: str, subtitle: str, is_second: bool = False) -> None:
|
||||||
|
"""Initialize the transition object.
|
||||||
|
title: str - The title of the chart.
|
||||||
|
subtitle: str - The subtitle of the chart.
|
||||||
|
is_second: bool - Whether this is the second half of the transition."""
|
||||||
self.is_finished = False
|
self.is_finished = False
|
||||||
self.rainbow_up = global_tex.get_animation(0)
|
self.rainbow_up = global_tex.get_animation(0)
|
||||||
self.mini_up = global_tex.get_animation(1)
|
self.mini_up = global_tex.get_animation(1)
|
||||||
@@ -16,6 +21,7 @@ class Transition:
|
|||||||
self.is_second = is_second
|
self.is_second = is_second
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
|
"""Start the transition effect."""
|
||||||
self.rainbow_up.start()
|
self.rainbow_up.start()
|
||||||
self.mini_up.start()
|
self.mini_up.start()
|
||||||
self.chara_down.start()
|
self.chara_down.start()
|
||||||
@@ -23,6 +29,7 @@ class Transition:
|
|||||||
self.song_info_fade_out.start()
|
self.song_info_fade_out.start()
|
||||||
|
|
||||||
def update(self, current_time_ms: float):
|
def update(self, current_time_ms: float):
|
||||||
|
"""Update the transition effect."""
|
||||||
self.rainbow_up.update(current_time_ms)
|
self.rainbow_up.update(current_time_ms)
|
||||||
self.chara_down.update(current_time_ms)
|
self.chara_down.update(current_time_ms)
|
||||||
self.mini_up.update(current_time_ms)
|
self.mini_up.update(current_time_ms)
|
||||||
@@ -30,7 +37,7 @@ class Transition:
|
|||||||
self.song_info_fade_out.update(current_time_ms)
|
self.song_info_fade_out.update(current_time_ms)
|
||||||
self.is_finished = self.song_info_fade.is_finished
|
self.is_finished = self.song_info_fade.is_finished
|
||||||
|
|
||||||
def draw_song_info(self):
|
def _draw_song_info(self):
|
||||||
color_1 = ray.fade(ray.WHITE, self.song_info_fade.attribute)
|
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))
|
color_2 = ray.fade(ray.WHITE, min(0.70, self.song_info_fade.attribute))
|
||||||
offset = 0
|
offset = 0
|
||||||
@@ -50,6 +57,7 @@ class Transition:
|
|||||||
self.subtitle.draw(self.subtitle.default_src, dest, ray.Vector2(0, 0), 0, color_1)
|
self.subtitle.draw(self.subtitle.default_src, dest, ray.Vector2(0, 0), 0, color_1)
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
|
"""Draw the transition effect."""
|
||||||
total_offset = 0
|
total_offset = 0
|
||||||
if self.is_second:
|
if self.is_second:
|
||||||
total_offset = 816
|
total_offset = 816
|
||||||
@@ -65,4 +73,4 @@ class Transition:
|
|||||||
global_tex.draw_texture('rainbow_transition', 'chara_right', x=self.mini_up.attribute//2 + chara_offset, y=-self.mini_up.attribute + offset - total_offset)
|
global_tex.draw_texture('rainbow_transition', 'chara_right', x=self.mini_up.attribute//2 + chara_offset, y=-self.mini_up.attribute + offset - total_offset)
|
||||||
global_tex.draw_texture('rainbow_transition', 'chara_center', y=-self.rainbow_up.attribute + offset - total_offset)
|
global_tex.draw_texture('rainbow_transition', 'chara_center', y=-self.rainbow_up.attribute + offset - total_offset)
|
||||||
|
|
||||||
self.draw_song_info()
|
self._draw_song_info()
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ def force_dedicated_gpu():
|
|||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
def rounded(num: float) -> int:
|
def rounded(num: float) -> int:
|
||||||
|
"""Round a number to the nearest integer"""
|
||||||
sign = 1 if (num >= 0) else -1
|
sign = 1 if (num >= 0) else -1
|
||||||
num = abs(num)
|
num = abs(num)
|
||||||
result = int(num)
|
result = int(num)
|
||||||
@@ -47,9 +48,11 @@ def rounded(num: float) -> int:
|
|||||||
return sign * result
|
return sign * result
|
||||||
|
|
||||||
def get_current_ms() -> int:
|
def get_current_ms() -> int:
|
||||||
|
"""Get the current time in milliseconds"""
|
||||||
return rounded(time.time() * 1000)
|
return rounded(time.time() * 1000)
|
||||||
|
|
||||||
def strip_comments(code: str) -> str:
|
def strip_comments(code: str) -> str:
|
||||||
|
"""Strip comments from a string of code"""
|
||||||
result = ''
|
result = ''
|
||||||
index = 0
|
index = 0
|
||||||
for line in code.splitlines():
|
for line in code.splitlines():
|
||||||
@@ -63,6 +66,7 @@ def strip_comments(code: str) -> str:
|
|||||||
|
|
||||||
@lru_cache
|
@lru_cache
|
||||||
def get_pixels_per_frame(bpm: float, time_signature: float, distance: float) -> float:
|
def get_pixels_per_frame(bpm: float, time_signature: float, distance: float) -> float:
|
||||||
|
"""Calculate the number of pixels per frame"""
|
||||||
if bpm == 0:
|
if bpm == 0:
|
||||||
return 0
|
return 0
|
||||||
beat_duration = 60 / bpm
|
beat_duration = 60 / bpm
|
||||||
@@ -71,6 +75,7 @@ def get_pixels_per_frame(bpm: float, time_signature: float, distance: float) ->
|
|||||||
return (distance / total_frames)
|
return (distance / total_frames)
|
||||||
|
|
||||||
def get_config() -> dict[str, Any]:
|
def get_config() -> dict[str, Any]:
|
||||||
|
"""Get the configuration from the TOML file"""
|
||||||
config_path = Path('dev-config.toml') if Path('dev-config.toml').exists() else Path('config.toml')
|
config_path = Path('dev-config.toml') if Path('dev-config.toml').exists() else Path('config.toml')
|
||||||
|
|
||||||
with open(config_path, "r", encoding="utf-8") as f:
|
with open(config_path, "r", encoding="utf-8") as f:
|
||||||
@@ -79,6 +84,7 @@ def get_config() -> dict[str, Any]:
|
|||||||
return json.loads(json.dumps(config_file))
|
return json.loads(json.dumps(config_file))
|
||||||
|
|
||||||
def save_config(config: dict[str, Any]) -> None:
|
def save_config(config: dict[str, Any]) -> None:
|
||||||
|
"""Save the configuration to the TOML file"""
|
||||||
if Path('dev-config.toml').exists():
|
if Path('dev-config.toml').exists():
|
||||||
with open(Path('dev-config.toml'), "w", encoding="utf-8") as f:
|
with open(Path('dev-config.toml'), "w", encoding="utf-8") as f:
|
||||||
tomlkit.dump(config, f)
|
tomlkit.dump(config, f)
|
||||||
@@ -87,6 +93,7 @@ def save_config(config: dict[str, Any]) -> None:
|
|||||||
tomlkit.dump(config, f)
|
tomlkit.dump(config, f)
|
||||||
|
|
||||||
def is_l_don_pressed() -> bool:
|
def is_l_don_pressed() -> bool:
|
||||||
|
"""Check if the left don button is pressed"""
|
||||||
if global_data.input_locked:
|
if global_data.input_locked:
|
||||||
return False
|
return False
|
||||||
keys = global_data.config["keys"]["left_don"]
|
keys = global_data.config["keys"]["left_don"]
|
||||||
@@ -111,6 +118,7 @@ def is_l_don_pressed() -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def is_r_don_pressed() -> bool:
|
def is_r_don_pressed() -> bool:
|
||||||
|
"""Check if the right don button is pressed"""
|
||||||
if global_data.input_locked:
|
if global_data.input_locked:
|
||||||
return False
|
return False
|
||||||
keys = global_data.config["keys"]["right_don"]
|
keys = global_data.config["keys"]["right_don"]
|
||||||
@@ -137,6 +145,7 @@ def is_r_don_pressed() -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def is_l_kat_pressed() -> bool:
|
def is_l_kat_pressed() -> bool:
|
||||||
|
"""Check if the left kat button is pressed"""
|
||||||
if global_data.input_locked:
|
if global_data.input_locked:
|
||||||
return False
|
return False
|
||||||
keys = global_data.config["keys"]["left_kat"]
|
keys = global_data.config["keys"]["left_kat"]
|
||||||
@@ -163,6 +172,7 @@ def is_l_kat_pressed() -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def is_r_kat_pressed() -> bool:
|
def is_r_kat_pressed() -> bool:
|
||||||
|
"""Check if the right kat button is pressed"""
|
||||||
if global_data.input_locked:
|
if global_data.input_locked:
|
||||||
return False
|
return False
|
||||||
keys = global_data.config["keys"]["right_kat"]
|
keys = global_data.config["keys"]["right_kat"]
|
||||||
@@ -190,6 +200,18 @@ def is_r_kat_pressed() -> bool:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class SessionData:
|
class SessionData:
|
||||||
|
"""Data class for storing session data. Wiped after the result screen.
|
||||||
|
selected_difficulty: The difficulty level selected by the user.
|
||||||
|
song_title: The title of the song being played.
|
||||||
|
genre_index: The index of the genre being played.
|
||||||
|
result_score: The score achieved in the game.
|
||||||
|
result_good: The number of good notes achieved in the game.
|
||||||
|
result_ok: The number of ok notes achieved in the game.
|
||||||
|
result_bad: The number of bad notes achieved in the game.
|
||||||
|
result_max_combo: The maximum combo achieved in the game.
|
||||||
|
result_total_drumroll: The total drumroll achieved in the game.
|
||||||
|
result_gauge_length: The length of the gauge achieved in the game.
|
||||||
|
prev_score: The previous score pulled from the database."""
|
||||||
selected_difficulty: int = 0
|
selected_difficulty: int = 0
|
||||||
song_title: str = ''
|
song_title: str = ''
|
||||||
genre_index: int = 0
|
genre_index: int = 0
|
||||||
@@ -206,6 +228,7 @@ session_data = SessionData()
|
|||||||
global_tex = TextureWrapper()
|
global_tex = TextureWrapper()
|
||||||
|
|
||||||
def reset_session():
|
def reset_session():
|
||||||
|
"""Reset the session data."""
|
||||||
return SessionData()
|
return SessionData()
|
||||||
|
|
||||||
text_cache = set()
|
text_cache = set()
|
||||||
@@ -218,7 +241,19 @@ for file in Path('cache/image').iterdir():
|
|||||||
text_cache.add(file.stem)
|
text_cache.add(file.stem)
|
||||||
|
|
||||||
class OutlinedText:
|
class OutlinedText:
|
||||||
|
"""Create an outlined text object."""
|
||||||
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):
|
||||||
|
"""
|
||||||
|
Create an outlined text object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text (str): The text to be displayed.
|
||||||
|
font_size (int): The size of the font.
|
||||||
|
color (ray.Color): The color of the text.
|
||||||
|
outline_color (ray.Color): The color of the outline.
|
||||||
|
outline_thickness (float): The thickness of the outline.
|
||||||
|
vertical (bool): Whether the text is vertical or not.
|
||||||
|
"""
|
||||||
self.text = text
|
self.text = text
|
||||||
self.hash = self._hash_text(text, font_size, color, vertical)
|
self.hash = self._hash_text(text, font_size, color, vertical)
|
||||||
self.outline_thickness = outline_thickness
|
self.outline_thickness = outline_thickness
|
||||||
@@ -463,6 +498,16 @@ class OutlinedText:
|
|||||||
return texture
|
return texture
|
||||||
|
|
||||||
def draw(self, src: ray.Rectangle, dest: ray.Rectangle, origin: ray.Vector2, rotation: float, color: ray.Color):
|
def draw(self, src: ray.Rectangle, dest: ray.Rectangle, origin: ray.Vector2, rotation: float, color: ray.Color):
|
||||||
|
"""
|
||||||
|
Draw the outlined text object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
src (ray.Rectangle): The source rectangle of the texture.
|
||||||
|
dest (ray.Rectangle): The destination rectangle of the texture.
|
||||||
|
origin (ray.Vector2): The origin of the texture.
|
||||||
|
rotation (float): The rotation of the texture.
|
||||||
|
color (ray.Color): The color of the text.
|
||||||
|
"""
|
||||||
if isinstance(color, tuple):
|
if isinstance(color, tuple):
|
||||||
alpha_value = ray.ffi.new('float*', color[3] / 255.0)
|
alpha_value = ray.ffi.new('float*', color[3] / 255.0)
|
||||||
else:
|
else:
|
||||||
@@ -475,5 +520,11 @@ class OutlinedText:
|
|||||||
ray.end_shader_mode()
|
ray.end_shader_mode()
|
||||||
|
|
||||||
def unload(self):
|
def unload(self):
|
||||||
|
"""
|
||||||
|
Unload the outlined text object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
None
|
||||||
|
"""
|
||||||
ray.unload_shader(self.shader)
|
ray.unload_shader(self.shader)
|
||||||
ray.unload_texture(self.texture)
|
ray.unload_texture(self.texture)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ from libs.utils import (
|
|||||||
|
|
||||||
|
|
||||||
class State:
|
class State:
|
||||||
|
"""State enum for the entry screen"""
|
||||||
SELECT_SIDE = 0
|
SELECT_SIDE = 0
|
||||||
SELECT_MODE = 1
|
SELECT_MODE = 1
|
||||||
|
|
||||||
@@ -234,6 +235,7 @@ class EntryScreen:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
class Box:
|
class Box:
|
||||||
|
"""Box class for the entry screen"""
|
||||||
def __init__(self, text: tuple[OutlinedText, OutlinedText], location: str):
|
def __init__(self, text: tuple[OutlinedText, OutlinedText], location: str):
|
||||||
self.text, self.text_highlight = text
|
self.text, self.text_highlight = text
|
||||||
self.location = location
|
self.location = location
|
||||||
@@ -250,6 +252,7 @@ class Box:
|
|||||||
self.moving_right = False
|
self.moving_right = False
|
||||||
|
|
||||||
def set_positions(self, x: int):
|
def set_positions(self, x: int):
|
||||||
|
"""Set the positions of the box"""
|
||||||
self.x = x
|
self.x = x
|
||||||
self.static_x = self.x
|
self.static_x = self.x
|
||||||
self.left_x = self.x
|
self.left_x = self.x
|
||||||
@@ -277,11 +280,13 @@ class Box:
|
|||||||
self.open.update(current_time_ms)
|
self.open.update(current_time_ms)
|
||||||
|
|
||||||
def move_left(self):
|
def move_left(self):
|
||||||
|
"""Move the box left"""
|
||||||
if not self.move.is_started:
|
if not self.move.is_started:
|
||||||
self.move.start()
|
self.move.start()
|
||||||
self.moving_left = True
|
self.moving_left = True
|
||||||
|
|
||||||
def move_right(self):
|
def move_right(self):
|
||||||
|
"""Move the box right"""
|
||||||
if not self.move.is_started:
|
if not self.move.is_started:
|
||||||
self.move.start()
|
self.move.start()
|
||||||
self.moving_right = True
|
self.moving_right = True
|
||||||
@@ -313,6 +318,7 @@ class Box:
|
|||||||
self._draw_text(color)
|
self._draw_text(color)
|
||||||
|
|
||||||
class BoxManager:
|
class BoxManager:
|
||||||
|
"""BoxManager class for the entry screen"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.box_titles: list[tuple[OutlinedText, OutlinedText]] = [
|
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.Color(109, 68, 24, 255), outline_thickness=5, vertical=True),
|
||||||
@@ -335,18 +341,23 @@ class BoxManager:
|
|||||||
box.move_right()
|
box.move_right()
|
||||||
|
|
||||||
def select_box(self):
|
def select_box(self):
|
||||||
|
"""Select the currently selected box"""
|
||||||
self.fade_out.start()
|
self.fade_out.start()
|
||||||
|
|
||||||
def is_box_selected(self):
|
def is_box_selected(self):
|
||||||
|
"""Check if the box is selected"""
|
||||||
return self.fade_out.is_started
|
return self.fade_out.is_started
|
||||||
|
|
||||||
def is_finished(self):
|
def is_finished(self):
|
||||||
|
"""Check if the animation is finished"""
|
||||||
return self.fade_out.is_finished
|
return self.fade_out.is_finished
|
||||||
|
|
||||||
def selected_box(self):
|
def selected_box(self):
|
||||||
|
"""Get the location of the currently selected box"""
|
||||||
return self.boxes[self.selected_box_index].location
|
return self.boxes[self.selected_box_index].location
|
||||||
|
|
||||||
def move_left(self):
|
def move_left(self):
|
||||||
|
"""Move the cursor to the left"""
|
||||||
prev_selection = self.selected_box_index
|
prev_selection = self.selected_box_index
|
||||||
if self.boxes[prev_selection].move.is_started and not self.boxes[prev_selection].move.is_finished:
|
if self.boxes[prev_selection].move.is_started and not self.boxes[prev_selection].move.is_finished:
|
||||||
return
|
return
|
||||||
@@ -358,6 +369,7 @@ class BoxManager:
|
|||||||
self.boxes[self.selected_box_index].move_right()
|
self.boxes[self.selected_box_index].move_right()
|
||||||
|
|
||||||
def move_right(self):
|
def move_right(self):
|
||||||
|
"""Move the cursor to the right"""
|
||||||
prev_selection = self.selected_box_index
|
prev_selection = self.selected_box_index
|
||||||
if self.boxes[prev_selection].move.is_started and not self.boxes[prev_selection].move.is_finished:
|
if self.boxes[prev_selection].move.is_started and not self.boxes[prev_selection].move.is_finished:
|
||||||
return
|
return
|
||||||
|
|||||||
374
scenes/game.py
374
scenes/game.py
@@ -51,6 +51,7 @@ class GameScreen:
|
|||||||
self.mask_shader = ray.load_shader("", "shader/mask.fs")
|
self.mask_shader = ray.load_shader("", "shader/mask.fs")
|
||||||
|
|
||||||
def load_hitsounds(self):
|
def load_hitsounds(self):
|
||||||
|
"""Load the hit sounds"""
|
||||||
sounds_dir = Path("Sounds")
|
sounds_dir = Path("Sounds")
|
||||||
if global_data.hit_sound == -1:
|
if global_data.hit_sound == -1:
|
||||||
self.sound_don = audio.load_sound(Path('none.wav'), 'hitsound_don')
|
self.sound_don = audio.load_sound(Path('none.wav'), 'hitsound_don')
|
||||||
@@ -63,6 +64,7 @@ class GameScreen:
|
|||||||
self.sound_kat = audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound) / "ka.ogg", 'hitsound_kat')
|
self.sound_kat = audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound) / "ka.ogg", 'hitsound_kat')
|
||||||
|
|
||||||
def init_tja(self, song: Path, difficulty: int):
|
def init_tja(self, song: Path, difficulty: int):
|
||||||
|
"""Initialize the TJA file"""
|
||||||
self.tja = TJAParser(song, start_delay=self.start_delay, distance=SCREEN_WIDTH - GameScreen.JUDGE_X)
|
self.tja = TJAParser(song, start_delay=self.start_delay, distance=SCREEN_WIDTH - GameScreen.JUDGE_X)
|
||||||
if self.tja.metadata.bgmovie != Path() and self.tja.metadata.bgmovie.exists():
|
if self.tja.metadata.bgmovie != Path() and self.tja.metadata.bgmovie.exists():
|
||||||
self.movie = VideoPlayer(self.tja.metadata.bgmovie)
|
self.movie = VideoPlayer(self.tja.metadata.bgmovie)
|
||||||
@@ -73,7 +75,9 @@ class GameScreen:
|
|||||||
if self.tja.metadata.wave.exists() and self.tja.metadata.wave.is_file() and self.song_music is None:
|
if self.tja.metadata.wave.exists() and self.tja.metadata.wave.is_file() and self.song_music is None:
|
||||||
self.song_music = audio.load_music_stream(self.tja.metadata.wave, 'song')
|
self.song_music = audio.load_music_stream(self.tja.metadata.wave, 'song')
|
||||||
|
|
||||||
self.player_1 = Player(self.tja, global_data.player_num, difficulty)
|
#tja_copy = copy.deepcopy(self.tja)
|
||||||
|
self.player_1 = Player(self.tja, global_data.player_num, difficulty, False)
|
||||||
|
#self.player_2 = Player(tja_copy, 2, difficulty-1, True)
|
||||||
self.start_ms = (get_current_ms() - self.tja.metadata.offset*1000)
|
self.start_ms = (get_current_ms() - self.tja.metadata.offset*1000)
|
||||||
|
|
||||||
def on_screen_start(self):
|
def on_screen_start(self):
|
||||||
@@ -92,7 +96,10 @@ 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(), '')
|
||||||
self.bpm = self.tja.metadata.bpm
|
self.bpm = self.tja.metadata.bpm
|
||||||
scene_preset = self.tja.metadata.scene_preset
|
scene_preset = self.tja.metadata.scene_preset
|
||||||
|
if self.movie is None:
|
||||||
self.background = Background(global_data.player_num, self.bpm, scene_preset=scene_preset)
|
self.background = Background(global_data.player_num, self.bpm, scene_preset=scene_preset)
|
||||||
|
else:
|
||||||
|
self.background = None
|
||||||
self.transition = Transition(session_data.song_title, subtitle, is_second=True)
|
self.transition = Transition(session_data.song_title, subtitle, is_second=True)
|
||||||
self.allnet_indicator = AllNetIcon()
|
self.allnet_indicator = AllNetIcon()
|
||||||
self.transition.start()
|
self.transition.start()
|
||||||
@@ -111,6 +118,7 @@ class GameScreen:
|
|||||||
return next_screen
|
return next_screen
|
||||||
|
|
||||||
def write_score(self):
|
def write_score(self):
|
||||||
|
"""Write the score to the database"""
|
||||||
if self.tja is None:
|
if self.tja is None:
|
||||||
return
|
return
|
||||||
if global_data.modifiers.auto:
|
if global_data.modifiers.auto:
|
||||||
@@ -178,7 +186,8 @@ class GameScreen:
|
|||||||
if self.song_music is not None:
|
if self.song_music is not None:
|
||||||
audio.update_music_stream(self.song_music)
|
audio.update_music_stream(self.song_music)
|
||||||
|
|
||||||
self.player_1.update(self, current_time)
|
self.player_1.update(self.current_ms, current_time, self.background)
|
||||||
|
#self.player_2.update(self.current_ms, current_time, self.background)
|
||||||
self.song_info.update(current_time)
|
self.song_info.update(current_time)
|
||||||
self.result_transition.update(current_time)
|
self.result_transition.update(current_time)
|
||||||
if self.result_transition.is_finished and not audio.is_sound_playing('result_transition'):
|
if self.result_transition.is_finished and not audio.is_sound_playing('result_transition'):
|
||||||
@@ -215,16 +224,20 @@ class GameScreen:
|
|||||||
audio.stop_music_stream(self.song_music)
|
audio.stop_music_stream(self.song_music)
|
||||||
return self.on_screen_end('SONG_SELECT')
|
return self.on_screen_end('SONG_SELECT')
|
||||||
|
|
||||||
|
def draw_overlay(self):
|
||||||
|
self.song_info.draw()
|
||||||
|
self.transition.draw()
|
||||||
|
self.result_transition.draw()
|
||||||
|
self.allnet_indicator.draw()
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
if self.movie is not None:
|
if self.movie is not None:
|
||||||
self.movie.draw()
|
self.movie.draw()
|
||||||
elif self.background is not None:
|
elif self.background is not None:
|
||||||
self.background.draw()
|
self.background.draw()
|
||||||
self.player_1.draw(self)
|
self.player_1.draw(self.current_ms, self.start_ms, self.mask_shader)
|
||||||
self.song_info.draw()
|
#self.player_2.draw(self.current_ms, self.start_ms, self.mask_shader)
|
||||||
self.transition.draw()
|
self.draw_overlay()
|
||||||
self.result_transition.draw()
|
|
||||||
self.allnet_indicator.draw()
|
|
||||||
|
|
||||||
class Player:
|
class Player:
|
||||||
TIMING_GOOD = 25.0250015258789
|
TIMING_GOOD = 25.0250015258789
|
||||||
@@ -235,8 +248,8 @@ class Player:
|
|||||||
TIMING_OK_EASY = 108.441665649414
|
TIMING_OK_EASY = 108.441665649414
|
||||||
TIMING_BAD_EASY = 125.125
|
TIMING_BAD_EASY = 125.125
|
||||||
|
|
||||||
def __init__(self, tja: TJAParser, player_number: int, difficulty: int):
|
def __init__(self, tja: TJAParser, player_number: int, difficulty: int, is_2p: bool):
|
||||||
|
self.is_2p = is_2p
|
||||||
self.player_number = str(player_number)
|
self.player_number = str(player_number)
|
||||||
self.difficulty = difficulty
|
self.difficulty = difficulty
|
||||||
self.visual_offset = global_data.config["general"]["visual_offset"]
|
self.visual_offset = global_data.config["general"]["visual_offset"]
|
||||||
@@ -303,8 +316,8 @@ class Player:
|
|||||||
self.balloon_anim: Optional[BalloonAnimation] = None
|
self.balloon_anim: Optional[BalloonAnimation] = None
|
||||||
self.kusudama_anim: Optional[KusudamaAnimation] = None
|
self.kusudama_anim: Optional[KusudamaAnimation] = None
|
||||||
self.base_score_list: list[ScoreCounterAnimation] = []
|
self.base_score_list: list[ScoreCounterAnimation] = []
|
||||||
self.combo_display = Combo(self.combo, 0)
|
self.combo_display = Combo(self.combo, 0, self.is_2p)
|
||||||
self.score_counter = ScoreCounter(self.score)
|
self.score_counter = ScoreCounter(self.score, self.is_2p)
|
||||||
self.gogo_time: Optional[GogoTime] = None
|
self.gogo_time: Optional[GogoTime] = None
|
||||||
self.combo_announce = ComboAnnounce(self.combo, 0)
|
self.combo_announce = ComboAnnounce(self.combo, 0)
|
||||||
self.branch_indicator = BranchIndicator() if tja and tja.metadata.course_data[self.difficulty].is_branching else None
|
self.branch_indicator = BranchIndicator() if tja and tja.metadata.course_data[self.difficulty].is_branching else None
|
||||||
@@ -320,13 +333,14 @@ class Player:
|
|||||||
|
|
||||||
self.input_log: dict[float, tuple] = dict()
|
self.input_log: dict[float, tuple] = dict()
|
||||||
stars = tja.metadata.course_data[self.difficulty].level
|
stars = tja.metadata.course_data[self.difficulty].level
|
||||||
self.gauge = Gauge(self.player_number, self.difficulty, stars, self.total_notes)
|
self.gauge = Gauge(self.player_number, self.difficulty, stars, self.total_notes, self.is_2p)
|
||||||
self.gauge_hit_effect: list[GaugeHitEffect] = []
|
self.gauge_hit_effect: list[GaugeHitEffect] = []
|
||||||
|
|
||||||
self.autoplay_hit_side = 'L'
|
self.autoplay_hit_side = 'L'
|
||||||
self.last_subdivision = -1
|
self.last_subdivision = -1
|
||||||
|
|
||||||
def merge_branch_section(self, branch_section: NoteList, current_ms: float):
|
def merge_branch_section(self, branch_section: NoteList, current_ms: float):
|
||||||
|
"""Merges the branch notes into the current notes"""
|
||||||
self.play_notes.extend(branch_section.play_notes)
|
self.play_notes.extend(branch_section.play_notes)
|
||||||
self.draw_note_list.extend(branch_section.draw_notes)
|
self.draw_note_list.extend(branch_section.draw_notes)
|
||||||
self.draw_bar_list.extend(branch_section.bars)
|
self.draw_bar_list.extend(branch_section.bars)
|
||||||
@@ -343,13 +357,16 @@ class Player:
|
|||||||
self.other_notes = deque([note for note in total_other if note.hit_ms > timing_threshold])
|
self.other_notes = deque([note for note in total_other if note.hit_ms > timing_threshold])
|
||||||
|
|
||||||
def get_result_score(self):
|
def get_result_score(self):
|
||||||
|
"""Returns the score, good count, ok count, bad count, max combo, and total drumroll"""
|
||||||
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:
|
||||||
|
"""Calculates the x-coordinate of a note based on its load time and current time"""
|
||||||
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
|
||||||
|
|
||||||
def get_position_y(self, current_ms: float, load_ms: float, pixels_per_frame: float, pixels_per_frame_x) -> int:
|
def get_position_y(self, current_ms: float, load_ms: float, pixels_per_frame: float, pixels_per_frame_x) -> int:
|
||||||
|
"""Calculates the y-coordinate of a note based on its load time and current time"""
|
||||||
time_diff = load_ms - current_ms
|
time_diff = load_ms - current_ms
|
||||||
return int((pixels_per_frame * 0.06 * time_diff) + ((866 * pixels_per_frame) / pixels_per_frame_x))
|
return int((pixels_per_frame * 0.06 * time_diff) + ((866 * pixels_per_frame) / pixels_per_frame_x))
|
||||||
|
|
||||||
@@ -368,6 +385,8 @@ class Player:
|
|||||||
animation_list[:] = remaining_animations
|
animation_list[:] = remaining_animations
|
||||||
|
|
||||||
def bar_manager(self, current_ms: float):
|
def bar_manager(self, current_ms: float):
|
||||||
|
"""Manages the bars and removes if necessary
|
||||||
|
Also sets branch conditions"""
|
||||||
#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
|
||||||
if self.draw_bar_list and current_ms > self.draw_bar_list[0].load_ms:
|
if self.draw_bar_list and current_ms > self.draw_bar_list[0].load_ms:
|
||||||
self.current_bars.append(self.draw_bar_list.popleft())
|
self.current_bars.append(self.draw_bar_list.popleft())
|
||||||
@@ -432,10 +451,14 @@ class Player:
|
|||||||
|
|
||||||
self.curr_branch_reqs = [e_req, m_req, branch_start_time, max(len(seen_notes), 1)]
|
self.curr_branch_reqs = [e_req, m_req, branch_start_time, max(len(seen_notes), 1)]
|
||||||
def play_note_manager(self, current_ms: float, background: Optional[Background]):
|
def play_note_manager(self, current_ms: float, background: Optional[Background]):
|
||||||
|
"""Manages the play_notes and removes if necessary"""
|
||||||
if self.don_notes and self.don_notes[0].hit_ms + Player.TIMING_BAD < current_ms:
|
if self.don_notes and self.don_notes[0].hit_ms + Player.TIMING_BAD < current_ms:
|
||||||
self.combo = 0
|
self.combo = 0
|
||||||
if background is not None:
|
if background is not None:
|
||||||
background.add_chibi(True)
|
if self.is_2p:
|
||||||
|
background.add_chibi(True, 2)
|
||||||
|
else:
|
||||||
|
background.add_chibi(True, 1)
|
||||||
self.bad_count += 1
|
self.bad_count += 1
|
||||||
self.gauge.add_bad()
|
self.gauge.add_bad()
|
||||||
self.don_notes.popleft()
|
self.don_notes.popleft()
|
||||||
@@ -445,7 +468,10 @@ class Player:
|
|||||||
if self.kat_notes and self.kat_notes[0].hit_ms + Player.TIMING_BAD < current_ms:
|
if self.kat_notes and self.kat_notes[0].hit_ms + Player.TIMING_BAD < current_ms:
|
||||||
self.combo = 0
|
self.combo = 0
|
||||||
if background is not None:
|
if background is not None:
|
||||||
background.add_chibi(True)
|
if self.is_2p:
|
||||||
|
background.add_chibi(True, 2)
|
||||||
|
else:
|
||||||
|
background.add_chibi(True, 1)
|
||||||
self.bad_count += 1
|
self.bad_count += 1
|
||||||
self.gauge.add_bad()
|
self.gauge.add_bad()
|
||||||
self.kat_notes.popleft()
|
self.kat_notes.popleft()
|
||||||
@@ -475,6 +501,7 @@ class Player:
|
|||||||
self.is_balloon = True
|
self.is_balloon = True
|
||||||
|
|
||||||
def draw_note_manager(self, current_ms: float):
|
def draw_note_manager(self, current_ms: float):
|
||||||
|
"""Manages the draw_notes and removes if necessary"""
|
||||||
if self.draw_note_list and current_ms + 1000 >= self.draw_note_list[0].load_ms:
|
if self.draw_note_list and current_ms + 1000 >= self.draw_note_list[0].load_ms:
|
||||||
current_note = self.draw_note_list.popleft()
|
current_note = self.draw_note_list.popleft()
|
||||||
if 5 <= current_note.type <= 7:
|
if 5 <= current_note.type <= 7:
|
||||||
@@ -501,14 +528,13 @@ class Player:
|
|||||||
if position < GameScreen.JUDGE_X + 650:
|
if position < GameScreen.JUDGE_X + 650:
|
||||||
self.current_notes_draw.pop(0)
|
self.current_notes_draw.pop(0)
|
||||||
|
|
||||||
def note_manager(self, current_ms: float, background: Optional[Background], current_time: float):
|
def note_manager(self, current_ms: float, background: Optional[Background]):
|
||||||
self.bar_manager(current_ms)
|
self.bar_manager(current_ms)
|
||||||
self.play_note_manager(current_ms, background)
|
self.play_note_manager(current_ms, background)
|
||||||
self.draw_note_manager(current_ms)
|
self.draw_note_manager(current_ms)
|
||||||
|
|
||||||
def note_correct(self, note: Note, current_time: float):
|
def note_correct(self, note: Note, current_time: float):
|
||||||
|
"""Removes a note from the appropriate separated list"""
|
||||||
# Remove from the appropriate separated list
|
|
||||||
if note.type in {1, 3} and self.don_notes and self.don_notes[0] == note:
|
if note.type in {1, 3} and self.don_notes and self.don_notes[0] == note:
|
||||||
self.don_notes.popleft()
|
self.don_notes.popleft()
|
||||||
elif note.type in {2, 4} and self.kat_notes and self.kat_notes[0] == note:
|
elif note.type in {2, 4} and self.kat_notes and self.kat_notes[0] == note:
|
||||||
@@ -531,14 +557,15 @@ class Player:
|
|||||||
self.max_combo = self.combo
|
self.max_combo = self.combo
|
||||||
|
|
||||||
if note.type != 9:
|
if note.type != 9:
|
||||||
self.draw_arc_list.append(NoteArc(note.type, current_time, 1, note.type == 3 or note.type == 4 or note.type == 7, note.type == 7))
|
self.draw_arc_list.append(NoteArc(note.type, current_time, self.is_2p + 1, note.type == 3 or note.type == 4 or note.type == 7, note.type == 7))
|
||||||
|
|
||||||
if note in self.current_notes_draw:
|
if note in self.current_notes_draw:
|
||||||
index = self.current_notes_draw.index(note)
|
index = self.current_notes_draw.index(note)
|
||||||
self.current_notes_draw.pop(index)
|
self.current_notes_draw.pop(index)
|
||||||
|
|
||||||
def check_drumroll(self, drum_type: int, background: Optional[Background], current_time: float):
|
def check_drumroll(self, drum_type: int, background: Optional[Background], current_time: float):
|
||||||
self.draw_arc_list.append(NoteArc(drum_type, current_time, 1, drum_type == 3 or drum_type == 4, False))
|
"""Checks if a note has been hit during a drumroll"""
|
||||||
|
self.draw_arc_list.append(NoteArc(drum_type, current_time, self.is_2p + 1, drum_type == 3 or drum_type == 4, False))
|
||||||
self.curr_drumroll_count += 1
|
self.curr_drumroll_count += 1
|
||||||
self.total_drumroll += 1
|
self.total_drumroll += 1
|
||||||
if self.is_branch and self.branch_condition == 'r':
|
if self.is_branch and self.branch_condition == 'r':
|
||||||
@@ -546,23 +573,24 @@ class Player:
|
|||||||
if background is not None:
|
if background is not None:
|
||||||
background.add_renda()
|
background.add_renda()
|
||||||
self.score += 100
|
self.score += 100
|
||||||
self.base_score_list.append(ScoreCounterAnimation(self.player_number, 100))
|
self.base_score_list.append(ScoreCounterAnimation(self.player_number, 100, self.is_2p))
|
||||||
if not isinstance(self.current_notes_draw[0], Drumroll):
|
if not isinstance(self.current_notes_draw[0], Drumroll):
|
||||||
return
|
return
|
||||||
self.current_notes_draw[0].color = max(0, 255 - (self.curr_drumroll_count * 10))
|
self.current_notes_draw[0].color = max(0, 255 - (self.curr_drumroll_count * 10))
|
||||||
|
|
||||||
def check_balloon(self, game_screen: GameScreen, drum_type: int, note: Balloon, current_time: float):
|
def check_balloon(self, drum_type: int, note: Balloon, current_time: float):
|
||||||
|
"""Checks if the player has popped a balloon"""
|
||||||
if drum_type != 1:
|
if drum_type != 1:
|
||||||
return
|
return
|
||||||
if note.is_kusudama:
|
if note.is_kusudama:
|
||||||
self.check_kusudama(game_screen, note)
|
self.check_kusudama(note)
|
||||||
return
|
return
|
||||||
if self.balloon_anim is None:
|
if self.balloon_anim is None:
|
||||||
self.balloon_anim = BalloonAnimation(current_time, note.count)
|
self.balloon_anim = BalloonAnimation(current_time, note.count)
|
||||||
self.curr_balloon_count += 1
|
self.curr_balloon_count += 1
|
||||||
self.total_drumroll += 1
|
self.total_drumroll += 1
|
||||||
self.score += 100
|
self.score += 100
|
||||||
self.base_score_list.append(ScoreCounterAnimation(self.player_number, 100))
|
self.base_score_list.append(ScoreCounterAnimation(self.player_number, 100, self.is_2p))
|
||||||
if self.curr_balloon_count == note.count:
|
if self.curr_balloon_count == note.count:
|
||||||
self.is_balloon = False
|
self.is_balloon = False
|
||||||
note.popped = True
|
note.popped = True
|
||||||
@@ -571,20 +599,22 @@ class Player:
|
|||||||
self.note_correct(note, current_time)
|
self.note_correct(note, current_time)
|
||||||
self.curr_balloon_count = 0
|
self.curr_balloon_count = 0
|
||||||
|
|
||||||
def check_kusudama(self, game_screen: GameScreen, note: Balloon):
|
def check_kusudama(self, note: Balloon):
|
||||||
|
"""Checks if the player has popped a kusudama"""
|
||||||
if self.kusudama_anim is None:
|
if self.kusudama_anim is None:
|
||||||
self.kusudama_anim = KusudamaAnimation(note.count)
|
self.kusudama_anim = KusudamaAnimation(note.count)
|
||||||
self.curr_balloon_count += 1
|
self.curr_balloon_count += 1
|
||||||
self.total_drumroll += 1
|
self.total_drumroll += 1
|
||||||
self.score += 100
|
self.score += 100
|
||||||
self.base_score_list.append(ScoreCounterAnimation(self.player_number, 100))
|
self.base_score_list.append(ScoreCounterAnimation(self.player_number, 100, self.is_2p))
|
||||||
if self.curr_balloon_count == note.count:
|
if self.curr_balloon_count == note.count:
|
||||||
audio.play_sound('kusudama_pop', 'hitsound')
|
audio.play_sound('kusudama_pop', 'hitsound')
|
||||||
self.is_balloon = False
|
self.is_balloon = False
|
||||||
note.popped = True
|
note.popped = True
|
||||||
self.curr_balloon_count = 0
|
self.curr_balloon_count = 0
|
||||||
|
|
||||||
def check_note(self, game_screen: GameScreen, drum_type: int, current_time: float):
|
def check_note(self, ms_from_start: float, drum_type: int, current_time: float, background: Optional[Background]):
|
||||||
|
"""Checks if the player has hit a note"""
|
||||||
if len(self.don_notes) == 0 and len(self.kat_notes) == 0 and len(self.other_notes) == 0:
|
if len(self.don_notes) == 0 and len(self.kat_notes) == 0 and len(self.other_notes) == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -599,11 +629,11 @@ class Player:
|
|||||||
|
|
||||||
curr_note = self.other_notes[0] if self.other_notes else None
|
curr_note = self.other_notes[0] if self.other_notes else None
|
||||||
if self.is_drumroll:
|
if self.is_drumroll:
|
||||||
self.check_drumroll(drum_type, game_screen.background, current_time)
|
self.check_drumroll(drum_type, background, current_time)
|
||||||
elif self.is_balloon:
|
elif self.is_balloon:
|
||||||
if not isinstance(curr_note, Balloon):
|
if not isinstance(curr_note, Balloon):
|
||||||
raise Exception("Balloon mode entered but current note is not balloon")
|
raise Exception("Balloon mode entered but current note is not balloon")
|
||||||
self.check_balloon(game_screen, drum_type, curr_note, current_time)
|
self.check_balloon(drum_type, curr_note, current_time)
|
||||||
else:
|
else:
|
||||||
self.curr_drumroll_count = 0
|
self.curr_drumroll_count = 0
|
||||||
|
|
||||||
@@ -619,37 +649,43 @@ class Player:
|
|||||||
return
|
return
|
||||||
|
|
||||||
#If the note is too far away, stop checking
|
#If the note is too far away, stop checking
|
||||||
if game_screen.current_ms > (curr_note.hit_ms + bad_window_ms):
|
if ms_from_start > (curr_note.hit_ms + bad_window_ms):
|
||||||
return
|
return
|
||||||
|
|
||||||
big = curr_note.type == 3 or curr_note.type == 4
|
big = curr_note.type == 3 or curr_note.type == 4
|
||||||
if (curr_note.hit_ms - good_window_ms) <= game_screen.current_ms <= (curr_note.hit_ms + good_window_ms):
|
if (curr_note.hit_ms - good_window_ms) <= ms_from_start <= (curr_note.hit_ms + good_window_ms):
|
||||||
self.draw_judge_list.append(Judgement('GOOD', big, ms_display=game_screen.current_ms - curr_note.hit_ms))
|
self.draw_judge_list.append(Judgement('GOOD', big, self.is_2p, ms_display=ms_from_start - curr_note.hit_ms))
|
||||||
self.lane_hit_effect = LaneHitEffect('GOOD')
|
self.lane_hit_effect = LaneHitEffect('GOOD', self.is_2p)
|
||||||
self.good_count += 1
|
self.good_count += 1
|
||||||
self.score += self.base_score
|
self.score += self.base_score
|
||||||
self.base_score_list.append(ScoreCounterAnimation(self.player_number, self.base_score))
|
self.base_score_list.append(ScoreCounterAnimation(self.player_number, self.base_score, self.is_2p))
|
||||||
self.note_correct(curr_note, current_time)
|
self.note_correct(curr_note, current_time)
|
||||||
self.gauge.add_good()
|
self.gauge.add_good()
|
||||||
if self.is_branch and self.branch_condition == 'p':
|
if self.is_branch and self.branch_condition == 'p':
|
||||||
self.branch_condition_count += 1
|
self.branch_condition_count += 1
|
||||||
if game_screen.background is not None:
|
if background is not None:
|
||||||
game_screen.background.add_chibi(False)
|
if self.is_2p:
|
||||||
|
background.add_chibi(False, 2)
|
||||||
|
else:
|
||||||
|
background.add_chibi(False, 1)
|
||||||
|
|
||||||
elif (curr_note.hit_ms - ok_window_ms) <= game_screen.current_ms <= (curr_note.hit_ms + ok_window_ms):
|
elif (curr_note.hit_ms - ok_window_ms) <= ms_from_start <= (curr_note.hit_ms + ok_window_ms):
|
||||||
self.draw_judge_list.append(Judgement('OK', big, ms_display=game_screen.current_ms - curr_note.hit_ms))
|
self.draw_judge_list.append(Judgement('OK', big, self.is_2p, ms_display=ms_from_start - curr_note.hit_ms))
|
||||||
self.ok_count += 1
|
self.ok_count += 1
|
||||||
self.score += 10 * math.floor(self.base_score / 2 / 10)
|
self.score += 10 * math.floor(self.base_score / 2 / 10)
|
||||||
self.base_score_list.append(ScoreCounterAnimation(self.player_number, 10 * math.floor(self.base_score / 2 / 10)))
|
self.base_score_list.append(ScoreCounterAnimation(self.player_number, 10 * math.floor(self.base_score / 2 / 10), self.is_2p))
|
||||||
self.note_correct(curr_note, current_time)
|
self.note_correct(curr_note, current_time)
|
||||||
self.gauge.add_ok()
|
self.gauge.add_ok()
|
||||||
if self.is_branch and self.branch_condition == 'p':
|
if self.is_branch and self.branch_condition == 'p':
|
||||||
self.branch_condition_count += 0.5
|
self.branch_condition_count += 0.5
|
||||||
if game_screen.background is not None:
|
if background is not None:
|
||||||
game_screen.background.add_chibi(False)
|
if self.is_2p:
|
||||||
|
background.add_chibi(False, 2)
|
||||||
|
else:
|
||||||
|
background.add_chibi(False, 1)
|
||||||
|
|
||||||
elif (curr_note.hit_ms - bad_window_ms) <= game_screen.current_ms <= (curr_note.hit_ms + bad_window_ms):
|
elif (curr_note.hit_ms - bad_window_ms) <= ms_from_start <= (curr_note.hit_ms + bad_window_ms):
|
||||||
self.draw_judge_list.append(Judgement('BAD', big, ms_display=game_screen.current_ms - curr_note.hit_ms))
|
self.draw_judge_list.append(Judgement('BAD', big, self.is_2p, ms_display=ms_from_start - curr_note.hit_ms))
|
||||||
self.bad_count += 1
|
self.bad_count += 1
|
||||||
self.combo = 0
|
self.combo = 0
|
||||||
# Remove from both the specific note list and the main play_notes list
|
# Remove from both the specific note list and the main play_notes list
|
||||||
@@ -658,10 +694,14 @@ class Player:
|
|||||||
else:
|
else:
|
||||||
self.kat_notes.popleft()
|
self.kat_notes.popleft()
|
||||||
self.gauge.add_bad()
|
self.gauge.add_bad()
|
||||||
if game_screen.background is not None:
|
if background is not None:
|
||||||
game_screen.background.add_chibi(True)
|
if self.is_2p:
|
||||||
|
background.add_chibi(True, 2)
|
||||||
|
else:
|
||||||
|
background.add_chibi(True, 1)
|
||||||
|
|
||||||
def drumroll_counter_manager(self, current_time: float):
|
def drumroll_counter_manager(self, current_time: float):
|
||||||
|
"""Manages drumroll counter behavior"""
|
||||||
if self.is_drumroll and self.curr_drumroll_count > 0 and self.drumroll_counter is None:
|
if self.is_drumroll and self.curr_drumroll_count > 0 and self.drumroll_counter is None:
|
||||||
self.drumroll_counter = DrumrollCounter(current_time)
|
self.drumroll_counter = DrumrollCounter(current_time)
|
||||||
|
|
||||||
@@ -672,6 +712,7 @@ class Player:
|
|||||||
self.drumroll_counter.update(current_time, self.curr_drumroll_count)
|
self.drumroll_counter.update(current_time, self.curr_drumroll_count)
|
||||||
|
|
||||||
def balloon_manager(self, current_time: float):
|
def balloon_manager(self, current_time: float):
|
||||||
|
"""Manages balloon and kusudama behavior"""
|
||||||
if self.balloon_anim is not None:
|
if self.balloon_anim is not None:
|
||||||
self.chara.set_animation('balloon_popping')
|
self.chara.set_animation('balloon_popping')
|
||||||
self.balloon_anim.update(current_time, self.curr_balloon_count, not self.is_balloon)
|
self.balloon_anim.update(current_time, self.curr_balloon_count, not self.is_balloon)
|
||||||
@@ -684,25 +725,26 @@ class Player:
|
|||||||
if self.kusudama_anim.is_finished:
|
if self.kusudama_anim.is_finished:
|
||||||
self.kusudama_anim = None
|
self.kusudama_anim = None
|
||||||
|
|
||||||
def handle_input(self, game_screen: GameScreen, current_time: float):
|
def handle_input(self, ms_from_start: float, current_time: float, background: Optional[Background]):
|
||||||
input_checks = [
|
input_checks = [
|
||||||
(is_l_don_pressed, 'DON', 'L', game_screen.sound_don),
|
(is_l_don_pressed, 'DON', 'L', 'hitsound_don'),
|
||||||
(is_r_don_pressed, 'DON', 'R', game_screen.sound_don),
|
(is_r_don_pressed, 'DON', 'R', 'hitsound_don'),
|
||||||
(is_l_kat_pressed, 'KAT', 'L', game_screen.sound_kat),
|
(is_l_kat_pressed, 'KAT', 'L', 'hitsound_kat'),
|
||||||
(is_r_kat_pressed, 'KAT', 'R', game_screen.sound_kat)
|
(is_r_kat_pressed, 'KAT', 'R', 'hitsound_kat')
|
||||||
]
|
]
|
||||||
for check_func, note_type, side, sound in input_checks:
|
for check_func, note_type, side, sound in input_checks:
|
||||||
if check_func():
|
if check_func():
|
||||||
self.lane_hit_effect = LaneHitEffect(note_type)
|
self.lane_hit_effect = LaneHitEffect(note_type, self.is_2p)
|
||||||
self.draw_drum_hit_list.append(DrumHitEffect(note_type, side))
|
self.draw_drum_hit_list.append(DrumHitEffect(note_type, side, self.is_2p))
|
||||||
|
|
||||||
audio.play_sound(sound, 'hitsound')
|
audio.play_sound(sound, 'hitsound')
|
||||||
|
|
||||||
drum_value = 1 if note_type == 'DON' else 2
|
drum_value = 1 if note_type == 'DON' else 2
|
||||||
self.check_note(game_screen, drum_value, current_time)
|
self.check_note(ms_from_start, drum_value, current_time, background)
|
||||||
self.input_log[game_screen.current_ms] = (note_type, side)
|
self.input_log[ms_from_start] = (note_type, side)
|
||||||
|
|
||||||
def autoplay_manager(self, game_screen: GameScreen, current_time: float):
|
def autoplay_manager(self, ms_from_start: float, current_time: float, background: Optional[Background]):
|
||||||
|
"""Manages autoplay behavior"""
|
||||||
if not global_data.modifiers.auto:
|
if not global_data.modifiers.auto:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -715,38 +757,39 @@ class Player:
|
|||||||
if bpm == 0:
|
if bpm == 0:
|
||||||
subdivision_in_ms = 0
|
subdivision_in_ms = 0
|
||||||
else:
|
else:
|
||||||
subdivision_in_ms = game_screen.current_ms // ((60000 * 4 / bpm) / 24)
|
subdivision_in_ms = ms_from_start // ((60000 * 4 / bpm) / 24)
|
||||||
if subdivision_in_ms > self.last_subdivision:
|
if subdivision_in_ms > self.last_subdivision:
|
||||||
self.last_subdivision = subdivision_in_ms
|
self.last_subdivision = subdivision_in_ms
|
||||||
hit_type = 'DON'
|
hit_type = 'DON'
|
||||||
self.lane_hit_effect = LaneHitEffect(hit_type)
|
self.lane_hit_effect = LaneHitEffect(hit_type, self.is_2p)
|
||||||
self.autoplay_hit_side = 'R' if self.autoplay_hit_side == 'L' else 'L'
|
self.autoplay_hit_side = 'R' if self.autoplay_hit_side == 'L' else 'L'
|
||||||
self.draw_drum_hit_list.append(DrumHitEffect(hit_type, self.autoplay_hit_side))
|
self.draw_drum_hit_list.append(DrumHitEffect(hit_type, self.autoplay_hit_side, self.is_2p))
|
||||||
audio.play_sound(game_screen.sound_don, 'hitsound')
|
audio.play_sound('hitsound_don', 'hitsound')
|
||||||
note_type = 3 if note.type == 6 else 1
|
note_type = 3 if note.type == 6 else 1
|
||||||
self.check_note(game_screen, note_type, current_time)
|
self.check_note(ms_from_start, note_type, current_time, background)
|
||||||
else:
|
else:
|
||||||
# Handle DON notes
|
# Handle DON notes
|
||||||
while self.don_notes and game_screen.current_ms >= self.don_notes[0].hit_ms:
|
while self.don_notes and ms_from_start >= self.don_notes[0].hit_ms:
|
||||||
note = self.don_notes[0]
|
note = self.don_notes[0]
|
||||||
hit_type = 'DON'
|
hit_type = 'DON'
|
||||||
self.lane_hit_effect = LaneHitEffect(hit_type)
|
self.lane_hit_effect = LaneHitEffect(hit_type, self.is_2p)
|
||||||
self.autoplay_hit_side = 'R' if self.autoplay_hit_side == 'L' else 'L'
|
self.autoplay_hit_side = 'R' if self.autoplay_hit_side == 'L' else 'L'
|
||||||
self.draw_drum_hit_list.append(DrumHitEffect(hit_type, self.autoplay_hit_side))
|
self.draw_drum_hit_list.append(DrumHitEffect(hit_type, self.autoplay_hit_side, self.is_2p))
|
||||||
audio.play_sound(game_screen.sound_don, 'hitsound')
|
audio.play_sound('hitsound_don', 'hitsound')
|
||||||
self.check_note(game_screen, 1, current_time)
|
self.check_note(ms_from_start, 1, current_time, background)
|
||||||
|
|
||||||
# Handle KAT notes
|
# Handle KAT notes
|
||||||
while self.kat_notes and game_screen.current_ms >= self.kat_notes[0].hit_ms:
|
while self.kat_notes and ms_from_start >= self.kat_notes[0].hit_ms:
|
||||||
note = self.kat_notes[0]
|
note = self.kat_notes[0]
|
||||||
hit_type = 'KAT'
|
hit_type = 'KAT'
|
||||||
self.lane_hit_effect = LaneHitEffect(hit_type)
|
self.lane_hit_effect = LaneHitEffect(hit_type, self.is_2p)
|
||||||
self.autoplay_hit_side = 'R' if self.autoplay_hit_side == 'L' else 'L'
|
self.autoplay_hit_side = 'R' if self.autoplay_hit_side == 'L' else 'L'
|
||||||
self.draw_drum_hit_list.append(DrumHitEffect(hit_type, self.autoplay_hit_side))
|
self.draw_drum_hit_list.append(DrumHitEffect(hit_type, self.autoplay_hit_side, self.is_2p))
|
||||||
audio.play_sound(game_screen.sound_kat, 'hitsound')
|
audio.play_sound('hitsound_kat', 'hitsound')
|
||||||
self.check_note(game_screen, 2, current_time)
|
self.check_note(ms_from_start, 2, current_time, background)
|
||||||
|
|
||||||
def evaluate_branch(self, current_ms):
|
def evaluate_branch(self, current_ms):
|
||||||
|
"""Evaluates the branch condition and updates the branch status"""
|
||||||
e_req, m_req, end_time, total_notes = self.curr_branch_reqs
|
e_req, m_req, end_time, total_notes = self.curr_branch_reqs
|
||||||
if current_ms >= end_time:
|
if current_ms >= end_time:
|
||||||
self.is_branch = False
|
self.is_branch = False
|
||||||
@@ -775,8 +818,8 @@ class Player:
|
|||||||
self.branch_e.pop(0)
|
self.branch_e.pop(0)
|
||||||
self.branch_condition_count = 0
|
self.branch_condition_count = 0
|
||||||
|
|
||||||
def update(self, game_screen: GameScreen, current_time: float):
|
def update(self, ms_from_start: float, current_time: float, background: Optional[Background]):
|
||||||
self.note_manager(game_screen.current_ms, game_screen.background, current_time)
|
self.note_manager(ms_from_start, background)
|
||||||
self.combo_display.update(current_time, self.combo)
|
self.combo_display.update(current_time, self.combo)
|
||||||
self.combo_announce.update(current_time)
|
self.combo_announce.update(current_time)
|
||||||
self.drumroll_counter_manager(current_time)
|
self.drumroll_counter_manager(current_time)
|
||||||
@@ -793,7 +836,7 @@ class Player:
|
|||||||
for i, anim in enumerate(self.draw_arc_list):
|
for i, anim in enumerate(self.draw_arc_list):
|
||||||
anim.update(current_time)
|
anim.update(current_time)
|
||||||
if anim.is_finished:
|
if anim.is_finished:
|
||||||
self.gauge_hit_effect.append(GaugeHitEffect(anim.note_type, anim.is_big))
|
self.gauge_hit_effect.append(GaugeHitEffect(anim.note_type, anim.is_big, self.is_2p))
|
||||||
finished_arcs.append(i)
|
finished_arcs.append(i)
|
||||||
for i in reversed(finished_arcs):
|
for i in reversed(finished_arcs):
|
||||||
self.draw_arc_list.pop(i)
|
self.draw_arc_list.pop(i)
|
||||||
@@ -801,8 +844,8 @@ class Player:
|
|||||||
self.animation_manager(self.gauge_hit_effect, current_time)
|
self.animation_manager(self.gauge_hit_effect, current_time)
|
||||||
self.animation_manager(self.base_score_list, current_time)
|
self.animation_manager(self.base_score_list, current_time)
|
||||||
self.score_counter.update(current_time, self.score)
|
self.score_counter.update(current_time, self.score)
|
||||||
self.autoplay_manager(game_screen, current_time)
|
self.autoplay_manager(ms_from_start, current_time, background)
|
||||||
self.handle_input(game_screen, current_time)
|
self.handle_input(ms_from_start, current_time, background)
|
||||||
self.nameplate.update(current_time)
|
self.nameplate.update(current_time)
|
||||||
self.gauge.update(current_time)
|
self.gauge.update(current_time)
|
||||||
if self.judge_counter is not None:
|
if self.judge_counter is not None:
|
||||||
@@ -813,7 +856,7 @@ class Player:
|
|||||||
self.ending_anim.update(current_time)
|
self.ending_anim.update(current_time)
|
||||||
|
|
||||||
if self.is_branch:
|
if self.is_branch:
|
||||||
self.evaluate_branch(game_screen.current_ms)
|
self.evaluate_branch(ms_from_start)
|
||||||
|
|
||||||
# Get the next note from any of the three lists for BPM and gogo time updates
|
# Get the next note from any of the three lists for BPM and gogo time updates
|
||||||
next_note = None
|
next_note = None
|
||||||
@@ -832,7 +875,7 @@ class Player:
|
|||||||
self.bpm = next_note.bpm
|
self.bpm = next_note.bpm
|
||||||
if next_note.gogo_time and not self.is_gogo_time:
|
if next_note.gogo_time and not self.is_gogo_time:
|
||||||
self.is_gogo_time = True
|
self.is_gogo_time = True
|
||||||
self.gogo_time = GogoTime()
|
self.gogo_time = GogoTime(self.is_2p)
|
||||||
self.chara.set_animation('gogo_start')
|
self.chara.set_animation('gogo_start')
|
||||||
if not next_note.gogo_time and self.is_gogo_time:
|
if not next_note.gogo_time and self.is_gogo_time:
|
||||||
self.is_gogo_time = False
|
self.is_gogo_time = False
|
||||||
@@ -841,6 +884,7 @@ class Player:
|
|||||||
self.chara.update(current_time, self.bpm, self.gauge.is_clear, self.gauge.is_rainbow)
|
self.chara.update(current_time, self.bpm, self.gauge.is_clear, self.gauge.is_rainbow)
|
||||||
|
|
||||||
def draw_drumroll(self, current_ms: float, head: Drumroll, current_eighth: int):
|
def draw_drumroll(self, current_ms: float, head: Drumroll, current_eighth: int):
|
||||||
|
"""Draws a drumroll in the player's lane"""
|
||||||
start_position = self.get_position_x(SCREEN_WIDTH, current_ms, head.load_ms, head.pixels_per_frame_x)
|
start_position = self.get_position_x(SCREEN_WIDTH, current_ms, head.load_ms, head.pixels_per_frame_x)
|
||||||
tail = next((note for note in self.current_notes_draw[1:] if note.type == 8 and note.index > head.index), self.current_notes_draw[1])
|
tail = next((note for note in self.current_notes_draw[1:] if note.type == 8 and note.index > head.index), self.current_notes_draw[1])
|
||||||
is_big = int(head.type == 6)
|
is_big = int(head.type == 6)
|
||||||
@@ -849,18 +893,19 @@ class Player:
|
|||||||
color = ray.Color(255, head.color, head.color, 255)
|
color = ray.Color(255, head.color, head.color, 255)
|
||||||
if head.display:
|
if head.display:
|
||||||
if length > 0:
|
if length > 0:
|
||||||
tex.draw_texture('notes', "8", frame=is_big, x=start_position+64, y=192, x2=length-47, color=color)
|
tex.draw_texture('notes', "8", frame=is_big, x=start_position+64, y=192+(self.is_2p*176), x2=length-47, color=color)
|
||||||
if is_big:
|
if is_big:
|
||||||
tex.draw_texture('notes', "drumroll_big_tail", x=end_position+64, y=192, color=color)
|
tex.draw_texture('notes', "drumroll_big_tail", x=end_position+64, y=192+(self.is_2p*176), color=color)
|
||||||
else:
|
else:
|
||||||
tex.draw_texture('notes', "drumroll_tail", x=end_position+64, y=192, color=color)
|
tex.draw_texture('notes', "drumroll_tail", x=end_position+64, y=192+(self.is_2p*176), 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+(self.is_2p*176), 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+(self.is_2p*176), x2=length)
|
||||||
tex.draw_texture('notes', 'moji', frame=head.moji, x=(start_position - (168//2)) + 64, y=323)
|
tex.draw_texture('notes', 'moji', frame=head.moji, x=(start_position - (168//2)) + 64, y=323+(self.is_2p*176))
|
||||||
tex.draw_texture('notes', 'moji', frame=tail.moji, x=(end_position - (168//2)) + 32, y=323)
|
tex.draw_texture('notes', 'moji', frame=tail.moji, x=(end_position - (168//2)) + 32, y=323+(self.is_2p*176))
|
||||||
|
|
||||||
def draw_balloon(self, current_ms: float, head: Balloon, current_eighth: int):
|
def draw_balloon(self, current_ms: float, head: Balloon, current_eighth: int):
|
||||||
|
"""Draws a balloon in the player's lane"""
|
||||||
offset = 12
|
offset = 12
|
||||||
start_position = self.get_position_x(SCREEN_WIDTH, current_ms, head.load_ms, head.pixels_per_frame_x)
|
start_position = self.get_position_x(SCREEN_WIDTH, current_ms, head.load_ms, head.pixels_per_frame_x)
|
||||||
tail = next((note for note in self.current_notes_draw[1:] if note.type == 8 and note.index > head.index), self.current_notes_draw[1])
|
tail = next((note for note in self.current_notes_draw[1:] if note.type == 8 and note.index > head.index), self.current_notes_draw[1])
|
||||||
@@ -873,10 +918,11 @@ class Player:
|
|||||||
else:
|
else:
|
||||||
position = start_position
|
position = start_position
|
||||||
if head.display:
|
if head.display:
|
||||||
tex.draw_texture('notes', str(head.type), frame=current_eighth % 2, x=position-offset, y=192)
|
tex.draw_texture('notes', str(head.type), frame=current_eighth % 2, x=position-offset, y=192+(self.is_2p*176))
|
||||||
tex.draw_texture('notes', '10', frame=current_eighth % 2, x=position-offset+128, y=192)
|
tex.draw_texture('notes', '10', frame=current_eighth % 2, x=position-offset+128, y=192+(self.is_2p*176))
|
||||||
|
|
||||||
def draw_bars(self, current_ms: float):
|
def draw_bars(self, current_ms: float):
|
||||||
|
"""Draw bars in the player's lane"""
|
||||||
if not self.current_bars:
|
if not self.current_bars:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -891,13 +937,14 @@ class Player:
|
|||||||
frame = 1
|
frame = 1
|
||||||
else:
|
else:
|
||||||
frame = 0
|
frame = 0
|
||||||
bar_draws.append((str(bar.type), frame, x_position+60, y_position+190))
|
bar_draws.append((str(bar.type), frame, x_position+60, y_position+190+(self.is_2p*176)))
|
||||||
|
|
||||||
# Draw all bars in one batch
|
# Draw all bars in one batch
|
||||||
for bar_type, frame, x, y in bar_draws:
|
for bar_type, frame, x, y in bar_draws:
|
||||||
tex.draw_texture('notes', bar_type, frame=frame, x=x, y=y)
|
tex.draw_texture('notes', bar_type, frame=frame, x=x, y=y)
|
||||||
|
|
||||||
def draw_notes(self, current_ms: float, start_ms: float):
|
def draw_notes(self, current_ms: float, start_ms: float):
|
||||||
|
"""Draw notes in the player's lane"""
|
||||||
if not self.current_notes_draw:
|
if not self.current_notes_draw:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -918,17 +965,17 @@ class Player:
|
|||||||
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)
|
||||||
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+(self.is_2p*176))
|
||||||
else:
|
else:
|
||||||
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 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+(self.is_2p*176), 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+(self.is_2p*176))
|
||||||
|
|
||||||
|
|
||||||
def draw_modifiers(self):
|
def draw_modifiers(self):
|
||||||
# Batch modifier texture draws to reduce state changes
|
"""Shows the currently selected modifiers"""
|
||||||
modifiers_to_draw = ['mod_shinuchi']
|
modifiers_to_draw = ['mod_shinuchi']
|
||||||
|
|
||||||
# Speed modifiers
|
# Speed modifiers
|
||||||
@@ -951,19 +998,17 @@ class Player:
|
|||||||
|
|
||||||
# Draw all modifiers in one batch
|
# Draw all modifiers in one batch
|
||||||
for modifier in modifiers_to_draw:
|
for modifier in modifiers_to_draw:
|
||||||
tex.draw_texture('lane', modifier)
|
tex.draw_texture('lane', modifier, index=self.is_2p)
|
||||||
|
|
||||||
def draw(self, game_screen: GameScreen):
|
|
||||||
current_ms = game_screen.current_ms
|
|
||||||
|
|
||||||
|
def draw(self, ms_from_start: float, start_ms: float, mask_shader: ray.Shader):
|
||||||
# Group 1: Background and lane elements
|
# Group 1: Background and lane elements
|
||||||
tex.draw_texture('lane', 'lane_background')
|
tex.draw_texture('lane', 'lane_background', index=self.is_2p)
|
||||||
if self.branch_indicator is not None:
|
if self.branch_indicator is not None:
|
||||||
self.branch_indicator.draw()
|
self.branch_indicator.draw()
|
||||||
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', index=self.is_2p)
|
||||||
|
|
||||||
# Group 2: Judgement and hit effects
|
# Group 2: Judgement and hit effects
|
||||||
if self.gogo_time is not None:
|
if self.gogo_time is not None:
|
||||||
@@ -972,14 +1017,14 @@ class Player:
|
|||||||
anim.draw()
|
anim.draw()
|
||||||
|
|
||||||
# Group 3: Notes and bars (game content)
|
# Group 3: Notes and bars (game content)
|
||||||
self.draw_bars(current_ms)
|
self.draw_bars(ms_from_start)
|
||||||
self.draw_notes(current_ms, game_screen.start_ms)
|
self.draw_notes(ms_from_start, start_ms)
|
||||||
|
|
||||||
# Group 4: Lane covers and UI elements (batch similar textures)
|
# 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', index=self.is_2p)
|
||||||
tex.draw_texture('lane', 'drum')
|
tex.draw_texture('lane', 'drum', index=self.is_2p)
|
||||||
if global_data.modifiers.auto:
|
if global_data.modifiers.auto:
|
||||||
tex.draw_texture('lane', 'auto_icon')
|
tex.draw_texture('lane', 'auto_icon', index=self.is_2p)
|
||||||
if self.ending_anim is not None:
|
if self.ending_anim is not None:
|
||||||
self.ending_anim.draw()
|
self.ending_anim.draw()
|
||||||
|
|
||||||
@@ -987,24 +1032,27 @@ class Player:
|
|||||||
for anim in self.draw_drum_hit_list:
|
for anim in self.draw_drum_hit_list:
|
||||||
anim.draw()
|
anim.draw()
|
||||||
for anim in self.draw_arc_list:
|
for anim in self.draw_arc_list:
|
||||||
anim.draw(game_screen.mask_shader)
|
anim.draw(mask_shader)
|
||||||
for anim in self.gauge_hit_effect:
|
for anim in self.gauge_hit_effect:
|
||||||
anim.draw()
|
anim.draw()
|
||||||
|
|
||||||
# Group 6: UI overlays
|
# Group 6: UI overlays
|
||||||
self.combo_display.draw()
|
self.combo_display.draw()
|
||||||
self.combo_announce.draw()
|
self.combo_announce.draw()
|
||||||
tex.draw_texture('lane', 'lane_score_cover')
|
tex.draw_texture('lane', 'lane_score_cover', index=self.is_2p)
|
||||||
tex.draw_texture('lane', f'{self.player_number}p_icon')
|
tex.draw_texture('lane', f'{self.player_number}p_icon', index=self.is_2p)
|
||||||
tex.draw_texture('lane', 'lane_difficulty', frame=self.difficulty)
|
tex.draw_texture('lane', 'lane_difficulty', frame=self.difficulty, index=self.is_2p)
|
||||||
if self.judge_counter is not None:
|
if self.judge_counter is not None:
|
||||||
self.judge_counter.draw()
|
self.judge_counter.draw()
|
||||||
|
|
||||||
# Group 7: Player-specific elements
|
# Group 7: Player-specific elements
|
||||||
if not global_data.modifiers.auto:
|
if not global_data.modifiers.auto:
|
||||||
|
if self.is_2p:
|
||||||
|
self.nameplate.draw(-62, 285+461)
|
||||||
|
else:
|
||||||
self.nameplate.draw(-62, 285)
|
self.nameplate.draw(-62, 285)
|
||||||
self.draw_modifiers()
|
self.draw_modifiers()
|
||||||
self.chara.draw()
|
self.chara.draw(y=(self.is_2p*536))
|
||||||
|
|
||||||
# Group 8: Special animations and counters
|
# Group 8: Special animations and counters
|
||||||
if self.drumroll_counter is not None:
|
if self.drumroll_counter is not None:
|
||||||
@@ -1019,7 +1067,9 @@ class Player:
|
|||||||
#ray.draw_circle(game_screen.width//2, game_screen.height, 300, ray.ORANGE)
|
#ray.draw_circle(game_screen.width//2, game_screen.height, 300, ray.ORANGE)
|
||||||
|
|
||||||
class Judgement:
|
class Judgement:
|
||||||
def __init__(self, type: str, big: bool, ms_display: Optional[float]=None):
|
"""Shows the judgement of the player's hit"""
|
||||||
|
def __init__(self, type: str, big: bool, is_2p: bool, ms_display: Optional[float]=None):
|
||||||
|
self.is_2p = is_2p
|
||||||
self.type = type
|
self.type = type
|
||||||
self.big = big
|
self.big = big
|
||||||
self.is_finished = False
|
self.is_finished = False
|
||||||
@@ -1051,25 +1101,27 @@ class Judgement:
|
|||||||
fade = self.fade_animation_2.attribute
|
fade = 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', fade=fade)
|
tex.draw_texture('hit_effect', 'hit_effect_good_big', fade=fade, index=self.is_2p)
|
||||||
tex.draw_texture('hit_effect', 'outer_good_big', frame=index, fade=hit_fade)
|
tex.draw_texture('hit_effect', 'outer_good_big', frame=index, fade=hit_fade, index=self.is_2p)
|
||||||
else:
|
else:
|
||||||
tex.draw_texture('hit_effect', 'hit_effect_good', fade=fade)
|
tex.draw_texture('hit_effect', 'hit_effect_good', fade=fade, index=self.is_2p)
|
||||||
tex.draw_texture('hit_effect', 'outer_good', frame=index, fade=hit_fade)
|
tex.draw_texture('hit_effect', 'outer_good', frame=index, fade=hit_fade, index=self.is_2p)
|
||||||
tex.draw_texture('hit_effect', 'judge_good', y=y, fade=fade)
|
tex.draw_texture('hit_effect', 'judge_good', y=y, fade=fade, index=self.is_2p)
|
||||||
elif self.type == 'OK':
|
elif self.type == 'OK':
|
||||||
if self.big:
|
if self.big:
|
||||||
tex.draw_texture('hit_effect', 'hit_effect_ok_big', fade=fade)
|
tex.draw_texture('hit_effect', 'hit_effect_ok_big', fade=fade, index=self.is_2p)
|
||||||
tex.draw_texture('hit_effect', 'outer_ok_big', frame=index, fade=hit_fade)
|
tex.draw_texture('hit_effect', 'outer_ok_big', frame=index, fade=hit_fade, index=self.is_2p)
|
||||||
else:
|
else:
|
||||||
tex.draw_texture('hit_effect', 'hit_effect_ok', fade=fade)
|
tex.draw_texture('hit_effect', 'hit_effect_ok', fade=fade, index=self.is_2p)
|
||||||
tex.draw_texture('hit_effect', 'outer_ok', frame=index, fade=hit_fade)
|
tex.draw_texture('hit_effect', 'outer_ok', frame=index, fade=hit_fade, index=self.is_2p)
|
||||||
tex.draw_texture('hit_effect', 'judge_ok', y=y, fade=fade)
|
tex.draw_texture('hit_effect', 'judge_ok', y=y, fade=fade, index=self.is_2p)
|
||||||
elif self.type == 'BAD':
|
elif self.type == 'BAD':
|
||||||
tex.draw_texture('hit_effect', 'judge_bad', y=y, fade=fade)
|
tex.draw_texture('hit_effect', 'judge_bad', y=y, fade=fade, index=self.is_2p)
|
||||||
|
|
||||||
class LaneHitEffect:
|
class LaneHitEffect:
|
||||||
def __init__(self, type: str):
|
"""Display a gradient overlay when the player hits the drum"""
|
||||||
|
def __init__(self, type: str, is_2p: bool):
|
||||||
|
self.is_2p = is_2p
|
||||||
self.type = type
|
self.type = type
|
||||||
self.fade = tex.get_animation(0, is_copy=True)
|
self.fade = tex.get_animation(0, is_copy=True)
|
||||||
self.fade.start()
|
self.fade.start()
|
||||||
@@ -1082,14 +1134,16 @@ class LaneHitEffect:
|
|||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
if self.type == 'GOOD':
|
if self.type == 'GOOD':
|
||||||
tex.draw_texture('lane', 'lane_hit_effect', frame=2, fade=self.fade.attribute)
|
tex.draw_texture('lane', 'lane_hit_effect', frame=2, index=self.is_2p, fade=self.fade.attribute)
|
||||||
elif self.type == 'DON':
|
elif self.type == 'DON':
|
||||||
tex.draw_texture('lane', 'lane_hit_effect', frame=0, fade=self.fade.attribute)
|
tex.draw_texture('lane', 'lane_hit_effect', frame=0, index=self.is_2p, fade=self.fade.attribute)
|
||||||
elif self.type == 'KAT':
|
elif self.type == 'KAT':
|
||||||
tex.draw_texture('lane', 'lane_hit_effect', frame=1, fade=self.fade.attribute)
|
tex.draw_texture('lane', 'lane_hit_effect', frame=1, index=self.is_2p, fade=self.fade.attribute)
|
||||||
|
|
||||||
class DrumHitEffect:
|
class DrumHitEffect:
|
||||||
def __init__(self, type: str, side: str):
|
"""Display the side of the drum hit"""
|
||||||
|
def __init__(self, type: str, side: str, is_2p: bool):
|
||||||
|
self.is_2p = is_2p
|
||||||
self.type = type
|
self.type = type
|
||||||
self.side = side
|
self.side = side
|
||||||
self.is_finished = False
|
self.is_finished = False
|
||||||
@@ -1104,20 +1158,21 @@ class DrumHitEffect:
|
|||||||
def draw(self):
|
def draw(self):
|
||||||
if self.type == 'DON':
|
if self.type == 'DON':
|
||||||
if self.side == 'L':
|
if self.side == 'L':
|
||||||
tex.draw_texture('lane', 'drum_don_l', fade=self.fade.attribute)
|
tex.draw_texture('lane', 'drum_don_l', index=self.is_2p, fade=self.fade.attribute)
|
||||||
elif self.side == 'R':
|
elif self.side == 'R':
|
||||||
tex.draw_texture('lane', 'drum_don_r', fade=self.fade.attribute)
|
tex.draw_texture('lane', 'drum_don_r', index=self.is_2p, fade=self.fade.attribute)
|
||||||
elif self.type == 'KAT':
|
elif self.type == 'KAT':
|
||||||
if self.side == 'L':
|
if self.side == 'L':
|
||||||
tex.draw_texture('lane', 'drum_kat_l', fade=self.fade.attribute)
|
tex.draw_texture('lane', 'drum_kat_l', index=self.is_2p, fade=self.fade.attribute)
|
||||||
elif self.side == 'R':
|
elif self.side == 'R':
|
||||||
tex.draw_texture('lane', 'drum_kat_r', fade=self.fade.attribute)
|
tex.draw_texture('lane', 'drum_kat_r', index=self.is_2p, fade=self.fade.attribute)
|
||||||
|
|
||||||
class GaugeHitEffect:
|
class GaugeHitEffect:
|
||||||
# Pre-define color thresholds for better performance
|
"""Effect when a note hits the gauge"""
|
||||||
_COLOR_THRESHOLDS = [(0.70, ray.WHITE), (0.80, ray.YELLOW), (0.90, ray.ORANGE), (1.00, ray.RED)]
|
_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, is_2p: bool):
|
||||||
|
self.is_2p = is_2p
|
||||||
self.note_type = note_type
|
self.note_type = note_type
|
||||||
self.is_big = big
|
self.is_big = big
|
||||||
self.texture_change = tex.get_animation(2, is_copy=True)
|
self.texture_change = tex.get_animation(2, is_copy=True)
|
||||||
@@ -1203,6 +1258,7 @@ class GaugeHitEffect:
|
|||||||
tex.draw_texture('gauge', 'hit_effect',
|
tex.draw_texture('gauge', 'hit_effect',
|
||||||
frame=self.texture_change.attribute,
|
frame=self.texture_change.attribute,
|
||||||
x2=self.x2_pos,
|
x2=self.x2_pos,
|
||||||
|
y=(self.is_2p*435),
|
||||||
y2=self.y2_pos,
|
y2=self.y2_pos,
|
||||||
color=ray.fade(self.texture_color, fade_value),
|
color=ray.fade(self.texture_color, fade_value),
|
||||||
origin=self.origin,
|
origin=self.origin,
|
||||||
@@ -1211,13 +1267,14 @@ class GaugeHitEffect:
|
|||||||
|
|
||||||
# Note type texture
|
# Note type texture
|
||||||
tex.draw_texture('notes', str(self.note_type),
|
tex.draw_texture('notes', str(self.note_type),
|
||||||
x=1158, y=101,
|
x=1158, y=101+(self.is_2p*435),
|
||||||
fade=fade_value)
|
fade=fade_value)
|
||||||
|
|
||||||
# Circle effect texture (use cached texture name)
|
# Circle effect texture (use cached texture name)
|
||||||
tex.draw_texture('gauge', self.circle_texture, color=self.color)
|
tex.draw_texture('gauge', self.circle_texture, color=self.color, y=(self.is_2p*435))
|
||||||
|
|
||||||
class NoteArc:
|
class NoteArc:
|
||||||
|
"""Note arcing from the player to the gauge"""
|
||||||
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
|
||||||
@@ -1234,6 +1291,9 @@ class NoteArc:
|
|||||||
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 == 2:
|
||||||
|
self.start_y += 176
|
||||||
|
self.end_y += 435
|
||||||
self.explosion_x = self.start_x
|
self.explosion_x = self.start_x
|
||||||
self.explosion_y = self.start_y
|
self.explosion_y = self.start_y
|
||||||
|
|
||||||
@@ -1312,6 +1372,7 @@ class NoteArc:
|
|||||||
tex.draw_texture('notes', str(self.note_type), x=self.x_i, y=self.y_i)
|
tex.draw_texture('notes', str(self.note_type), x=self.x_i, y=self.y_i)
|
||||||
|
|
||||||
class DrumrollCounter:
|
class DrumrollCounter:
|
||||||
|
"""Displays a drumroll counter, stays alive until is_drumroll is false"""
|
||||||
def __init__(self, current_ms: float):
|
def __init__(self, current_ms: float):
|
||||||
self.create_ms = current_ms
|
self.create_ms = current_ms
|
||||||
self.is_finished = False
|
self.is_finished = False
|
||||||
@@ -1347,6 +1408,7 @@ class DrumrollCounter:
|
|||||||
tex.draw_texture('drumroll_counter', 'counter', color=color, frame=int(digit), x=-(total_width//2)+(i*52), y=-self.stretch_animation.attribute, y2=self.stretch_animation.attribute)
|
tex.draw_texture('drumroll_counter', 'counter', color=color, frame=int(digit), x=-(total_width//2)+(i*52), y=-self.stretch_animation.attribute, y2=self.stretch_animation.attribute)
|
||||||
|
|
||||||
class BalloonAnimation:
|
class BalloonAnimation:
|
||||||
|
"""Draws a Balloon"""
|
||||||
def __init__(self, current_ms: float, balloon_total: int):
|
def __init__(self, current_ms: float, balloon_total: int):
|
||||||
self.create_ms = current_ms
|
self.create_ms = current_ms
|
||||||
self.is_finished = False
|
self.is_finished = False
|
||||||
@@ -1393,6 +1455,7 @@ class BalloonAnimation:
|
|||||||
tex.draw_texture('balloon', 'counter', frame=int(digit), color=self.color, x=-(total_width // 2) + (i * 52), y=-self.stretch_animation.attribute, y2=self.stretch_animation.attribute)
|
tex.draw_texture('balloon', 'counter', frame=int(digit), color=self.color, x=-(total_width // 2) + (i * 52), y=-self.stretch_animation.attribute, y2=self.stretch_animation.attribute)
|
||||||
|
|
||||||
class KusudamaAnimation:
|
class KusudamaAnimation:
|
||||||
|
"""Draws a Kusudama"""
|
||||||
def __init__(self, balloon_total: int):
|
def __init__(self, balloon_total: int):
|
||||||
self.balloon_total = balloon_total
|
self.balloon_total = balloon_total
|
||||||
self.move_down = tex.get_animation(11)
|
self.move_down = tex.get_animation(11)
|
||||||
@@ -1458,8 +1521,10 @@ class KusudamaAnimation:
|
|||||||
tex.draw_texture('kusudama', 'counter', frame=int(digit), x=-(total_width // 2) + (i * 150), y=-self.stretch_animation.attribute, y2=self.stretch_animation.attribute)
|
tex.draw_texture('kusudama', 'counter', frame=int(digit), x=-(total_width // 2) + (i * 150), y=-self.stretch_animation.attribute, y2=self.stretch_animation.attribute)
|
||||||
|
|
||||||
class Combo:
|
class Combo:
|
||||||
def __init__(self, combo: int, current_ms: float):
|
"""Displays the current combo"""
|
||||||
|
def __init__(self, combo: int, current_ms: float, is_2p: bool):
|
||||||
self.combo = combo
|
self.combo = combo
|
||||||
|
self.is_2p = is_2p
|
||||||
self.stretch_animation = tex.get_animation(5)
|
self.stretch_animation = tex.get_animation(5)
|
||||||
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}
|
||||||
@@ -1511,22 +1576,24 @@ class Combo:
|
|||||||
if self.combo < 100:
|
if self.combo < 100:
|
||||||
margin = 30
|
margin = 30
|
||||||
total_width = len(counter) * margin
|
total_width = len(counter) * margin
|
||||||
tex.draw_texture('combo', 'combo')
|
tex.draw_texture('combo', 'combo', index=self.is_2p)
|
||||||
for i, digit in enumerate(counter):
|
for i, digit in enumerate(counter):
|
||||||
tex.draw_texture('combo', 'counter', frame=int(digit), x=-(total_width // 2) + (i * margin), y=-self.stretch_animation.attribute, y2=self.stretch_animation.attribute)
|
tex.draw_texture('combo', 'counter', frame=int(digit), x=-(total_width // 2) + (i * margin), y=-self.stretch_animation.attribute, y2=self.stretch_animation.attribute, index=self.is_2p)
|
||||||
else:
|
else:
|
||||||
margin = 35
|
margin = 35
|
||||||
total_width = len(counter) * margin
|
total_width = len(counter) * margin
|
||||||
tex.draw_texture('combo', 'combo_100')
|
tex.draw_texture('combo', 'combo_100', index=self.is_2p)
|
||||||
for i, digit in enumerate(counter):
|
for i, digit in enumerate(counter):
|
||||||
tex.draw_texture('combo', 'counter_100', frame=int(digit), x=-(total_width // 2) + (i * margin), y=-self.stretch_animation.attribute, y2=self.stretch_animation.attribute)
|
tex.draw_texture('combo', 'counter_100', frame=int(digit), x=-(total_width // 2) + (i * margin), y=-self.stretch_animation.attribute, y2=self.stretch_animation.attribute, index=self.is_2p)
|
||||||
glimmer_positions = [(225, 210), (200, 230), (250, 230)]
|
glimmer_positions = [(225, 210), (200, 230), (250, 230)]
|
||||||
for j, (x, y) in enumerate(glimmer_positions):
|
for j, (x, y) in enumerate(glimmer_positions):
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
tex.draw_texture('combo', 'gleam', x=x+(i*30), y=y+self.glimmer_dict[j], color=self.color[j])
|
tex.draw_texture('combo', 'gleam', x=x+(i*30), y=y+self.glimmer_dict[j] + (self.is_2p*176), color=self.color[j])
|
||||||
|
|
||||||
class ScoreCounter:
|
class ScoreCounter:
|
||||||
def __init__(self, score: int):
|
"""Displays the total score"""
|
||||||
|
def __init__(self, score: int, is_2p: bool):
|
||||||
|
self.is_2p = is_2p
|
||||||
self.score = score
|
self.score = score
|
||||||
self.stretch = tex.get_animation(4)
|
self.stretch = tex.get_animation(4)
|
||||||
|
|
||||||
@@ -1547,7 +1614,7 @@ class ScoreCounter:
|
|||||||
self._cached_score_str = str(self.score)
|
self._cached_score_str = str(self.score)
|
||||||
counter = self._cached_score_str
|
counter = self._cached_score_str
|
||||||
|
|
||||||
x, y = 150, 185
|
x, y = 150, 185 + (self.is_2p*176)
|
||||||
margin = 20
|
margin = 20
|
||||||
total_width = len(counter) * margin
|
total_width = len(counter) * margin
|
||||||
start_x = x - total_width
|
start_x = x - total_width
|
||||||
@@ -1555,7 +1622,9 @@ class ScoreCounter:
|
|||||||
tex.draw_texture('lane', 'score_number', frame=int(digit), x=start_x + (i * margin), y=y - self.stretch.attribute, y2=self.stretch.attribute)
|
tex.draw_texture('lane', 'score_number', frame=int(digit), x=start_x + (i * margin), y=y - self.stretch.attribute, y2=self.stretch.attribute)
|
||||||
|
|
||||||
class ScoreCounterAnimation:
|
class ScoreCounterAnimation:
|
||||||
def __init__(self, player_num: str, counter: int):
|
"""Displays the score init being added to the total score"""
|
||||||
|
def __init__(self, player_num: str, counter: int, is_2p: bool):
|
||||||
|
self.is_2p = is_2p
|
||||||
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.fade_animation_1.start()
|
||||||
@@ -1617,10 +1686,11 @@ class ScoreCounterAnimation:
|
|||||||
tex.draw_texture('lane', 'score_number',
|
tex.draw_texture('lane', 'score_number',
|
||||||
frame=int(digit),
|
frame=int(digit),
|
||||||
x=start_x + (i * self.margin),
|
x=start_x + (i * self.margin),
|
||||||
y=y,
|
y=y + (self.is_2p * 535),
|
||||||
color=self.color)
|
color=self.color)
|
||||||
|
|
||||||
class SongInfo:
|
class SongInfo:
|
||||||
|
"""Displays the song name and genre"""
|
||||||
def __init__(self, song_name: str, genre: int):
|
def __init__(self, song_name: str, genre: int):
|
||||||
self.song_name = song_name
|
self.song_name = song_name
|
||||||
self.genre = genre
|
self.genre = genre
|
||||||
@@ -1642,6 +1712,7 @@ class SongInfo:
|
|||||||
tex.draw_texture('song_info', 'genre', fade=1 - self.fade.attribute, frame=self.genre)
|
tex.draw_texture('song_info', 'genre', fade=1 - self.fade.attribute, frame=self.genre)
|
||||||
|
|
||||||
class ResultTransition:
|
class ResultTransition:
|
||||||
|
"""Displays the result transition animation"""
|
||||||
def __init__(self, player_num: int):
|
def __init__(self, player_num: int):
|
||||||
self.player_num = player_num
|
self.player_num = player_num
|
||||||
self.move = global_tex.get_animation(5)
|
self.move = global_tex.get_animation(5)
|
||||||
@@ -1668,7 +1739,9 @@ class ResultTransition:
|
|||||||
x += 256
|
x += 256
|
||||||
|
|
||||||
class GogoTime:
|
class GogoTime:
|
||||||
def __init__(self):
|
"""Displays the Gogo Time fire and fireworks"""
|
||||||
|
def __init__(self, is_2p: bool):
|
||||||
|
self.is_2p = is_2p
|
||||||
self.explosion_anim = tex.get_animation(23)
|
self.explosion_anim = tex.get_animation(23)
|
||||||
self.fire_resize = tex.get_animation(24)
|
self.fire_resize = tex.get_animation(24)
|
||||||
self.fire_change = tex.get_animation(25)
|
self.fire_change = tex.get_animation(25)
|
||||||
@@ -1682,12 +1755,13 @@ class GogoTime:
|
|||||||
self.fire_change.update(current_time_ms)
|
self.fire_change.update(current_time_ms)
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
tex.draw_texture('gogo_time', 'fire', scale=self.fire_resize.attribute, frame=self.fire_change.attribute, fade=0.5, center=True)
|
tex.draw_texture('gogo_time', 'fire', scale=self.fire_resize.attribute, frame=self.fire_change.attribute, fade=0.5, center=True, index=self.is_2p)
|
||||||
if not self.explosion_anim.is_finished:
|
if not self.explosion_anim.is_finished:
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
tex.draw_texture('gogo_time', 'explosion', frame=self.explosion_anim.attribute, index=i)
|
tex.draw_texture('gogo_time', 'explosion', frame=self.explosion_anim.attribute, index=i)
|
||||||
|
|
||||||
class ComboAnnounce:
|
class ComboAnnounce:
|
||||||
|
"""Displays the combo every 100 combos"""
|
||||||
def __init__(self, combo: int, current_time_ms: float):
|
def __init__(self, combo: int, current_time_ms: float):
|
||||||
self.combo = combo
|
self.combo = combo
|
||||||
self.wait = current_time_ms
|
self.wait = current_time_ms
|
||||||
@@ -1702,7 +1776,7 @@ class ComboAnnounce:
|
|||||||
self.is_finished = True
|
self.is_finished = True
|
||||||
|
|
||||||
self.fade.update(current_time_ms)
|
self.fade.update(current_time_ms)
|
||||||
if not self.audio_played:
|
if not self.audio_played and self.combo >= 100:
|
||||||
audio.play_sound(f'combo_{self.combo}_{global_data.player_num}p', 'voice')
|
audio.play_sound(f'combo_{self.combo}_{global_data.player_num}p', 'voice')
|
||||||
self.audio_played = True
|
self.audio_played = True
|
||||||
|
|
||||||
@@ -1735,6 +1809,7 @@ class ComboAnnounce:
|
|||||||
tex.draw_texture('combo', 'announce_text', x=-text_offset/2, fade=fade)
|
tex.draw_texture('combo', 'announce_text', x=-text_offset/2, fade=fade)
|
||||||
|
|
||||||
class BranchIndicator:
|
class BranchIndicator:
|
||||||
|
"""Displays the branch difficulty and changes"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.difficulty = 'normal'
|
self.difficulty = 'normal'
|
||||||
self.diff_2 = self.difficulty
|
self.diff_2 = self.difficulty
|
||||||
@@ -1781,6 +1856,7 @@ class BranchIndicator:
|
|||||||
tex.draw_texture('branch', self.difficulty, y=(self.diff_up.attribute * (self.direction*-1)) - (70*self.direction*-1), fade=1 - self.diff_fade.attribute)
|
tex.draw_texture('branch', self.difficulty, y=(self.diff_up.attribute * (self.direction*-1)) - (70*self.direction*-1), fade=1 - self.diff_fade.attribute)
|
||||||
|
|
||||||
class FailAnimation:
|
class FailAnimation:
|
||||||
|
"""Animates the fail effect"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.bachio_fade_in = Animation.create_fade(150, initial_opacity=0.0, final_opacity=1.0)
|
self.bachio_fade_in = Animation.create_fade(150, initial_opacity=0.0, final_opacity=1.0)
|
||||||
self.bachio_fade_in.start()
|
self.bachio_fade_in.start()
|
||||||
@@ -1826,6 +1902,7 @@ class FailAnimation:
|
|||||||
tex.draw_texture('ending_anim', 'bachio_boom', index=1, fade=self.bachio_boom_fade_in.attribute, center=True, scale=self.bachio_boom_scale.attribute)
|
tex.draw_texture('ending_anim', 'bachio_boom', index=1, fade=self.bachio_boom_fade_in.attribute, center=True, scale=self.bachio_boom_scale.attribute)
|
||||||
|
|
||||||
class ClearAnimation:
|
class ClearAnimation:
|
||||||
|
"""Animates the clear effect"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.bachio_fade_in = Animation.create_fade(150, initial_opacity=0.0, final_opacity=1.0)
|
self.bachio_fade_in = Animation.create_fade(150, initial_opacity=0.0, final_opacity=1.0)
|
||||||
self.bachio_fade_in.start()
|
self.bachio_fade_in.start()
|
||||||
@@ -1876,6 +1953,7 @@ class ClearAnimation:
|
|||||||
tex.draw_texture('ending_anim', 'bachio_r_' + self.name, x=self.bachio_move_out.attribute, frame=self.frame, fade=self.bachio_fade_in.attribute)
|
tex.draw_texture('ending_anim', 'bachio_r_' + self.name, x=self.bachio_move_out.attribute, frame=self.frame, fade=self.bachio_fade_in.attribute)
|
||||||
|
|
||||||
class FCAnimation:
|
class FCAnimation:
|
||||||
|
"""Animates the full combo effect"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.bachio_fade_in = Animation.create_fade(150, initial_opacity=0.0, final_opacity=1.0)
|
self.bachio_fade_in = Animation.create_fade(150, initial_opacity=0.0, final_opacity=1.0)
|
||||||
self.bachio_fade_in.start()
|
self.bachio_fade_in.start()
|
||||||
@@ -1950,6 +2028,7 @@ class FCAnimation:
|
|||||||
tex.draw_texture('ending_anim', 'bachio_r_' + self.name, x=(self.bachio_move_out.attribute + self.bachio_move_out_2.attribute)*1.15, y=-self.bachio_move_up.attribute, frame=self.frame, fade=self.bachio_fade_in.attribute)
|
tex.draw_texture('ending_anim', 'bachio_r_' + self.name, x=(self.bachio_move_out.attribute + self.bachio_move_out_2.attribute)*1.15, y=-self.bachio_move_up.attribute, frame=self.frame, fade=self.bachio_fade_in.attribute)
|
||||||
|
|
||||||
class JudgeCounter:
|
class JudgeCounter:
|
||||||
|
"""Counts the number of good, ok, bad, and drumroll notes in real time"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.good = 0
|
self.good = 0
|
||||||
self.ok = 0
|
self.ok = 0
|
||||||
@@ -1994,7 +2073,9 @@ class JudgeCounter:
|
|||||||
|
|
||||||
|
|
||||||
class Gauge:
|
class Gauge:
|
||||||
def __init__(self, player_num: str, difficulty: int, level: int, total_notes: int):
|
"""The player's gauge"""
|
||||||
|
def __init__(self, player_num: str, difficulty: int, level: int, total_notes: int, is_2p: bool):
|
||||||
|
self.is_2p = is_2p
|
||||||
self.player_num = player_num
|
self.player_num = player_num
|
||||||
self.string_diff = "_hard"
|
self.string_diff = "_hard"
|
||||||
self.gauge_length = 0
|
self.gauge_length = 0
|
||||||
@@ -2062,6 +2143,7 @@ class Gauge:
|
|||||||
self.rainbow_animation = None
|
self.rainbow_animation = None
|
||||||
|
|
||||||
def add_good(self):
|
def add_good(self):
|
||||||
|
"""Adds a good note to the gauge"""
|
||||||
self.gauge_update_anim.start()
|
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"]))
|
||||||
@@ -2069,6 +2151,7 @@ class Gauge:
|
|||||||
self.gauge_length = self.gauge_max
|
self.gauge_length = self.gauge_max
|
||||||
|
|
||||||
def add_ok(self):
|
def add_ok(self):
|
||||||
|
"""Adds an ok note to the gauge"""
|
||||||
self.gauge_update_anim.start()
|
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"]))
|
||||||
@@ -2076,6 +2159,7 @@ class Gauge:
|
|||||||
self.gauge_length = self.gauge_max
|
self.gauge_length = self.gauge_max
|
||||||
|
|
||||||
def add_bad(self):
|
def add_bad(self):
|
||||||
|
"""Adds a bad note to the gauge"""
|
||||||
self.previous_length = int(self.gauge_length)
|
self.previous_length = int(self.gauge_length)
|
||||||
self.gauge_length += ((1 * self.table[self.difficulty][self.level]["bad_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]["bad_multiplier"]) / self.total_notes) * (100 * (self.clear_start[self.difficulty] / self.table[self.difficulty][self.level]["clear_rate"]))
|
||||||
if self.gauge_length < 0:
|
if self.gauge_length < 0:
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ from libs.utils import (
|
|||||||
|
|
||||||
|
|
||||||
class State:
|
class State:
|
||||||
|
"""
|
||||||
|
Enum representing the state of the result screen.
|
||||||
|
"""
|
||||||
FAIL = 0
|
FAIL = 0
|
||||||
CLEAR = 1
|
CLEAR = 1
|
||||||
RAINBOW = 2
|
RAINBOW = 2
|
||||||
@@ -81,6 +84,9 @@ class ResultScreen:
|
|||||||
return "SONG_SELECT"
|
return "SONG_SELECT"
|
||||||
|
|
||||||
def update_score_animation(self):
|
def update_score_animation(self):
|
||||||
|
"""
|
||||||
|
Update the score animation if a high score is achieved.
|
||||||
|
"""
|
||||||
if self.is_skipped:
|
if self.is_skipped:
|
||||||
if self.update_index == len(self.update_list) - 1:
|
if self.update_index == len(self.update_list) - 1:
|
||||||
return
|
return
|
||||||
@@ -161,6 +167,9 @@ class ResultScreen:
|
|||||||
self.chara.update(current_time, 100, False, False)
|
self.chara.update(current_time, 100, False, False)
|
||||||
|
|
||||||
def draw_score_info(self):
|
def draw_score_info(self):
|
||||||
|
"""
|
||||||
|
Draw the score information.
|
||||||
|
"""
|
||||||
if self.good != '':
|
if self.good != '':
|
||||||
for i in range(len(str(self.good))):
|
for i in range(len(str(self.good))):
|
||||||
tex.draw_texture('score', 'judge_num', frame=int(str(self.good)[::-1][i]), x=943-(i*24), y=186)
|
tex.draw_texture('score', 'judge_num', frame=int(str(self.good)[::-1][i]), x=943-(i*24), y=186)
|
||||||
@@ -178,6 +187,9 @@ class ResultScreen:
|
|||||||
tex.draw_texture('score', 'judge_num', frame=int(str(self.total_drumroll)[::-1][i]), x=1217-(i*24), y=227)
|
tex.draw_texture('score', 'judge_num', frame=int(str(self.total_drumroll)[::-1][i]), x=1217-(i*24), y=227)
|
||||||
|
|
||||||
def draw_total_score(self):
|
def draw_total_score(self):
|
||||||
|
"""
|
||||||
|
Draw the total score.
|
||||||
|
"""
|
||||||
if not self.fade_in.is_finished:
|
if not self.fade_in.is_finished:
|
||||||
return
|
return
|
||||||
tex.draw_texture('score', 'score_shinuchi')
|
tex.draw_texture('score', 'score_shinuchi')
|
||||||
@@ -186,6 +198,7 @@ class ResultScreen:
|
|||||||
tex.draw_texture('score', 'score_num', x=-(i*21), frame=int(str(self.score)[::-1][i]))
|
tex.draw_texture('score', 'score_num', x=-(i*21), frame=int(str(self.score)[::-1][i]))
|
||||||
|
|
||||||
def draw_bottom_textures(self):
|
def draw_bottom_textures(self):
|
||||||
|
"""Draw the bottom textures."""
|
||||||
if self.state == State.FAIL:
|
if self.state == State.FAIL:
|
||||||
tex.draw_texture('background', 'gradient_fail', fade=min(0.4, self.fade_in_bottom.attribute))
|
tex.draw_texture('background', 'gradient_fail', fade=min(0.4, self.fade_in_bottom.attribute))
|
||||||
else:
|
else:
|
||||||
@@ -193,6 +206,7 @@ class ResultScreen:
|
|||||||
self.bottom_characters.draw()
|
self.bottom_characters.draw()
|
||||||
|
|
||||||
def draw_modifiers(self):
|
def draw_modifiers(self):
|
||||||
|
"""Draw the modifiers if enabled."""
|
||||||
if global_data.modifiers.display:
|
if global_data.modifiers.display:
|
||||||
tex.draw_texture('score', 'mod_doron')
|
tex.draw_texture('score', 'mod_doron')
|
||||||
if global_data.modifiers.inverse:
|
if global_data.modifiers.inverse:
|
||||||
@@ -260,6 +274,7 @@ class ResultScreen:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
class Crown:
|
class Crown:
|
||||||
|
"""Represents a crown animation"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.resize = tex.get_animation(2)
|
self.resize = tex.get_animation(2)
|
||||||
self.resize_fix = tex.get_animation(3)
|
self.resize_fix = tex.get_animation(3)
|
||||||
@@ -293,6 +308,7 @@ class Crown:
|
|||||||
tex.draw_texture('crown', 'gleam', frame=self.gleam.attribute)
|
tex.draw_texture('crown', 'gleam', frame=self.gleam.attribute)
|
||||||
|
|
||||||
class BottomCharacters:
|
class BottomCharacters:
|
||||||
|
"""Represents the bottom characters animation"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.move_up = tex.get_animation(7)
|
self.move_up = tex.get_animation(7)
|
||||||
self.move_down = tex.get_animation(8)
|
self.move_down = tex.get_animation(8)
|
||||||
@@ -370,6 +386,7 @@ class BottomCharacters:
|
|||||||
tex.draw_texture('bottom', 'chara_1', frame=self.chara_1_index, y=y)
|
tex.draw_texture('bottom', 'chara_1', frame=self.chara_1_index, y=y)
|
||||||
|
|
||||||
class FadeIn:
|
class FadeIn:
|
||||||
|
"""A fade out disguised as a fade in"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.fadein = tex.get_animation(15)
|
self.fadein = tex.get_animation(15)
|
||||||
self.fadein.start()
|
self.fadein.start()
|
||||||
@@ -389,6 +406,7 @@ class FadeIn:
|
|||||||
x += 256
|
x += 256
|
||||||
|
|
||||||
class ScoreAnimator:
|
class ScoreAnimator:
|
||||||
|
"""Animates a number from left to right"""
|
||||||
def __init__(self, target_score):
|
def __init__(self, target_score):
|
||||||
self.target_score = str(target_score)
|
self.target_score = str(target_score)
|
||||||
self.current_score_list = [[0,0] for _ in range(len(self.target_score))]
|
self.current_score_list = [[0,0] for _ in range(len(self.target_score))]
|
||||||
@@ -396,6 +414,7 @@ class ScoreAnimator:
|
|||||||
self.is_finished = False
|
self.is_finished = False
|
||||||
|
|
||||||
def next_score(self) -> str:
|
def next_score(self) -> str:
|
||||||
|
"""Returns the next number in the animation"""
|
||||||
if self.digit_index == -1:
|
if self.digit_index == -1:
|
||||||
self.is_finished = True
|
self.is_finished = True
|
||||||
return str(int(''.join([str(item[0]) for item in self.current_score_list])))
|
return str(int(''.join([str(item[0]) for item in self.current_score_list])))
|
||||||
@@ -414,6 +433,7 @@ class ScoreAnimator:
|
|||||||
return str(int(ret_val))
|
return str(int(ret_val))
|
||||||
|
|
||||||
class HighScoreIndicator:
|
class HighScoreIndicator:
|
||||||
|
"""Indicates the difference between the old and new high score"""
|
||||||
def __init__(self, old_score: int, new_score: int):
|
def __init__(self, old_score: int, new_score: int):
|
||||||
self.score_diff = new_score - old_score
|
self.score_diff = new_score - old_score
|
||||||
self.move = tex.get_animation(18)
|
self.move = tex.get_animation(18)
|
||||||
@@ -432,6 +452,7 @@ class HighScoreIndicator:
|
|||||||
|
|
||||||
|
|
||||||
class Gauge:
|
class Gauge:
|
||||||
|
"""The gauge from the game screen, at 0.9x scale"""
|
||||||
def __init__(self, player_num: str, gauge_length: int):
|
def __init__(self, player_num: str, gauge_length: int):
|
||||||
self.player_num = player_num
|
self.player_num = player_num
|
||||||
self.difficulty = min(2, session_data.selected_difficulty)
|
self.difficulty = min(2, session_data.selected_difficulty)
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ class SongSelectScreen:
|
|||||||
self.indicator = Indicator(Indicator.State.SELECT)
|
self.indicator = Indicator(Indicator.State.SELECT)
|
||||||
|
|
||||||
def load_navigator(self):
|
def load_navigator(self):
|
||||||
|
"""To be called on boot."""
|
||||||
self.navigator = FileNavigator(self.root_dir)
|
self.navigator = FileNavigator(self.root_dir)
|
||||||
|
|
||||||
def on_screen_start(self):
|
def on_screen_start(self):
|
||||||
@@ -119,6 +120,7 @@ class SongSelectScreen:
|
|||||||
return next_screen
|
return next_screen
|
||||||
|
|
||||||
def reset_demo_music(self):
|
def reset_demo_music(self):
|
||||||
|
"""Reset the preview music to the song select bgm."""
|
||||||
if self.demo_song is not None:
|
if self.demo_song is not None:
|
||||||
audio.stop_music_stream(self.demo_song)
|
audio.stop_music_stream(self.demo_song)
|
||||||
audio.unload_music_stream(self.demo_song)
|
audio.unload_music_stream(self.demo_song)
|
||||||
@@ -127,6 +129,7 @@ class SongSelectScreen:
|
|||||||
self.navigator.get_current_item().box.wait = get_current_ms()
|
self.navigator.get_current_item().box.wait = get_current_ms()
|
||||||
|
|
||||||
def handle_input_browsing(self):
|
def handle_input_browsing(self):
|
||||||
|
"""Handle input for browsing songs."""
|
||||||
if ray.is_key_pressed(ray.KeyboardKey.KEY_LEFT_CONTROL) or (is_l_kat_pressed() and get_current_ms() <= self.last_moved + 50):
|
if ray.is_key_pressed(ray.KeyboardKey.KEY_LEFT_CONTROL) or (is_l_kat_pressed() and get_current_ms() <= self.last_moved + 50):
|
||||||
self.reset_demo_music()
|
self.reset_demo_music()
|
||||||
for _ in range(10):
|
for _ in range(10):
|
||||||
@@ -188,7 +191,7 @@ class SongSelectScreen:
|
|||||||
audio.play_sound('add_favorite', 'sound')
|
audio.play_sound('add_favorite', 'sound')
|
||||||
|
|
||||||
def handle_input_selected(self):
|
def handle_input_selected(self):
|
||||||
# Handle song selection confirmation or cancel
|
"""Handle input for selecting difficulty."""
|
||||||
if self.neiro_selector is not None:
|
if self.neiro_selector is not None:
|
||||||
if is_l_kat_pressed():
|
if is_l_kat_pressed():
|
||||||
self.neiro_selector.move_left()
|
self.neiro_selector.move_left()
|
||||||
@@ -222,6 +225,9 @@ class SongSelectScreen:
|
|||||||
self._confirm_selection()
|
self._confirm_selection()
|
||||||
|
|
||||||
def get_current_song():
|
def get_current_song():
|
||||||
|
"""
|
||||||
|
Returns the currently selected song.
|
||||||
|
"""
|
||||||
selected_song = self.navigator.get_current_item()
|
selected_song = self.navigator.get_current_item()
|
||||||
if isinstance(selected_song, Directory):
|
if isinstance(selected_song, Directory):
|
||||||
raise Exception("Directory was chosen instead of song")
|
raise Exception("Directory was chosen instead of song")
|
||||||
@@ -247,6 +253,9 @@ class SongSelectScreen:
|
|||||||
self._toggle_ura_mode()
|
self._toggle_ura_mode()
|
||||||
|
|
||||||
def handle_input_diff_sort(self):
|
def handle_input_diff_sort(self):
|
||||||
|
"""
|
||||||
|
Handle input for sorting difficulty.
|
||||||
|
"""
|
||||||
if self.diff_sort_selector is None:
|
if self.diff_sort_selector is None:
|
||||||
raise Exception("Diff sort selector was not able to be created")
|
raise Exception("Diff sort selector was not able to be created")
|
||||||
if is_l_kat_pressed():
|
if is_l_kat_pressed():
|
||||||
@@ -592,6 +601,7 @@ class SongSelectScreen:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
class SongBox:
|
class SongBox:
|
||||||
|
"""A box for the song select screen."""
|
||||||
OUTLINE_MAP = {
|
OUTLINE_MAP = {
|
||||||
1: ray.Color(0, 77, 104, 255),
|
1: ray.Color(0, 77, 104, 255),
|
||||||
2: ray.Color(156, 64, 2, 255),
|
2: ray.Color(156, 64, 2, 255),
|
||||||
@@ -851,6 +861,7 @@ class SongBox:
|
|||||||
self._draw_closed(x, y)
|
self._draw_closed(x, y)
|
||||||
|
|
||||||
class YellowBox:
|
class YellowBox:
|
||||||
|
"""A song box when it is opened."""
|
||||||
def __init__(self, name: OutlinedText, is_back: bool, tja: Optional[TJAParser] = None):
|
def __init__(self, name: OutlinedText, is_back: bool, tja: Optional[TJAParser] = None):
|
||||||
self.is_diff_select = False
|
self.is_diff_select = False
|
||||||
self.name = name
|
self.name = name
|
||||||
@@ -1064,6 +1075,7 @@ class YellowBox:
|
|||||||
self._draw_text(song_box)
|
self._draw_text(song_box)
|
||||||
|
|
||||||
class GenreBG:
|
class GenreBG:
|
||||||
|
"""The background for a genre box."""
|
||||||
def __init__(self, start_box: SongBox, end_box: SongBox, title: OutlinedText, diff_sort: Optional[int]):
|
def __init__(self, start_box: SongBox, end_box: SongBox, title: OutlinedText, diff_sort: Optional[int]):
|
||||||
self.start_box = start_box
|
self.start_box = start_box
|
||||||
self.end_box = end_box
|
self.end_box = end_box
|
||||||
@@ -1117,6 +1129,7 @@ class GenreBG:
|
|||||||
self.title.draw(self.title.default_src, dest, ray.Vector2(0, 0), 0, ray.fade(ray.WHITE, self.fade_in.attribute))
|
self.title.draw(self.title.default_src, dest, ray.Vector2(0, 0), 0, ray.fade(ray.WHITE, self.fade_in.attribute))
|
||||||
|
|
||||||
class UraSwitchAnimation:
|
class UraSwitchAnimation:
|
||||||
|
"""The animation for the ura switch."""
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.texture_change = tex.get_animation(7)
|
self.texture_change = tex.get_animation(7)
|
||||||
self.fade_out = tex.get_animation(8)
|
self.fade_out = tex.get_animation(8)
|
||||||
@@ -1134,6 +1147,7 @@ class UraSwitchAnimation:
|
|||||||
tex.draw_texture('diff_select', 'ura_switch', frame=self.texture_change.attribute, fade=self.fade_out.attribute)
|
tex.draw_texture('diff_select', 'ura_switch', frame=self.texture_change.attribute, fade=self.fade_out.attribute)
|
||||||
|
|
||||||
class DiffSortSelect:
|
class DiffSortSelect:
|
||||||
|
"""The menu for selecting the difficulty sort and level sort."""
|
||||||
def __init__(self, statistics: dict[int, dict[int, list[int]]], prev_diff: int, prev_level: int):
|
def __init__(self, statistics: dict[int, dict[int, list[int]]], prev_diff: int, prev_level: int):
|
||||||
self.selected_box = -1
|
self.selected_box = -1
|
||||||
self.selected_level = 1
|
self.selected_level = 1
|
||||||
@@ -1368,6 +1382,7 @@ class DiffSortSelect:
|
|||||||
self.draw_diff_select()
|
self.draw_diff_select()
|
||||||
|
|
||||||
class NeiroSelector:
|
class NeiroSelector:
|
||||||
|
"""The menu for selecting the game hitsounds."""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.selected_sound = global_data.hit_sound
|
self.selected_sound = global_data.hit_sound
|
||||||
with open(Path("Sounds") / 'hit_sounds' / 'neiro_list.txt', encoding='utf-8-sig') as neiro_list:
|
with open(Path("Sounds") / 'hit_sounds' / 'neiro_list.txt', encoding='utf-8-sig') as neiro_list:
|
||||||
@@ -1477,6 +1492,7 @@ class NeiroSelector:
|
|||||||
self.text_2.draw(self.text_2.default_src, dest, ray.Vector2(0, 0), 0, ray.fade(ray.WHITE, 1 - self.fade_sideways.attribute))
|
self.text_2.draw(self.text_2.default_src, dest, ray.Vector2(0, 0), 0, ray.fade(ray.WHITE, 1 - self.fade_sideways.attribute))
|
||||||
|
|
||||||
class ModifierSelector:
|
class ModifierSelector:
|
||||||
|
"""The menu for selecting the game modifiers."""
|
||||||
TEX_MAP = {
|
TEX_MAP = {
|
||||||
"auto": "mod_auto",
|
"auto": "mod_auto",
|
||||||
"speed": "mod_baisaku",
|
"speed": "mod_baisaku",
|
||||||
@@ -1671,7 +1687,15 @@ class ModifierSelector:
|
|||||||
tex.draw_texture('modifier', 'blue_arrow', y=move + (i*50), x=x+110 + self.blue_arrow_move.attribute, mirror='horizontal', fade=self.blue_arrow_fade.attribute)
|
tex.draw_texture('modifier', 'blue_arrow', y=move + (i*50), x=x+110 + self.blue_arrow_move.attribute, mirror='horizontal', fade=self.blue_arrow_fade.attribute)
|
||||||
|
|
||||||
class ScoreHistory:
|
class ScoreHistory:
|
||||||
|
"""The score information that appears while hovering over a song"""
|
||||||
def __init__(self, scores: dict[int, tuple[int, int, int, int]], current_ms):
|
def __init__(self, scores: dict[int, tuple[int, int, int, int]], current_ms):
|
||||||
|
"""
|
||||||
|
Initialize the score history with the given scores and current time.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
scores (dict[int, tuple[int, int, int, int]]): A dictionary of scores for each difficulty level.
|
||||||
|
current_ms (int): The current time in milliseconds.
|
||||||
|
"""
|
||||||
self.scores = {k: v for k, v in scores.items() if v is not None}
|
self.scores = {k: v for k, v in scores.items() if v is not None}
|
||||||
self.difficulty_keys = list(self.scores.keys())
|
self.difficulty_keys = list(self.scores.keys())
|
||||||
self.curr_difficulty_index = 0
|
self.curr_difficulty_index = 0
|
||||||
@@ -2164,8 +2188,6 @@ class FileNavigator:
|
|||||||
self.load_current_directory()
|
self.load_current_directory()
|
||||||
self.box_open = False
|
self.box_open = False
|
||||||
|
|
||||||
# ... (rest of the methods remain the same: navigate_left, navigate_right, etc.)
|
|
||||||
|
|
||||||
def _count_tja_files(self, folder_path: Path):
|
def _count_tja_files(self, folder_path: Path):
|
||||||
"""Count TJA files in directory"""
|
"""Count TJA files in directory"""
|
||||||
tja_count = 0
|
tja_count = 0
|
||||||
@@ -2389,10 +2411,12 @@ class FileNavigator:
|
|||||||
raise Exception("No current item available")
|
raise Exception("No current item available")
|
||||||
|
|
||||||
def reset_items(self):
|
def reset_items(self):
|
||||||
|
"""Reset the items in the song select scene"""
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
item.box.reset()
|
item.box.reset()
|
||||||
|
|
||||||
def add_recent(self):
|
def add_recent(self):
|
||||||
|
"""Add the current song to the recent list"""
|
||||||
song = self.get_current_item()
|
song = self.get_current_item()
|
||||||
if isinstance(song, Directory):
|
if isinstance(song, Directory):
|
||||||
return
|
return
|
||||||
@@ -2414,6 +2438,7 @@ class FileNavigator:
|
|||||||
print("Added recent: ", song.hash, song.tja.metadata.title['en'], song.tja.metadata.subtitle['en'])
|
print("Added recent: ", song.hash, song.tja.metadata.title['en'], song.tja.metadata.subtitle['en'])
|
||||||
|
|
||||||
def add_favorite(self) -> bool:
|
def add_favorite(self) -> bool:
|
||||||
|
"""Add the current song to the favorites list"""
|
||||||
song = self.get_current_item()
|
song = self.get_current_item()
|
||||||
if isinstance(song, Directory):
|
if isinstance(song, Directory):
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ class TitleScreen:
|
|||||||
return "ENTRY"
|
return "ENTRY"
|
||||||
|
|
||||||
def scene_manager(self, current_time):
|
def scene_manager(self, current_time):
|
||||||
|
"""Manage the scene transitions"""
|
||||||
if self.state == State.OP_VIDEO:
|
if self.state == State.OP_VIDEO:
|
||||||
if self.op_video is None:
|
if self.op_video is None:
|
||||||
self.op_video = VideoPlayer(random.choice(self.op_video_list))
|
self.op_video = VideoPlayer(random.choice(self.op_video_list))
|
||||||
@@ -123,7 +124,9 @@ class TitleScreen:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
class WarningScreen:
|
class WarningScreen:
|
||||||
|
"""Warning screen for the game"""
|
||||||
class X:
|
class X:
|
||||||
|
"""Giant X behind the characters for the warning screen"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.resize = tex.get_animation(0)
|
self.resize = tex.get_animation(0)
|
||||||
self.resize.start()
|
self.resize.start()
|
||||||
@@ -149,6 +152,7 @@ class WarningScreen:
|
|||||||
tex.draw_texture('warning', 'x_red', fade=self.fadein.attribute, scale=self.resize.attribute, center=True)
|
tex.draw_texture('warning', 'x_red', fade=self.fadein.attribute, scale=self.resize.attribute, center=True)
|
||||||
|
|
||||||
class BachiHit:
|
class BachiHit:
|
||||||
|
"""Bachi hitting the player animation for the warning screen"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.resize = tex.get_animation(3)
|
self.resize = tex.get_animation(3)
|
||||||
self.fadein = tex.get_animation(4)
|
self.fadein = tex.get_animation(4)
|
||||||
@@ -170,6 +174,7 @@ class WarningScreen:
|
|||||||
tex.draw_texture('warning', 'bachi')
|
tex.draw_texture('warning', 'bachi')
|
||||||
|
|
||||||
class Characters:
|
class Characters:
|
||||||
|
"""Characters for the warning screen"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.shadow_fade = tex.get_animation(5)
|
self.shadow_fade = tex.get_animation(5)
|
||||||
self.chara_0_frame = tex.get_animation(7)
|
self.chara_0_frame = tex.get_animation(7)
|
||||||
@@ -201,6 +206,7 @@ class WarningScreen:
|
|||||||
tex.draw_texture('warning', 'chara_1', frame=self.chara_1_frame.attribute, fade=fade, y=y_pos)
|
tex.draw_texture('warning', 'chara_1', frame=self.chara_1_frame.attribute, fade=fade, y=y_pos)
|
||||||
|
|
||||||
class Board:
|
class Board:
|
||||||
|
"""Background Board for the warning screen"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.move_down = tex.get_animation(10)
|
self.move_down = tex.get_animation(10)
|
||||||
self.move_down.start()
|
self.move_down.start()
|
||||||
|
|||||||
Reference in New Issue
Block a user