various fixes

This commit is contained in:
Yonokid
2025-05-12 15:33:25 -04:00
parent 37489d12ed
commit 959ad35535
6 changed files with 279 additions and 81 deletions

View File

@@ -1,7 +1,7 @@
[general] [general]
fps_counter = false fps_counter = false
judge_offset = 0 judge_offset = 0
autoplay = false autoplay = true
[paths] [paths]
tja_path = 'Songs' tja_path = 'Songs'

View File

@@ -393,6 +393,8 @@ class TJAParser:
# Bars can be sorted like this because they don't need hit detection # Bars can be sorted like this because they don't need hit detection
draw_note_list = deque(sorted(play_note_list, key=lambda n: n.load_ms)) draw_note_list = deque(sorted(play_note_list, key=lambda n: n.load_ms))
bar_list = deque(sorted(bar_list, key=lambda b: b.load_ms)) bar_list = deque(sorted(bar_list, key=lambda b: b.load_ms))
for note in play_note_list:
print(note)
return play_note_list, draw_note_list, bar_list return play_note_list, draw_note_list, bar_list
def hash_note_data(self, notes: list): def hash_note_data(self, notes: list):

View File

@@ -3,7 +3,7 @@ import tempfile
import time import time
import tomllib import tomllib
import zipfile import zipfile
from dataclasses import dataclass from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
@@ -132,100 +132,244 @@ class OutlinedText:
text_color: ray.Color text_color: ray.Color
outline_color: ray.Color outline_color: ray.Color
outline_thickness: int = 2 outline_thickness: int = 2
vertical: bool = False
line_spacing: float = 1.0 # Line spacing for vertical text
lowercase_spacing_factor: float = 0.85 # Adjust spacing for lowercase letters and whitespace
vertical_chars: set = field(default_factory=lambda: {'-', '|', '/', '\\', ''})
no_space_chars: set = field(default_factory=lambda: {
'', '','', '','', '','', '','', '',
'', '','', '','', '','', '','', '',
'', '','','','','','','','','','',
'','','','','','',''
})
def __post_init__(self): def __post_init__(self):
self.texture = self._create_texture() self.texture = self._create_texture()
def _calculate_vertical_spacing(self, current_char, next_char=None):
# Check if current char is lowercase or whitespace
is_spacing_char = current_char.islower() or current_char.isspace() or current_char in self.no_space_chars
# Additional check for capitalization transition
if next_char and current_char.isupper() and next_char.islower() or next_char in self.no_space_chars:
is_spacing_char = True
# Apply spacing factor if it's a spacing character
if is_spacing_char:
return self.font_size * (self.line_spacing * self.lowercase_spacing_factor)
return self.font_size * self.line_spacing
def _draw_rotated_char(self, image, font, char, pos, font_size, color, is_outline=False):
# Calculate character size
char_size = ray.measure_text_ex(font, char, font_size, 1.0)
# Create a temporary image for the rotated character
temp_image = ray.gen_image_color(int(char_size.y), int(char_size.x), ray.Color(0, 0, 0, 0))
# Draw the character on the temporary image
ray.image_draw_text_ex(
temp_image,
font,
char,
ray.Vector2(0, 0),
font_size,
1.0,
color
)
# Rotate the temporary image 90 degrees
rotated_image = ray.gen_image_color(int(char_size.x), int(char_size.y), ray.Color(0, 0, 0, 0))
for x in range(int(char_size.y)):
for y in range(int(char_size.x)):
pixel = ray.get_image_color(temp_image, y, int(char_size.y) - x - 1)
ray.image_draw_pixel(
rotated_image,
x,
y,
pixel
)
# Unload temporary image
ray.unload_image(temp_image)
# Draw the rotated image
ray.image_draw(
image,
rotated_image,
ray.Rectangle(0, 0, rotated_image.width, rotated_image.height),
ray.Rectangle(int(pos.x), int(pos.y), rotated_image.width, rotated_image.height),
ray.WHITE
)
# Unload rotated image
ray.unload_image(rotated_image)
def _create_texture(self): def _create_texture(self):
# Measure text size
text_size = ray.measure_text_ex(self.font, self.text, self.font_size, 1.0) text_size = ray.measure_text_ex(self.font, self.text, self.font_size, 1.0)
padding = self.outline_thickness * 2 # Determine dimensions based on orientation
width = int(text_size.x + padding * 2) if not self.vertical:
height = int(text_size.y + padding * 2) width = int(text_size.x + self.outline_thickness * 4)
height = int(text_size.y + self.outline_thickness * 4)
padding_x, padding_y = self.outline_thickness * 2, self.outline_thickness * 2
else:
# For vertical text, calculate total height and max character width
char_heights = [
self._calculate_vertical_spacing(
self.text[i],
self.text[i+1] if i+1 < len(self.text) else None
)
for i in range(len(self.text))
]
# Calculate the maximum character width (including outline)
char_widths = []
for char in self.text:
if char in self.vertical_chars:
# For vertically drawn characters, use font size as width
char_width = self.font_size
else:
# Normal character width
char_width = ray.measure_text_ex(self.font, char, self.font_size, 1.0).x
char_widths.append(char_width)
max_char_width = max(char_widths) if char_widths else 0
total_height = sum(char_heights) if char_heights else 0
# Adjust dimensions to be tighter around the text
width = int(max_char_width + self.outline_thickness * 2) # Reduced padding
height = int(total_height + self.outline_thickness * 2) # Reduced padding
padding_x = self.outline_thickness
padding_y = self.outline_thickness
# Create transparent image
image = ray.gen_image_color(width, height, ray.Color(0, 0, 0, 0)) image = ray.gen_image_color(width, height, ray.Color(0, 0, 0, 0))
for dx in range(-self.outline_thickness, self.outline_thickness + 1): # Draw outline
for dy in range(-self.outline_thickness, self.outline_thickness + 1): if not self.vertical:
if dx == 0 and dy == 0: # Horizontal text outline
continue for dx in range(-self.outline_thickness, self.outline_thickness + 1):
for dy in range(-self.outline_thickness, self.outline_thickness + 1):
distance = (dx * dx + dy * dy) ** 0.5 if dx == 0 and dy == 0:
if distance <= self.outline_thickness: continue
ray.image_draw_text_ex( ray.image_draw_text_ex(
image, image,
self.font, self.font,
self.text, self.text,
ray.Vector2(padding + dx, padding + dy), ray.Vector2(padding_x + dx, padding_y + dy),
self.font_size, self.font_size,
1.0, 1.0,
self.outline_color self.outline_color
) )
else:
# Vertical text outline
current_y = padding_y
for dx in range(-self.outline_thickness, self.outline_thickness + 1):
for dy in range(-self.outline_thickness, self.outline_thickness + 1):
if dx == 0 and dy == 0:
continue
ray.image_draw_text_ex( current_y = padding_y
image, for i, char in enumerate(self.text):
self.font, if char in self.vertical_chars:
self.text, char_width = self.font_size
ray.Vector2(padding, padding), else:
self.font_size, char_width = ray.measure_text_ex(self.font, char, self.font_size, 1.0).x
1.0,
self.text_color
)
# Calculate centered position
center_offset = (width - char_width) // 2
char_height = self._calculate_vertical_spacing(
char,
self.text[i+1] if i+1 < len(self.text) else None
)
if char in self.vertical_chars:
self._draw_rotated_char(
image,
self.font,
char,
ray.Vector2(
center_offset + dx,
current_y + dy
),
self.font_size,
self.outline_color,
is_outline=True
)
else:
ray.image_draw_text_ex(
image,
self.font,
char,
ray.Vector2(center_offset + dx, current_y + dy),
self.font_size,
1.0,
self.outline_color
)
current_y += char_height
# Draw main text
if not self.vertical:
# Horizontal text
ray.image_draw_text_ex(
image,
self.font,
self.text,
ray.Vector2(padding_x, padding_y),
self.font_size,
1.0,
self.text_color
)
else:
# Vertical text
current_y = padding_y
for i, char in enumerate(self.text):
if char in self.vertical_chars:
char_width = self.font_size
else:
char_width = ray.measure_text_ex(self.font, char, self.font_size, 1.0).x
# Calculate centered position
center_offset = (width - char_width) // 2
char_height = self._calculate_vertical_spacing(
char,
self.text[i+1] if i+1 < len(self.text) else None
)
if char in self.vertical_chars:
self._draw_rotated_char(
image,
self.font,
char,
ray.Vector2(
center_offset,
current_y
),
self.font_size,
self.text_color
)
else:
ray.image_draw_text_ex(
image,
self.font,
char,
ray.Vector2(center_offset, current_y),
self.font_size,
1.0,
self.text_color
)
current_y += char_height
# Create texture and clean up
texture = ray.load_texture_from_image(image) texture = ray.load_texture_from_image(image)
ray.unload_image(image) ray.unload_image(image)
return texture return texture
def draw(self, x: int, y: int, color: ray.Color): def draw(self, src: ray.Rectangle, dest: ray.Rectangle, origin: ray.Vector2, rotation: float, color: ray.Color):
ray.draw_texture(self.texture, x, y, color) ray.draw_texture_pro(self.texture, src, dest, origin, rotation, color)
def unload(self): def unload(self):
ray.unload_texture(self.texture) ray.unload_texture(self.texture)
'''
class RenderTextureStack:
def __init__(self):
"""Initialize an empty stack for render textures."""
self.texture_stack = []
def load_render_texture(self, width, height):
"""Create and return a render texture with the specified dimensions."""
return ray.load_render_texture(width, height)
def begin_texture_mode(self, target):
"""Begin drawing to the render texture and add it to the stack."""
ray.begin_texture_mode(target)
self.texture_stack.append(target)
return target
def end_texture_mode(self, pop_count=1):
"""End the texture mode for the specified number of textures in the stack."""
if not self.texture_stack:
raise IndexError("Cannot end texture mode: texture stack is empty")
# Ensure pop_count is within valid range
pop_count = min(pop_count, len(self.texture_stack))
# End the texture modes and pop from stack
for _ in range(pop_count):
ray.end_texture_mode()
self.texture_stack.pop()
def get_texture(self, target):
"""Get the texture from the render texture."""
return ray.get_texture_default(target)
def draw_texture(self, texture, pos_x, pos_y, tint=ray.WHITE):
"""Draw a texture at the specified position with the given tint."""
ray.draw_texture(texture, pos_x, pos_y, tint)
def get_current_target(self):
"""Get the current active render target from the stack."""
if not self.texture_stack:
return None
return self.texture_stack[-1]
render_stack = RenderTextureStack()
'''

View File

@@ -12,7 +12,6 @@ from libs.backgrounds import Background
from libs.tja import Balloon, Drumroll, Note, TJAParser, calculate_base_score from libs.tja import Balloon, Drumroll, Note, TJAParser, calculate_base_score
from libs.utils import ( from libs.utils import (
OutlinedText, OutlinedText,
draw_scaled_texture,
get_config, get_config,
get_current_ms, get_current_ms,
global_data, global_data,
@@ -1115,7 +1114,7 @@ class SongInfo:
self.font = self._load_font_for_text(song_name) self.font = self._load_font_for_text(song_name)
self.song_title = OutlinedText( self.song_title = OutlinedText(
self.font, song_name, 40, ray.WHITE, ray.BLACK, outline_thickness=5 self.font, song_name, 40, ray.WHITE, ray.BLACK, outline_thickness=4
) )
self.fade_in = Animation.create_fade(self.FADE_DURATION, initial_opacity=0.0, final_opacity=1.0) self.fade_in = Animation.create_fade(self.FADE_DURATION, initial_opacity=0.0, final_opacity=1.0)
self.fade_out = Animation.create_fade(self.FADE_DURATION, delay=self.DISPLAY_DURATION) self.fade_out = Animation.create_fade(self.FADE_DURATION, delay=self.DISPLAY_DURATION)
@@ -1157,7 +1156,9 @@ class SongInfo:
text_x = 1252 - self.song_title.texture.width text_x = 1252 - self.song_title.texture.width
text_y = int(50 - self.song_title.texture.height / 2) text_y = int(50 - self.song_title.texture.height / 2)
self.song_title.draw(text_x, text_y, self.song_name_fade) src = ray.Rectangle(0, 0, self.song_title.texture.width, self.song_title.texture.height)
dest = ray.Rectangle(text_x, text_y, self.song_title.texture.width, self.song_title.texture.height)
self.song_title.draw(src, dest, ray.Vector2(0, 0), 0, self.song_name_fade)
class ResultTransition: class ResultTransition:
def __init__(self, screen_height: int): def __init__(self, screen_height: int):

View File

@@ -207,7 +207,7 @@ class FontText:
codepoints_no_dup.update(session_data.song_title) codepoints_no_dup.update(session_data.song_title)
codepoints = ray.load_codepoints(''.join(codepoints_no_dup), codepoint_count) codepoints = ray.load_codepoints(''.join(codepoints_no_dup), codepoint_count)
self.font = ray.load_font_ex(str(Path('Graphics/Modified-DFPKanteiryu-XB.ttf')), 32, codepoints, 0) self.font = ray.load_font_ex(str(Path('Graphics/Modified-DFPKanteiryu-XB.ttf')), 32, codepoints, 0)
self.text = OutlinedText(self.font, str(text), font_size, ray.WHITE, ray.BLACK, outline_thickness=5) self.text = OutlinedText(self.font, str(text), font_size, ray.WHITE, ray.BLACK, outline_thickness=4)
self.texture = self.text.texture self.texture = self.text.texture

View File

@@ -3,9 +3,16 @@ from pathlib import Path
import pyray as ray import pyray as ray
from libs.animation import Animation
from libs.audio import audio from libs.audio import audio
from libs.tja import TJAParser from libs.tja import TJAParser
from libs.utils import get_config, session_data from libs.utils import (
OutlinedText,
get_config,
get_current_ms,
load_all_textures_from_zip,
session_data,
)
class SongSelectScreen: class SongSelectScreen:
@@ -13,16 +20,22 @@ class SongSelectScreen:
self.width = width self.width = width
self.height = height self.height = height
self.song_list: dict[str, list] = dict() self.song_list: dict[str, list] = dict()
self.song_name_textures: list[OutlinedText] = []
self.selected_song = 0 self.selected_song = 0
self.selected_difficulty = 0 self.selected_difficulty = 0
self.selected_index = 0 self.selected_index = 0
for dirpath, dirnames, filenames in os.walk(f'{get_config()["paths"]["tja_path"]}'):
for filename in filenames:
if filename.endswith(".tja"):
self.song_list[dirpath] = TJAParser(dirpath).get_metadata()
self.screen_init = False self.screen_init = False
def _load_font_for_text(self, text: str) -> ray.Font:
codepoint_count = ray.ffi.new('int *', 0)
unique_codepoints = set(text)
codepoints = ray.load_codepoints(''.join(unique_codepoints), codepoint_count)
return ray.load_font_ex(str(Path('Graphics/Modified-DFPKanteiryu-XB.ttf')), 32, codepoints, 0)
def load_textures(self):
self.textures = load_all_textures_from_zip(Path('Graphics/lumendata/song_select.zip'))
def load_sounds(self): def load_sounds(self):
sounds_dir = Path("Sounds") sounds_dir = Path("Sounds")
self.sound_don = audio.load_sound(str(sounds_dir / "inst_00_don.wav")) self.sound_don = audio.load_sound(str(sounds_dir / "inst_00_don.wav"))
@@ -30,16 +43,31 @@ class SongSelectScreen:
def on_screen_start(self): def on_screen_start(self):
if not self.screen_init: if not self.screen_init:
self.load_textures()
self.load_sounds() self.load_sounds()
for dirpath, dirnames, filenames in os.walk(f'{get_config()["paths"]["tja_path"]}'):
for filename in filenames:
if filename.endswith(".tja"):
self.song_list[dirpath] = TJAParser(dirpath).get_metadata()
name = self.song_list[dirpath][1]
if name == '':
name = self.song_list[dirpath][0]
if len(self.song_name_textures) < 17:
font = self._load_font_for_text(name)
self.song_name_textures.append(OutlinedText(font, name, 40, ray.WHITE, ray.BLACK, outline_thickness=4, vertical=True))
self.screen_init = True self.screen_init = True
self.is_song_select = True self.is_song_select = True
self.is_difficulty_select = False self.is_difficulty_select = False
self.background_move = Animation.create_move(15000, start_position=0, total_distance=1280)
def on_screen_end(self): def on_screen_end(self):
self.screen_init = False self.screen_init = False
audio.play_sound(self.sound_don) audio.play_sound(self.sound_don)
session_data.selected_song = list(self.song_list.keys())[self.selected_song] session_data.selected_song = list(self.song_list.keys())[self.selected_song]
session_data.selected_difficulty = self.selected_difficulty session_data.selected_difficulty = self.selected_difficulty
for zip in self.textures:
for texture in self.textures[zip]:
ray.unload_texture(texture)
return "GAME" return "GAME"
def update_song_select(self): def update_song_select(self):
@@ -69,12 +97,35 @@ class SongSelectScreen:
def update(self): def update(self):
self.on_screen_start() self.on_screen_start()
self.background_move.update(get_current_ms())
if self.background_move.is_finished:
self.background_move = Animation.create_move(15000, start_position=0, total_distance=1280)
if self.is_song_select: if self.is_song_select:
self.update_song_select() self.update_song_select()
elif self.is_difficulty_select: elif self.is_difficulty_select:
return self.update_difficulty_select() return self.update_difficulty_select()
def draw_box(self, x: int, y: int, texture_index: int):
ray.draw_texture(self.textures['song_select'][texture_index+1], x, y, ray.WHITE)
for i in range(0, self.textures['song_select'][texture_index].width * 4, self.textures['song_select'][texture_index].width):
ray.draw_texture(self.textures['song_select'][texture_index], (x+32)+i, y, ray.WHITE)
ray.draw_texture(self.textures['song_select'][texture_index+2], x+64, y, ray.WHITE)
ray.draw_texture(self.textures['song_select'][texture_index+3], x+12, y+16, ray.WHITE)
def draw_song_select(self): def draw_song_select(self):
texture = self.textures['song_select'][784]
for i in range(0, texture.width * 4, texture.width):
ray.draw_texture(self.textures['song_select'][784], i - int(self.background_move.attribute), 0, ray.WHITE)
ray.draw_texture(self.textures['song_select'][244], 5, 5, ray.WHITE)
ray.draw_texture(self.textures['song_select'][394], 0, self.height - self.textures['song_select'][394].height, ray.WHITE)
for i in range(-1, 15):
self.draw_box(44 + (i*100), 95, 620)
texture = self.song_name_textures[i+1]
src = ray.Rectangle(0, 0, texture.texture.width, texture.texture.height)
dest = ray.Rectangle((91 + (i*100)) - texture.texture.width / 2, 130, texture.texture.width, min(texture.texture.height, 417))
texture.draw(src, dest, ray.Vector2(0, 0), 0, ray.WHITE)
visible_songs = 36 visible_songs = 36
song_paths = list(self.song_list.keys()) # Get all paths as a list song_paths = list(self.song_list.keys()) # Get all paths as a list
total_songs = len(song_paths) total_songs = len(song_paths)