add 1080p and more support

This commit is contained in:
Anthony Samms
2025-11-20 02:07:46 -05:00
parent 802d9c5b37
commit 1fae2ebd18
31 changed files with 631 additions and 602 deletions

View File

@@ -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)

View File

@@ -30,6 +30,7 @@ rainbow = false
[paths]
tja_path = ['Songs']
video_path = ['Videos']
graphics_path = 'Graphics/GreenVer'
[keys]
exit_key = 'Q'

View File

@@ -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()

View File

@@ -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:

View File

@@ -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)

View File

@@ -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')

View File

@@ -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):

View File

@@ -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)

View File

@@ -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)

151
libs/config.py Normal file
View File

@@ -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)

View File

@@ -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

View File

@@ -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:
"""

View File

@@ -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

View File

@@ -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__)

View File

@@ -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."""

View File

@@ -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):

View File

@@ -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)

View File

@@ -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)

View File

@@ -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"""

View File

@@ -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"""

View File

@@ -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()

View File

@@ -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}

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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__)

View File

@@ -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()

View File

@@ -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)

View File

@@ -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)