diff --git a/PyTaiko.py b/PyTaiko.py index dacb705..1d3f9e7 100644 --- a/PyTaiko.py +++ b/PyTaiko.py @@ -19,10 +19,10 @@ from libs.screen import Screen from libs.tja import TJAParser from libs.utils import ( force_dedicated_gpu, - get_config, global_data, global_tex ) +from libs.config import get_config from scenes.devtest import DevScreen from scenes.entry import EntryScreen from scenes.game import GameScreen @@ -119,8 +119,8 @@ def main(): logger.info("Starting PyTaiko") logger.debug(f"Loaded config: {global_data.config}") - screen_width = global_data.config["video"]["screen_width"] - screen_height = global_data.config["video"]["screen_height"] + screen_width = global_tex.screen_width + screen_height = global_tex.screen_height if global_data.config["video"]["vsync"]: ray.set_config_flags(ray.ConfigFlags.FLAG_VSYNC_HINT) diff --git a/config.toml b/config.toml index 42a0bc2..0afd890 100644 --- a/config.toml +++ b/config.toml @@ -30,6 +30,7 @@ rainbow = false [paths] tja_path = ['Songs'] video_path = ['Videos'] +graphics_path = 'Graphics/GreenVer' [keys] exit_key = 'Q' diff --git a/libs/audio.py b/libs/audio.py index 5c8d885..67629cc 100644 --- a/libs/audio.py +++ b/libs/audio.py @@ -4,8 +4,8 @@ import platform import logging from pathlib import Path -from libs.global_data import VolumeConfig -from libs.utils import get_config +from libs.config import VolumeConfig +from libs.config import get_config ffi = cffi.FFI() diff --git a/libs/background.py b/libs/background.py index b36d163..b3fbe60 100644 --- a/libs/background.py +++ b/libs/background.py @@ -177,7 +177,7 @@ class Background: self.bg_normal.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.don_bg_2.draw(self.tex_wrapper, y=self.tex_wrapper.skin_config["game_2p_offset"].y) if self.renda is not None: self.renda.draw() if self.dancer is not None: diff --git a/libs/bg_objects/bg_fever.py b/libs/bg_objects/bg_fever.py index 9b8a5a0..0376139 100644 --- a/libs/bg_objects/bg_fever.py +++ b/libs/bg_objects/bg_fever.py @@ -17,6 +17,7 @@ class BGFever: class BGFeverBase: def __init__(self, tex: TextureWrapper, index: int, path: str): self.name = 'bg_fever_' + str(index) + self.tex = tex tex.load_zip(path, f'bg_fever/{self.name}') self.transitioned = False def start(self): @@ -28,13 +29,13 @@ class BGFeverBase: class BGFever1(BGFeverBase): class Tile: - def __init__(self): - self.expansion = Animation.create_move(166, total_distance=360) + def __init__(self, tex): + self.expansion = Animation.create_move(166, total_distance=tex.screen_height//2) self.expansion.start() def update(self, current_time_ms): self.expansion.update(current_time_ms) def draw(self, tex: TextureWrapper, name: str, x: int, frame: int): - tex.draw_texture(name, 'background', frame=frame, x=x, y2=-360+self.expansion.attribute, y=360+(180-self.expansion.attribute/2)) + tex.draw_texture(name, 'background', frame=frame, x=x, y2=-tex.screen_height//2+self.expansion.attribute, y=tex.screen_height//2+(tex.screen_height//4-self.expansion.attribute/2)) def __init__(self, tex: TextureWrapper, index: int, path): super().__init__(tex, index, path) self.wait = 0 @@ -89,7 +90,7 @@ class BGFever1(BGFeverBase): def update(self, current_time_ms: float): if len(self.bg_tiles) < 20 and current_time_ms >= self.wait + 66: - self.bg_tiles.append(BGFever1.Tile()) + self.bg_tiles.append(BGFever1.Tile(self.tex)) self.wait = current_time_ms for tile in self.bg_tiles: tile.update(current_time_ms) diff --git a/libs/bg_objects/bg_normal.py b/libs/bg_objects/bg_normal.py index d31b866..37a46cf 100644 --- a/libs/bg_objects/bg_normal.py +++ b/libs/bg_objects/bg_normal.py @@ -15,6 +15,7 @@ class BGNormal: class BGNormalBase: def __init__(self, tex: TextureWrapper, index: int, path: str): self.name = "bg_" + str(index) + self.tex = tex tex.load_zip(path, f'bg_normal/{self.name}') def update(self, current_time_ms: float): pass @@ -69,29 +70,29 @@ class BGNormal3(BGNormalBase): class BGNormal4(BGNormalBase): class Petal: - def __init__(self): - self.spawn_point = self.random_excluding_range() + def __init__(self, tex: TextureWrapper): + self.spawn_point = self.random_excluding_range(tex.screen_width) duration = random.randint(1400, 2000) self.move_x = Animation.create_move(duration, total_distance=random.randint(-300, 300)) - self.move_y = Animation.create_move(duration, total_distance=360) + self.move_y = Animation.create_move(duration, total_distance=tex.screen_height//2) self.move_x.start() self.move_y.start() - def random_excluding_range(self): + def random_excluding_range(self, screen_width): while True: - num = random.randint(0, 1280) + num = random.randint(0, screen_width) if num < 260 or num > 540: return num def update(self, current_time_ms): self.move_x.update(current_time_ms) self.move_y.update(current_time_ms) def draw(self, name: str, tex: TextureWrapper): - tex.draw_texture(name, 'petal', x=self.spawn_point + self.move_x.attribute, y=360+self.move_y.attribute, fade=0.75) + tex.draw_texture(name, 'petal', x=self.spawn_point + self.move_x.attribute, y=tex.screen_height//2+self.move_y.attribute, fade=0.75) def __init__(self, tex: TextureWrapper, index: int, path: str): super().__init__(tex, index, path) self.flicker = tex.get_animation(11) self.turtle_move = tex.get_animation(12) self.turtle_change = tex.get_animation(13) - self.petals = {self.Petal(), self.Petal(), self.Petal(), self.Petal(), self.Petal()} + self.petals = {self.Petal(tex), self.Petal(tex), self.Petal(tex), self.Petal(tex), self.Petal(tex)} def update(self, current_time_ms: float): self.flicker.update(current_time_ms) self.turtle_move.update(current_time_ms) @@ -100,7 +101,7 @@ class BGNormal4(BGNormalBase): petal.update(current_time_ms) if petal.move_y.is_finished: self.petals.remove(petal) - self.petals.add(self.Petal()) + self.petals.add(self.Petal(self.tex)) def draw(self, tex: TextureWrapper): tex.draw_texture(self.name, 'background') tex.draw_texture(self.name, 'chara') diff --git a/libs/bg_objects/chibi.py b/libs/bg_objects/chibi.py index 0932313..fbf7b32 100644 --- a/libs/bg_objects/chibi.py +++ b/libs/bg_objects/chibi.py @@ -20,9 +20,10 @@ class BaseChibi: self.name = 'chibi_' + str(index) self.bpm = bpm self.is_2p = is_2p - self.hori_move = Animation.create_move(60000 / self.bpm * 5, total_distance=1280) + self.tex = tex + self.hori_move = Animation.create_move(60000 / self.bpm * 5, total_distance=tex.screen_width) 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 * tex.screen_scale, reverse_delay=0) self.vert_move.start() self.index = random.randint(0, len([item for item in tex.textures[self.name] if item[0].isdigit()])-1) tex_list = tex.textures[self.name][str(self.index)].texture @@ -47,9 +48,9 @@ class BaseChibi: textures = [((duration / len(self.keyframes))*i, (duration / len(self.keyframes))*(i+1), index) for i, index in enumerate(self.keyframes)] self.texture_change = Animation.create_texture_change(duration, textures=textures) self.texture_change.start() - 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=self.tex.screen_width) 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 * self.tex.screen_scale, reverse_delay=0) self.vert_move.start() def draw(self, tex: TextureWrapper): @@ -62,9 +63,9 @@ class ChibiBad(BaseChibi): self.index = random.randint(0, 2) self.keyframes = [3, 4] duration = (60000 / self.bpm) / 2 - self.hori_move = Animation.create_move(duration * 10, total_distance=1280) + self.hori_move = Animation.create_move(duration * 10, total_distance=1280 * self.tex.screen_width) self.hori_move.start() - self.vert_move = Animation.create_move(duration, total_distance=50, reverse_delay=0) + self.vert_move = Animation.create_move(duration, total_distance=50 * self.tex.screen_scale, reverse_delay=0) self.vert_move.start() self.fade_in = Animation.create_fade(duration, initial_opacity=0.0, final_opacity=1.0) self.fade_in.start() @@ -95,7 +96,7 @@ class Chibi0(BaseChibi): class Chibi2(BaseChibi): def __init__(self, index: int, bpm: float, tex: TextureWrapper, is_2p: bool): super().__init__(index, bpm, tex, is_2p) - self.rotate = Animation.create_move(60000 / self.bpm, total_distance=360) + self.rotate = Animation.create_move(60000 / self.bpm, total_distance=self.tex.screen_height//2) self.rotate.start() def update(self, current_time_ms: float, bpm: float): diff --git a/libs/bg_objects/dancer.py b/libs/bg_objects/dancer.py index 014096d..8f7b47e 100644 --- a/libs/bg_objects/dancer.py +++ b/libs/bg_objects/dancer.py @@ -165,7 +165,7 @@ class BaseDancerGroup(): dancer.update(current_time_ms, bpm) def draw(self, tex: TextureWrapper): - total_width = 1280 + total_width = tex.screen_width num_dancers = len(self.active_dancers) first_dancer = next((dancer for dancer in self.active_dancers if dancer is not None), None) diff --git a/libs/bg_objects/renda.py b/libs/bg_objects/renda.py index 0d85e3d..0c827b8 100644 --- a/libs/bg_objects/renda.py +++ b/libs/bg_objects/renda.py @@ -15,7 +15,7 @@ class Renda: class BaseRenda: def __init__(self, tex: TextureWrapper, index: int): self.name = 'renda_' + str(index) - self.hori_move = Animation.create_move(1500, total_distance=1280) + self.hori_move = Animation.create_move(1500, total_distance=tex.screen_width) self.hori_move.start() def update(self, current_time_ms: float): @@ -24,13 +24,13 @@ class BaseRenda: class Renda0(BaseRenda): def __init__(self, tex: TextureWrapper, index: int): super().__init__(tex, index) - self.vert_move = Animation.create_move(1500, total_distance=800) + self.vert_move = Animation.create_move(1500, total_distance=tex.screen_height + (80 * tex.screen_scale)) self.vert_move.start() tex_list = tex.textures['renda'][self.name].texture num_of_rendas = len(tex_list) if isinstance(tex_list, list) else 0 self.frame = random.randint(0, num_of_rendas - 1) - self.x = random.randint(0, 500) - self.y = random.randint(0, 20) + self.x = random.randint(0, int(500 * tex.screen_scale)) + self.y = random.randint(0, int(20 * tex.screen_scale)) def update(self, current_time_ms: float): super().update(current_time_ms) @@ -43,8 +43,8 @@ class Renda1(BaseRenda): def __init__(self, tex: TextureWrapper, index: int): super().__init__(tex, index) self.frame = random.randint(0, 5) - self.y = random.randint(0, 200) - self.rotate = Animation.create_move(800, total_distance=360) + self.y = random.randint(0, int(200 * tex.screen_scale)) + self.rotate = Animation.create_move(800, total_distance=tex.screen_height//2) self.rotate.start() self.origin = ray.Vector2(64, 64) @@ -60,10 +60,10 @@ class Renda1(BaseRenda): class Renda2(BaseRenda): def __init__(self, tex: TextureWrapper, index: int): super().__init__(tex, index) - self.vert_move = Animation.create_move(1500, total_distance=800) + self.vert_move = Animation.create_move(1500, total_distance=tex.screen_height + (80 * tex.screen_scale)) self.vert_move.start() - self.x = random.randint(0, 500) - self.y = random.randint(0, 20) + self.x = random.randint(0, int(500 * tex.screen_scale)) + self.y = random.randint(0, int(20 * tex.screen_scale)) def update(self, current_time_ms: float): super().update(current_time_ms) diff --git a/libs/config.py b/libs/config.py new file mode 100644 index 0000000..28d8b34 --- /dev/null +++ b/libs/config.py @@ -0,0 +1,151 @@ +from pathlib import Path +import tomlkit +import json +from typing import TypedDict + +import pyray as ray + +class GeneralConfig(TypedDict): + fps_counter: bool + audio_offset: int + visual_offset: int + language: str + hard_judge: int + touch_enabled: bool + timer_frozen: bool + judge_counter: bool + nijiiro_notes: bool + log_level: int + fake_online: bool + +class NameplateConfig(TypedDict): + name: str + title: str + title_bg: int + dan: int + gold: bool + rainbow: bool + +class PathsConfig(TypedDict): + tja_path: list[Path] + video_path: list[Path] + graphics_path: Path + +class KeysConfig(TypedDict): + exit_key: int + fullscreen_key: int + borderless_key: int + pause_key: int + back_key: int + restart_key: int + +class Keys1PConfig(TypedDict): + left_kat: list[int] + left_don: list[int] + right_don: list[int] + right_kat: list[int] + +class Keys2PConfig(TypedDict): + left_kat: list[int] + left_don: list[int] + right_don: list[int] + right_kat: list[int] + +class GamepadConfig(TypedDict): + left_kat: list[int] + left_don: list[int] + right_don: list[int] + right_kat: list[int] + +class AudioConfig(TypedDict): + device_type: int + sample_rate: int + buffer_size: int + +class VolumeConfig(TypedDict): + sound: float + music: float + voice: float + hitsound: float + attract_mode: float + +class VideoConfig(TypedDict): + fullscreen: bool + borderless: bool + target_fps: int + vsync: bool + +class Config(TypedDict): + general: GeneralConfig + nameplate_1p: NameplateConfig + nameplate_2p: NameplateConfig + paths: PathsConfig + keys: KeysConfig + keys_1p: Keys1PConfig + keys_2p: Keys2PConfig + gamepad: GamepadConfig + audio: AudioConfig + volume: VolumeConfig + video: VideoConfig + +def get_key_string(key_code: int) -> str: + """Convert a key code back to its string representation""" + if 65 <= key_code <= 90: + return chr(key_code) + if 48 <= key_code <= 57: + return chr(key_code) + + for attr_name in dir(ray): + if attr_name.startswith('KEY_'): + if getattr(ray, attr_name) == key_code: + return attr_name[4:].lower() + + raise ValueError(f"Unknown key code: {key_code}") + +def get_key_code(key: str) -> int: + if len(key) == 1 and key.isalnum(): + return ord(key.upper()) + else: + key_code = getattr(ray, f"KEY_{key.upper()}", None) + if key_code is None: + raise ValueError(f"Invalid key: {key}") + return key_code + +def get_config() -> Config: + """Get the configuration from the TOML file""" + 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: + config_file = tomlkit.load(f) + + config: Config = json.loads(json.dumps(config_file)) + for key in config['keys']: + config['keys'][key] = get_key_code(config['keys'][key]) + for key in config['keys_1p']: + bindings = config['keys_1p'][key] + for i, bind in enumerate(bindings): + config['keys_1p'][key][i] = get_key_code(bind) + for key in config['keys_2p']: + bindings = config['keys_2p'][key] + for i, bind in enumerate(bindings): + config['keys_2p'][key][i] = get_key_code(bind) + return config + +def save_config(config: Config) -> None: + """Save the configuration to the TOML file""" + config_to_save = json.loads(json.dumps(config)) + + for key in config_to_save['keys']: + config_to_save['keys'][key] = get_key_string(config_to_save['keys'][key]) + for key in config_to_save['keys_1p']: + bindings = config_to_save['keys_1p'][key] + for i, bind in enumerate(bindings): + config_to_save['keys_1p'][key][i] = get_key_string(bind) + for key in config_to_save['keys_2p']: + bindings = config_to_save['keys_2p'][key] + for i, bind in enumerate(bindings): + config_to_save['keys_2p'][key][i] = get_key_string(bind) + + config_path = Path('dev-config.toml') if Path('dev-config.toml').exists() else Path('config.toml') + with open(config_path, "w", encoding="utf-8") as f: + tomlkit.dump(config_to_save, f) diff --git a/libs/file_navigator.py b/libs/file_navigator.py index 20f61d2..cc899ab 100644 --- a/libs/file_navigator.py +++ b/libs/file_navigator.py @@ -8,13 +8,13 @@ from libs.audio import audio from libs.animation import Animation, MoveAnimation from libs.global_data import Crown, Difficulty from libs.tja import TJAParser, test_encodings -from libs.texture import SCREEN_SCALE, SCREEN_WIDTH, tex +from libs.texture import tex from libs.utils import OutlinedText, get_current_ms, global_data from datetime import datetime, timedelta import sqlite3 import pyray as ray -BOX_CENTER = 594 * SCREEN_SCALE +BOX_CENTER = 594 * tex.screen_scale logger = logging.getLogger(__name__) @@ -37,6 +37,7 @@ class BaseBox(): } BACK_INDEX = 17 DEFAULT_INDEX = 9 + DIFFICULTY_SORT_INDEX = 14 """Base class for all box types in the song select screen.""" def __init__(self, name: str, texture_index: int): self.text_name = name @@ -44,7 +45,7 @@ class BaseBox(): self.position = float('inf') self.start_position: float = -1 self.target_position: float = -1 - self.open_anim = Animation.create_move(133, total_distance=150*SCREEN_SCALE, delay=83.33) + self.open_anim = Animation.create_move(133, total_distance=150*tex.screen_scale, delay=83.33) self.open_fade = Animation.create_fade(200, initial_opacity=0, final_opacity=1.0) self.move = None self.is_open = False @@ -52,7 +53,7 @@ class BaseBox(): self.wait = 0 def load_text(self): - self.name = OutlinedText(self.text_name, 40, ray.WHITE, outline_thickness=5, vertical=True) + self.name = OutlinedText(self.text_name, tex.skin_config["song_box_name"].font_size, ray.WHITE, outline_thickness=5, vertical=True) def move_box(self, current_time: float): if self.position != self.target_position and self.move is None: @@ -60,12 +61,12 @@ class BaseBox(): direction = 1 else: direction = -1 - if abs(self.target_position - self.position) > 250: + if abs(self.target_position - self.position) > 250 * tex.screen_scale: direction *= -1 - self.move = Animation.create_move(83.3, total_distance=100 * direction * SCREEN_SCALE, ease_out='cubic') + self.move = Animation.create_move(83.3, total_distance=100 * direction * tex.screen_scale, ease_out='cubic') self.move.start() if self.is_open or self.target_position == BOX_CENTER: - self.move.total_distance = 250 * direction + self.move.total_distance = int(250 * direction * tex.screen_scale) self.start_position = self.position if self.move is not None: self.move.update(current_time) @@ -81,12 +82,12 @@ class BaseBox(): def _draw_closed(self, x: float, y: float): tex.draw_texture('box', 'folder_texture_left', frame=self.texture_index, x=x) - offset = 1 if self.texture_index == 3 or self.texture_index >= 9 and self.texture_index not in {10,11,12} else 0 - tex.draw_texture('box', 'folder_texture', frame=self.texture_index, x=x, x2=32, y=offset) + offset = 1 * tex.screen_scale if self.texture_index == 3 or self.texture_index >= 9 and self.texture_index not in {10,11,12} else 0 + tex.draw_texture('box', 'folder_texture', frame=self.texture_index, x=x, x2=tex.skin_config["song_box_bg"].width, y=offset) tex.draw_texture('box', 'folder_texture_right', frame=self.texture_index, x=x) - if self.texture_index == SongBox.DEFAULT_INDEX: + if self.texture_index == BaseBox.DEFAULT_INDEX: tex.draw_texture('box', 'genre_overlay', x=x, y=y) - elif self.texture_index == 14: + elif self.texture_index == BaseBox.DIFFICULTY_SORT_INDEX: tex.draw_texture('box', 'diff_overlay', x=x, y=y) def _draw_open(self, x: float, y: float, fade_override: Optional[float], is_ura: bool): @@ -200,7 +201,7 @@ class SongBox(BaseBox): def _draw_closed(self, x: float, y: float): super()._draw_closed(x, y) - self.name.draw(outline_color=SongBox.OUTLINE_MAP.get(self.name_texture_index, ray.Color(101, 0, 82, 255)), x=x + 47 - int(self.name.texture.width / 2), y=y+35, y2=min(self.name.texture.height, 417)-self.name.texture.height) + self.name.draw(outline_color=SongBox.OUTLINE_MAP.get(self.name_texture_index, ray.Color(101, 0, 82, 255)), x=x + tex.skin_config["song_box_name"].x - int(self.name.texture.width / 2), y=y+tex.skin_config["song_box_name"].y, y2=min(self.name.texture.height, tex.skin_config["song_box_name"].height)-self.name.texture.height) if self.tja.ex_data.new: tex.draw_texture('yellow_box', 'ex_data_new_song_balloon', x=x, y=y) @@ -230,18 +231,14 @@ class FolderBox(BaseBox): super().__init__(name, texture_index) self.box_texture_path = Path(box_texture) if box_texture else None self.is_back = self.texture_index == SongBox.BACK_INDEX - if self.is_back: - for i in range(1, SongBox.BACK_INDEX-1): - if audio.is_sound_playing(f'genre_voice_{i}'): - audio.stop_sound(f'genre_voice_{i}') self.tja_count = tja_count self.crown = dict() def load_text(self): super().load_text() - self.hori_name = OutlinedText(self.text_name, 40, ray.WHITE, outline_thickness=5) + self.hori_name = OutlinedText(self.text_name, tex.skin_config['song_hori_name'].font_size, ray.WHITE, outline_thickness=5) self.box_texture = ray.load_texture(str(self.box_texture_path)) if self.box_texture_path and self.box_texture_path.exists() else None - self.tja_count_text = OutlinedText(str(self.tja_count), 35, ray.WHITE, outline_thickness=5) + self.tja_count_text = OutlinedText(str(self.tja_count), tex.skin_config['song_tja_count'].font_size, ray.WHITE, outline_thickness=5) self.text_loaded = True def update(self, current_time: float, is_diff_select: bool): @@ -261,10 +258,10 @@ class FolderBox(BaseBox): def _draw_closed(self, x: float, y: float): super()._draw_closed(x, y) - offset = 1 if self.texture_index == 3 or self.texture_index >= 9 and self.texture_index not in {10,11,12} else 0 - tex.draw_texture('box', 'folder_clip', frame=self.texture_index, x=x - (1 - offset), y=y) + offset = 1 * tex.screen_scale if self.texture_index == 3 or self.texture_index >= 9 and self.texture_index not in {10,11,12} else 0 + tex.draw_texture('box', 'folder_clip', frame=self.texture_index, x=x - ((1 * tex.screen_scale) - offset), y=y) - self.name.draw(outline_color=SongBox.OUTLINE_MAP.get(self.texture_index, ray.Color(101, 0, 82, 255)), x=x + 47 - int(self.name.texture.width / 2), y=y+35, y2=min(self.name.texture.height, 417)-self.name.texture.height) + self.name.draw(outline_color=SongBox.OUTLINE_MAP.get(self.texture_index, ray.Color(101, 0, 82, 255)), x=x + tex.skin_config["song_box_name"].x - int(self.name.texture.width / 2), y=y+tex.skin_config["song_box_name"].y, y2=min(self.name.texture.height, tex.skin_config["song_box_name"].height)-self.name.texture.height) if self.crown: #Folder lamp highest_crown = max(self.crown) @@ -279,37 +276,37 @@ class FolderBox(BaseBox): color = ray.WHITE if fade_override is not None: color = ray.fade(ray.WHITE, fade_override) - if not self.is_back and self.open_anim.attribute >= 100: + if not self.is_back and self.open_anim.attribute >= (100 * tex.screen_scale): tex.draw_texture('box', 'folder_top_edge', x=x, y=y - self.open_anim.attribute, color=color, mirror='horizontal', frame=self.texture_index) tex.draw_texture('box', 'folder_top', x=x, y=y - self.open_anim.attribute, color=color, frame=self.texture_index) - tex.draw_texture('box', 'folder_top_edge', x=x+268, y=y - self.open_anim.attribute, color=color, frame=self.texture_index) - dest_width = min(300, self.hori_name.texture.width) - self.hori_name.draw(outline_color=ray.BLACK, x=(x + 48) - (dest_width//2), y=y + 107 - self.open_anim.attribute, x2=dest_width-self.hori_name.texture.width, color=color) + tex.draw_texture('box', 'folder_top_edge', x=x+tex.skin_config["song_folder_top"].x, y=y - self.open_anim.attribute, color=color, frame=self.texture_index) + dest_width = min(tex.skin_config["song_hori_name"].width, self.hori_name.texture.width) + self.hori_name.draw(outline_color=ray.BLACK, x=(x + tex.skin_config["song_hori_name"].x) - (dest_width//2), y=y + tex.skin_config["song_hori_name"].y - self.open_anim.attribute, x2=dest_width-self.hori_name.texture.width, color=color) tex.draw_texture('box', 'folder_texture_left', frame=self.texture_index, x=x - self.open_anim.attribute) - offset = 1 if self.texture_index == 3 or self.texture_index >= 9 and self.texture_index not in {10,11,12} else 0 - tex.draw_texture('box', 'folder_texture', frame=self.texture_index, x=x - self.open_anim.attribute, y=offset, x2=(self.open_anim.attribute*2)+32) + offset = 1 * tex.screen_scale if self.texture_index == 3 or self.texture_index >= 9 and self.texture_index not in {10,11,12} else 0 + tex.draw_texture('box', 'folder_texture', frame=self.texture_index, x=x - self.open_anim.attribute, y=offset, x2=(self.open_anim.attribute*2)+tex.skin_config["song_box_bg"].width) tex.draw_texture('box', 'folder_texture_right', frame=self.texture_index, x=x + self.open_anim.attribute) - if self.texture_index == SongBox.DEFAULT_INDEX: + if self.texture_index == BaseBox.DEFAULT_INDEX: tex.draw_texture('box', 'genre_overlay_large', x=x, y=y, color=color) - elif self.texture_index == 14: + elif self.texture_index == BaseBox.DIFFICULTY_SORT_INDEX: tex.draw_texture('box', 'diff_overlay_large', x=x, y=y, color=color) color = ray.WHITE if fade_override is not None: color = ray.fade(ray.WHITE, fade_override) - if self.texture_index != 14: + if self.texture_index != BaseBox.DIFFICULTY_SORT_INDEX: tex.draw_texture('yellow_box', 'song_count_back', color=color, fade=0.5) tex.draw_texture('yellow_box', 'song_count_num', color=color) tex.draw_texture('yellow_box', 'song_count_songs', color=color) - dest_width = min(124, self.tja_count_text.texture.width) - self.tja_count_text.draw(outline_color=ray.BLACK, x=560 - (dest_width//2), y=126, x2=dest_width-self.tja_count_text.texture.width, color=color) + dest_width = min(tex.skin_config["song_tja_count"].width, self.tja_count_text.texture.width) + self.tja_count_text.draw(outline_color=ray.BLACK, x=tex.skin_config["song_tja_count"].x - (dest_width//2), y=tex.skin_config["song_tja_count"].y, x2=dest_width-self.tja_count_text.texture.width, color=color) if self.texture_index != SongBox.DEFAULT_INDEX: tex.draw_texture('box', 'folder_graphic', color=color, frame=self.texture_index) tex.draw_texture('box', 'folder_text', color=color, frame=self.texture_index) elif self.box_texture is not None: - ray.draw_texture(self.box_texture, int((x+48) - (self.box_texture.width//2)), int((y+240) - (self.box_texture.height//2)), color) + ray.draw_texture(self.box_texture, int((x+tex.skin_config["box_texture"].x) - (self.box_texture.width//2)), int((y+tex.skin_config["box_texture"].y) - (self.box_texture.height//2)), color) class YellowBox: """A song box when it is opened.""" @@ -319,7 +316,7 @@ class YellowBox: self.tja = tja if self.tja is not None: subtitle_text = self.tja.metadata.subtitle.get(global_data.config['general']['language'], '') - font_size = 30 if len(subtitle_text) < 30 else 20 + font_size = tex.skin_config["yb_subtitle"].font_size if len(subtitle_text) < 30 else tex.skin_config["yb_subtitle"].font_size - int(10 * tex.screen_scale) self.subtitle = OutlinedText(subtitle_text, font_size, ray.WHITE, outline_thickness=5, vertical=True) self.is_dan = is_dan self.subtitle = None @@ -401,16 +398,17 @@ class YellowBox: def _draw_tja_data(self, song_box: SongBox, color: ray.Color, fade: float): if not self.tja: return + offset = tex.skin_config['yb_diff_offset'].x for diff in self.tja.metadata.course_data: if diff >= Difficulty.URA: continue - elif diff in song_box.scores and song_box.scores[diff] is not None and song_box.scores[diff][4] is not None and song_box.scores[diff][4] == Crown.DFC: - tex.draw_texture('yellow_box', 's_crown_dfc', x=(diff*60), color=color) + if diff in song_box.scores and song_box.scores[diff] is not None and song_box.scores[diff][4] is not None and song_box.scores[diff][4] == Crown.DFC: + tex.draw_texture('yellow_box', 's_crown_dfc', x=(diff*offset), color=color) elif diff in song_box.scores and song_box.scores[diff] is not None and song_box.scores[diff][4] is not None and song_box.scores[diff][4] == Crown.FC: - tex.draw_texture('yellow_box', 's_crown_fc', x=(diff*60), color=color) + tex.draw_texture('yellow_box', 's_crown_fc', x=(diff*offset), color=color) elif diff in song_box.scores and song_box.scores[diff] is not None and song_box.scores[diff][4] is not None and song_box.scores[diff][4] >= Crown.CLEAR: - tex.draw_texture('yellow_box', 's_crown_clear', x=(diff*60), color=color) - tex.draw_texture('yellow_box', 's_crown_outline', x=(diff*60), fade=min(fade, 0.25)) + tex.draw_texture('yellow_box', 's_crown_clear', x=(diff*offset), color=color) + tex.draw_texture('yellow_box', 's_crown_outline', x=(diff*offset), fade=min(fade, 0.25)) if self.tja.ex_data.new_audio: tex.draw_texture('yellow_box', 'ex_data_new_audio', color=color) @@ -424,17 +422,17 @@ class YellowBox: tex.draw_texture('yellow_box', f'favorite_{global_data.player_num}p', color=color) for i in range(4): - tex.draw_texture('yellow_box', 'difficulty_bar', frame=i, x=(i*60), color=color) + tex.draw_texture('yellow_box', 'difficulty_bar', frame=i, x=(i*offset), color=color) if i not in self.tja.metadata.course_data: - tex.draw_texture('yellow_box', 'difficulty_bar_shadow', frame=i, x=(i*60), fade=min(fade, 0.25)) + tex.draw_texture('yellow_box', 'difficulty_bar_shadow', frame=i, x=(i*offset), fade=min(fade, 0.25)) for diff in self.tja.metadata.course_data: if diff >= Difficulty.URA: continue for j in range(self.tja.metadata.course_data[diff].level): - tex.draw_texture('yellow_box', 'star', x=(diff*60), y=(j*-17), color=color) + tex.draw_texture('yellow_box', 'star', x=(diff*offset), y=(j*tex.skin_config['yb_diff_offset'].y), color=color) if self.tja.metadata.course_data[diff].is_branching and (get_current_ms() // 1000) % 2 == 0: - tex.draw_texture('yellow_box', 'branch_indicator', x=(diff*60), color=color) + tex.draw_texture('yellow_box', 'branch_indicator', x=(diff*offset), color=color) def _draw_tja_data_diff(self, is_ura: bool, song_box: SongBox): if not self.tja: @@ -443,37 +441,39 @@ class YellowBox: tex.draw_texture('diff_select', 'option', fade=self.fade_in.attribute) tex.draw_texture('diff_select', 'neiro', fade=self.fade_in.attribute) + offset_x = tex.skin_config['yb_diff_offset_diff_select'].x + offset_y = tex.skin_config['yb_diff_offset_diff_select'].y for diff in self.tja.metadata.course_data: if diff >= Difficulty.URA: continue elif diff in song_box.scores and song_box.scores[diff] is not None and ((song_box.scores[diff][4] is not None and song_box.scores[diff][4] == 2 and song_box.scores[diff][2] == 0) or (song_box.scores[diff][2] == 0 and song_box.scores[diff][3] == 0)): - tex.draw_texture('yellow_box', 's_crown_dfc', x=(diff*115)+8, y=-120, fade=self.fade_in.attribute) + tex.draw_texture('yellow_box', 's_crown_dfc', x=(diff*offset_x)+tex.skin_config['yb_diff_offset_crown'].x, y=offset_y, fade=self.fade_in.attribute) elif diff in song_box.scores and song_box.scores[diff] is not None and ((song_box.scores[diff][4] is not None and song_box.scores[diff][4] == 2) or (song_box.scores[diff][3] == 0)): - tex.draw_texture('yellow_box', 's_crown_fc', x=(diff*115)+8, y=-120, fade=self.fade_in.attribute) + tex.draw_texture('yellow_box', 's_crown_fc', x=(diff*offset_x)+tex.skin_config['yb_diff_offset_crown'].x, y=offset_y, fade=self.fade_in.attribute) elif diff in song_box.scores and song_box.scores[diff] is not None and song_box.scores[diff][4] is not None and song_box.scores[diff][4] >= 1: - tex.draw_texture('yellow_box', 's_crown_clear', x=(diff*115)+8, y=-120, fade=self.fade_in.attribute) - tex.draw_texture('yellow_box', 's_crown_outline', x=(diff*115)+8, y=-120, fade=min(self.fade_in.attribute, 0.25)) + tex.draw_texture('yellow_box', 's_crown_clear', x=(diff*offset_x)+tex.skin_config['yb_diff_offset_crown'].x, y=offset_y, fade=self.fade_in.attribute) + tex.draw_texture('yellow_box', 's_crown_outline', x=(diff*offset_x)+tex.skin_config['yb_diff_offset_crown'].x, y=offset_y, fade=min(self.fade_in.attribute, 0.25)) for i in range(4): if i == Difficulty.ONI and is_ura: - tex.draw_texture('diff_select', 'diff_tower', frame=4, x=(i*115), fade=self.fade_in.attribute) + tex.draw_texture('diff_select', 'diff_tower', frame=4, x=(i*offset_x), fade=self.fade_in.attribute) tex.draw_texture('diff_select', 'ura_oni_plate', fade=self.fade_in.attribute) else: - tex.draw_texture('diff_select', 'diff_tower', frame=i, x=(i*115), fade=self.fade_in.attribute) + tex.draw_texture('diff_select', 'diff_tower', frame=i, x=(i*offset_x), fade=self.fade_in.attribute) if i not in self.tja.metadata.course_data: - tex.draw_texture('diff_select', 'diff_tower_shadow', frame=i, x=(i*115), fade=min(self.fade_in.attribute, 0.25)) + tex.draw_texture('diff_select', 'diff_tower_shadow', frame=i, x=(i*offset_x), fade=min(self.fade_in.attribute, 0.25)) for course in self.tja.metadata.course_data: if (course == Difficulty.URA and not is_ura) or (course == Difficulty.ONI and is_ura): continue for j in range(self.tja.metadata.course_data[course].level): - tex.draw_texture('yellow_box', 'star_ura', x=min(course, Difficulty.ONI)*115, y=(j*-20), fade=self.fade_in.attribute) + tex.draw_texture('yellow_box', 'star_ura', x=min(course, Difficulty.ONI)*offset_x, y=(j*tex.skin_config["yb_diff_offset_crown"].y), fade=self.fade_in.attribute) if self.tja.metadata.course_data[course].is_branching and (get_current_ms() // 1000) % 2 == 0: if course == Difficulty.URA: name = 'branch_indicator_ura' else: name = 'branch_indicator_diff' - tex.draw_texture('yellow_box', name, x=min(course, Difficulty.ONI)*115, fade=self.fade_in.attribute) + tex.draw_texture('yellow_box', name, x=min(course, Difficulty.ONI)*offset_x, fade=self.fade_in.attribute) def _draw_text(self, song_box, name: OutlinedText): if not isinstance(self.right_out, MoveAnimation): @@ -487,11 +487,11 @@ class YellowBox: tex.draw_texture('box', 'back_text_highlight', x=x) else: texture = name.texture - name.draw(outline_color=ray.BLACK, x=x + 30, y=35 + self.top_y_out.attribute, y2=min(texture.height, 417)-texture.height, color=ray.WHITE) + name.draw(outline_color=ray.BLACK, x=x + tex.skin_config["yb_name"].x, y=tex.skin_config["yb_name"].y + self.top_y_out.attribute, y2=min(texture.height, tex.skin_config["yb_name"].height)-texture.height, color=ray.WHITE) if self.subtitle is not None: texture = self.subtitle.texture - y = self.bottom_y - min(texture.height, 410) + 10 + self.top_y_out.attribute - self.top_y_out.start_position - self.subtitle.draw(outline_color=ray.BLACK, x=x-15, y=y, y2=min(texture.height, 410)-texture.height) + y = self.bottom_y - min(texture.height, tex.skin_config["yb_subtitle"].height) + tex.skin_config["yb_subtitle"].y + self.top_y_out.attribute - self.top_y_out.start_position + self.subtitle.draw(outline_color=ray.BLACK, x=x+tex.skin_config["yb_subtitle"].x, y=y, y2=min(texture.height, tex.skin_config["yb_subtitle"].height)-texture.height) def _draw_yellow_box(self): tex.draw_texture('yellow_box', 'yellow_box_bottom_right', x=self.right_x) @@ -541,12 +541,12 @@ class DanBox(BaseBox): def load_text(self): super().load_text() - self.hori_name = OutlinedText(self.text_name, 40, ray.WHITE) + self.hori_name = OutlinedText(self.text_name, tex.skin_config["dan_title"].font_size, ray.WHITE) for song, genre, difficulty, level in self.songs: title = song.metadata.title.get(global_data.config["general"]["language"], song.metadata.title["en"]) subtitle = song.metadata.subtitle.get(global_data.config["general"]["language"], "") - title_text = OutlinedText(title, 40, ray.WHITE, vertical=True) - font_size = 30 if len(subtitle) < 30 else 20 + title_text = OutlinedText(title, tex.skin_config["dan_title"].font_size, ray.WHITE, vertical=True) + font_size = tex.skin_config["dan_subtitle"].font_size if len(subtitle) < 30 else tex.skin_config["dan_subtitle"].font_size - int(10 * tex.screen_scale) subtitle_text = OutlinedText(subtitle, font_size, ray.WHITE, vertical=True) self.song_text.append((title_text, subtitle_text)) self.text_loaded = True @@ -575,28 +575,29 @@ class DanBox(BaseBox): tex.draw_texture('yellow_box', 'exam_box_center') tex.draw_texture('yellow_box', 'exam_header') + offset = tex.skin_config["exam_box_offset"].y for i, exam in enumerate(self.exams): - tex.draw_texture('yellow_box', 'judge_box', y=(i*83)) - tex.draw_texture('yellow_box', 'exam_' + self.exams[i].type, y=(i*83)) - counter = str(self.exams[i].red) - margin = 20 - if self.exams[i].type == 'gauge': - tex.draw_texture('yellow_box', 'exam_percent', y=(i*83)) - offset = -8 + tex.draw_texture('yellow_box', 'judge_box', y=(i*offset)) + tex.draw_texture('yellow_box', 'exam_' + exam.type, y=(i*offset)) + counter = str(exam.red) + margin = tex.skin_config["exam_counter_margin"].x + if exam.type == 'gauge': + tex.draw_texture('yellow_box', 'exam_percent', y=(i*offset)) + x_offset = tex.skin_config["exam_gauge_offset"].x else: - offset = 0 + x_offset = 0 for j in range(len(counter)): - tex.draw_texture('yellow_box', 'judge_num', frame=int(counter[j]), x=offset-(len(counter) - j) * margin, y=(i*83)) + tex.draw_texture('yellow_box', 'judge_num', frame=int(counter[j]), x=x_offset-(len(counter) - j) * margin, y=(i*offset)) - if self.exams[i].range == 'more': - tex.draw_texture('yellow_box', 'exam_more', x=(offset*-1.7), y=(i*83)) - elif self.exams[i].range == 'less': - tex.draw_texture('yellow_box', 'exam_less', x=(offset*-1.7), y=(i*83)) + if exam.range == 'more': + tex.draw_texture('yellow_box', 'exam_more', x=(x_offset*-1.7), y=(i*offset)) + elif exam.range == 'less': + tex.draw_texture('yellow_box', 'exam_less', x=(x_offset*-1.7), y=(i*offset)) def _draw_closed(self, x: float, y: float): tex.draw_texture('box', 'folder', frame=self.texture_index, x=x) if self.name is not None: - self.name.draw(outline_color=ray.BLACK, x=x + 47 - int(self.name.texture.width / 2), y=y+35, y2=min(self.name.texture.height, 417)-self.name.texture.height) + self.name.draw(outline_color=ray.BLACK, x=x + tex.skin_config["song_box_name"].x - int(self.name.texture.width / 2), y=y+tex.skin_config["song_box_name"].height, y2=min(self.name.texture.height, tex.skin_config["song_box_name"].height)-self.name.texture.height) def _draw_open(self, x: float, y: float, fade_override: Optional[float], is_ura: bool): if fade_override is not None: @@ -607,29 +608,32 @@ class DanBox(BaseBox): self.yellow_box.draw(None, None, False, self.name) for i, song in enumerate(self.song_text): title, subtitle = song - x = i * 140 + x = i * tex.skin_config["dan_yellow_box_offset"].x tex.draw_texture('yellow_box', 'genre_banner', x=x, frame=self.songs[i][1], fade=fade) tex.draw_texture('yellow_box', 'difficulty', x=x, frame=self.songs[i][2], fade=fade) tex.draw_texture('yellow_box', 'difficulty_x', x=x, fade=fade) tex.draw_texture('yellow_box', 'difficulty_star', x=x, fade=fade) level = self.songs[i][0].metadata.course_data[self.songs[i][2]].level counter = str(level) - total_width = len(counter) * 10 + margin = tex.skin_config["dan_level_counter_margin"].x + total_width = len(counter) * margin for i in range(len(counter)): - tex.draw_texture('yellow_box', 'difficulty_num', frame=int(counter[i]), x=x-(total_width // 2) + (i * 10), fade=fade) + tex.draw_texture('yellow_box', 'difficulty_num', frame=int(counter[i]), x=x-(total_width // 2) + (i * margin), fade=fade) - title.draw(outline_color=ray.BLACK, x=665+x, y=127, y2=min(title.texture.height, 400)-title.texture.height, fade=fade) - subtitle.draw(outline_color=ray.BLACK, x=620+x, y=525-min(subtitle.texture.height, 400), y2=min(subtitle.texture.height, 400)-subtitle.texture.height, fade=fade) + title_data = tex.skin_config["dan_title"] + subtitle_data = tex.skin_config["dan_subtitle"] + title.draw(outline_color=ray.BLACK, x=title_data.x+x, y=title_data.y, y2=min(title.texture.height, title_data.height)-title.texture.height, fade=fade) + subtitle.draw(outline_color=ray.BLACK, x=subtitle_data.x+x, y=subtitle_data.y-min(subtitle.texture.height, subtitle_data.height), y2=min(subtitle.texture.height, subtitle_data.height)-subtitle.texture.height, fade=fade) tex.draw_texture('yellow_box', 'total_notes_bg', fade=fade) tex.draw_texture('yellow_box', 'total_notes', fade=fade) counter = str(self.total_notes) for i in range(len(counter)): - tex.draw_texture('yellow_box', 'total_notes_counter', frame=int(counter[i]), x=(i * 25), fade=fade) + tex.draw_texture('yellow_box', 'total_notes_counter', frame=int(counter[i]), x=(i * tex.skin_config["total_notes_counter_margin"].x), fade=fade) tex.draw_texture('yellow_box', 'frame', frame=self.texture_index, fade=fade) if self.hori_name is not None: - self.hori_name.draw(outline_color=ray.BLACK, x=434 - (self.hori_name.texture.width//2), y=84, x2=min(self.hori_name.texture.width, 275)-self.hori_name.texture.width, fade=fade) + self.hori_name.draw(outline_color=ray.BLACK, x=tex.skin_config["dan_hori_name"].x - (self.hori_name.texture.width//2), y=tex.skin_config["dan_hori_name"].y, x2=min(self.hori_name.texture.width, tex.skin_config["dan_hori_name"].width)-self.hori_name.texture.width, fade=fade) self._draw_exam_box() @@ -649,42 +653,42 @@ class GenreBG: self.end_position = self.end_box.position self.fade_in.update(current_ms) def draw(self, y): - offset = -150 if self.start_box.is_open else 0 + offset = (tex.skin_config["genre_bg_offset"].x * -1) if self.start_box.is_open else 0 tex.draw_texture('box', 'folder_background_edge', frame=self.end_box.texture_index, x=self.start_position+offset, y=y, mirror="horizontal", fade=self.fade_in.attribute) - extra_distance = 155 if self.end_box.is_open or (self.start_box.is_open and 844 <= self.end_position <= 1144) else 0 - if self.start_position >= -56 and self.end_position < self.start_position: - x2 = self.start_position + 1400 + extra_distance = tex.skin_config["genre_bg_extra_distance"].x if self.end_box.is_open or (self.start_box.is_open and (844 * tex.screen_scale) <= self.end_position <= (1144 * tex.screen_scale)) else 0 + if self.start_position >= tex.skin_config["genre_bg_left_max"].x and self.end_position < self.start_position: + x2 = self.start_position + tex.skin_config["genre_bg_offset_2"].x x = self.start_position+offset - elif (self.start_position <= -56) and (self.end_position < self.start_position): + elif (self.start_position <= tex.skin_config["genre_bg_left_max"].x) and (self.end_position < self.start_position): x = 0 - x2 = 1280 + x2 = tex.screen_width else: - x2 = abs(self.end_position) - self.start_position + extra_distance + 57 + x2 = abs(self.end_position) - self.start_position + extra_distance + (-1 * tex.skin_config["genre_bg_left_max"].x + (1 * tex.screen_scale)) x = self.start_position+offset tex.draw_texture('box', 'folder_background', x=x, y=y, x2=x2, frame=self.end_box.texture_index) - if self.end_position < self.start_position and self.end_position >= -56: - x2 = min(self.end_position+75, 1280) + extra_distance - tex.draw_texture('box', 'folder_background', x=-18, y=y, x2=x2, frame=self.end_box.texture_index) + if self.end_position < self.start_position and self.end_position >= tex.skin_config["genre_bg_left_max"].x: + x2 = min(self.end_position+tex.skin_config["genre_bg_folder_background"].width, tex.screen_width) + extra_distance + tex.draw_texture('box', 'folder_background', x=tex.skin_config["genre_bg_folder_background"].x, y=y, x2=x2, frame=self.end_box.texture_index) - offset = 150 if self.end_box.is_open else 0 - tex.draw_texture('box', 'folder_background_edge', x=self.end_position+80+offset, y=y, fade=self.fade_in.attribute, frame=self.end_box.texture_index) + offset = tex.skin_config["genre_bg_offset"].x if self.end_box.is_open else 0 + tex.draw_texture('box', 'folder_background_edge', x=self.end_position+tex.skin_config["genre_bg_folder_edge"].x+offset, y=y, fade=self.fade_in.attribute, frame=self.end_box.texture_index) - if ((self.start_position <= 594 and self.end_position >= 594) or - ((self.start_position <= 594 or self.end_position >= 594) and (self.start_position > self.end_position))): - offset = 100 if self.diff_num is not None else 0 - dest_width = min(300, self.title.texture.width) - tex.draw_texture('box', 'folder_background_folder', x=-((offset+dest_width)//2), y=y-2, x2=dest_width+offset - 10, fade=self.fade_in.attribute, frame=self.end_box.texture_index) - tex.draw_texture('box', 'folder_background_folder_edge', x=-((offset+dest_width)//2), y=y-2, fade=self.fade_in.attribute, frame=self.end_box.texture_index, mirror="horizontal") - tex.draw_texture('box', 'folder_background_folder_edge', x=((offset+dest_width)//2)+20, y=y-2, fade=self.fade_in.attribute, frame=self.end_box.texture_index) + if ((self.start_position <= BOX_CENTER and self.end_position >= BOX_CENTER) or + ((self.start_position <= BOX_CENTER or self.end_position >= BOX_CENTER) and (self.start_position > self.end_position))): + offset = tex.skin_config["genre_bg_offset_3"].x if self.diff_num is not None else 0 + dest_width = min(tex.skin_config["genre_bg_title"].width, self.title.texture.width) + tex.draw_texture('box', 'folder_background_folder', x=-((offset+dest_width)//2), y=y+tex.skin_config["genre_bg_folder_background_folder"].y, x2=dest_width+offset++tex.skin_config["genre_bg_folder_background_folder"].width, fade=self.fade_in.attribute, frame=self.end_box.texture_index) + tex.draw_texture('box', 'folder_background_folder_edge', x=-((offset+dest_width)//2), y=y+tex.skin_config["genre_bg_folder_background_folder"].y, fade=self.fade_in.attribute, frame=self.end_box.texture_index, mirror="horizontal") + tex.draw_texture('box', 'folder_background_folder_edge', x=((offset+dest_width)//2)+tex.skin_config["genre_bg_folder_background_folder"].x, y=y+tex.skin_config["genre_bg_folder_background_folder"].y, fade=self.fade_in.attribute, frame=self.end_box.texture_index) if self.diff_num is not None: - tex.draw_texture('diff_sort', 'star_num', frame=self.diff_num, x=-150 + (dest_width//2), y=-143) - self.title.draw(outline_color=ray.BLACK, x=(SCREEN_WIDTH//2) - (dest_width//2)-(offset//2), y=y-60, x2=dest_width - self.title.texture.width, color=ray.fade(ray.WHITE, self.fade_in.attribute)) + tex.draw_texture('diff_sort', 'star_num', frame=self.diff_num, x=(tex.skin_config["genre_bg_offset"].x * -1) + (dest_width//2), y=tex.skin_config["diff_sort_star_num"].y) + self.title.draw(outline_color=ray.BLACK, x=(tex.screen_width//2) - (dest_width//2)-(offset//2), y=y+tex.skin_config["genre_bg_title"].y, x2=dest_width - self.title.texture.width, color=ray.fade(ray.WHITE, self.fade_in.attribute)) class ScoreHistory: """The score information that appears while hovering over a song""" @@ -726,7 +730,7 @@ class ScoreHistory: tex.draw_texture('leaderboard', 'difficulty', frame=self.curr_difficulty, index=self.long) for i in range(4): - tex.draw_texture('leaderboard', 'normal', index=self.long, y=50+(i*50)) + tex.draw_texture('leaderboard', 'normal', index=self.long, y=tex.skin_config["score_info_bg_offset"].y+(i*tex.skin_config["score_info_bg_offset"].y)) tex.draw_texture('leaderboard', 'judge_good') tex.draw_texture('leaderboard', 'judge_ok') @@ -739,12 +743,12 @@ class ScoreHistory: if counter is None: continue counter = str(counter) - margin = 24 + margin = tex.skin_config["score_info_counter_margin"].x for i in range(len(counter)): if j == 0: - tex.draw_texture('leaderboard', 'counter', frame=int(counter[i]), x=-((len(counter) * 14) // 2) + (i * 14), color=ray.WHITE, index=self.long) + tex.draw_texture('leaderboard', 'counter', frame=int(counter[i]), x=-((len(counter) * tex.skin_config["score_info_counter_margin"].width) // 2) + (i * tex.skin_config["score_info_counter_margin"].width), color=ray.WHITE, index=self.long) else: - tex.draw_texture('leaderboard', 'judge_num', frame=int(counter[i]), x=-(len(counter) - i) * margin, y=j*50) + tex.draw_texture('leaderboard', 'judge_num', frame=int(counter[i]), x=-(len(counter) - i) * margin, y=j*tex.skin_config["score_info_bg_offset"].y) def draw(self): if self.long: @@ -766,19 +770,19 @@ class ScoreHistory: tex.draw_texture('leaderboard','ura') tex.draw_texture('leaderboard', 'pts', color=color) - tex.draw_texture('leaderboard', 'pts', y=50) + tex.draw_texture('leaderboard', 'pts', y=tex.skin_config["score_info_bg_offset"].y) tex.draw_texture('leaderboard', 'difficulty', frame=self.curr_difficulty) counter = str(self.curr_score) - total_width = len(counter) * 14 + total_width = len(counter) * tex.skin_config["score_info_counter_margin"].width for i in range(len(counter)): - tex.draw_texture('leaderboard', 'counter', frame=int(counter[i]), x=-(total_width // 2) + (i * 14), color=color) + tex.draw_texture('leaderboard', 'counter', frame=int(counter[i]), x=-(total_width // 2) + (i * tex.skin_config["score_info_counter_margin"].width), color=color) counter = str(self.curr_score_su) - total_width = len(counter) * 14 + total_width = len(counter) * tex.skin_config["score_info_counter_margin"].width for i in range(len(counter)): - tex.draw_texture('leaderboard', 'counter', frame=int(counter[i]), x=-(total_width // 2) + (i * 14), y=50, color=ray.WHITE) + tex.draw_texture('leaderboard', 'counter', frame=int(counter[i]), x=-(total_width // 2) + (i * tex.skin_config["score_info_counter_margin"].width), y=tex.skin_config["score_info_bg_offset"].y, color=ray.WHITE) def parse_box_def(path: Path): """Parse box.def file for directory metadata""" @@ -1469,26 +1473,23 @@ class FileNavigator: offset += len(self.items) # Adjust spacing based on dan select mode - base_spacing = 100 - center_offset = 150 - side_offset_l = 0 - side_offset_r = 300 + base_spacing = 100 * tex.screen_scale + center_offset = 150 * tex.screen_scale + side_offset_l = 0 * tex.screen_scale + side_offset_r = 300 * tex.screen_scale if self.in_dan_select: - base_spacing = 150 - side_offset_l = 200 - side_offset_r = 500 + base_spacing = 150 * tex.screen_scale + side_offset_l = 200 * tex.screen_scale + side_offset_r = 500 * tex.screen_scale - position = (BOX_CENTER - 150) + (base_spacing * offset) - if position == BOX_CENTER - 150: - #item.box.is_open = True + position = (BOX_CENTER - center_offset) + (base_spacing * offset) + if position == BOX_CENTER - center_offset: position += center_offset - elif position > BOX_CENTER - 150: - #item.box.is_open = False + elif position > BOX_CENTER - center_offset: position += side_offset_r else: position -= side_offset_l - #item.box.is_open = False if item.box.position == float('inf'): item.box.position = position diff --git a/libs/global_data.py b/libs/global_data.py index bc286cf..1b266ed 100644 --- a/libs/global_data.py +++ b/libs/global_data.py @@ -1,7 +1,9 @@ from dataclasses import dataclass, field from enum import IntEnum from pathlib import Path -from typing import Any, TypedDict +from typing import Any + +from libs.config import Config class PlayerNum(IntEnum): ALL = 0 @@ -26,90 +28,6 @@ class Crown(IntEnum): FC = 2 DFC = 3 -class GeneralConfig(TypedDict): - fps_counter: bool - audio_offset: int - visual_offset: int - language: str - hard_judge: int - touch_enabled: bool - timer_frozen: bool - judge_counter: bool - nijiiro_notes: bool - log_level: int - fake_online: bool - -class NameplateConfig(TypedDict): - name: str - title: str - title_bg: int - dan: int - gold: bool - rainbow: bool - -class PathsConfig(TypedDict): - tja_path: list[Path] - video_path: list[Path] - -class KeysConfig(TypedDict): - exit_key: int - fullscreen_key: int - borderless_key: int - pause_key: int - back_key: int - restart_key: int - -class Keys1PConfig(TypedDict): - left_kat: list[int] - left_don: list[int] - right_don: list[int] - right_kat: list[int] - -class Keys2PConfig(TypedDict): - left_kat: list[int] - left_don: list[int] - right_don: list[int] - right_kat: list[int] - -class GamepadConfig(TypedDict): - left_kat: list[int] - left_don: list[int] - right_don: list[int] - right_kat: list[int] - -class AudioConfig(TypedDict): - device_type: int - sample_rate: int - buffer_size: int - -class VolumeConfig(TypedDict): - sound: float - music: float - voice: float - hitsound: float - attract_mode: float - -class VideoConfig(TypedDict): - screen_width: int - screen_height: int - fullscreen: bool - borderless: bool - target_fps: int - vsync: bool - -class Config(TypedDict): - general: GeneralConfig - nameplate_1p: NameplateConfig - nameplate_2p: NameplateConfig - paths: PathsConfig - keys: KeysConfig - keys_1p: Keys1PConfig - keys_2p: Keys2PConfig - gamepad: GamepadConfig - audio: AudioConfig - volume: VolumeConfig - video: VideoConfig - @dataclass class Modifiers: """ diff --git a/libs/global_objects.py b/libs/global_objects.py index d943444..ab70600 100644 --- a/libs/global_objects.py +++ b/libs/global_objects.py @@ -3,7 +3,8 @@ from typing import Callable import pyray as ray from libs.global_data import PlayerNum -from libs.utils import OutlinedText, get_config, global_tex +from libs.utils import OutlinedText, global_tex +from libs.config import get_config from libs.audio import audio diff --git a/libs/song_hash.py b/libs/song_hash.py index 9913781..34baf25 100644 --- a/libs/song_hash.py +++ b/libs/song_hash.py @@ -7,7 +7,8 @@ from pathlib import Path from libs.global_data import Crown from libs.tja import NoteList, TJAParser, test_encodings -from libs.utils import get_config, global_data +from libs.utils import global_data +from libs.config import get_config logger = logging.getLogger(__name__) diff --git a/libs/texture.py b/libs/texture.py index 9b29f32..240610a 100644 --- a/libs/texture.py +++ b/libs/texture.py @@ -12,9 +12,7 @@ from pyray import Vector2, Rectangle, Color from libs.animation import BaseAnimation, parse_animations -SCREEN_WIDTH = 1280 -SCREEN_HEIGHT = 720 -SCREEN_SCALE = SCREEN_WIDTH / 1280 +from libs.config import get_config logger = logging.getLogger(__name__) @@ -52,12 +50,15 @@ class TextureWrapper: self.textures: dict[str, dict[str, Texture]] = dict() self.animations: dict[int, BaseAnimation] = dict() self.skin_config: dict[str, SkinInfo] = dict() - self.graphics_path = Path("Graphics") + self.graphics_path = Path(get_config()['paths']['graphics_path']) if (self.graphics_path / "skin_config.json").exists(): data = json.loads((self.graphics_path / "skin_config.json").read_text()) self.skin_config: dict[str, SkinInfo] = { k: SkinInfo(v.get('x', 0), v.get('y', 0), v.get('font_size', 0), v.get('width', 0), v.get('height', 0)) for k, v in data.items() } + self.screen_width = int(self.skin_config["screen"].width) + self.screen_height = int(self.skin_config["screen"].height) + self.screen_scale = self.screen_width / 1280 def unload_textures(self): """Unload all textures and animations.""" diff --git a/libs/tja.py b/libs/tja.py index 4373049..cfb4f79 100644 --- a/libs/tja.py +++ b/libs/tja.py @@ -11,7 +11,7 @@ from pathlib import Path from typing import Optional from libs.global_data import Modifiers -from libs.utils import get_pixels_per_frame, strip_comments +from libs.utils import get_pixels_per_frame, strip_comments, global_tex @lru_cache(maxsize=64) @@ -321,7 +321,7 @@ class TJAParser: data (list): The data extracted from the TJA file. """ 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: float = 866): """ Initialize a TJA object. @@ -1012,10 +1012,10 @@ def modifier_speed(notes: NoteList, value: float): modded_bars = notes.bars.copy() for note in modded_notes: note.pixels_per_frame_x *= value - note.load_ms = note.hit_ms - (866 / get_pixels_per_ms(note.pixels_per_frame_x)) + note.load_ms = note.hit_ms - (866 * global_tex.screen_scale / get_pixels_per_ms(note.pixels_per_frame_x)) for bar in modded_bars: bar.pixels_per_frame_x *= value - bar.load_ms = bar.hit_ms - (866 / get_pixels_per_ms(bar.pixels_per_frame_x)) + bar.load_ms = bar.hit_ms - (866 * global_tex.screen_scale / get_pixels_per_ms(bar.pixels_per_frame_x)) return modded_notes, modded_bars def modifier_display(notes: NoteList): diff --git a/libs/transition.py b/libs/transition.py index a5b3165..de7a0c0 100644 --- a/libs/transition.py +++ b/libs/transition.py @@ -20,8 +20,8 @@ class Transition: self.title = '' self.subtitle = '' else: - self.title = OutlinedText(title, 40, ray.WHITE) - self.subtitle = OutlinedText(subtitle, 30, ray.WHITE) + self.title = OutlinedText(title, global_tex.skin_config['transition_title'].font_size, ray.WHITE) + self.subtitle = OutlinedText(subtitle, global_tex.skin_config['transition_subtitle'].font_size, ray.WHITE) self.is_second = is_second def start(self): @@ -48,26 +48,26 @@ class Transition: if self.is_second: color_1 = ray.fade(ray.WHITE, self.song_info_fade_out.attribute) color_2 = ray.fade(ray.WHITE, min(0.70, self.song_info_fade_out.attribute)) - offset = 816 - self.rainbow_up.attribute + offset = global_tex.skin_config['transition_offset'].y - self.rainbow_up.attribute global_tex.draw_texture('rainbow_transition', 'text_bg', y=-self.rainbow_up.attribute - offset, color=color_2) if isinstance(self.title, OutlinedText): texture = self.title.texture - x = 1280//2 - texture.width//2 - y = 1176 - texture.height//2 - int(self.rainbow_up.attribute) - offset - 20 + x = global_tex.screen_width//2 - texture.width//2 + y = global_tex.skin_config['transition_title'].y - texture.height//2 - int(self.rainbow_up.attribute) - offset self.title.draw(outline_color=ray.BLACK, x=x, y=y, color=color_1) if isinstance(self.subtitle, OutlinedText): texture = self.subtitle.texture - x = 1280//2 - texture.width//2 - y = 1176 - texture.height//2 - int(self.rainbow_up.attribute) - offset - 20 - self.subtitle.draw(outline_color=ray.BLACK, x=x, y=y + 50, color=color_1) + x = global_tex.screen_width//2 - texture.width//2 + y = global_tex.skin_config['transition_subtitle'].y - texture.height//2 - int(self.rainbow_up.attribute) - offset + self.subtitle.draw(outline_color=ray.BLACK, x=x, y=y, color=color_1) def draw(self): """Draw the transition effect.""" total_offset = 0 if self.is_second: - total_offset = 816 + total_offset = global_tex.skin_config['transition_offset'].y global_tex.draw_texture('rainbow_transition', 'rainbow_bg_bottom', y=-self.rainbow_up.attribute - total_offset) global_tex.draw_texture('rainbow_transition', 'rainbow_bg_top', y=-self.rainbow_up.attribute - total_offset) global_tex.draw_texture('rainbow_transition', 'rainbow_bg', y=-self.rainbow_up.attribute - total_offset) @@ -75,7 +75,7 @@ class Transition: chara_offset = 0 if self.is_second: offset = self.chara_down.attribute - self.mini_up.attribute//3 - chara_offset = 408 + chara_offset = global_tex.skin_config['transition_chara_offset'].y if self.title == '' and self.subtitle == '': return global_tex.draw_texture('rainbow_transition', 'chara_left', x=-self.mini_up.attribute//2 - chara_offset, y=-self.mini_up.attribute + offset - total_offset) diff --git a/libs/utils.py b/libs/utils.py index 714019b..9830430 100644 --- a/libs/utils.py +++ b/libs/utils.py @@ -3,22 +3,20 @@ import hashlib import sys import logging import time -import json -from libs.global_data import Config, PlayerNum, global_data +from libs.global_data import PlayerNum, global_data from functools import lru_cache from pathlib import Path from typing import Optional import pyray as ray import raylib as rl -import tomlkit from raylib import ( SHADER_UNIFORM_FLOAT, SHADER_UNIFORM_VEC2, SHADER_UNIFORM_VEC4, ) -from libs.texture import SCREEN_WIDTH, TextureWrapper +from libs.texture import TextureWrapper logger = logging.getLogger(__name__) @@ -75,68 +73,6 @@ def get_pixels_per_frame(bpm: float, time_signature: float, distance: float) -> total_frames = 60 * total_time return (distance / total_frames) -def get_config() -> Config: - """Get the configuration from the TOML file""" - 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: - config_file = tomlkit.load(f) - - config: Config = json.loads(json.dumps(config_file)) - for key in config['keys']: - config['keys'][key] = get_key_code(config['keys'][key]) - for key in config['keys_1p']: - bindings = config['keys_1p'][key] - for i, bind in enumerate(bindings): - config['keys_1p'][key][i] = get_key_code(bind) - for key in config['keys_2p']: - bindings = config['keys_2p'][key] - for i, bind in enumerate(bindings): - config['keys_2p'][key][i] = get_key_code(bind) - return config - -def save_config(config: Config) -> None: - """Save the configuration to the TOML file""" - config_to_save = json.loads(json.dumps(config)) - - for key in config_to_save['keys']: - config_to_save['keys'][key] = get_key_string(config_to_save['keys'][key]) - for key in config_to_save['keys_1p']: - bindings = config_to_save['keys_1p'][key] - for i, bind in enumerate(bindings): - config_to_save['keys_1p'][key][i] = get_key_string(bind) - for key in config_to_save['keys_2p']: - bindings = config_to_save['keys_2p'][key] - for i, bind in enumerate(bindings): - config_to_save['keys_2p'][key][i] = get_key_string(bind) - - config_path = Path('dev-config.toml') if Path('dev-config.toml').exists() else Path('config.toml') - with open(config_path, "w", encoding="utf-8") as f: - tomlkit.dump(config_to_save, f) - -def get_key_string(key_code: int) -> str: - """Convert a key code back to its string representation""" - if 65 <= key_code <= 90: - return chr(key_code) - if 48 <= key_code <= 57: - return chr(key_code) - - for attr_name in dir(ray): - if attr_name.startswith('KEY_'): - if getattr(ray, attr_name) == key_code: - return attr_name[4:].lower() - - raise ValueError(f"Unknown key code: {key_code}") - -def get_key_code(key: str) -> int: - if len(key) == 1 and key.isalnum(): - return ord(key.upper()) - else: - key_code = getattr(ray, f"KEY_{key.upper()}", None) - if key_code is None: - raise ValueError(f"Invalid key: {key}") - return key_code - def is_input_key_pressed(keys: list[int], gamepad_buttons: list[int]): if global_data.input_locked: return False @@ -230,7 +166,7 @@ class OutlinedText: """ self.text = text self.hash = self._hash_text(text, font_size, color, vertical) - self.outline_thickness = outline_thickness * (SCREEN_WIDTH/1280) + self.outline_thickness = outline_thickness * global_tex.screen_scale if self.hash in text_cache: self.texture = ray.load_texture(f'cache/image/{self.hash}.png') else: @@ -496,7 +432,7 @@ class OutlinedText: final_color = ray.fade(color, fade) else: final_color = color - dest_rect = ray.Rectangle(x, y, self.texture.width+x2, self.texture.height+y2) + dest_rect = ray.Rectangle(x, y+((10 * global_tex.screen_scale)-10), self.texture.width+x2, self.texture.height+y2) if self.outline_thickness > 0: ray.begin_shader_mode(self.shader) ray.draw_texture_pro(self.texture, self.default_src, dest_rect, origin, rotation, final_color) diff --git a/libs/video.py b/libs/video.py index 51457a1..88c872a 100644 --- a/libs/video.py +++ b/libs/video.py @@ -6,7 +6,7 @@ from moviepy import VideoFileClip from libs.audio import audio from libs.utils import get_current_ms -from libs.texture import SCREEN_WIDTH, SCREEN_HEIGHT +from libs.texture import tex logger = logging.getLogger(__name__) @@ -120,7 +120,7 @@ class VideoPlayer: def draw(self): """Draw video frames to the raylib canvas""" if self.texture is not None: - ray.DrawTexturePro(self.texture, (0, 0, self.texture.width, self.texture.height), (0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), (0, 0), 0, ray.WHITE) + ray.DrawTexturePro(self.texture, (0, 0, self.texture.width, self.texture.height), (0, 0, tex.screen_width, tex.screen_height), (0, 0), 0, ray.WHITE) def stop(self): """Stops the video, audio, and clears its buffer""" diff --git a/scenes/dan/dan_result.py b/scenes/dan/dan_result.py index aead9fb..1cc08e3 100644 --- a/scenes/dan/dan_result.py +++ b/scenes/dan/dan_result.py @@ -28,13 +28,13 @@ class DanResultScreen(Screen): self.coin_overlay = CoinOverlay() self.allnet_indicator = AllNetIcon() self.start_ms = get_current_ms() - self.background = Background(PlayerNum.DAN, 1280) + self.background = Background(PlayerNum.DAN, tex.screen_width) self.player = DanResultPlayer(global_data.player_num) self.is_result_2 = False self.result_2_fade_in = tex.get_animation(1) self.gauge = DanGauge(global_data.player_num, global_data.session_data[global_data.player_num].dan_result_data.gauge_length) - self.song_names = [OutlinedText(song.song_title, 40, ray.WHITE) for song in global_data.session_data[global_data.player_num].dan_result_data.songs] - self.hori_name = OutlinedText(global_data.session_data[global_data.player_num].dan_result_data.dan_title, 40, ray.WHITE) + self.song_names = [OutlinedText(song.song_title, tex.skin_config["dan_title"].font_size, ray.WHITE) for song in global_data.session_data[global_data.player_num].dan_result_data.songs] + self.hori_name = OutlinedText(global_data.session_data[global_data.player_num].dan_result_data.dan_title, tex.skin_config["dan_title"].font_size, ray.WHITE) self.exam_info = global_data.session_data[global_data.player_num].dan_result_data.exams self.exam_data = global_data.session_data[global_data.player_num].dan_result_data.exam_data print(global_data.session_data[global_data.player_num].dan_result_data.songs) @@ -67,29 +67,29 @@ class DanResultScreen(Screen): return self.on_screen_end("DAN_SELECT") def draw_overlay(self): - ray.draw_rectangle(0, 0, 1280, 720, ray.fade(ray.BLACK, self.fade_out.attribute)) + ray.draw_rectangle(0, 0, tex.screen_width, tex.screen_height, ray.fade(ray.BLACK, self.fade_out.attribute)) self.coin_overlay.draw() self.allnet_indicator.draw() def draw_song_info_1(self): result_data = global_data.session_data[global_data.player_num].dan_result_data - height = 191 + height = tex.skin_config["dan_result_info_height"].y for i in range(len(result_data.songs)): song = result_data.songs[i] tex.draw_texture('background', 'genre_banner', y=i*height, frame=song.genre_index) - self.song_names[i].draw(x=1230 - self.song_names[i].texture.width, y=i*height + 90) + self.song_names[i].draw(x=tex.skin_config["dan_result_song_name"].x - self.song_names[i].texture.width, y=i*height + tex.skin_config["dan_result_song_name"].y) tex.draw_texture('result_info', 'song_num', frame=i, y=i*height) tex.draw_texture('result_info', 'difficulty', frame=song.selected_difficulty, y=i*height) tex.draw_texture('result_info', 'diff_star', y=i*height) tex.draw_texture('result_info', 'diff_x', y=i*height) counter = str(song.diff_level)[::-1] - margin = 12 + margin = tex.skin_config["dan_result_diff_num_margin"].x for j, digit in enumerate(counter): tex.draw_texture('result_info', 'diff_num', frame=int(digit), x=-(j*margin), y=i*height) tex.draw_texture('result_info', 'good', y=i*height) - margin = 24 + margin = tex.skin_config["score_info_counter_margin"].x counter = str(song.good)[::-1] for j, digit in enumerate(counter): tex.draw_texture('result_info', 'counter', index=0, frame=int(digit), x=-(j*margin), y=i*height) @@ -113,11 +113,11 @@ class DanResultScreen(Screen): tex.draw_texture('background', 'result_2_divider', fade=fade, x=i*240) tex.draw_texture('background', 'result_2_pullout', fade=fade) tex.draw_texture('result_info', 'dan_emblem', fade=fade, frame=result_data.dan_color) - self.hori_name.draw(outline_color=ray.BLACK, x=276 - (self.hori_name.texture.width//2), - y=123, x2=min(self.hori_name.texture.width, 275)-self.hori_name.texture.width, color=ray.fade(ray.WHITE, fade)) + self.hori_name.draw(outline_color=ray.BLACK, x=tex.skin_config["dan_result_hori_name"].x - (self.hori_name.texture.width//2), + y=tex.skin_config["dan_result_hori_name"].y, x2=min(self.hori_name.texture.width, tex.skin_config["dan_result_hori_name"].width)-self.hori_name.texture.width, color=ray.fade(ray.WHITE, fade)) tex.draw_texture('result_info', 'good', index=1, fade=fade) - margin = 24 + margin = tex.skin_config["score_info_counter_margin"].x counter = str(sum(song.good for song in result_data.songs))[::-1] for j, digit in enumerate(counter): tex.draw_texture('result_info', 'counter', index=4, frame=int(digit), x=-(j*margin), fade=fade) @@ -144,7 +144,7 @@ class DanResultScreen(Screen): tex.draw_texture('result_info', 'exam_header', fade=fade) tex.draw_texture('result_info', 'score_box', fade=fade) - margin = 22 + margin = tex.skin_config['dan_score_box_margin'].x counter = str(result_data.score)[::-1] for j, digit in enumerate(counter): tex.draw_texture('result_info', 'score_counter', frame=int(digit), x=-(j*margin), fade=fade) @@ -164,14 +164,14 @@ class DanResultScreen(Screen): # Draw exam info for i, exam in enumerate(self.exam_info): exam_data = self.exam_data[i] - y_offset = i * 94 * scale # Scale the y offset + y_offset = i * tex.skin_config["dan_exam_info"].y * scale # Scale the y offset tex.draw_texture('exam_info', 'exam_bg', y=y_offset, fade=fade, scale=scale) tex.draw_texture('exam_info', 'exam_overlay_1', y=y_offset, fade=fade, scale=scale) # Draw progress bar - tex.draw_texture('exam_info', exam_data.bar_texture, x2=940*exam_data.progress*scale, y=y_offset, fade=fade, scale=scale) + tex.draw_texture('exam_info', exam_data.bar_texture, x2=tex.skin_config["dan_exam_info"].width *exam_data.progress*scale, y=y_offset, fade=fade, scale=scale) # Draw exam type and red value counter red_counter = str(exam.red) - self._draw_counter(red_counter, margin=22*scale, texture='value_counter', index=0, y=y_offset, fade=fade, scale=scale) + self._draw_counter(red_counter, margin=tex.skin_config["dan_exam_info"].x*scale, texture='value_counter', index=0, y=y_offset, fade=fade, scale=scale) tex.draw_texture('exam_info', f'exam_{exam.type}', y=y_offset, x=-len(red_counter)*20*scale, fade=fade, scale=scale) # Draw range indicator if exam.range == 'less': @@ -181,7 +181,7 @@ class DanResultScreen(Screen): # Draw current value counter tex.draw_texture('exam_info', 'exam_overlay_2', y=y_offset, fade=fade, scale=scale) value_counter = str(exam_data.counter_value) - self._draw_counter(value_counter, margin=22*scale, texture='value_counter', index=1, y=y_offset, fade=fade, scale=scale) + self._draw_counter(value_counter, margin=tex.skin_config["dan_exam_info"].x*scale, texture='value_counter', index=1, y=y_offset, fade=fade, scale=scale) if exam.type == 'gauge': tex.draw_texture('exam_info', 'exam_percent', y=y_offset, index=1, fade=fade, scale=scale) if exam_data.failed: @@ -207,15 +207,15 @@ class DanResultPlayer: def __init__(self, player_num: PlayerNum): plate_info = global_data.config[f'nameplate_{player_num}p'] self.nameplate = Nameplate(plate_info['name'], plate_info['title'], player_num, plate_info['dan'], plate_info['gold'], plate_info['rainbow'], plate_info['title_bg']) - self.chara = Chara2D(player_num-1, 100) + self.chara = Chara2D(player_num-1) def update(self, current_time_ms: float): self.nameplate.update(current_time_ms) self.chara.update(current_time_ms, 100, False, False) def draw(self): - self.nameplate.draw(10, 585) - self.chara.draw(0, 405) + self.nameplate.draw(tex.skin_config['dan_result_nameplate'].x, tex.skin_config['dan_result_nameplate'].y) + self.chara.draw(tex.skin_config['dan_result_chara'].x, tex.skin_config['dan_result_chara'].y) class DanGauge(Gauge): """The player's gauge""" diff --git a/scenes/dan/dan_select.py b/scenes/dan/dan_select.py index d2fb781..295b158 100644 --- a/scenes/dan/dan_select.py +++ b/scenes/dan/dan_select.py @@ -4,7 +4,7 @@ import pyray as ray from libs.audio import audio from libs.global_data import PlayerNum, global_data -from libs.texture import SCREEN_HEIGHT, SCREEN_WIDTH, tex +from libs.texture import tex from libs.chara_2d import Chara2D from libs.global_objects import AllNetIcon, CoinOverlay, Indicator, Nameplate, Timer from libs.screen import Screen @@ -103,15 +103,15 @@ class DanSelectScreen(Screen): tex.draw_texture('global', 'footer') for item in self.navigator.items: box = item.box - if -156 <= box.position <= SCREEN_WIDTH + 144: - if box.position <= 500: - box.draw(box.position, 95, False) + if (-156 * tex.screen_scale) <= box.position <= tex.screen_width + (144 * tex.screen_scale): + if box.position <= (500 * tex.screen_scale): + box.draw(box.position, tex.skin_config["boxes"].y, False) else: - box.draw(box.position, 95, False) + box.draw(box.position, tex.skin_config["boxes"].y, False) if self.state == State.SONG_SELECTED: - ray.draw_rectangle(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ray.fade(ray.BLACK, min(0.5, self.player.confirmation_window.fade_in.attribute))) + ray.draw_rectangle(0, 0, tex.screen_width, tex.screen_height, ray.fade(ray.BLACK, min(0.5, self.player.confirmation_window.fade_in.attribute))) self.player.draw() - self.indicator.draw(410, 575) + self.indicator.draw(tex.skin_config["dan_select_indicator"].x, tex.skin_config["dan_select_indicator"].y) self.timer.draw() self.coin_overlay.draw() tex.draw_texture('global', 'dan_select') @@ -133,7 +133,7 @@ class DanSelectPlayer: self.confirmation_window = ConfirmationWindow() # Player-specific objects - self.chara = Chara2D(int(self.player_num) - 1, 100) + self.chara = Chara2D(int(self.player_num) - 1) plate_info = global_data.config[f'nameplate_{self.player_num}p'] self.nameplate = Nameplate(plate_info['name'], plate_info['title'], self.player_num, plate_info['dan'], plate_info['gold'], plate_info['rainbow'], plate_info['title_bg']) @@ -212,11 +212,11 @@ class DanSelectPlayer: def draw(self): if self.player_num == PlayerNum.P1: - self.nameplate.draw(30, 640) - self.chara.draw(x=-50, y=410) + self.nameplate.draw(tex.skin_config["dan_select_nameplate_1p"].x, tex.skin_config["dan_select_nameplate_1p"].y) + self.chara.draw(x=tex.skin_config["dan_select_chara_1p"].x, y=tex.skin_config["dan_select_chara_1p"].y) else: - self.nameplate.draw(950, 640) - self.chara.draw(mirror=True, x=950, y=410) + self.nameplate.draw(tex.skin_config["dan_select_nameplate_2p"].x, tex.skin_config["dan_select_nameplate_2p"].y) + self.chara.draw(mirror=True, x=tex.skin_config["dan_select_chara_2p"].x, y=tex.skin_config["dan_select_chara_2p"].y) self.confirmation_window.draw() diff --git a/scenes/dan/game_dan.py b/scenes/dan/game_dan.py index 1b4789a..c0a9269 100644 --- a/scenes/dan/game_dan.py +++ b/scenes/dan/game_dan.py @@ -1,5 +1,5 @@ import copy -from typing import override +from typing import Optional, override import pyray as ray import logging from libs.animation import Animation @@ -11,7 +11,7 @@ from libs.global_objects import AllNetIcon from libs.tja import TJAParser from libs.transition import Transition from libs.utils import OutlinedText, get_current_ms -from libs.texture import SCREEN_WIDTH, tex +from libs.texture import tex from scenes.game import ClearAnimation, FCAnimation, FailAnimation, GameScreen, Gauge, ResultTransition, SongInfo logger = logging.getLogger(__name__) @@ -87,7 +87,7 @@ class DanGameScreen(GameScreen): song, genre_index, difficulty, level = songs[self.song_index] session_data.selected_difficulty = difficulty self.player_1.difficulty = difficulty - self.tja = TJAParser(song.file_path, start_delay=self.start_delay, distance=SCREEN_WIDTH - GameScreen.JUDGE_X) + self.tja = TJAParser(song.file_path, start_delay=self.start_delay, distance=tex.screen_width - GameScreen.JUDGE_X) if self.song_music is not None: audio.unload_music_stream(self.song_music) self.song_music = None @@ -281,22 +281,22 @@ class DanGameScreen(GameScreen): # Draw total notes counter tex.draw_texture('dan_info', 'total_notes') counter = str(cache['remaining_notes']) - self._draw_counter(counter, margin=45, texture='total_notes_counter') + self._draw_counter(counter, margin=tex.skin_config["dan_total_notes_margin"].x, texture='total_notes_counter') # Draw exam info for i, exam_info in enumerate(cache['exam_data']): - y_offset = i * 94 + y_offset = i * tex.skin_config["dan_exam_info"].y exam = exam_info['exam'] tex.draw_texture('dan_info', 'exam_bg', y=y_offset) tex.draw_texture('dan_info', 'exam_overlay_1', y=y_offset) # Draw progress bar - tex.draw_texture('dan_info', exam_info['bar_texture'], x2=940*exam_info['progress'], y=y_offset) + tex.draw_texture('dan_info', exam_info['bar_texture'], x2=tex.skin_config["dan_exam_info"].width*exam_info['progress'], y=y_offset) # Draw exam type and red value counter red_counter = str(exam_info['red_value']) - self._draw_counter(red_counter, margin=22, texture='value_counter', index=0, y=y_offset) + self._draw_counter(red_counter, margin=tex.skin_config["dan_score_box_margin"].x, texture='value_counter', index=0, y=y_offset) tex.draw_texture('dan_info', f'exam_{exam.type}', y=y_offset, x=-len(red_counter)*20) # Draw range indicator @@ -308,7 +308,7 @@ class DanGameScreen(GameScreen): # Draw current value counter tex.draw_texture('dan_info', 'exam_overlay_2', y=y_offset) value_counter = str(exam_info['counter_value']) - self._draw_counter(value_counter, margin=22, texture='value_counter', index=1, y=y_offset) + self._draw_counter(value_counter, margin=tex.skin_config["dan_score_box_margin"].x, texture='value_counter', index=1, y=y_offset) if exam.type == 'gauge': tex.draw_texture('dan_info', 'exam_percent', y=y_offset, index=1) @@ -320,10 +320,10 @@ class DanGameScreen(GameScreen): # Draw frame and title tex.draw_texture('dan_info', 'frame', frame=self.color) if self.hori_name is not None: - self.hori_name.draw(outline_color=ray.BLACK, x=154 - (self.hori_name.texture.width//2), - y=392, x2=min(self.hori_name.texture.width, 275)-self.hori_name.texture.width) + self.hori_name.draw(outline_color=ray.BLACK, x=tex.skin_config["dan_game_hori_name"].x - (self.hori_name.texture.width//2), + y=tex.skin_config["dan_game_hori_name"].y, x2=min(self.hori_name.texture.width, tex.skin_config["dan_game_hori_name"].width)-self.hori_name.texture.width) - def _draw_counter(self, counter, margin, texture, index=None, y=0): + def _draw_counter(self, counter: str, margin: float, texture: str, index: Optional[int] = None, y: float = 0): """Helper to draw digit counters""" for j in range(len(counter)): kwargs = {'frame': int(counter[j]), 'x': -(len(counter) - j) * margin, 'y': y} diff --git a/scenes/entry.py b/scenes/entry.py index bc1066b..49c5cdd 100644 --- a/scenes/entry.py +++ b/scenes/entry.py @@ -6,7 +6,7 @@ from libs.audio import audio from libs.chara_2d import Chara2D from libs.global_data import PlayerNum from libs.global_objects import AllNetIcon, CoinOverlay, Nameplate, Indicator, EntryOverlay, Timer -from libs.texture import SCREEN_HEIGHT, SCREEN_WIDTH, tex +from libs.texture import tex from libs.screen import Screen from libs.utils import ( OutlinedText, @@ -212,7 +212,7 @@ class EntryScreen(Screen): tex.draw_texture('global', 'player_entry') if self.box_manager.is_finished(): - ray.draw_rectangle(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ray.BLACK) + ray.draw_rectangle(0, 0, tex.screen_width, tex.screen_height, ray.BLACK) self.timer.draw() self.entry_overlay.draw(y=tex.skin_config['entry_overlay_entry'].y) @@ -452,7 +452,7 @@ class BoxManager: spacing = tex.skin_config["entry_box_spacing"].x box_width = self.boxes[0].texture.width total_width = self.num_boxes * box_width + (self.num_boxes - 1) * spacing - start_x = SCREEN_WIDTH//2 - total_width//2 + start_x = tex.screen_width//2 - total_width//2 for i, box in enumerate(self.boxes): box.set_positions(start_x + i * (box_width + spacing)) if i > 0: diff --git a/scenes/game.py b/scenes/game.py index c5298a6..183914a 100644 --- a/scenes/game.py +++ b/scenes/game.py @@ -16,7 +16,7 @@ from libs.chara_2d import Chara2D from libs.global_data import Crown, Difficulty, Modifiers, PlayerNum from libs.global_objects import AllNetIcon, Nameplate from libs.screen import Screen -from libs.texture import SCREEN_HEIGHT, SCREEN_WIDTH, tex +from libs.texture import tex from libs.tja import ( Balloon, Drumroll, @@ -57,7 +57,7 @@ class Judgments(IntEnum): BAD = 2 class GameScreen(Screen): - JUDGE_X = 414 + JUDGE_X = 414 * tex.screen_scale def on_screen_start(self): super().on_screen_start() self.mask_shader = ray.load_shader("shader/outline.vs", "shader/mask.fs") @@ -133,7 +133,7 @@ class GameScreen(Screen): def init_tja(self, song: Path): """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=tex.screen_width - GameScreen.JUDGE_X) if self.tja.metadata.bgmovie != Path() and self.tja.metadata.bgmovie.exists(): self.movie = VideoPlayer(self.tja.metadata.bgmovie) self.movie.set_volume(0.0) @@ -447,12 +447,12 @@ class Player: 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 - return int(width + pixels_per_frame * 0.06 * time_diff - 64) - self.visual_offset + return int(width + pixels_per_frame * 0.06 * time_diff - (tex.textures["notes"]["1"].width//2)) - self.visual_offset 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 - 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) + ((self.tja.distance * pixels_per_frame) / pixels_per_frame_x)) def get_judge_position(self, current_ms: float): """Get the current judgment circle position based on bar data""" @@ -462,8 +462,8 @@ class Player: # Find the most recent bar with judge position data for bar in self.current_bars: if hasattr(bar, 'judge_pos_x') and bar.hit_ms <= current_ms: - judge_x = bar.judge_pos_x - judge_y = bar.judge_pos_y + judge_x = bar.judge_pos_x * tex.screen_scale + judge_y = bar.judge_pos_y * tex.screen_scale elif bar.hit_ms > current_ms: break @@ -495,10 +495,10 @@ class Player: return # More efficient removal with early exit - removal_threshold = GameScreen.JUDGE_X + 650 + removal_threshold = GameScreen.JUDGE_X + (650 * tex.screen_scale) bars_to_keep = [] for bar in self.current_bars: - position = self.get_position_x(SCREEN_WIDTH, current_ms, bar.hit_ms, bar.pixels_per_frame_x) + position = self.get_position_x(tex.screen_width, current_ms, bar.hit_ms, bar.pixels_per_frame_x) if position >= removal_threshold: bars_to_keep.append(bar) self.current_bars = bars_to_keep @@ -630,8 +630,8 @@ class Player: note = self.current_notes_draw[0] if note.type in {NoteType.ROLL_HEAD, NoteType.ROLL_HEAD_L, NoteType.BALLOON_HEAD, NoteType.KUSUDAMA} and len(self.current_notes_draw) > 1: note = self.current_notes_draw[1] - position = self.get_position_x(SCREEN_WIDTH, current_ms, note.hit_ms, note.pixels_per_frame_x) - if position < GameScreen.JUDGE_X + 650: + position = self.get_position_x(tex.screen_width, current_ms, note.hit_ms, note.pixels_per_frame_x) + if position < GameScreen.JUDGE_X + (650 * tex.screen_scale): self.current_notes_draw.pop(0) def note_manager(self, current_ms: float, background: Optional[Background]): @@ -1000,36 +1000,39 @@ class Player: 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(tex.screen_width, current_ms, head.load_ms, head.pixels_per_frame_x) start_position += self.judge_x tail = next((note for note in self.current_notes_draw[1:] if note.type == NoteType.TAIL and note.index > head.index), self.current_notes_draw[1]) is_big = int(head.type == NoteType.ROLL_HEAD_L) - end_position = self.get_position_x(SCREEN_WIDTH, current_ms, tail.load_ms, tail.pixels_per_frame_x) + end_position = self.get_position_x(tex.screen_width, current_ms, tail.load_ms, tail.pixels_per_frame_x) end_position += self.judge_x length = end_position - start_position color = ray.Color(255, head.color, head.color, 255) + y = tex.skin_config["notes"].y + moji_y = tex.skin_config["moji"].y + moji_x = tex.skin_config["moji"].x if head.display: if length > 0: - tex.draw_texture('notes', "8", frame=is_big, x=start_position+64, y=192+(self.is_2p*176)+self.judge_y, x2=length-47, color=color) + tex.draw_texture('notes', "8", frame=is_big, x=start_position+(tex.textures["notes"]["8"].width//2), y=y+(self.is_2p*tex.skin_config["2p_offset"].y)+self.judge_y, x2=length+tex.skin_config["drumroll_width_offset"].width, color=color) if is_big: - tex.draw_texture('notes', "drumroll_big_tail", x=end_position+64, y=192+(self.is_2p*176)+self.judge_y, color=color) + tex.draw_texture('notes', "drumroll_big_tail", x=end_position+tex.textures["notes"]["drumroll_big_tail"].width//2, y=y+(self.is_2p*tex.skin_config["2p_offset"].y)+self.judge_y, color=color) else: - tex.draw_texture('notes', "drumroll_tail", x=end_position+64, y=192+(self.is_2p*176)+self.judge_y, color=color) - tex.draw_texture('notes', str(head.type), frame=current_eighth % 2, x=start_position, y=192+(self.is_2p*176)+self.judge_y, color=color) + tex.draw_texture('notes', "drumroll_tail", x=end_position+tex.textures["notes"]["drumroll_tail"].width//2, y=y+(self.is_2p*tex.skin_config["2p_offset"].y)+self.judge_y, color=color) + tex.draw_texture('notes', str(head.type), frame=current_eighth % 2, x=start_position, y=y+(self.is_2p*tex.skin_config["2p_offset"].y)+self.judge_y, color=color) - tex.draw_texture('notes', 'moji_drumroll_mid', x=start_position + 60, y=323+(self.is_2p*176)+self.judge_y, x2=length) - tex.draw_texture('notes', 'moji', frame=head.moji, x=(start_position - (168//2)) + 64, y=323+(self.is_2p*176)+self.judge_y) - tex.draw_texture('notes', 'moji', frame=tail.moji, x=(end_position - (168//2)) + 32, y=323+(self.is_2p*176)+self.judge_y) + tex.draw_texture('notes', 'moji_drumroll_mid', x=start_position + tex.skin_config["moji_drumroll"].x, y=moji_y+(self.is_2p*tex.skin_config["2p_offset"].y)+self.judge_y, x2=length) + tex.draw_texture('notes', 'moji', frame=head.moji, x=start_position - moji_x, y=moji_y+(self.is_2p*tex.skin_config["2p_offset"].y)+self.judge_y) + tex.draw_texture('notes', 'moji', frame=tail.moji, x=end_position - tex.skin_config["moji_drumroll"].width, y=moji_y+(self.is_2p*tex.skin_config["2p_offset"].y)+self.judge_y) def draw_balloon(self, current_ms: float, head: Balloon, current_eighth: int): """Draws a balloon in the player's lane""" - offset = 12 - start_position = self.get_position_x(SCREEN_WIDTH, current_ms, head.load_ms, head.pixels_per_frame_x) + offset = tex.skin_config["balloon_offset"].x + start_position = self.get_position_x(tex.screen_width, current_ms, head.load_ms, head.pixels_per_frame_x) start_position += self.judge_x tail = next((note for note in self.current_notes_draw[1:] if note.type == NoteType.TAIL and note.index > head.index), self.current_notes_draw[1]) - end_position = self.get_position_x(SCREEN_WIDTH, current_ms, tail.load_ms, tail.pixels_per_frame_x) + end_position = self.get_position_x(tex.screen_width, current_ms, tail.load_ms, tail.pixels_per_frame_x) end_position += self.judge_x - pause_position = 349 + self.judge_x + pause_position = tex.skin_config["balloon_pause_position"].x + self.judge_x if current_ms >= tail.hit_ms: position = end_position elif current_ms >= head.hit_ms: @@ -1037,8 +1040,8 @@ class Player: else: position = start_position if head.display: - tex.draw_texture('notes', str(head.type), frame=current_eighth % 2, x=position-offset, y=192+(self.is_2p*176)+self.judge_y) - tex.draw_texture('notes', '10', frame=current_eighth % 2, x=position-offset+128, y=192+(self.is_2p*176)+self.judge_y) + tex.draw_texture('notes', str(head.type), frame=current_eighth % 2, x=position-offset, y=tex.skin_config["notes"].y+(self.is_2p*tex.skin_config["2p_offset"].y)+self.judge_y) + tex.draw_texture('notes', '10', frame=current_eighth % 2, x=position-offset+tex.textures["notes"]["10"].width, y=tex.skin_config["notes"].y+(self.is_2p*tex.skin_config["2p_offset"].y)+self.judge_y) def draw_bars(self, current_ms: float): """Draw bars in the player's lane""" @@ -1048,7 +1051,7 @@ class Player: for bar in reversed(self.current_bars): if not bar.display: continue - x_position = self.get_position_x(SCREEN_WIDTH, current_ms, bar.load_ms, bar.pixels_per_frame_x) + x_position = self.get_position_x(tex.screen_width, current_ms, bar.load_ms, bar.pixels_per_frame_x) y_position = self.get_position_y(current_ms, bar.load_ms, bar.pixels_per_frame_y, bar.pixels_per_frame_x) x_position += self.judge_x y_position += self.judge_y @@ -1060,7 +1063,7 @@ class Player: angle = math.degrees(math.atan2(bar.pixels_per_frame_y, bar.pixels_per_frame_x)) else: angle = 0 - tex.draw_texture('notes', str(bar.type), frame=frame, x=x_position+60, y=y_position+190+(self.is_2p*176), rotation=angle) + tex.draw_texture('notes', str(bar.type), frame=frame, x=x_position+tex.skin_config["moji_drumroll"].x, y=y_position+tex.skin_config["moji_drumroll"].y+(self.is_2p*tex.skin_config["2p_offset"].y), rotation=angle) def draw_notes(self, current_ms: float, start_ms: float): @@ -1090,10 +1093,10 @@ class Player: else: effective_ms = current_ms - x_position = self.get_position_x(SCREEN_WIDTH, effective_ms, note.load_ms, note.pixels_per_frame_x) + x_position = self.get_position_x(tex.screen_width, effective_ms, note.load_ms, note.pixels_per_frame_x) y_position = self.get_position_y(effective_ms, note.load_ms, note.pixels_per_frame_y, note.pixels_per_frame_x) 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(tex.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) x_position += self.judge_x y_position += self.judge_y @@ -1101,13 +1104,13 @@ class Player: self.draw_drumroll(current_ms, note, current_eighth) elif isinstance(note, Balloon) and not note.is_kusudama: self.draw_balloon(current_ms, note, current_eighth) - tex.draw_texture('notes', 'moji', frame=note.moji, x=x_position - (168//2) + 64, y=323 + y_position+(self.is_2p*176)) + tex.draw_texture('notes', 'moji', frame=note.moji, x=x_position, y=tex.skin_config["moji"].y + y_position+(self.is_2p*tex.skin_config["2p_offset"].y)) else: if note.display: - 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+(self.is_2p*176)) + tex.draw_texture('notes', str(note.type), frame=current_eighth % 2, x=x_position, y=y_position+tex.skin_config["notes"].y+(self.is_2p*tex.skin_config["2p_offset"].y), center=True) + tex.draw_texture('notes', 'moji', frame=note.moji, x=x_position - (tex.textures["notes"]["moji"].width//2) + (tex.textures["notes"]["1"].width//2), y=tex.skin_config["moji"].y + y_position+(self.is_2p*tex.skin_config["2p_offset"].y)) - ray.draw_text(self.current_notes_draw[0].lyric, SCREEN_WIDTH//2 - (ray.measure_text(self.current_notes_draw[0].lyric, 40)//2), SCREEN_HEIGHT - 50, 40, ray.BLUE) + ray.draw_text(self.current_notes_draw[0].lyric, tex.screen_width//2 - (ray.measure_text(self.current_notes_draw[0].lyric, int(40 * tex.screen_scale))//2), tex.screen_height - int(50 * tex.screen_scale), int(40 * tex.screen_scale), ray.BLUE) def draw_modifiers(self): @@ -1171,13 +1174,13 @@ class Player: # Group 7: Player-specific elements if not self.modifiers.auto: if self.is_2p: - self.nameplate.draw(-62, 371) + self.nameplate.draw(tex.skin_config["game_nameplate_1p"].x, tex.skin_config["game_nameplate_1p"].y) else: - self.nameplate.draw(-62, 285) + self.nameplate.draw(tex.skin_config["game_nameplate_2p"].x, tex.skin_config["game_nameplate_2p"].y) else: tex.draw_texture('lane', 'auto_icon', index=self.is_2p) self.draw_modifiers() - self.chara.draw(y=(self.is_2p*536)) + self.chara.draw(y=(self.is_2p*tex.skin_config["game_2p_offset"].y)) # Group 8: Special animations and counters if self.drumroll_counter is not None: @@ -1334,13 +1337,15 @@ class GaugeHitEffect: self.color = ray.fade(ray.YELLOW, self.circle_fadein.attribute) self.is_finished = False + self.width = tex.textures["gauge"]["hit_effect"].width + self.texture_color = ray.WHITE - self.dest_width = 152 - self.dest_height = 152 - self.origin = ray.Vector2(76, 76) # 152/2 + self.dest_width = self.width * tex.screen_scale + self.dest_height = self.width * tex.screen_scale + self.origin = ray.Vector2(self.width//2, self.width//2) self.rotation_angle = 0 - self.x2_pos = -152 - self.y2_pos = -152 + self.x2_pos = -self.width + self.y2_pos = -self.width # Cache for texture selection self.circle_texture = 'hit_effect_circle_big' if self.is_big else 'hit_effect_circle' @@ -1385,11 +1390,11 @@ class GaugeHitEffect: if abs(resize_val - getattr(self, '_last_resize_calc', -1)) > 0.005: self._last_resize_calc = resize_val self.texture_color = self._get_texture_color_for_resize(resize_val) - self.dest_width = 152 * resize_val - self.dest_height = 152 * resize_val + self.dest_width = self.width * resize_val + self.dest_height = self.width * resize_val self.origin = ray.Vector2(self.dest_width / 2, self.dest_height / 2) - self.x2_pos = -152 + (152 * resize_val) - self.y2_pos = -152 + (152 * resize_val) + self.x2_pos = -self.width + (self.width * resize_val) + self.y2_pos = -self.width + (self.width * resize_val) self.rotation_angle = self.rotation.attribute * 100 @@ -1412,8 +1417,9 @@ class GaugeHitEffect: center=True) # Note type texture + pos_data = tex.skin_config["gauge_hit_effect_note"] tex.draw_texture('notes', str(self.note_type), - x=1158, y=101+(self.is_2p*(435-32)), + x=pos_data.x, y=pos_data.y+(self.is_2p*(pos_data.height)), fade=fade_value) # Circle effect texture (use cached texture name) @@ -1434,12 +1440,12 @@ class NoteArc: self.explosion_point_index = 0 self.points_per_explosion = 5 - curve_height = 425 - self.start_x, self.start_y = start_x + 350, start_y + 192 - self.end_x, self.end_y = 1158, 101 + curve_height = 425 * tex.screen_scale + self.start_x, self.start_y = start_x + (350 * tex.screen_scale), start_y + (192 * tex.screen_scale) + self.end_x, self.end_y = 1158 * tex.screen_scale, 101 * tex.screen_scale if self.player_num == PlayerNum.P2: - self.start_y += 176 - self.end_y += 372 + self.start_y += (176 * tex.screen_scale) + self.end_y += (372 * tex.screen_scale) self.explosion_x = self.start_x self.explosion_y = self.start_y @@ -1511,12 +1517,12 @@ class NoteArc: if crop_width > 0: src = ray.Rectangle(crop_start_x, 0, crop_width, rainbow_height) mirror = 'vertical' if self.player_num == PlayerNum.P2 else '' - y = 435 if self.player_num == PlayerNum.P2 else 0 + y = (435 * tex.screen_scale) if self.player_num == PlayerNum.P2 else 0 ray.begin_shader_mode(mask_shader) tex.draw_texture('balloon', 'rainbow_mask', src=src, x=crop_start_x, x2=-rainbow.width + crop_width, mirror=mirror, y=y) ray.end_shader_mode() - tex.draw_texture('balloon', 'explosion', x=self.explosion_x, y=self.explosion_y-30, frame=self.explosion_anim.attribute) + tex.draw_texture('balloon', 'explosion', x=self.explosion_x, y=self.explosion_y-(30 * tex.screen_scale), frame=self.explosion_anim.attribute) ''' elif self.is_big: tex.draw_texture('hit_effect', 'explosion', x=self.explosion_x, y=self.explosion_y-30, frame=self.explosion_anim.attribute) @@ -1552,9 +1558,9 @@ class DrumrollCounter: color = ray.fade(ray.WHITE, self.fade_animation.attribute) tex.draw_texture('drumroll_counter', 'bubble', color=color, index=self.is_2p) counter = str(self.drumroll_count) - total_width = len(counter) * 52 + total_width = len(counter) * tex.skin_config["drumroll_counter_margin"].x for i, digit in enumerate(counter): - tex.draw_texture('drumroll_counter', 'counter', color=color, index=self.is_2p, 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, index=self.is_2p, frame=int(digit), x=-(total_width//2)+(i*tex.skin_config["drumroll_counter_margin"].x), y=-self.stretch_animation.attribute, y2=self.stretch_animation.attribute) class BalloonAnimation: """Draws a Balloon""" @@ -1594,16 +1600,16 @@ class BalloonAnimation: def draw(self): if self.is_popped: - tex.draw_texture('balloon', 'pop', frame=7, color=self.color, y=self.is_2p*176) + tex.draw_texture('balloon', 'pop', frame=7, color=self.color, y=self.is_2p*tex.skin_config["2p_offset"].y) elif self.balloon_count >= 1: balloon_index = min(6, (self.balloon_count - 1) * 6 // self.balloon_total) - tex.draw_texture('balloon', 'pop', frame=balloon_index, color=self.color, index=self.player_num-1, y=self.is_2p*176) + tex.draw_texture('balloon', 'pop', frame=balloon_index, color=self.color, index=self.player_num-1, y=self.is_2p*tex.skin_config["2p_offset"].y) if self.balloon_count > 0: - tex.draw_texture('balloon', 'bubble', y=self.is_2p*410, mirror='vertical' if self.is_2p else '') + tex.draw_texture('balloon', 'bubble', y=self.is_2p*(410 * tex.screen_scale), mirror='vertical' if self.is_2p else '') counter = str(max(0, self.balloon_total - self.balloon_count + 1)) - total_width = len(counter) * 52 + total_width = len(counter) * tex.skin_config["drumroll_counter_margin"].x for i, digit in enumerate(counter): - tex.draw_texture('balloon', 'counter', frame=int(digit), color=self.color, x=-(total_width // 2) + (i * 52), y=-self.stretch_animation.attribute+(self.is_2p*435), y2=self.stretch_animation.attribute) + tex.draw_texture('balloon', 'counter', frame=int(digit), color=self.color, x=-(total_width // 2) + (i * tex.skin_config["drumroll_counter_margin"].x), y=-self.stretch_animation.attribute+(self.is_2p*435), y2=self.stretch_animation.attribute) class KusudamaAnimation: """Draws a Kusudama""" @@ -1667,9 +1673,9 @@ class KusudamaAnimation: counter = str(max(0, self.balloon_total - self.balloon_count)) if counter == '0': return - total_width = len(counter) * 150 + total_width = len(counter) * tex.skin_config["kusudama_counter_margin"].x for i, digit in enumerate(counter): - 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 * tex.skin_config["kusudama_counter_margin"].x), y=-self.stretch_animation.attribute, y2=self.stretch_animation.attribute) class Combo: """Displays the current combo""" @@ -1725,21 +1731,21 @@ class Combo: counter = self._cached_combo_str if self.combo < 100: - margin = 30 + margin = tex.skin_config["combo_margin"].x total_width = len(counter) * margin tex.draw_texture('combo', 'combo', index=self.is_2p) 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, index=self.is_2p) else: - margin = 35 + margin = tex.skin_config["combo_margin"].y total_width = len(counter) * margin tex.draw_texture('combo', 'combo_100', index=self.is_2p) 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, index=self.is_2p) - glimmer_positions = [(225, 210), (200, 230), (250, 230)] + glimmer_positions = [(225 * tex.screen_scale, 210 * tex.screen_scale), (200 * tex.screen_scale, 230 * tex.screen_scale), (250 * tex.screen_scale, 230 * tex.screen_scale)] for j, (x, y) in enumerate(glimmer_positions): for i in range(3): - tex.draw_texture('combo', 'gleam', x=x+(i*30), y=y+self.glimmer_dict[j] + (self.is_2p*176), color=self.color[j]) + tex.draw_texture('combo', 'gleam', x=x+(i*tex.skin_config["combo_margin"].x), y=y+self.glimmer_dict[j] + (self.is_2p*tex.skin_config["2p_offset"].y), color=self.color[j]) class ScoreCounter: """Displays the total score""" @@ -1765,8 +1771,8 @@ class ScoreCounter: self._cached_score_str = str(self.score) counter = self._cached_score_str - x, y = 150, 185 + (self.is_2p*310) - margin = 20 + x, y = 150 * tex.screen_scale, (185 * tex.screen_scale) + (self.is_2p*310*tex.screen_scale) + margin = tex.skin_config["score_counter_margin"].x total_width = len(counter) * margin start_x = x - total_width for i, digit in enumerate(counter): @@ -1800,7 +1806,7 @@ class ScoreCounterAnimation: # Cache string and layout calculations self.counter_str = str(counter) - self.margin = 20 + self.margin = tex.skin_config["score_counter_margin"].x self.total_width = len(self.counter_str) * self.margin self.y_pos_list = [] @@ -1834,14 +1840,14 @@ class ScoreCounterAnimation: elif self.move_animation_2.is_finished: y = self.move_animation_3.attribute else: - y = 148 + y = 148 * tex.screen_scale y_offset = y * self.direction tex.draw_texture('lane', 'score_number', frame=int(digit), x=start_x + (i * self.margin), - y=y_offset + (self.is_2p * 680), + y=y_offset + (self.is_2p * 680 * tex.screen_scale), color=self.color) class SongInfo: @@ -1849,7 +1855,7 @@ class SongInfo: def __init__(self, song_name: str, genre: int): self.song_name = song_name self.genre = genre - self.song_title = OutlinedText(song_name, 40, ray.WHITE, outline_thickness=5) + self.song_title = OutlinedText(song_name, tex.skin_config["song_info"].font_size, ray.WHITE, outline_thickness=5) self.fade = tex.get_animation(3) def update(self, current_ms: float): @@ -1858,8 +1864,8 @@ class SongInfo: def draw(self): tex.draw_texture('song_info', 'song_num', fade=self.fade.attribute, frame=global_data.songs_played % 4) - text_x = 1252 - self.song_title.texture.width - text_y = 50 - self.song_title.texture.height//2 + text_x = tex.skin_config["song_info"].x - self.song_title.texture.width + text_y = tex.skin_config["song_info"].y - self.song_title.texture.height//2 self.song_title.draw(outline_color=ray.BLACK, x=text_x, y=text_y, color=ray.fade(ray.WHITE, 1 - self.fade.attribute)) if self.genre < 9: @@ -1884,19 +1890,19 @@ class ResultTransition: def draw(self): x = 0 - screen_width = 1280 - while x < screen_width: + while x < tex.screen_width: + tex_height = global_tex.textures['result_transition']['1p_shutter_footer'].height if self.player_num == PlayerNum.TWO_PLAYER: - global_tex.draw_texture('result_transition', '1p_shutter', frame=0, x=x, y=-720 + self.move.attribute) - global_tex.draw_texture('result_transition', '2p_shutter', frame=0, x=x, y=720 - self.move.attribute) - global_tex.draw_texture('result_transition', '1p_shutter_footer', x=x, y=-432 + self.move.attribute) - global_tex.draw_texture('result_transition', '2p_shutter_footer', x=x, y=1008 - self.move.attribute) + global_tex.draw_texture('result_transition', '1p_shutter', frame=0, x=x, y=-tex.screen_height + self.move.attribute) + global_tex.draw_texture('result_transition', '2p_shutter', frame=0, x=x, y=tex.screen_height - self.move.attribute) + global_tex.draw_texture('result_transition', '1p_shutter_footer', x=x, y=-tex_height + self.move.attribute) + global_tex.draw_texture('result_transition', '2p_shutter_footer', x=x, y=tex.screen_height + tex_height - self.move.attribute) else: - global_tex.draw_texture('result_transition', f'{self.player_num}p_shutter', frame=0, x=x, y=-720 + self.move.attribute) - global_tex.draw_texture('result_transition', f'{self.player_num}p_shutter', frame=0, x=x, y=720 - self.move.attribute) - global_tex.draw_texture('result_transition', f'{self.player_num}p_shutter_footer', x=x, y=-432 + self.move.attribute) - global_tex.draw_texture('result_transition', f'{self.player_num}p_shutter_footer', x=x, y=1008 - self.move.attribute) - x += 256 + global_tex.draw_texture('result_transition', f'{self.player_num}p_shutter', frame=0, x=x, y=-tex.screen_height + self.move.attribute) + global_tex.draw_texture('result_transition', f'{self.player_num}p_shutter', frame=0, x=x, y=tex.screen_height - self.move.attribute) + global_tex.draw_texture('result_transition', f'{self.player_num}p_shutter_footer', x=x, y=-tex_height + self.move.attribute) + global_tex.draw_texture('result_transition', f'{self.player_num}p_shutter_footer', x=x, y=tex.screen_height + tex_height - self.move.attribute) + x += tex.screen_width // 5 class GogoTime: """Displays the Gogo Time fire and fireworks""" @@ -1957,14 +1963,14 @@ class ComboAnnounce: thousands_offset = -110 hundreds_offset = 20 if self.combo % 1000 == 0: - tex.draw_texture('combo', 'announce_number', frame=thousands-1, x=-23, fade=fade, index=self.is_2p) - tex.draw_texture('combo', 'announce_add', frame=0, x=435, fade=fade, index=self.is_2p) + tex.draw_texture('combo', 'announce_number', frame=thousands-1, x=-23 * tex.screen_scale, fade=fade, index=self.is_2p) + tex.draw_texture('combo', 'announce_add', frame=0, x=435 * tex.screen_scale, fade=fade, index=self.is_2p) else: if thousands <= 5: - tex.draw_texture('combo', 'announce_add', frame=thousands, x=429 + thousands_offset, fade=fade, index=self.is_2p) + tex.draw_texture('combo', 'announce_add', frame=thousands, x=429 * tex.screen_scale + thousands_offset, fade=fade, index=self.is_2p) if remaining_hundreds > 0: tex.draw_texture('combo', 'announce_number', frame=remaining_hundreds-1, x=hundreds_offset, fade=fade, index=self.is_2p) - text_offset = -30 + text_offset = -30 * tex.screen_scale else: text_offset = 0 tex.draw_texture('combo', 'announce_number', frame=self.combo // 100 - 1, x=0, fade=fade, index=self.is_2p) @@ -2016,7 +2022,7 @@ class BranchIndicator: else: tex.draw_texture('branch', 'level_up', scale=self.level_scale.attribute, fade=self.level_fade.attribute, center=True, index=self.is_2p) tex.draw_texture('branch', self.diff_2, y=(self.diff_down.attribute - self.diff_up.attribute) * self.direction, fade=self.diff_fade.attribute, index=self.is_2p) - tex.draw_texture('branch', self.difficulty, y=(self.diff_up.attribute * (self.direction*-1)) - (70*self.direction*-1), fade=1 - self.diff_fade.attribute, index=self.is_2p) + tex.draw_texture('branch', self.difficulty, y=(self.diff_up.attribute * (self.direction*-1)) - ((70 * tex.screen_scale)*self.direction*-1), fade=1 - self.diff_fade.attribute, index=self.is_2p) class FailAnimation: """Animates the fail effect""" @@ -2062,8 +2068,8 @@ class FailAnimation: tex.draw_texture('ending_anim', 'fail', fade=self.text_fade_in.attribute, index=self.is_2p) tex.draw_texture('ending_anim', 'bachio_l_' + self.name, x=-self.bachio_move_out.attribute - (self.bachio_up.attribute/2), y=self.bachio_down.attribute - self.bachio_up.attribute, frame=self.frame, fade=self.bachio_fade_in.attribute, index=self.is_2p) tex.draw_texture('ending_anim', 'bachio_r_' + self.name, x=self.bachio_move_out.attribute + (self.bachio_up.attribute/2), y=self.bachio_down.attribute - self.bachio_up.attribute, frame=self.frame, fade=self.bachio_fade_in.attribute, index=self.is_2p) - tex.draw_texture('ending_anim', 'bachio_boom', index=0, fade=self.bachio_boom_fade_in.attribute, center=True, scale=self.bachio_boom_scale.attribute, y=(self.is_2p*176)) - tex.draw_texture('ending_anim', 'bachio_boom', index=1, fade=self.bachio_boom_fade_in.attribute, center=True, scale=self.bachio_boom_scale.attribute, y=(self.is_2p*176)) + tex.draw_texture('ending_anim', 'bachio_boom', index=0, fade=self.bachio_boom_fade_in.attribute, center=True, scale=self.bachio_boom_scale.attribute, y=(self.is_2p*tex.skin_config["2p_offset"].y)) + tex.draw_texture('ending_anim', 'bachio_boom', index=1, fade=self.bachio_boom_fade_in.attribute, center=True, scale=self.bachio_boom_scale.attribute, y=(self.is_2p*tex.skin_config["2p_offset"].y)) class ClearAnimation: """Animates the clear effect""" @@ -2112,7 +2118,7 @@ class ClearAnimation: tex.draw_texture('ending_anim', 'clear', index=self.is_2p) else: for i in range(4, -1, -1): - tex.draw_texture('ending_anim', 'clear_separated', frame=i, fade=self.clear_separate_fade_in[i].attribute, x=i*60, y=-self.clear_separate_stretch[i].attribute, y2=self.clear_separate_stretch[i].attribute, index=self.is_2p) + tex.draw_texture('ending_anim', 'clear_separated', frame=i, fade=self.clear_separate_fade_in[i].attribute, x=i*60 * tex.screen_scale, y=-self.clear_separate_stretch[i].attribute, y2=self.clear_separate_stretch[i].attribute, index=self.is_2p) tex.draw_texture('ending_anim', 'clear_highlight', fade=self.clear_highlight_fade_in.attribute, index=self.is_2p) tex.draw_texture('ending_anim', 'bachio_l_' + self.name, x=-self.bachio_move_out.attribute, frame=self.frame, fade=self.bachio_fade_in.attribute, index=self.is_2p) tex.draw_texture('ending_anim', 'bachio_r_' + self.name, x=self.bachio_move_out.attribute, frame=self.frame, fade=self.bachio_fade_in.attribute, index=self.is_2p) @@ -2188,7 +2194,7 @@ class FCAnimation: tex.draw_texture('ending_anim', 'fan_r', frame=self.fan_texture_change.attribute, fade=self.fan_fade_in.attribute, index=self.is_2p) else: for i in range(4, -1, -1): - tex.draw_texture('ending_anim', 'clear_separated', frame=i, fade=self.clear_separate_fade_in[i].attribute, x=i*60, y=-self.clear_separate_stretch[i].attribute, y2=self.clear_separate_stretch[i].attribute, index=self.is_2p) + tex.draw_texture('ending_anim', 'clear_separated', frame=i, fade=self.clear_separate_fade_in[i].attribute, x=i*60 * tex.screen_scale, y=-self.clear_separate_stretch[i].attribute, y2=self.clear_separate_stretch[i].attribute, index=self.is_2p) tex.draw_texture('ending_anim', 'clear_highlight', fade=self.clear_highlight_fade_in.attribute, index=self.is_2p) tex.draw_texture('ending_anim', 'bachio_l_' + 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, index=self.is_2p) 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, index=self.is_2p) @@ -2207,7 +2213,7 @@ class JudgeCounter: self.ok = ok self.bad = bad self.drumrolls = drumrolls - def draw_counter(self, counter: float, x: int, y: int, margin: int, color: ray.Color): + def draw_counter(self, counter: float, x: float, y: float, margin: float, color: ray.Color): counter_str = str(rounded(counter)) counter_len = len(counter_str) for i, digit in enumerate(counter_str): @@ -2224,14 +2230,15 @@ class JudgeCounter: total_notes = self.good + self.ok + self.bad if total_notes == 0: total_notes = 1 - self.draw_counter(self.good / total_notes * 100, 260, 440, 23, self.orange) - self.draw_counter(self.ok / total_notes * 100, 260, 477, 23, self.orange) - self.draw_counter(self.bad / total_notes * 100, 260, 515, 23, self.orange) - self.draw_counter((self.good + self.ok) / total_notes * 100, 270, 388, 23, self.orange) - self.draw_counter(self.good, 180, 440, 23, self.white) - self.draw_counter(self.ok, 180, 477, 23, self.white) - self.draw_counter(self.bad, 180, 515, 23, self.white) - self.draw_counter(self.drumrolls, 180, 577, 23, self.white) + margin = tex.skin_config["judge_counter_margin"].x + self.draw_counter(self.good / total_notes * 100, tex.skin_config["judge_counter_1"].x, tex.skin_config["judge_counter_1"].y, margin, self.orange) + self.draw_counter(self.ok / total_notes * 100, tex.skin_config["judge_counter_1"].x, tex.skin_config["judge_counter_3"].y, margin, self.orange) + self.draw_counter(self.bad / total_notes * 100, tex.skin_config["judge_counter_1"].x, tex.skin_config["judge_counter_4"].x, margin, self.orange) + self.draw_counter((self.good + self.ok) / total_notes * 100, tex.skin_config["judge_counter_3"].x, tex.skin_config["judge_counter_4"].y, margin, self.orange) + self.draw_counter(self.good, tex.skin_config["judge_counter_2"].x, tex.skin_config["judge_counter_1"].y, margin, self.white) + self.draw_counter(self.ok, tex.skin_config["judge_counter_2"].x, tex.skin_config["judge_counter_3"].y, margin, self.white) + self.draw_counter(self.bad, tex.skin_config["judge_counter_2"].x, tex.skin_config["judge_counter_4"].x, margin, self.white) + self.draw_counter(self.drumrolls, tex.skin_config["judge_counter_2"].x, tex.skin_config["judge_counter_4"].width, margin, self.white) class Gauge: """The player's gauge""" @@ -2342,12 +2349,13 @@ class Gauge: tex.draw_texture('gauge', f'{self.player_num}p_unfilled' + self.string_diff, index=self.is_2p, mirror=mirror) gauge_length = int(self.gauge_length) clear_point = self.clear_start[self.difficulty] - tex.draw_texture('gauge', f'{self.player_num}p_bar', x2=min(gauge_length*8, (clear_point - 1)*8)-8, index=self.is_2p) + bar_width = tex.textures["gauge"][f"{self.player_num}p_bar"].width + tex.draw_texture('gauge', f'{self.player_num}p_bar', x2=min(gauge_length*bar_width, (clear_point - 1)*bar_width)-bar_width, index=self.is_2p) if gauge_length >= clear_point - 1: - tex.draw_texture('gauge', 'bar_clear_transition', x=(clear_point - 1)*8, index=self.is_2p, mirror=mirror) + tex.draw_texture('gauge', 'bar_clear_transition', x=(clear_point - 1)*bar_width, index=self.is_2p, mirror=mirror) if gauge_length > clear_point: - tex.draw_texture('gauge', 'bar_clear_top', x=(clear_point) * 8, x2=(gauge_length-clear_point)*8, index=self.is_2p, mirror=mirror) - tex.draw_texture('gauge', 'bar_clear_bottom', x=(clear_point) * 8, x2=(gauge_length-clear_point)*8, index=self.is_2p) + tex.draw_texture('gauge', 'bar_clear_top', x=(clear_point) * bar_width, x2=(gauge_length-clear_point)*bar_width, index=self.is_2p, mirror=mirror) + tex.draw_texture('gauge', 'bar_clear_bottom', x=(clear_point) * bar_width, x2=(gauge_length-clear_point)*bar_width, index=self.is_2p) # Rainbow effect for full gauge if gauge_length == self.gauge_max and self.rainbow_fade_in is not None: @@ -2356,11 +2364,11 @@ class Gauge: tex.draw_texture('gauge', 'rainbow' + self.string_diff, frame=self.rainbow_animation.attribute, fade=self.rainbow_fade_in.attribute, index=self.is_2p, mirror=mirror) if self.gauge_update_anim is not None and gauge_length <= self.gauge_max and gauge_length > self.previous_length: if gauge_length == self.clear_start[self.difficulty]: - tex.draw_texture('gauge', 'bar_clear_transition_fade', x=gauge_length*8, fade=self.gauge_update_anim.attribute, index=self.is_2p, mirror=mirror) + tex.draw_texture('gauge', 'bar_clear_transition_fade', x=gauge_length*bar_width, fade=self.gauge_update_anim.attribute, index=self.is_2p, mirror=mirror) elif gauge_length > self.clear_start[self.difficulty]: - tex.draw_texture('gauge', 'bar_clear_fade', x=gauge_length*8, fade=self.gauge_update_anim.attribute, index=self.is_2p) + tex.draw_texture('gauge', 'bar_clear_fade', x=gauge_length*bar_width, fade=self.gauge_update_anim.attribute, index=self.is_2p) else: - tex.draw_texture('gauge', f'{self.player_num}p_bar_fade', x=gauge_length*8, fade=self.gauge_update_anim.attribute, index=self.is_2p) + tex.draw_texture('gauge', f'{self.player_num}p_bar_fade', x=gauge_length*bar_width, fade=self.gauge_update_anim.attribute, index=self.is_2p) tex.draw_texture('gauge', 'overlay' + self.string_diff, fade=0.15, index=self.is_2p, mirror=mirror) # Draw clear status indicators diff --git a/scenes/loading.py b/scenes/loading.py index c03951f..bef9ed2 100644 --- a/scenes/loading.py +++ b/scenes/loading.py @@ -7,7 +7,7 @@ from libs.animation import Animation from libs.global_objects import AllNetIcon from libs.screen import Screen from libs.song_hash import build_song_hashes -from libs.texture import SCREEN_HEIGHT, SCREEN_WIDTH, tex +from libs.texture import tex from libs.utils import get_current_ms, global_data from libs.file_navigator import navigator @@ -17,8 +17,8 @@ logger = logging.getLogger(__name__) class LoadScreen(Screen): def __init__(self, name: str): super().__init__(name) - self.width = SCREEN_WIDTH - self.height = SCREEN_HEIGHT + self.width = tex.screen_width + self.height = tex.screen_height self.songs_loaded = False self.navigator_started = False self.loading_complete = False diff --git a/scenes/practice/game.py b/scenes/practice/game.py index 4d43c4b..2b75950 100644 --- a/scenes/practice/game.py +++ b/scenes/practice/game.py @@ -12,7 +12,7 @@ from libs.global_data import Modifiers, PlayerNum, global_data from libs.tja import Balloon, Drumroll, Note, NoteType, TJAParser, apply_modifiers from libs.utils import get_current_ms from libs.texture import tex -from scenes.game import DrumHitEffect, DrumType, GameScreen, JudgeCounter, LaneHitEffect, Player, SCREEN_WIDTH, Side +from scenes.game import DrumHitEffect, DrumType, GameScreen, JudgeCounter, LaneHitEffect, Player, Side logger = logging.getLogger(__name__) @@ -23,8 +23,8 @@ class PracticeGameScreen(GameScreen): def init_tja(self, song: Path): """Initialize the TJA file""" - self.tja = TJAParser(song, start_delay=self.start_delay, distance=SCREEN_WIDTH - GameScreen.JUDGE_X) - self.scrobbling_tja = TJAParser(song, start_delay=self.start_delay, distance=SCREEN_WIDTH - GameScreen.JUDGE_X) + self.tja = TJAParser(song, start_delay=self.start_delay, distance=tex.screen_width - GameScreen.JUDGE_X) + self.scrobbling_tja = TJAParser(song, start_delay=self.start_delay, distance=tex.screen_width - GameScreen.JUDGE_X) global_data.session_data[global_data.player_num].song_title = self.tja.metadata.title.get(global_data.config['general']['language'].lower(), self.tja.metadata.title['en']) 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') @@ -155,41 +155,44 @@ class PracticeGameScreen(GameScreen): time_diff = load_ms - self.scrobble_time - self.scrobble_move.attribute else: time_diff = load_ms - current_ms - return int(width + pixels_per_frame * 0.06 * time_diff - 64) + return int(width + pixels_per_frame * 0.06 * time_diff - (tex.textures["notes"]["1"].width//2)) 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 - 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) + ((self.tja.distance * pixels_per_frame) / pixels_per_frame_x)) def draw_drumroll(self, current_ms: float, head: Drumroll, current_eighth: int, index: 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(tex.screen_width, current_ms, head.load_ms, head.pixels_per_frame_x) tail = next((note for note in self.scrobble_note_list if note.index == index+1), self.scrobble_note_list[index+1]) is_big = int(head.type == NoteType.ROLL_HEAD_L) - end_position = self.get_position_x(SCREEN_WIDTH, current_ms, tail.load_ms, tail.pixels_per_frame_x) + end_position = self.get_position_x(tex.screen_width, current_ms, tail.load_ms, tail.pixels_per_frame_x) length = end_position - start_position color = ray.Color(255, head.color, head.color, 255) + y = tex.skin_config["notes"].y + moji_y = tex.skin_config["moji"].y + moji_x = tex.skin_config["moji"].x if head.display: 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+(tex.textures["notes"]["8"].width//2), y=y, x2=length+tex.skin_config["drumroll_width_offset"].width, color=color) 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+tex.textures["notes"]["drumroll_big_tail"].width//2, y=y, color=color) else: - tex.draw_texture('notes', "drumroll_tail", x=end_position+64, y=192, color=color) - tex.draw_texture('notes', str(head.type), frame=current_eighth % 2, x=start_position, y=192, color=color) + tex.draw_texture('notes', "drumroll_tail", x=end_position+tex.textures["notes"]["drumroll_tail"].width//2, y=y, color=color) + tex.draw_texture('notes', str(head.type), frame=current_eighth % 2, x=start_position, y=y, color=color) - tex.draw_texture('notes', 'moji_drumroll_mid', x=start_position + 60, y=323, x2=length) - tex.draw_texture('notes', 'moji', frame=head.moji, x=(start_position - (168//2)) + 64, y=323) - tex.draw_texture('notes', 'moji', frame=tail.moji, x=(end_position - (168//2)) + 32, y=323) + tex.draw_texture('notes', 'moji_drumroll_mid', x=start_position + tex.skin_config["moji_drumroll"].x, y=moji_y, x2=length) + tex.draw_texture('notes', 'moji', frame=head.moji, x=start_position - moji_x, y=moji_y) + tex.draw_texture('notes', 'moji', frame=tail.moji, x=end_position - tex.skin_config["moji_drumroll"].width, y=moji_y) def draw_balloon(self, current_ms: float, head: Balloon, current_eighth: int, index: int): """Draws a balloon in the player's lane""" - offset = 12 - start_position = self.get_position_x(SCREEN_WIDTH, current_ms, head.load_ms, head.pixels_per_frame_x) + offset = tex.skin_config["balloon_offset"].x + start_position = self.get_position_x(tex.screen_width, current_ms, head.load_ms, head.pixels_per_frame_x) tail = next((note for note in self.scrobble_note_list if note.index == index+1), self.scrobble_note_list[index+1]) - end_position = self.get_position_x(SCREEN_WIDTH, current_ms, tail.load_ms, tail.pixels_per_frame_x) - pause_position = 349 + end_position = self.get_position_x(tex.screen_width, current_ms, tail.load_ms, tail.pixels_per_frame_x) + pause_position = tex.skin_config["balloon_pause_position"].x if current_ms >= tail.hit_ms: position = end_position elif current_ms >= head.hit_ms: @@ -197,23 +200,23 @@ class PracticeGameScreen(GameScreen): else: position = start_position if head.display: - tex.draw_texture('notes', str(head.type), frame=current_eighth % 2, x=position-offset, y=192) - tex.draw_texture('notes', '10', frame=current_eighth % 2, x=position-offset+128, y=192) + tex.draw_texture('notes', str(head.type), frame=current_eighth % 2, x=position-offset, y=tex.skin_config["notes"].y) + tex.draw_texture('notes', '10', frame=current_eighth % 2, x=position-offset+tex.textures["notes"]["10"].width, y=tex.skin_config["notes"].y) def draw_scrobble_list(self): bar_draws = [] for bar in reversed(self.bars): if not bar.display: continue - x_position = self.get_position_x(SCREEN_WIDTH, self.current_ms, bar.load_ms, bar.pixels_per_frame_x) + x_position = self.get_position_x(tex.screen_width, self.current_ms, bar.load_ms, bar.pixels_per_frame_x) y_position = self.get_position_y(self.current_ms, bar.load_ms, bar.pixels_per_frame_y, bar.pixels_per_frame_x) - if x_position < 236 or x_position > SCREEN_WIDTH: + if x_position < tex.skin_config["past_judge_circle"].x or x_position > tex.screen_width: continue if hasattr(bar, 'is_branch_start'): frame = 1 else: frame = 0 - bar_draws.append((str(bar.type), frame, x_position+60, y_position+190)) + bar_draws.append((str(bar.type), frame, x_position + tex.skin_config["moji_drumroll"].x, y_position+tex.skin_config["moji_drumroll"].y)) for bar_type, frame, x, y in bar_draws: tex.draw_texture('notes', bar_type, frame=frame, x=x, y=y) @@ -225,27 +228,27 @@ class PracticeGameScreen(GameScreen): if isinstance(note, Drumroll): self.draw_drumroll(self.current_ms, note, 0, note.index) elif isinstance(note, Balloon) and not note.is_kusudama: - x_position = self.get_position_x(SCREEN_WIDTH, self.current_ms, note.load_ms, note.pixels_per_frame_x) + x_position = self.get_position_x(tex.screen_width, self.current_ms, note.load_ms, note.pixels_per_frame_x) y_position = self.get_position_y(self.current_ms, note.load_ms, note.pixels_per_frame_y, note.pixels_per_frame_x) - if x_position < 236 or x_position > SCREEN_WIDTH: + if x_position < tex.skin_config["past_judge_circle"].x or x_position > tex.screen_width: continue self.draw_balloon(self.current_ms, note, 0, note.index) - 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 - tex.skin_config["moji"].x, y=tex.skin_config["moji"].y + y_position) else: - x_position = self.get_position_x(SCREEN_WIDTH, self.current_ms, note.load_ms, note.pixels_per_frame_x) + x_position = self.get_position_x(tex.screen_width, self.current_ms, note.load_ms, note.pixels_per_frame_x) y_position = self.get_position_y(self.current_ms, note.load_ms, note.pixels_per_frame_y, note.pixels_per_frame_x) - if x_position < 236 or x_position > SCREEN_WIDTH: + if x_position < tex.skin_config["past_judge_circle"].x or x_position > tex.screen_width: continue if note.display: - tex.draw_texture('notes', str(note.type), x=x_position, y=y_position+192, center=True) + tex.draw_texture('notes', str(note.type), x=x_position, y=y_position+tex.skin_config["notes"].y, center=True) color = ray.WHITE if note.index in self.player_1.input_log: if self.player_1.input_log[note.index] == 'GOOD': color = ray.Color(255, 233, 0, 255) elif self.player_1.input_log[note.index] == 'BAD': color = ray.Color(34, 189, 243, 255) - tex.draw_texture('notes', 'moji', frame=note.moji, x=x_position - (168//2) + 64, y=323 + y_position, color=color) + tex.draw_texture('notes', 'moji', frame=note.moji, x=x_position - tex.skin_config["moji"].x, y=tex.skin_config["moji"].y + y_position, color=color) def draw(self): self.background.draw() @@ -263,9 +266,9 @@ class PracticeGameScreen(GameScreen): progress = min((self.scrobble_time + self.scrobble_move.attribute - self.bars[0].hit_ms) / self.player_1.end_time, 1) else: progress = min(self.current_ms / self.player_1.end_time, 1) - tex.draw_texture('practice', 'progress_bar', x2=progress * 890) + tex.draw_texture('practice', 'progress_bar', x2=progress * tex.skin_config["practice_progress_bar_width"].width) for marker in self.markers: - tex.draw_texture('practice', 'gogo_marker', x=((marker - self.bars[0].hit_ms) / self.player_1.end_time) * 890) + tex.draw_texture('practice', 'gogo_marker', x=((marker - self.bars[0].hit_ms) / self.player_1.end_time) * tex.skin_config["practice_progress_bar_width"].width) self.draw_overlay() @@ -276,9 +279,9 @@ class PracticePlayer(Player): self.gauge = None self.paused = False - def spawn_hit_effects(self, note_type: DrumType, side: Side): - self.lane_hit_effect = LaneHitEffect(note_type, self.is_2p) - self.draw_drum_hit_list.append(PracticeDrumHitEffect(note_type, side, self.is_2p, player_num=self.player_num)) + def spawn_hit_effects(self, drum_type: DrumType, side: Side): + self.lane_hit_effect = LaneHitEffect(drum_type, self.is_2p) + self.draw_drum_hit_list.append(PracticeDrumHitEffect(drum_type, side, self.is_2p, player_num=self.player_num)) def draw_overlays(self, mask_shader: ray.Shader): # Group 4: Lane covers and UI elements (batch similar textures) @@ -306,13 +309,13 @@ class PracticePlayer(Player): # Group 7: Player-specific elements if not self.modifiers.auto: if self.is_2p: - self.nameplate.draw(-62, 371) + self.nameplate.draw(tex.skin_config["game_nameplate_1p"].x, tex.skin_config["game_nameplate_1p"].y) else: - self.nameplate.draw(-62, 285) + self.nameplate.draw(tex.skin_config["game_nameplate_2p"].x, tex.skin_config["game_nameplate_2p"].y) else: tex.draw_texture('lane', 'auto_icon', index=self.is_2p) self.draw_modifiers() - self.chara.draw(y=(self.is_2p*536)) + self.chara.draw(y=(self.is_2p*tex.skin_config["game_2p_offset"].y)) # Group 8: Special animations and counters if self.drumroll_counter is not None: @@ -321,7 +324,6 @@ class PracticePlayer(Player): self.balloon_anim.draw() if self.kusudama_anim is not None: self.kusudama_anim.draw() - #ray.draw_circle(game_screen.width//2, game_screen.height, 300, ray.ORANGE) def draw(self, ms_from_start: float, start_ms: float, mask_shader: ray.Shader, dan_transition = None): # Group 1: Background and lane elements diff --git a/scenes/result.py b/scenes/result.py index 8f81820..49c02cf 100644 --- a/scenes/result.py +++ b/scenes/result.py @@ -26,7 +26,7 @@ class State: class ResultScreen(Screen): def on_screen_start(self): super().on_screen_start() - self.song_info = OutlinedText(global_data.session_data[global_data.player_num].song_title, 40, ray.WHITE, outline_thickness=5) + self.song_info = OutlinedText(global_data.session_data[global_data.player_num].song_title, tex.skin_config["song_info_result"].font_size, ray.WHITE, outline_thickness=5) audio.play_sound('bgm', 'music') self.fade_in = FadeIn(global_data.player_num) self.fade_out = tex.get_animation(0) @@ -34,7 +34,7 @@ class ResultScreen(Screen): self.allnet_indicator = AllNetIcon() self.start_ms = get_current_ms() self.is_skipped = False - self.background = Background(global_data.player_num, 1280) + self.background = Background(global_data.player_num, tex.screen_width) self.player_1 = ResultPlayer(global_data.player_num, False, False) def on_screen_end(self, next_screen: str): @@ -66,13 +66,13 @@ class ResultScreen(Screen): def draw_overlay(self): self.fade_in.draw() - ray.draw_rectangle(0, 0, 1280, 720, ray.fade(ray.BLACK, self.fade_out.attribute)) + ray.draw_rectangle(0, 0, tex.screen_width, tex.screen_height, ray.fade(ray.BLACK, self.fade_out.attribute)) self.coin_overlay.draw() self.allnet_indicator.draw() def draw_song_info(self): tex.draw_texture('song_info', 'song_num', frame=global_data.songs_played%4) - self.song_info.draw(outline_color=ray.BLACK, x=1252 - self.song_info.texture.width, y=35 - self.song_info.texture.height / 2) + self.song_info.draw(outline_color=ray.BLACK, x=tex.skin_config["song_info_result"].x - self.song_info.texture.width, y=tex.skin_config["song_info_result"].y - self.song_info.texture.height / 2) def draw(self): self.background.draw() @@ -82,25 +82,26 @@ class ResultScreen(Screen): class Background: - def __init__(self, player_num: PlayerNum, width: int): + def __init__(self, player_num: PlayerNum, width: float): self.player_num = player_num self.width = width def draw(self): x = 0 + footer_height = tex.textures["background"]["footer_1p"].height if self.player_num == PlayerNum.TWO_PLAYER: while x < self.width: - tex.draw_texture('background', 'background_1p', x=x, y=-360) - tex.draw_texture('background', 'background_2p', x=x, y=360) - tex.draw_texture('background', 'footer_1p', x=x, y=-72) - tex.draw_texture('background', 'footer_2p', x=x, y=648) - x += 256 + tex.draw_texture('background', 'background_1p', x=x, y=-(tex.screen_height//2)) + tex.draw_texture('background', 'background_2p', x=x, y=tex.screen_height//2) + tex.draw_texture('background', 'footer_1p', x=x, y=-footer_height) + tex.draw_texture('background', 'footer_2p', x=x, y=tex.screen_height-footer_height) + x += tex.screen_width // 5 else: while x < self.width: - tex.draw_texture('background', f'background_{self.player_num}p', x=x, y=-360) - tex.draw_texture('background', f'background_{self.player_num}p', x=x, y=360) - tex.draw_texture('background', f'footer_{self.player_num}p', x=x, y=-72) - tex.draw_texture('background', f'footer_{self.player_num}p', x=x, y=648) - x += 256 + tex.draw_texture('background', f'background_{self.player_num}p', x=x, y=-(tex.screen_height//2)) + tex.draw_texture('background', f'background_{self.player_num}p', x=x, y=(tex.screen_height//2)) + tex.draw_texture('background', f'footer_{self.player_num}p', x=x, y=-footer_height) + tex.draw_texture('background', f'footer_{self.player_num}p', x=x, y=tex.screen_height-footer_height) + x += tex.screen_width // 5 tex.draw_texture('background', 'result_text') class ResultPlayer: @@ -116,7 +117,7 @@ class ResultPlayer: self.crown = None self.state = None self.high_score_indicator = None - self.chara = Chara2D(int(self.player_num) - 1, 100) + self.chara = Chara2D(int(self.player_num) - 1) session_data = global_data.session_data[self.player_num] self.score_animator = ScoreAnimator(session_data.result_data.score) plate_info = global_data.config[f'nameplate_{self.player_num}p'] @@ -206,7 +207,7 @@ class ResultPlayer: continue score_str = str(score)[::-1] for i, digit in enumerate(score_str): - tex.draw_texture('score', 'judge_num', frame=int(digit), x=-(i*24), index=j+(self.is_2p*5)) + tex.draw_texture('score', 'judge_num', frame=int(digit), x=-(i*tex.skin_config["score_info_counter_margin"].x), index=j+(self.is_2p*5)) def draw_total_score(self): """ @@ -217,7 +218,7 @@ class ResultPlayer: tex.draw_texture('score', 'score_shinuchi', index=self.is_2p) if self.score != '': for i in range(len(str(self.score))): - tex.draw_texture('score', 'score_num', x=-(i*21), frame=int(str(self.score)[::-1][i]), index=self.is_2p) + tex.draw_texture('score', 'score_num', x=-(i*tex.skin_config["result_score_margin"].x), frame=int(str(self.score)[::-1][i]), index=self.is_2p) def draw_modifiers(self): """Draw the modifiers if enabled.""" @@ -243,7 +244,7 @@ class ResultPlayer: elif self.state == State.CLEAR: tex.draw_texture('background', 'gradient_clear', fade=min(0.4, self.fade_in_bottom.attribute)) else: - y = -288 if self.has_2p else 0 + y = tex.skin_config["result_2p_offset"].y if self.has_2p else 0 if self.state == State.FAIL: tex.draw_texture('background', 'gradient_fail', fade=min(0.4, self.fade_in_bottom.attribute), y=y) elif self.state == State.CLEAR: @@ -270,10 +271,10 @@ class ResultPlayer: if self.high_score_indicator is not None: self.high_score_indicator.draw() - self.chara.draw(y=100+(self.is_2p*360)) + self.chara.draw(y=tex.skin_config["result_chara"].y+(self.is_2p*tex.screen_height//2)) if self.gauge is not None: self.gauge.draw() - self.nameplate.draw(265, 80+(self.is_2p*300)) + self.nameplate.draw(tex.skin_config["result_nameplate"].x, tex.skin_config["result_nameplate"].y+(self.is_2p*tex.skin_config["result_nameplate"].height)) class Crown: """Represents a crown animation""" @@ -375,7 +376,7 @@ class BottomCharacters: def draw_flowers(self): tex.draw_texture('bottom','flowers', y=-self.flower_up.attribute, frame=self.flower_index) - tex.draw_texture('bottom','flowers', y=-self.flower_up.attribute, frame=self.flower_index, x=792, mirror='horizontal') + tex.draw_texture('bottom','flowers', y=-self.flower_up.attribute, frame=self.flower_index, x=tex.skin_config["result_flowers_offset"].x, mirror='horizontal') def draw(self): self.draw_flowers() @@ -402,20 +403,21 @@ class FadeIn: def draw(self): x = 0 + footer_height = tex.textures["background"]["footer_1p"].height if self.player_num == PlayerNum.TWO_PLAYER: - while x < 1280: - tex.draw_texture('background', 'background_1p', x=x, y=-360, fade=self.fadein.attribute) - tex.draw_texture('background', 'background_2p', x=x, y=360, fade=self.fadein.attribute) - tex.draw_texture('background', 'footer_1p', x=x, y=-72, fade=self.fadein.attribute) - tex.draw_texture('background', 'footer_2p', x=x, y=648, fade=self.fadein.attribute) - x += 256 + while x < tex.screen_width: + tex.draw_texture('background', 'background_1p', x=x, y=-tex.screen_height//2, fade=self.fadein.attribute) + tex.draw_texture('background', 'background_2p', x=x, y=tex.screen_height//2, fade=self.fadein.attribute) + tex.draw_texture('background', 'footer_1p', x=x, y=-footer_height, fade=self.fadein.attribute) + tex.draw_texture('background', 'footer_2p', x=x, y=tex.screen_height - footer_height, fade=self.fadein.attribute) + x += tex.screen_width // 5 else: - while x < 1280: - tex.draw_texture('background', f'background_{self.player_num}p', x=x, y=-360, fade=self.fadein.attribute) - tex.draw_texture('background', f'background_{self.player_num}p', x=x, y=360, fade=self.fadein.attribute) - tex.draw_texture('background', f'footer_{self.player_num}p', x=x, y=-72, fade=self.fadein.attribute) - tex.draw_texture('background', f'footer_{self.player_num}p', x=x, y=648, fade=self.fadein.attribute) - x += 256 + while x < tex.screen_width: + tex.draw_texture('background', f'background_{self.player_num}p', x=x, y=-tex.screen_height//2, fade=self.fadein.attribute) + tex.draw_texture('background', f'background_{self.player_num}p', x=x, y=tex.screen_height//2, fade=self.fadein.attribute) + tex.draw_texture('background', f'footer_{self.player_num}p', x=x, y=-footer_height, fade=self.fadein.attribute) + tex.draw_texture('background', f'footer_{self.player_num}p', x=x, y=tex.screen_height - footer_height, fade=self.fadein.attribute) + x += tex.screen_width // 5 class ScoreAnimator: """Animates a number from left to right""" @@ -461,7 +463,7 @@ class HighScoreIndicator: def draw(self): tex.draw_texture('score', 'high_score', y=self.move.attribute, fade=self.fade.attribute, index=self.is_2p) for i in range(len(str(self.score_diff))): - tex.draw_texture('score', 'high_score_num', x=-(i*14), frame=int(str(self.score_diff)[::-1][i]), y=self.move.attribute, fade=self.fade.attribute, index=self.is_2p) + tex.draw_texture('score', 'high_score_num', x=-(i*tex.skin_config['high_score_indicator_margin'].x), frame=int(str(self.score_diff)[::-1][i]), y=self.move.attribute, fade=self.fade.attribute, index=self.is_2p) class Gauge: @@ -504,16 +506,17 @@ class Gauge: scale = 10/11 tex.draw_texture('gauge', f'{self.player_num}p_unfilled' + self.string_diff, scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p) gauge_length = int(self.gauge_length) + bar_width = tex.textures["gauge"][f"{self.player_num}p_bar"].width if gauge_length == self.gauge_max: if 0 < self.rainbow_animation.attribute < 8: tex.draw_texture('gauge', 'rainbow' + self.string_diff, frame=self.rainbow_animation.attribute-1, scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p) tex.draw_texture('gauge', 'rainbow' + self.string_diff, frame=self.rainbow_animation.attribute, scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p) else: - tex.draw_texture('gauge', f'{self.player_num}p_bar', x2=(gauge_length*8)-48, scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p) + tex.draw_texture('gauge', f'{self.player_num}p_bar', x2=(gauge_length*bar_width)-(bar_width*6), scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p) if gauge_length >= self.clear_start[self.difficulty] - 1: - tex.draw_texture('gauge', 'bar_clear_transition', x=(self.clear_start[self.difficulty]*8)-56, scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p) - tex.draw_texture('gauge', 'bar_clear_top', x=(self.clear_start[self.difficulty]*8)-48, x2=(gauge_length - self.clear_start[self.difficulty])*8, scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p) - tex.draw_texture('gauge', 'bar_clear_bottom', x=(self.clear_start[self.difficulty]*8)-48, x2=(gauge_length - self.clear_start[self.difficulty])*8, scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p) + tex.draw_texture('gauge', 'bar_clear_transition', x=(self.clear_start[self.difficulty]*bar_width)-(bar_width*7), scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p) + tex.draw_texture('gauge', 'bar_clear_top', x=(self.clear_start[self.difficulty]*bar_width)-(bar_width*6), x2=(gauge_length - self.clear_start[self.difficulty])*bar_width, scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p) + tex.draw_texture('gauge', 'bar_clear_bottom', x=(self.clear_start[self.difficulty]*bar_width)-(bar_width*6), x2=(gauge_length - self.clear_start[self.difficulty])*bar_width, scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p) tex.draw_texture('gauge', 'overlay' + self.string_diff, scale=scale, fade=min(0.15, self.gauge_fade_in.attribute), index=self.is_2p) tex.draw_texture('gauge', 'footer', scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p) diff --git a/scenes/settings.py b/scenes/settings.py index 89023c2..5d492e5 100644 --- a/scenes/settings.py +++ b/scenes/settings.py @@ -9,8 +9,8 @@ from libs.utils import ( is_l_kat_pressed, is_r_don_pressed, is_r_kat_pressed, - save_config, ) +from libs.config import save_config logger = logging.getLogger(__name__) diff --git a/scenes/song_select.py b/scenes/song_select.py index b2c0c26..da7ae57 100644 --- a/scenes/song_select.py +++ b/scenes/song_select.py @@ -12,7 +12,7 @@ from libs.file_navigator import Directory, SongBox, SongFile from libs.global_data import Difficulty, Modifiers, PlayerNum from libs.global_objects import AllNetIcon, CoinOverlay, Nameplate, Indicator, Timer from libs.screen import Screen -from libs.texture import SCREEN_WIDTH, tex +from libs.texture import tex from libs.transition import Transition from libs.utils import ( OutlinedText, @@ -329,15 +329,15 @@ class SongSelectScreen(Screen): self.draw_background_diffs() if self.navigator.genre_bg is not None and self.state == State.BROWSING: - self.navigator.genre_bg.draw(95) + self.navigator.genre_bg.draw(tex.skin_config["boxes"].y) for i, item in enumerate(self.navigator.items): box = item.box - if -156 <= box.position <= SCREEN_WIDTH + 144: - if box.position <= 500: - box.draw(box.position - int(self.move_away.attribute), 95, self.player_1.is_ura, fade_override=self.diff_fade_out.attribute) + if (-156 * tex.screen_scale) <= box.position <= (tex.screen_width + 144) * tex.screen_scale: + if box.position <= (500 * tex.screen_scale): + box.draw(box.position - int(self.move_away.attribute), tex.skin_config["boxes"].y, self.player_1.is_ura, fade_override=self.diff_fade_out.attribute) else: - box.draw(box.position + int(self.move_away.attribute), 95, self.player_1.is_ura, fade_override=self.diff_fade_out.attribute) + box.draw(box.position + int(self.move_away.attribute), tex.skin_config["boxes"].y, self.player_1.is_ura, fade_override=self.diff_fade_out.attribute) if self.state == State.BROWSING: tex.draw_texture('global', 'arrow', index=0, x=-(self.blue_arrow_move.attribute*2), fade=self.blue_arrow_fade.attribute) @@ -363,7 +363,7 @@ class SongSelectScreen(Screen): if isinstance(curr_item, SongFile): curr_item.box.draw_score_history() - self.indicator.draw(410, 575) + self.indicator.draw(tex.skin_config['song_select_indicator'].x, tex.skin_config['song_select_indicator'].y) tex.draw_texture('global', 'song_num_bg', fade=0.75) tex.draw_texture('global', 'song_num', frame=global_data.songs_played % 4) @@ -403,7 +403,7 @@ class SongSelectPlayer: self.selected_diff_text_fadein = tex.get_animation(37, is_copy=True) # Player-specific objects - self.chara = Chara2D(int(self.player_num) - 1, 100) + self.chara = Chara2D(int(self.player_num) - 1) plate_info = global_data.config[f'nameplate_{self.player_num}p'] self.nameplate = Nameplate(plate_info['name'], plate_info['title'], self.player_num, plate_info['dan'], plate_info['gold'], plate_info['rainbow'], plate_info['title_bg']) @@ -436,10 +436,10 @@ class SongSelectPlayer: def on_song_selected(self, selected_song): """Called when a song is selected""" - if 4 not in selected_song.tja.metadata.course_data: + if Difficulty.URA not in selected_song.tja.metadata.course_data: self.is_ura = False - elif (4 in selected_song.tja.metadata.course_data and - 3 not in selected_song.tja.metadata.course_data): + elif (Difficulty.URA in selected_song.tja.metadata.course_data and + Difficulty.ONI not in selected_song.tja.metadata.course_data): self.is_ura = True def handle_input_browsing(self, last_moved, selected_item): @@ -644,46 +644,47 @@ class SongSelectPlayer: def draw_selector(self, is_half: bool): fade = 0.5 if (self.neiro_selector is not None or self.modifier_selector is not None) else self.text_fade_in.attribute direction = 1 if self.diff_select_move_right else -1 + offset = tex.skin_config["selector_offset"].x if self.selected_difficulty <= -1 or self.prev_diff == -1: if self.prev_diff == -1 and self.selected_difficulty >= 0: if not self.diff_selector_move_2.is_finished: name = f'{self.player_num}p_balloon_half' if is_half else f'{self.player_num}p_balloon' - tex.draw_texture('diff_select', name, x=((self.prev_diff+3) * 70) - 220 + (self.diff_selector_move_2.attribute * direction), fade=fade) + tex.draw_texture('diff_select', name, x=((self.prev_diff+3) * tex.skin_config["selector_balloon_offset_2"].x) + tex.skin_config["selector_balloon_offset_1"].x + (self.diff_selector_move_2.attribute * direction), fade=fade) name = f'{self.player_num}p_outline_back_half' if is_half else f'{self.player_num}p_outline_back' - tex.draw_texture('diff_select', name, x=((self.prev_diff+3) * 70) + (self.diff_selector_move_2.attribute * direction)) + tex.draw_texture('diff_select', name, x=((self.prev_diff+3) * tex.skin_config["selector_balloon_offset_2"].x) + (self.diff_selector_move_2.attribute * direction)) else: difficulty = min(Difficulty.ONI, self.selected_difficulty) name = f'{self.player_num}p_balloon_half' if is_half else f'{self.player_num}p_balloon' - tex.draw_texture('diff_select', name, x=(difficulty * 115), fade=fade) + tex.draw_texture('diff_select', name, x=(difficulty * offset), fade=fade) name = f'{self.player_num}p_outline_half' if is_half else f'{self.player_num}p_outline' - tex.draw_texture('diff_select', name, x=(difficulty * 115)) + tex.draw_texture('diff_select', name, x=(difficulty * offset)) elif not self.diff_selector_move_2.is_finished: name = f'{self.player_num}p_outline_back_half' if is_half else f'{self.player_num}p_outline_back' - tex.draw_texture('diff_select', name, x=((self.prev_diff+3) * 70) + (self.diff_selector_move_2.attribute * direction)) + tex.draw_texture('diff_select', name, x=((self.prev_diff+3) * tex.skin_config["selector_balloon_offset_2"].x) + (self.diff_selector_move_2.attribute * direction)) if self.selected_difficulty != -3: name = f'{self.player_num}p_balloon_half' if is_half else f'{self.player_num}p_balloon' - tex.draw_texture('diff_select', name, x=((self.prev_diff+3) * 70) - 220 + (self.diff_selector_move_2.attribute * direction), fade=fade) + tex.draw_texture('diff_select', name, x=((self.prev_diff+3) * tex.skin_config["selector_balloon_offset_2"].x) + tex.skin_config["selector_balloon_offset_1"].x + (self.diff_selector_move_2.attribute * direction), fade=fade) else: name = f'{self.player_num}p_outline_back_half' if is_half else f'{self.player_num}p_outline_back' - tex.draw_texture('diff_select', name, x=((self.selected_difficulty+3) * 70)) + tex.draw_texture('diff_select', name, x=((self.selected_difficulty+3) * tex.skin_config["selector_balloon_offset_2"].x)) if self.selected_difficulty != -3: name = f'{self.player_num}p_balloon_half' if is_half else f'{self.player_num}p_balloon' - tex.draw_texture('diff_select', name, x=((self.selected_difficulty+3) * 70) - 220, fade=fade) + tex.draw_texture('diff_select', name, x=((self.selected_difficulty+3) * tex.skin_config["selector_balloon_offset_2"].x) + tex.skin_config["selector_balloon_offset_1"].x, fade=fade) else: if self.prev_diff == -1: return if not self.diff_selector_move_1.is_finished: difficulty = min(Difficulty.ONI, self.prev_diff) name = f'{self.player_num}p_balloon_half' if is_half else f'{self.player_num}p_balloon' - tex.draw_texture('diff_select', name, x=(difficulty * 115) + (self.diff_selector_move_1.attribute * direction), fade=fade) + tex.draw_texture('diff_select', name, x=(difficulty * offset) + (self.diff_selector_move_1.attribute * direction), fade=fade) name = f'{self.player_num}p_outline_half' if is_half else f'{self.player_num}p_outline' - tex.draw_texture('diff_select', name, x=(difficulty * 115) + (self.diff_selector_move_1.attribute * direction)) + tex.draw_texture('diff_select', name, x=(difficulty * offset) + (self.diff_selector_move_1.attribute * direction)) else: difficulty = min(Difficulty.ONI, self.selected_difficulty) name = f'{self.player_num}p_balloon_half' if is_half else f'{self.player_num}p_balloon' - tex.draw_texture('diff_select', name, x=(difficulty * 115), fade=fade) + tex.draw_texture('diff_select', name, x=(difficulty * offset), fade=fade) name = f'{self.player_num}p_outline_half' if is_half else f'{self.player_num}p_outline' - tex.draw_texture('diff_select', name, x=(difficulty * 115)) + tex.draw_texture('diff_select', name, x=(difficulty * offset)) def draw_background_diffs(self, state: int): if (self.selected_song and state == State.SONG_SELECTED and self.selected_difficulty >= Difficulty.EASY): @@ -710,21 +711,21 @@ class SongSelectPlayer: if self.neiro_selector is not None: offset = self.neiro_selector.move.attribute if self.neiro_selector.is_confirmed: - offset += -370 + offset += tex.skin_config["song_select_offset"].x else: offset *= -1 if self.modifier_selector is not None: offset = self.modifier_selector.move.attribute if self.modifier_selector.is_confirmed: - offset += -370 + offset += tex.skin_config["song_select_offset"].x else: offset *= -1 if self.player_num == PlayerNum.P1: - self.nameplate.draw(30, 640) - self.chara.draw(x=-50, y=410 + (offset*0.6)) + self.nameplate.draw(tex.skin_config["song_select_nameplate_1p"].x, tex.skin_config["song_select_nameplate_1p"].y) + self.chara.draw(x=tex.skin_config["song_select_chara_1p"].x, y=tex.skin_config["song_select_chara_1p"].y + (offset*0.6)) else: - self.nameplate.draw(950, 640) - self.chara.draw(mirror=True, x=950, y=410 + (offset*0.6)) + self.nameplate.draw(tex.skin_config["song_select_nameplate_2p"].x, tex.skin_config["song_select_nameplate_2p"].y) + self.chara.draw(mirror=True, x=tex.skin_config["song_select_chara_2p"].x, y=tex.skin_config["song_select_chara_2p"].y + (offset*0.6)) if self.neiro_selector is not None: self.neiro_selector.draw() diff --git a/scenes/two_player/game.py b/scenes/two_player/game.py index 8f76758..b597d82 100644 --- a/scenes/two_player/game.py +++ b/scenes/two_player/game.py @@ -8,7 +8,8 @@ from libs.audio import audio from libs.utils import global_data from libs.video import VideoPlayer import pyray as ray -from scenes.game import ClearAnimation, FCAnimation, FailAnimation, GameScreen, Player, Background, SCREEN_WIDTH, ResultTransition +from libs.texture import tex +from scenes.game import ClearAnimation, FCAnimation, FailAnimation, GameScreen, Player, Background, ResultTransition logger = logging.getLogger(__name__) @@ -74,7 +75,7 @@ class TwoPlayerGameScreen(GameScreen): def init_tja(self, song: Path): """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=tex.screen_width - GameScreen.JUDGE_X) if self.tja.metadata.bgmovie != Path() and self.tja.metadata.bgmovie.exists(): self.movie = VideoPlayer(self.tja.metadata.bgmovie) self.movie.set_volume(0.0) diff --git a/scenes/two_player/result.py b/scenes/two_player/result.py index 530957f..7da60dd 100644 --- a/scenes/two_player/result.py +++ b/scenes/two_player/result.py @@ -1,5 +1,6 @@ import logging from libs.global_data import PlayerNum +from libs.texture import tex from libs.utils import get_current_ms from scenes.result import Background, FadeIn, ResultPlayer, ResultScreen @@ -8,7 +9,7 @@ logger = logging.getLogger(__name__) class TwoPlayerResultScreen(ResultScreen): def on_screen_start(self): super().on_screen_start() - self.background = Background(PlayerNum.TWO_PLAYER, 1280) + self.background = Background(PlayerNum.TWO_PLAYER, tex.screen_width) self.fade_in = FadeIn(PlayerNum.TWO_PLAYER) self.player_1 = ResultPlayer(PlayerNum.P1, True, False) self.player_2 = ResultPlayer(PlayerNum.P2, True, True)