add custom box color support

This commit is contained in:
Anthony Samms
2025-12-20 12:39:13 -05:00
parent ed8b8fb185
commit 3f9a1e3a4c
6 changed files with 288 additions and 164 deletions

View File

@@ -1,3 +1,4 @@
#TITLE:Recommended
#TITLEJA:
#COLLECTION:RECOMMENDED
#FORECOLOR:#8c275c

View File

@@ -1,3 +1,4 @@
#TITLE:Favorites
#TITLEJA:
#COLLECTION:FAVORITE
#FORECOLOR:#97391e

View File

@@ -1,3 +1,4 @@
#TITLE:Recently Played
#TITLEJA:
#COLLECTION:RECENT
#FORECOLOR:#237b67

View File

@@ -1,3 +1,5 @@
#TITLE:Diff Sort
#TITLEJA:
#COLLECTION:DIFFICULTY
#BACKCOLOR:#ff555f
#FORECOLOR:#9d0d1f

View File

@@ -1,11 +1,12 @@
from dataclasses import dataclass
from enum import IntEnum
import json
import logging
from pathlib import Path
import random
from typing import Optional, Union
from raylib import SHADER_UNIFORM_FLOAT, SHADER_UNIFORM_VEC3
from raylib import SHADER_UNIFORM_VEC3
from libs.audio import audio
from libs.animation import Animation, MoveAnimation
from libs.global_data import Crown, Difficulty
@@ -51,7 +52,6 @@ def calculate_hue_shift(source_rgb, target_rgb):
shift = (target_hue - source_hue) / 360.0
# Normalize to 0.0-1.0 range
while shift < 0:
shift += 1.0
while shift >= 1.0:
@@ -59,68 +59,78 @@ def calculate_hue_shift(source_rgb, target_rgb):
return shift
def darken_color(rgb: tuple[int, int, int]):
r, g, b = rgb
darkening_factor = 0.63
darkened_r = int(r * darkening_factor)
darkened_g = int(g * darkening_factor)
darkened_b = int(b * darkening_factor)
return (darkened_r, darkened_g, darkened_b)
class TextureIndex(IntEnum):
BLANK = 0
VOCALOID = 1
DEFAULT = 2
RECOMMENDED = 3
FAVORITE = 4
RECENT = 5
class GenreIndex(IntEnum):
TUTORIAL = 0
JPOP = 1
ANIME = 2
VOCALOID = 3
CHILDREN = 4
VARIETY = 5
CLASSICAL = 6
GAME = 7
NAMCO = 8
DEFAULT = 9
RECOMMENDED = 10
FAVORITE = 11
RECENT = 12
DAN = 13
DIFFICULTY = 14
class BaseBox():
OUTLINE_MAP = {
1: ray.Color(0, 77, 104, 255),
2: ray.Color(156, 64, 2, 255),
3: ray.Color(84, 101, 126, 255),
4: ray.Color(153, 4, 46, 255),
5: ray.Color(60, 104, 0, 255),
6: ray.Color(134, 88, 0, 255),
7: ray.Color(79, 40, 134, 255),
8: ray.Color(148, 24, 0, 255),
9: ray.Color(101, 0, 82, 255),
10: ray.Color(140, 39, 92, 255),
11: ray.Color(151, 57, 30, 255),
12: ray.Color(35, 123, 103, 255),
13: ray.Color(25, 68, 137, 255),
14: ray.Color(157, 13, 31, 255)
}
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):
def __init__(self, name: str, back_color: Optional[tuple[int, int, int]], fore_color: Optional[tuple[int, int, int]], texture_index: TextureIndex):
self.text_name = name
self.texture_index = texture_index
self.genre_index = GenreIndex.DEFAULT
self.back_color = back_color
if fore_color is not None:
self.fore_color = ray.Color(fore_color[0], fore_color[1], fore_color[2], 255)
elif self.back_color is not None:
dark_ver = darken_color(self.back_color)
self.fore_color = ray.Color(dark_ver[0], dark_ver[1], dark_ver[2], 255)
else:
self.fore_color = ray.Color(101, 0, 82, 255)
self.position = float('inf')
self.start_position: float = -1
self.target_position: float = -1
self.start_position = -1.0
self.target_position = -1.0
self.open_anim = Animation.create_move(233, total_distance=150*tex.screen_scale, delay=50)
self.open_fade = Animation.create_fade(200, initial_opacity=0, final_opacity=1.0)
self.move = None
self.shader = None
self.is_open = False
self.text_loaded = False
self.wait = 0
def __lt__(self, other):
return self.position < other.position
def __le__(self, other):
return self.position <= other.position
def __gt__(self, other):
return self.position > other.position
def __ge__(self, other):
return self.position >= other.position
def __eq__(self, other):
return self.position == other.position
def load_text(self):
self.name = OutlinedText(self.text_name, tex.skin_config["song_box_name"].font_size, ray.WHITE, outline_thickness=5, vertical=True)
'''
if self.back_color is not None:
self.shader = ray.load_shader('', 'shader/colortransform.fs')
source_rgb = (142, 212, 30)
target_rgb = (209, 162, 19)
target_rgb = self.back_color
source_color = ray.ffi.new('float[3]', [source_rgb[0]/255.0, source_rgb[1]/255.0, source_rgb[2]/255.0])
target_color = ray.ffi.new('float[3]', [target_rgb[0]/255.0, target_rgb[1]/255.0, target_rgb[2]/255.0])
source_loc = ray.get_shader_location(self.shader, 'sourceColor')
target_loc = ray.get_shader_location(self.shader, 'targetColor')
ray.set_shader_value(self.shader, source_loc, source_color, SHADER_UNIFORM_VEC3)
ray.set_shader_value(self.shader, target_loc, target_color, SHADER_UNIFORM_VEC3)
'''
def move_box(self, current_time: float):
if self.position != self.target_position and self.move is None:
@@ -148,15 +158,16 @@ class BaseBox():
self.open_fade.update(current_time)
def _draw_closed(self, x: float, y: float, outer_fade_override: float):
#ray.begin_shader_mode(self.shader)
if self.shader is not None and self.texture_index == TextureIndex.BLANK:
ray.begin_shader_mode(self.shader)
tex.draw_texture('box', 'folder_texture_left', frame=self.texture_index, x=x, fade=outer_fade_override)
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, fade=outer_fade_override)
tex.draw_texture('box', 'folder_texture', frame=self.texture_index, x=x, x2=tex.skin_config["song_box_bg"].width, fade=outer_fade_override)
tex.draw_texture('box', 'folder_texture_right', frame=self.texture_index, x=x, fade=outer_fade_override)
#ray.end_shader_mode()
if self.texture_index == BaseBox.DEFAULT_INDEX:
if self.shader is not None and self.texture_index == TextureIndex.BLANK:
ray.end_shader_mode()
if self.texture_index == TextureIndex.DEFAULT:
tex.draw_texture('box', 'genre_overlay', x=x, y=y, fade=outer_fade_override)
elif self.texture_index == BaseBox.DIFFICULTY_SORT_INDEX:
if self.genre_index == GenreIndex.DIFFICULTY:
tex.draw_texture('box', 'diff_overlay', x=x, y=y, fade=outer_fade_override)
def _draw_open(self, x: float, y: float, fade_override: Optional[float], is_ura: bool):
@@ -169,8 +180,9 @@ class BaseBox():
self._draw_closed(x, y, outer_fade_override)
class BackBox(BaseBox):
def __init__(self, name: str, texture_index: int):
super().__init__(name, texture_index)
COLOR = (170, 115, 35)
def __init__(self, name: str):
super().__init__(name, BackBox.COLOR, BackBox.COLOR, TextureIndex.BLANK)
self.yellow_box = None
def load_text(self):
@@ -200,12 +212,8 @@ class BackBox(BaseBox):
self.yellow_box.draw(self, fade_override, is_ura, self.name)
class SongBox(BaseBox):
def __init__(self, name: str, texture_index: int, tja: TJAParser, name_texture_index: Optional[int] = None):
super().__init__(name, texture_index)
if name_texture_index is None:
self.name_texture_index = texture_index
else:
self.name_texture_index = name_texture_index
def __init__(self, name: str, back_color: Optional[tuple[int, int, int]], fore_color: Optional[tuple[int, int, int]], texture_index: TextureIndex, tja: TJAParser):
super().__init__(name, back_color, fore_color, texture_index)
self.scores = dict()
self.hash = dict()
self.score_history = None
@@ -270,7 +278,7 @@ class SongBox(BaseBox):
def _draw_closed(self, x: float, y: float, outer_fade_override: float):
super()._draw_closed(x, y, outer_fade_override)
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, fade=outer_fade_override)
self.name.draw(outline_color=self.fore_color, 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, fade=outer_fade_override)
if self.tja.ex_data.new:
tex.draw_texture('yellow_box', 'ex_data_new_song_balloon', x=x, y=y, fade=outer_fade_override)
@@ -295,13 +303,13 @@ class SongBox(BaseBox):
self.score_history.draw()
class FolderBox(BaseBox):
def __init__(self, name: str, texture_index: int, tja_count: int = 0,
box_texture: Optional[str] = None):
super().__init__(name, texture_index)
def __init__(self, name: str, back_color: Optional[tuple[int, int, int]], fore_color: Optional[tuple[int, int, int]], texture_index: TextureIndex, genre_index: GenreIndex, tja_count: int = 0, box_texture: Optional[str] = None):
super().__init__(name, back_color, fore_color, texture_index)
self.box_texture_path = Path(box_texture) if box_texture else None
self.is_back = self.texture_index == SongBox.BACK_INDEX
self.is_back = self.back_color == BackBox.COLOR
self.tja_count = tja_count
self.crown = dict()
self.genre_index = genre_index
def load_text(self):
super().load_text()
@@ -323,17 +331,20 @@ class FolderBox(BaseBox):
self.open_anim.start()
self.open_fade.start()
self.wait = current_time
if self.texture_index != SongBox.BACK_INDEX and not audio.is_sound_playing('voice_enter'):
audio.play_sound(f'genre_voice_{self.texture_index}', 'voice')
elif not self.is_open and is_open_prev and self.texture_index != 17 and audio.is_sound_playing(f'genre_voice_{self.texture_index}'):
audio.stop_sound(f'genre_voice_{self.texture_index}')
if self.back_color != BackBox.COLOR and not audio.is_sound_playing('voice_enter'):
audio.play_sound(f'genre_voice_{self.genre_index}', 'voice')
elif not self.is_open and is_open_prev and self.back_color != BackBox.COLOR and audio.is_sound_playing(f'genre_voice_{self.genre_index}'):
audio.stop_sound(f'genre_voice_{self.genre_index}')
def _draw_closed(self, x: float, y: float, outer_fade_override: float):
super()._draw_closed(x, y, outer_fade_override)
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, fade=outer_fade_override)
if self.shader is not None and self.texture_index == TextureIndex.BLANK:
ray.begin_shader_mode(self.shader)
tex.draw_texture('box', 'folder_clip', frame=self.texture_index, x=x - ((1 * tex.screen_scale)), y=y, fade=outer_fade_override)
if self.shader is not None and self.texture_index == TextureIndex.BLANK:
ray.end_shader_mode()
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, fade=outer_fade_override)
self.name.draw(outline_color=self.fore_color, 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, fade=outer_fade_override)
if self.crown: #Folder lamp
highest_crown = max(self.crown)
@@ -349,34 +360,42 @@ class FolderBox(BaseBox):
if fade_override is not None:
color = ray.fade(ray.WHITE, fade_override)
if not self.is_back and self.open_anim.attribute >= (100 * tex.screen_scale):
if self.shader is not None and self.texture_index == TextureIndex.BLANK:
ray.begin_shader_mode(self.shader)
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+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)
if self.shader is not None and self.texture_index == TextureIndex.BLANK:
ray.end_shader_mode()
if self.shader is not None and self.texture_index == TextureIndex.BLANK:
ray.begin_shader_mode(self.shader)
tex.draw_texture('box', 'folder_texture_left', frame=self.texture_index, x=x - self.open_anim.attribute)
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.shader is not None and self.texture_index == TextureIndex.BLANK:
ray.end_shader_mode()
if self.texture_index == BaseBox.DEFAULT_INDEX:
if self.texture_index == TextureIndex.DEFAULT:
tex.draw_texture('box', 'genre_overlay_large', x=x, y=y, color=color)
elif self.texture_index == BaseBox.DIFFICULTY_SORT_INDEX:
if self.genre_index == GenreIndex.DIFFICULTY:
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 != BaseBox.DIFFICULTY_SORT_INDEX:
if self.genre_index != GenreIndex.DIFFICULTY:
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(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)
if self.texture_index != TextureIndex.DEFAULT:
tex.draw_texture('box', 'folder_graphic', color=color, frame=self.genre_index)
tex.draw_texture('box', 'folder_text', color=color, frame=self.genre_index)
elif self.box_texture is not None:
scaled_width = self.box_texture.width * tex.screen_scale
scaled_height = self.box_texture.height * tex.screen_scale
@@ -600,8 +619,8 @@ class YellowBox:
self._draw_text(song_box, name)
class DanBox(BaseBox):
def __init__(self, name, color: int, songs: list[tuple[TJAParser, int, int, int]], exams: list['Exam']):
super().__init__(name, color)
def __init__(self, name, color: TextureIndex, songs: list[tuple[TJAParser, int, int, int]], exams: list['Exam']):
super().__init__(name, None, None, color)
self.songs = songs
self.exams = exams
self.song_text: list[tuple[OutlinedText, OutlinedText]] = []
@@ -718,29 +737,48 @@ class DanBox(BaseBox):
class GenreBG:
"""The background for a genre box."""
def __init__(self, start_box: BaseBox, end_box: BaseBox, title: OutlinedText, diff_sort: Optional[int]):
self.title = title
self.start_box = start_box
self.end_box = end_box
self.start_position = start_box.position
self.end_position_final = end_box.position
self.title = title
self.fade_in = Animation.create_fade(133, initial_opacity=0.0, final_opacity=1.0, ease_in='quadratic', delay=50)
self.fade_in.start()
self.move = Animation.create_move(316, delay=self.fade_in.duration/2, total_distance=abs(self.end_position_final - self.start_position), ease_in='quadratic')
self.move.start()
self.box_fade_in = Animation.create_fade(66.67*2, delay=self.move.duration, initial_opacity=0.0, final_opacity=1.0)
self.fade_in.start()
self.move.start()
self.box_fade_in.start()
self.end_position = self.start_position + self.move.attribute
self.diff_num = diff_sort
self.color = self.end_box.back_color
self.shader = None
self.shader_loaded = False
def load_shader(self):
if self.color is not None:
self.shader = ray.load_shader('', 'shader/colortransform.fs')
source_rgb = (142, 212, 30)
target_rgb = self.color
source_color = ray.ffi.new('float[3]', [source_rgb[0]/255.0, source_rgb[1]/255.0, source_rgb[2]/255.0])
target_color = ray.ffi.new('float[3]', [target_rgb[0]/255.0, target_rgb[1]/255.0, target_rgb[2]/255.0])
source_loc = ray.get_shader_location(self.shader, 'sourceColor')
target_loc = ray.get_shader_location(self.shader, 'targetColor')
ray.set_shader_value(self.shader, source_loc, source_color, SHADER_UNIFORM_VEC3)
ray.set_shader_value(self.shader, target_loc, target_color, SHADER_UNIFORM_VEC3)
self.shader_loaded = True
def update(self, current_ms):
self.start_position = self.start_box.position
#self.end_position_final = self.end_box.position
self.end_position = self.start_position + self.move.attribute
if self.move.is_finished:
self.end_position = self.end_box.position
self.box_fade_in.update(current_ms)
self.fade_in.update(current_ms)
self.move.update(current_ms)
def draw(self, y):
if self.shader is not None and self.end_box.texture_index == TextureIndex.BLANK:
ray.begin_shader_mode(self.shader)
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)
@@ -767,13 +805,19 @@ class GenreBG:
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.shader is not None and self.end_box.texture_index == TextureIndex.BLANK:
ray.end_shader_mode()
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)
if self.shader is not None and self.end_box.texture_index == TextureIndex.BLANK:
ray.begin_shader_mode(self.shader)
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.shader is not None and self.end_box.texture_index == TextureIndex.BLANK:
ray.end_shader_mode()
if self.diff_num is not None:
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))
@@ -872,59 +916,108 @@ class ScoreHistory:
for i in range(len(counter)):
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_hex_color(color) -> tuple[int, int, int]:
"""Parse hex color to RGB tuple"""
color = color.lstrip('#')
if len(color) == 3:
color = ''.join([c*2 for c in color])
res = tuple(int(color[i:i+2], 16) for i in (0, 2, 4))
return (res[0], res[1], res[2])
def get_genre_index(genre_string: str) -> GenreIndex:
genre_upper = genre_string.upper()
for genre_index, genre_set in FileSystemItem.GENRE_MAP.items():
if genre_upper in genre_set:
return genre_index
return GenreIndex.DEFAULT
DEFAULT_COLORS = {
GenreIndex.JPOP: [(32, 160, 186), (0, 77, 104)],
GenreIndex.ANIME: [(255, 152, 0), (156, 64, 2)],
GenreIndex.VOCALOID: [None, (84, 101, 126)],
GenreIndex.CHILDREN: [(255, 82, 134), (153, 4, 46)],
GenreIndex.VARIETY: [(142, 212, 30), (60, 104, 0)],
GenreIndex.CLASSICAL: [(209, 162, 19), (134, 88, 0)],
GenreIndex.GAME: [(156, 117, 189), (79, 40, 134)],
GenreIndex.NAMCO: [(255, 90, 19), (148, 24, 0)],
GenreIndex.DEFAULT: [None, (101, 0, 82)],
GenreIndex.RECOMMENDED: [None, (140, 39, 92)],
GenreIndex.FAVORITE: [None, (151, 57, 30)],
GenreIndex.RECENT: [None, (35, 123, 103)],
GenreIndex.DAN: [(35, 102, 170), (25, 68, 137)],
GenreIndex.DIFFICULTY: [(255, 85, 95), (157, 13, 31)]
}
def parse_box_def(path: Path):
"""Parse box.def file for directory metadata"""
texture_index = SongBox.DEFAULT_INDEX
name = path.name
genre = ''
texture_index = TextureIndex.DEFAULT
genre_index = GenreIndex.DEFAULT
collection = None
back_color = None
fore_color = None
encoding = test_encodings(path / "box.def")
try:
with open(path / "box.def", 'r', encoding=encoding) as box_def:
for line in box_def:
line = line.strip()
if line.startswith("#GENRE:"):
genre = line.split(":", 1)[1].strip()
texture_index = FileSystemItem.GENRE_MAP.get(genre, SongBox.DEFAULT_INDEX)
if texture_index == SongBox.DEFAULT_INDEX:
texture_index = FileSystemItem.GENRE_MAP_2.get(genre, SongBox.DEFAULT_INDEX)
texture_index = FileSystemItem.TEXTURE_MAP.get(genre, texture_index)
genre_index = get_genre_index(genre)
elif line.startswith("#TITLE:"):
name = line.split(":", 1)[1].strip()
elif line.startswith("#TITLEJA:"):
if global_data.config['general']['language'] == 'ja':
name = line.split(":", 1)[1].strip()
elif line.startswith("#COLLECTION"):
elif line.startswith("#COLLECTION:"):
collection = line.split(":", 1)[1].strip()
texture_index = FileSystemItem.TEXTURE_MAP.get(collection, texture_index)
genre_index = get_genre_index(collection)
elif line.startswith("#BACKCOLOR:"):
back_color = parse_hex_color(line.split(":", 1)[1].strip())
texture_index = TextureIndex.BLANK
elif line.startswith("#FORECOLOR:"):
fore_color = parse_hex_color(line.split(":", 1)[1].strip())
if name == '':
if genre:
name = genre
else:
name = path.name
except Exception as e:
logger.error(f"Error parsing box.def in {path}: {e}")
return name, texture_index, collection
if back_color is None and fore_color is None and genre_index in DEFAULT_COLORS:
back_color, fore_color = DEFAULT_COLORS[genre_index]
if genre_index != GenreIndex.DEFAULT:
texture_index = TextureIndex.BLANK
return name, texture_index, genre_index, collection, back_color, fore_color
class FileSystemItem:
GENRE_MAP = {
'J-POP': 1,
'アニメ': 2,
'VOCALOID': 3,
'どうよう': 4,
'バラエティー': 5,
'クラシック': 6,
'ゲームミュージック': 7,
'ナムコオリジナル': 8,
'RECOMMENDED': 10,
'FAVORITE': 11,
'RECENT': 12,
'段位道場': 13,
'DIFFICULTY': 14
TEXTURE_MAP = {
'VOCALOID': TextureIndex.VOCALOID,
'ボーカロイド': TextureIndex.VOCALOID,
'RECOMMENDED': TextureIndex.RECOMMENDED,
'FAVORITE': TextureIndex.FAVORITE,
'RECENT': TextureIndex.RECENT,
}
GENRE_MAP_2 = {
'ボーカロイド': 3,
'バラエティ': 5
GENRE_MAP = {
GenreIndex.TUTORIAL: {"TUTORIAL"},
GenreIndex.JPOP: {"J-POP"},
GenreIndex.ANIME: {"ANIME", "アニメ"},
GenreIndex.CHILDREN: {"CHILDREN", "どうよう"},
GenreIndex.VOCALOID: {"VOCALOID", "ボーカロイド"},
GenreIndex.VARIETY: {"VARIETY", "バラエティー", "バラエティ"},
GenreIndex.CLASSICAL: {"CLASSICAL", "クラシック"},
GenreIndex.GAME: {"GAME", "ゲームミュージック"},
GenreIndex.NAMCO: {"NAMCO", "ナムコオリジナル"},
GenreIndex.RECOMMENDED: {"RECOMMENDED"},
GenreIndex.FAVORITE: {"FAVORITE"},
GenreIndex.RECENT: {"RECENT"},
GenreIndex.DAN: {"DAN", "段位道場"},
GenreIndex.DIFFICULTY: {"DIFFICULTY"},
}
"""Base class for files and directories in the navigation system"""
def __init__(self, path: Path, name: str):
@@ -940,7 +1033,7 @@ class Directory(FileSystemItem):
'DIFFICULTY',
'RECOMMENDED'
]
def __init__(self, path: Path, name: str, texture_index: int, has_box_def=False, to_root=False, back=False, tja_count=0, box_texture=None, collection=None):
def __init__(self, path: Path, name: str, back_color: Optional[tuple[int, int, int]], fore_color: Optional[tuple[int, int, int]], texture_index: TextureIndex, genre_index: GenreIndex, has_box_def=False, to_root=False, back=False, tja_count=0, box_texture=None, collection=None):
super().__init__(path, name)
self.has_box_def = has_box_def
self.to_root = to_root
@@ -949,27 +1042,23 @@ class Directory(FileSystemItem):
self.collection = None
if collection in Directory.COLLECTIONS:
self.collection = collection
if collection in FileSystemItem.GENRE_MAP:
texture_index = FileSystemItem.GENRE_MAP[collection]
elif self.to_root or self.back:
texture_index = SongBox.BACK_INDEX
if self.back:
self.box = BackBox(name, texture_index)
self.box = BackBox(name)
else:
self.box = FolderBox(name, texture_index, tja_count=tja_count, box_texture=box_texture)
self.box = FolderBox(name, back_color, fore_color, texture_index, genre_index, tja_count=tja_count, box_texture=box_texture)
class SongFile(FileSystemItem):
"""Represents a song file (TJA) in the navigation system"""
def __init__(self, path: Path, name: str, texture_index: int, tja=None, name_texture_index: Optional[int]=None):
def __init__(self, path: Path, name: str, back_color: Optional[tuple[int, int, int]], fore_color: Optional[tuple[int, int, int]], texture_index: TextureIndex):
super().__init__(path, name)
self.is_recent = (datetime.now() - datetime.fromtimestamp(path.stat().st_mtime)) <= timedelta(days=7)
self.tja = tja or TJAParser(path)
self.tja = TJAParser(path)
if self.is_recent:
self.tja.ex_data.new = True
title = self.tja.metadata.title.get(global_data.config['general']['language'].lower(), self.tja.metadata.title['en'])
self.hash = global_data.song_paths[path]
self.box = SongBox(title, texture_index, self.tja, name_texture_index=name_texture_index if name_texture_index is not None else texture_index)
self.box = SongBox(title, back_color, fore_color, texture_index, self.tja)
self.box.hash = global_data.song_hashes[self.hash][0]["diff_hashes"]
self.box.get_scores()
@@ -1008,11 +1097,11 @@ class DanCourse(FileSystemItem):
path = Path(global_data.song_hashes[hash_val][i]["file_path"])
break
if (path.parent.parent / "box.def").exists():
_, genre_index, _ = parse_box_def(path.parent.parent)
texture_index = parse_box_def(path.parent.parent)[1]
else:
genre_index = 9
texture_index = TextureIndex.DEFAULT
tja = TJAParser(path)
self.charts.append((tja, genre_index, difficulty, tja.metadata.course_data[difficulty].level))
self.charts.append((tja, texture_index, difficulty, tja.metadata.course_data[difficulty].level))
self.exams = []
for exam in data["exams"]:
self.exams.append(Exam(exam["type"], exam["value"][0], exam["value"][1], exam["range"]))
@@ -1126,11 +1215,12 @@ class FileNavigator:
if has_box_def:
# Parse box.def if it exists
name = dir_path.name if dir_path.name else str(dir_path)
texture_index = SongBox.DEFAULT_INDEX
box_texture = None
collection = None
back_color = None
fore_color = None
name, texture_index, collection = parse_box_def(dir_path)
name, texture_index, genre_index, collection, back_color, fore_color = parse_box_def(dir_path)
box_png_path = dir_path / "box.png"
if box_png_path.exists():
box_texture = str(box_png_path)
@@ -1144,11 +1234,11 @@ class FileNavigator:
# Create Directory object
directory_obj = Directory(
dir_path, name, texture_index,
dir_path, name, back_color, fore_color, texture_index, genre_index,
has_box_def=has_box_def,
tja_count=tja_count,
box_texture=box_texture,
collection=collection
collection=collection,
)
if directory_obj.collection == Directory.COLLECTIONS[2]:
self.favorite_folder = directory_obj
@@ -1185,7 +1275,7 @@ class FileNavigator:
song_obj = DanCourse(tja_path, tja_path.name)
self.all_song_files[song_key] = song_obj
elif song_key not in self.all_song_files and tja_path in global_data.song_paths:
song_obj = SongFile(tja_path, tja_path.name, texture_index)
song_obj = SongFile(tja_path, tja_path.name, back_color, fore_color, texture_index)
song_obj.box.get_scores()
for course in song_obj.tja.metadata.course_data:
level = song_obj.tja.metadata.course_data[course].level
@@ -1210,7 +1300,7 @@ class FileNavigator:
elif is_cleared:
self.diff_sort_statistics[course][level][2] += 1
if song_obj.is_recent:
self.new_items.append(SongFile(tja_path, tja_path.name, SongBox.DEFAULT_INDEX, name_texture_index=texture_index))
self.new_items.append(SongFile(tja_path, tja_path.name, back_color, fore_color, texture_index))
self.song_count += 1
global_data.song_progress = self.song_count / global_data.total_songs
self.all_song_files[song_key] = song_obj
@@ -1232,7 +1322,7 @@ class FileNavigator:
song_key = str(tja_path)
if song_key not in self.all_song_files:
try:
song_obj = SongFile(tja_path, tja_path.name, SongBox.DEFAULT_INDEX)
song_obj = SongFile(tja_path, tja_path.name, None, None, TextureIndex.DEFAULT)
self.song_count += 1
global_data.song_progress = self.song_count / global_data.total_songs
self.all_song_files[song_key] = song_obj
@@ -1269,7 +1359,7 @@ class FileNavigator:
# Add back navigation item (only if not at root)
if not self.is_at_root():
back_dir = Directory(self.current_dir.parent, "", SongBox.BACK_INDEX, back=True)
back_dir = Directory(self.current_dir.parent, "", BackBox.COLOR, BackBox.COLOR, TextureIndex.BLANK, GenreIndex.DEFAULT, back=True)
if not has_children:
start_box = back_dir.box
self.items.insert(self.selected_index, back_dir)
@@ -1330,11 +1420,13 @@ class FileNavigator:
for item in content_items:
if isinstance(item, SongFile) and not has_children:
if i % 10 == 0 and i != 0:
back_dir = Directory(self.current_dir.parent, "", SongBox.BACK_INDEX, back=True)
back_dir = Directory(self.current_dir.parent, "", BackBox.COLOR, BackBox.COLOR, TextureIndex.BLANK, GenreIndex.DEFAULT, back=True)
self.items.insert(self.selected_index+i, back_dir)
i += 1
if not has_children:
if selected_item is not None:
item.box.back_color = selected_item.box.back_color
item.box.genre_index = selected_item.box.genre_index
item.box.texture_index = selected_item.box.texture_index
self.items.insert(self.selected_index+i, item)
else:

View File

@@ -5,7 +5,9 @@ from pathlib import Path
import pyray as ray
import logging
from libs.file_navigator import BackBox, DanCourse, navigator
from raylib import SHADER_UNIFORM_VEC3
from libs.file_navigator import DEFAULT_COLORS, BackBox, DanCourse, GenreIndex, navigator
from libs.audio import audio
from libs.chara_2d import Chara2D
from libs.file_navigator import Directory, SongBox, SongFile
@@ -57,14 +59,17 @@ class SongSelectScreen(Screen):
self.coin_overlay = CoinOverlay()
self.allnet_indicator = AllNetIcon()
self.indicator = Indicator(Indicator.State.SELECT)
self.texture_index = SongBox.DEFAULT_INDEX
self.last_texture_index = SongBox.DEFAULT_INDEX
self.genre_index = GenreIndex.DEFAULT
self.last_genre_index = self.genre_index
self.last_moved = get_current_ms()
self.timer_browsing = Timer(100, get_current_ms(), self.navigator.select_current_item)
self.timer_selected = Timer(40, get_current_ms(), self._confirm_selection_wrapper)
self.screen_init = True
self.ura_switch_animation = UraSwitchAnimation()
self.dan_transition = DanTransition()
self.shader = ray.load_shader('', 'shader/colortransform.fs')
self.color = None
self.load_shader_values(self.color)
session_data = global_data.session_data[global_data.player_num]
self.player_1 = SongSelectPlayer(global_data.player_num, self.text_fade_in)
@@ -83,11 +88,22 @@ class SongSelectScreen(Screen):
curr_item.box.get_scores()
self.navigator.add_recent()
def load_shader_values(self, target_rgb):
source_rgb = (142, 212, 30)
if target_rgb is None:
target_rgb = source_rgb
source_color = ray.ffi.new('float[3]', [source_rgb[0]/255.0, source_rgb[1]/255.0, source_rgb[2]/255.0])
target_color = ray.ffi.new('float[3]', [target_rgb[0]/255.0, target_rgb[1]/255.0, target_rgb[2]/255.0])
source_loc = ray.get_shader_location(self.shader, 'sourceColor')
target_loc = ray.get_shader_location(self.shader, 'targetColor')
ray.set_shader_value(self.shader, source_loc, source_color, SHADER_UNIFORM_VEC3)
ray.set_shader_value(self.shader, target_loc, target_color, SHADER_UNIFORM_VEC3)
def finalize_song(self, current_item: SongFile):
global_data.session_data[global_data.player_num].selected_song = current_item.path
global_data.session_data[global_data.player_num].song_hash = global_data.song_hashes[current_item.hash][0]["diff_hashes"][self.player_1.selected_difficulty]
global_data.session_data[global_data.player_num].selected_difficulty = self.player_1.selected_difficulty
global_data.session_data[global_data.player_num].genre_index = current_item.box.name_texture_index
global_data.session_data[global_data.player_num].genre_index = current_item.box.genre_index
def on_screen_end(self, next_screen):
self.screen_init = False
@@ -268,11 +284,11 @@ class SongSelectScreen(Screen):
elif self.state == State.SONG_SELECTED:
self.timer_selected.update(current_time)
if self.last_texture_index != self.texture_index:
if self.last_genre_index != self.genre_index:
if not self.background_fade_change.is_started:
self.background_fade_change.start()
if self.background_fade_change.is_finished:
self.last_texture_index = self.texture_index
self.last_genre_index = self.genre_index
self.background_fade_change.reset()
if self.game_transition is not None:
@@ -287,6 +303,8 @@ class SongSelectScreen(Screen):
if self.navigator.genre_bg is not None:
self.navigator.genre_bg.update(current_time)
if not self.navigator.genre_bg.shader_loaded:
self.navigator.genre_bg.load_shader()
if self.diff_sort_selector is not None:
self.diff_sort_selector.update(current_time)
@@ -309,7 +327,9 @@ class SongSelectScreen(Screen):
if song.box.is_open:
current_box = song.box
if not isinstance(current_box, BackBox) and current_time >= song.box.wait + (83.33*3):
self.texture_index = current_box.texture_index
self.genre_index = current_box.genre_index
self.color = current_box.back_color
self.load_shader_values(self.color)
if ray.is_key_pressed(global_data.config["keys"]["back_key"]):
logger.info("Back key pressed, returning to ENTRY screen")
@@ -323,9 +343,16 @@ class SongSelectScreen(Screen):
def draw(self):
width = tex.textures['box']['background'].width
genre_index = self.genre_index
last_genre_index = self.last_genre_index
if genre_index in DEFAULT_COLORS and self.color != DEFAULT_COLORS[genre_index][0]:
ray.begin_shader_mode(self.shader)
genre_index = GenreIndex.VARIETY
last_genre_index = GenreIndex.VARIETY
for i in range(0, width * 4, width):
tex.draw_texture('box', 'background', frame=self.last_texture_index, x=i-self.background_move.attribute)
tex.draw_texture('box', 'background', frame=self.texture_index, x=i-self.background_move.attribute, fade=1 - self.background_fade_change.attribute)
tex.draw_texture('box', 'background', frame=last_genre_index, x=i-self.background_move.attribute)
tex.draw_texture('box', 'background', frame=genre_index, x=i-self.background_move.attribute, fade=1 - self.background_fade_change.attribute)
ray.end_shader_mode()
self.draw_background_diffs()