fix double free bug, add logging, update to python 3.14

This commit is contained in:
Anthony Samms
2025-10-27 18:41:28 -04:00
parent 104ec726b0
commit 3b0a6bed97
29 changed files with 739 additions and 640 deletions

View File

@@ -1,38 +1,17 @@
import pyray as ray
from libs.utils import get_current_ms
from libs.texture import tex
from scenes.game import JudgeCounter
from libs.screen import Screen
class DevScreen:
def __init__(self):
self.width = 1280
self.height = 720
self.screen_init = False
self.length = 100
def on_screen_start(self):
if not self.screen_init:
self.screen_init = True
tex.load_screen_textures('game')
self.obj = JudgeCounter()
class DevScreen(Screen):
def on_screen_start(self, screen_name: str):
super().on_screen_start(screen_name)
def on_screen_end(self, next_screen: str):
self.screen_init = False
return next_screen
return super().on_screen_end(next_screen)
def update(self):
self.on_screen_start()
self.obj.update(0, 0, 0, 0)
if ray.is_key_pressed(ray.KeyboardKey.KEY_ENTER):
return self.on_screen_end('GAME')
if ray.is_key_pressed(ray.KeyboardKey.KEY_SPACE):
self.obj = JudgeCounter()
super().update()
def draw(self):
ray.draw_rectangle(0, 0, 1280, 720, ray.GREEN)
self.obj.draw()
def draw_3d(self):
pass

View File

@@ -1,9 +1,12 @@
import logging
from typing import Optional
import pyray as ray
from libs.audio import audio
from libs.chara_2d import Chara2D
from libs.global_objects import AllNetIcon, CoinOverlay, Nameplate, Indicator, EntryOverlay, Timer
from libs.texture import tex
from libs.screen import Screen
from libs.utils import (
OutlinedText,
get_current_ms,
@@ -14,53 +17,45 @@ from libs.utils import (
is_r_kat_pressed,
)
logger = logging.getLogger(__name__)
class State:
"""State enum for the entry screen"""
SELECT_SIDE = 0
SELECT_MODE = 1
class EntryScreen:
def __init__(self):
self.screen_init = False
class EntryScreen(Screen):
def on_screen_start(self):
if not self.screen_init:
tex.load_screen_textures('entry')
audio.load_screen_sounds('entry')
self.side = 1
self.is_2p = False
self.box_manager = BoxManager()
self.state = State.SELECT_SIDE
super().on_screen_start()
self.side = 1
self.is_2p = False
self.box_manager = BoxManager()
self.state = State.SELECT_SIDE
# Initial nameplate for side selection
plate_info = global_data.config['nameplate_1p']
self.nameplate = Nameplate(plate_info['name'], plate_info['title'], -1, -1, False)
# Initial nameplate for side selection
plate_info = global_data.config['nameplate_1p']
self.nameplate = Nameplate(plate_info['name'], plate_info['title'], -1, -1, False)
self.coin_overlay = CoinOverlay()
self.allnet_indicator = AllNetIcon()
self.entry_overlay = EntryOverlay()
self.timer = Timer(60, get_current_ms(), self.box_manager.select_box)
self.screen_init = True
self.side_select_fade = tex.get_animation(0)
self.bg_flicker = tex.get_animation(1)
self.side_select_fade.start()
self.chara = Chara2D(0, 100)
self.announce_played = False
self.players = [None, None]
audio.play_sound('bgm', 'music')
self.coin_overlay = CoinOverlay()
self.allnet_indicator = AllNetIcon()
self.entry_overlay = EntryOverlay()
self.timer = Timer(60, get_current_ms(), self.box_manager.select_box)
self.screen_init = True
self.side_select_fade = tex.get_animation(0)
self.bg_flicker = tex.get_animation(1)
self.side_select_fade.start()
self.chara = Chara2D(0, 100)
self.announce_played = False
self.players: list[Optional[EntryPlayer]] = [None, None]
audio.play_sound('bgm', 'music')
def on_screen_end(self, next_screen: str):
self.screen_init = False
audio.stop_sound('bgm')
self.nameplate.unload()
for player in self.players:
if player:
player.unload()
tex.unload_textures()
audio.unload_all_sounds()
audio.unload_all_music()
return next_screen
return super().on_screen_end(next_screen)
def handle_input(self):
if self.state == State.SELECT_SIDE:
@@ -118,7 +113,7 @@ class EntryScreen:
self.side = 1
def update(self):
self.on_screen_start()
super().update()
current_time = get_current_ms()
self.side_select_fade.update(current_time)
self.bg_flicker.update(current_time)
@@ -130,6 +125,7 @@ class EntryScreen:
if player:
player.update(current_time)
if self.box_manager.is_finished():
logger.info(f"Box selection finished, transitioning to {self.box_manager.selected_box()}")
return self.on_screen_end(self.box_manager.selected_box())
for player in self.players:
if player and player.cloud_fade.is_finished and not audio.is_sound_playing(f'entry_start_{global_data.player_num}p') and not self.announce_played:

View File

@@ -1,5 +1,6 @@
import bisect
import math
import logging
import sqlite3
from collections import deque
from pathlib import Path
@@ -13,6 +14,7 @@ from libs.background import Background
from libs.chara_2d import Chara2D
from libs.global_data import Modifiers
from libs.global_objects import AllNetIcon, Nameplate
from libs.screen import Screen
from libs.texture import tex
from libs.tja import (
Balloon,
@@ -33,22 +35,70 @@ from libs.utils import (
is_l_kat_pressed,
is_r_don_pressed,
is_r_kat_pressed,
rounded
)
from libs.video import VideoPlayer
SCREEN_WIDTH = 1280
SCREEN_HEIGHT = 720
class GameScreen:
logger = logging.getLogger(__name__)
class GameScreen(Screen):
JUDGE_X = 414
def __init__(self):
def on_screen_start(self):
super().on_screen_start()
self.mask_shader = ray.load_shader("shader/outline.vs", "shader/mask.fs")
self.current_ms = 0
self.screen_init = False
self.end_ms = 0
self.start_delay = 1000
self.song_started = False
self.mask_shader = ray.load_shader("shader/outline.vs", "shader/mask.fs")
self.screen_init = True
self.movie = None
self.song_music = None
tex.load_screen_textures('game')
logger.info("Game screen textures loaded")
if global_data.config["general"]["nijiiro_notes"]:
# drop original
if "notes" in tex.textures:
del tex.textures["notes"]
# load nijiiro, rename "notes"
# to leave hardcoded 'notes' in calls below
tex.load_zip("game", "notes_nijiiro")
tex.textures["notes"] = tex.textures.pop("notes_nijiiro")
logger.info("Loaded nijiiro notes textures")
audio.load_screen_sounds('game')
logger.info("Game screen sounds loaded")
ray.set_shader_value_texture(self.mask_shader, ray.get_shader_location(self.mask_shader, "texture0"), tex.textures['balloon']['rainbow_mask'].texture)
ray.set_shader_value_texture(self.mask_shader, ray.get_shader_location(self.mask_shader, "texture1"), tex.textures['balloon']['rainbow'].texture)
session_data = global_data.session_data[global_data.player_num-1]
self.init_tja(global_data.selected_song)
logger.info(f"TJA initialized for song: {global_data.selected_song}")
self.load_hitsounds()
self.song_info = SongInfo(session_data.song_title, session_data.genre_index)
self.result_transition = ResultTransition(global_data.player_num)
subtitle = self.tja.metadata.subtitle.get(global_data.config['general']['language'].lower(), '')
self.bpm = self.tja.metadata.bpm
scene_preset = self.tja.metadata.scene_preset
if self.movie is None:
self.background = Background(global_data.player_num, self.bpm, scene_preset=scene_preset)
logger.info("Background initialized")
else:
self.background = None
logger.info("Movie initialized")
self.transition = Transition(session_data.song_title, subtitle, is_second=True)
self.allnet_indicator = AllNetIcon()
self.transition.start()
def on_screen_end(self, next_screen):
self.song_started = False
self.end_ms = 0
if self.movie is not None:
self.movie.stop()
logger.info("Movie stopped")
if self.background is not None:
self.background.unload()
logger.info("Background unloaded")
return super().on_screen_end(next_screen)
def load_hitsounds(self):
"""Load the hit sounds"""
@@ -56,16 +106,19 @@ class GameScreen:
if global_data.hit_sound == -1:
audio.load_sound(Path('none.wav'), 'hitsound_don_1p')
audio.load_sound(Path('none.wav'), 'hitsound_kat_1p')
logger.info("Loaded default (none) hit sounds for 1P")
if global_data.hit_sound == 0:
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound[0]) / "don.wav", 'hitsound_don_1p')
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound[0]) / "ka.wav", 'hitsound_kat_1p')
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound[1]) / "don.wav", 'hitsound_don_2p')
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound[1]) / "ka.wav", 'hitsound_kat_2p')
logger.info("Loaded wav hit sounds for 1P and 2P")
else:
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound[0]) / "don.ogg", 'hitsound_don_1p')
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound[0]) / "ka.ogg", 'hitsound_kat_1p')
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound[1]) / "don.ogg", 'hitsound_don_2p')
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound[1]) / "ka.ogg", 'hitsound_kat_2p')
logger.info("Loaded ogg hit sounds for 1P and 2P")
def init_tja(self, song: Path):
"""Initialize the TJA file"""
@@ -82,52 +135,6 @@ class GameScreen:
self.player_1 = Player(self.tja, global_data.player_num, global_data.session_data[global_data.player_num-1].selected_difficulty, False, global_data.modifiers[0])
self.start_ms = (get_current_ms() - self.tja.metadata.offset*1000)
def on_screen_start(self):
if not self.screen_init:
self.screen_init = True
self.movie = None
self.song_music = None
tex.load_screen_textures('game')
if global_data.config["general"]["nijiiro_notes"]:
# drop original
if "notes" in tex.textures:
del tex.textures["notes"]
# load nijiiro, rename "notes"
# to leave hardcoded 'notes' in calls below
tex.load_zip("game", "notes_nijiiro")
tex.textures["notes"] = tex.textures.pop("notes_nijiiro")
audio.load_screen_sounds('game')
ray.set_shader_value_texture(self.mask_shader, ray.get_shader_location(self.mask_shader, "texture0"), tex.textures['balloon']['rainbow_mask'].texture)
ray.set_shader_value_texture(self.mask_shader, ray.get_shader_location(self.mask_shader, "texture1"), tex.textures['balloon']['rainbow'].texture)
session_data = global_data.session_data[global_data.player_num-1]
self.init_tja(global_data.selected_song)
self.load_hitsounds()
self.song_info = SongInfo(session_data.song_title, session_data.genre_index)
self.result_transition = ResultTransition(global_data.player_num)
subtitle = self.tja.metadata.subtitle.get(global_data.config['general']['language'].lower(), '')
self.bpm = self.tja.metadata.bpm
scene_preset = self.tja.metadata.scene_preset
if self.movie is None:
self.background = Background(global_data.player_num, self.bpm, scene_preset=scene_preset)
else:
self.background = None
self.transition = Transition(session_data.song_title, subtitle, is_second=True)
self.allnet_indicator = AllNetIcon()
self.transition.start()
def on_screen_end(self, next_screen):
self.screen_init = False
tex.unload_textures()
audio.unload_all_sounds()
audio.unload_all_music()
self.song_started = False
self.end_ms = 0
if self.movie is not None:
self.movie.stop()
if self.background is not None:
self.background.unload()
return next_screen
def write_score(self):
"""Write the score to the database"""
if self.tja is None:
@@ -193,7 +200,7 @@ class GameScreen:
if (self.current_ms >= self.tja.metadata.offset*1000 + self.start_delay - global_data.config["general"]["audio_offset"]) and not self.song_started:
if self.song_music is not None:
audio.play_music_stream(self.song_music, 'music')
print(f"Song started at {self.current_ms}")
logger.info(f"Song started at {self.current_ms}")
if self.movie is not None:
self.movie.start(current_time)
self.song_started = True
@@ -229,7 +236,7 @@ class GameScreen:
self.background.update(current_time, self.bpm, self.player_1.gauge, None)
def update(self):
self.on_screen_start()
super().update()
current_time = get_current_ms()
self.transition.update(current_time)
self.current_ms = current_time - self.start_ms
@@ -243,6 +250,7 @@ class GameScreen:
self.song_info.update(current_time)
self.result_transition.update(current_time)
if self.result_transition.is_finished and not audio.is_sound_playing('result_transition'):
logger.info("Result transition finished, moving to RESULT screen")
return self.on_screen_end('RESULT')
elif self.current_ms >= self.player_1.end_time:
session_data = global_data.session_data[global_data.player_num-1]
@@ -252,11 +260,13 @@ class GameScreen:
if current_time >= self.end_ms + 1000:
if self.player_1.ending_anim is None:
self.write_score()
logger.info("Score written and ending animations spawned")
self.spawn_ending_anims()
if current_time >= self.end_ms + 8533.34:
if not self.result_transition.is_started:
self.result_transition.start()
audio.play_sound('result_transition', 'voice')
logger.info("Result transition started and voice played")
else:
self.end_ms = current_time
@@ -692,7 +702,7 @@ class Player:
big = curr_note.type == 3 or curr_note.type == 4
if (curr_note.hit_ms - good_window_ms) <= ms_from_start <= (curr_note.hit_ms + good_window_ms):
self.draw_judge_list.append(Judgement('GOOD', big, self.is_2p, ms_display=ms_from_start - curr_note.hit_ms))
self.draw_judge_list.append(Judgement('GOOD', big, self.is_2p))
self.lane_hit_effect = LaneHitEffect('GOOD', self.is_2p)
self.good_count += 1
self.score += self.base_score
@@ -708,7 +718,7 @@ class Player:
background.add_chibi(False, 1)
elif (curr_note.hit_ms - ok_window_ms) <= ms_from_start <= (curr_note.hit_ms + ok_window_ms):
self.draw_judge_list.append(Judgement('OK', big, self.is_2p, ms_display=ms_from_start - curr_note.hit_ms))
self.draw_judge_list.append(Judgement('OK', big, self.is_2p))
self.ok_count += 1
self.score += 10 * math.floor(self.base_score / 2 / 10)
self.base_score_list.append(ScoreCounterAnimation(self.player_number, 10 * math.floor(self.base_score / 2 / 10), self.is_2p))
@@ -723,7 +733,7 @@ class Player:
background.add_chibi(False, 1)
elif (curr_note.hit_ms - bad_window_ms) <= ms_from_start <= (curr_note.hit_ms + bad_window_ms):
self.draw_judge_list.append(Judgement('BAD', big, self.is_2p, ms_display=ms_from_start - curr_note.hit_ms))
self.draw_judge_list.append(Judgement('BAD', big, self.is_2p))
self.bad_count += 1
self.combo = 0
# Remove from both the specific note list and the main play_notes list
@@ -1109,14 +1119,11 @@ class Player:
class Judgement:
"""Shows the judgement of the player's hit"""
def __init__(self, type: str, big: bool, is_2p: bool, ms_display: Optional[float]=None):
def __init__(self, type: str, big: bool, is_2p: bool):
self.is_2p = is_2p
self.type = type
self.big = big
self.is_finished = False
self.curr_hit_ms = None
if ms_display is not None:
self.curr_hit_ms = str(round(ms_display, 2))
self.fade_animation_1 = Animation.create_fade(132, initial_opacity=0.5, delay=100)
self.fade_animation_1.start()
@@ -2120,14 +2127,14 @@ 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, ray.Color(253, 161, 0, 255))
self.draw_counter(self.ok / total_notes * 100, 260, 477, 23, ray.Color(253, 161, 0, 255))
self.draw_counter(self.bad / total_notes * 100, 260, 515, 23, ray.Color(253, 161, 0, 255))
self.draw_counter((self.good + self.ok) / total_notes * 100, 270, 388, 23, ray.Color(253, 161, 0, 255))
self.draw_counter(self.good, 180, 440, 23, ray.WHITE)
self.draw_counter(self.ok, 180, 477, 23, ray.WHITE)
self.draw_counter(self.bad, 180, 515, 23, ray.WHITE)
self.draw_counter(self.drumrolls, 180, 577, 23, ray.WHITE)
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)
class Gauge:
"""The player's gauge"""

View File

@@ -1,20 +1,24 @@
import logging
import threading
import pyray as ray
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 tex
from libs.utils import get_current_ms, global_data
from libs.file_navigator import navigator
class LoadScreen:
def __init__(self):
logger = logging.getLogger(__name__)
class LoadScreen(Screen):
def __init__(self, name: str):
super().__init__(name)
self.width = 1280
self.height = 720
self.screen_init = False
self.songs_loaded = False
self.navigator_started = False
self.loading_complete = False
@@ -37,42 +41,44 @@ class LoadScreen:
"""Background thread function to load song hashes"""
global_data.song_hashes = build_song_hashes()
self.songs_loaded = True
logger.info("Song hashes loaded")
def _load_navigator(self):
"""Background thread function to load navigator"""
self.navigator.initialize(global_data.config["paths"]["tja_path"])
self.loading_complete = True
logger.info("Navigator initialized")
def on_screen_start(self):
if not self.screen_init:
tex.load_screen_textures('loading')
self.loading_thread = threading.Thread(target=self._load_song_hashes)
self.loading_thread.daemon = True
self.loading_thread.start()
self.screen_init = True
super().on_screen_start()
self.loading_thread = threading.Thread(target=self._load_song_hashes)
self.loading_thread.daemon = True
self.loading_thread.start()
logger.info("Started song hashes loading thread")
def on_screen_end(self, next_screen: str):
self.screen_init = False
tex.unload_textures()
if self.loading_thread and self.loading_thread.is_alive():
self.loading_thread.join(timeout=1.0)
logger.info("Joined song hashes loading thread")
if self.navigator_thread and self.navigator_thread.is_alive():
self.navigator_thread.join(timeout=1.0)
return next_screen
logger.info("Joined navigator loading thread")
return super().on_screen_end(next_screen)
def update(self):
self.on_screen_start()
super().update()
if self.songs_loaded and not self.navigator_started:
self.navigator_thread = threading.Thread(target=self._load_navigator)
self.navigator_thread.daemon = True
self.navigator_thread.start()
self.navigator_started = True
logger.info("Started navigator loading thread")
if self.loading_complete and self.fade_in is None:
self.fade_in = Animation.create_fade(1000, initial_opacity=0.0, final_opacity=1.0, ease_in='cubic')
self.fade_in.start()
logger.info("Fade-in animation started")
if self.fade_in is not None:
self.fade_in.update(get_current_ms())
@@ -107,5 +113,3 @@ class LoadScreen:
if self.fade_in is not None:
ray.draw_rectangle(0, 0, self.width, self.height, ray.fade(ray.WHITE, self.fade_in.attribute))
self.allnet_indicator.draw()
def draw_3d(self):
pass

View File

@@ -1,9 +1,11 @@
import logging
import pyray as ray
from libs.global_data import reset_session
from libs.audio import audio
from libs.chara_2d import Chara2D
from libs.global_objects import AllNetIcon, CoinOverlay, Nameplate
from libs.screen import Screen
from libs.texture import tex
from libs.utils import (
OutlinedText,
@@ -13,46 +15,32 @@ from libs.utils import (
is_r_don_pressed
)
logger = logging.getLogger(__name__)
class State:
"""
Enum representing the state of the result screen.
"""
"""Enum representing the state of the result screen."""
FAIL = 0
CLEAR = 1
RAINBOW = 2
class ResultScreen:
def __init__(self):
self.width = 1280
self.height = 720
self.screen_init = False
class ResultScreen(Screen):
def on_screen_start(self):
if not self.screen_init:
tex.load_screen_textures('result')
audio.load_screen_sounds('result')
self.screen_init = True
self.song_info = OutlinedText(global_data.session_data[0].song_title, 40, ray.WHITE, ray.BLACK, outline_thickness=5)
audio.play_sound('bgm', 'music')
self.fade_in = FadeIn(str(global_data.player_num))
self.fade_out = tex.get_animation(0)
self.coin_overlay = CoinOverlay()
self.allnet_indicator = AllNetIcon()
self.start_ms = get_current_ms()
self.is_skipped = False
self.background = Background(str(global_data.player_num), self.width)
self.player_1 = ResultPlayer(str(global_data.player_num), False, False)
super().on_screen_start()
self.song_info = OutlinedText(global_data.session_data[0].song_title, 40, ray.WHITE, ray.BLACK, outline_thickness=5)
audio.play_sound('bgm', 'music')
self.fade_in = FadeIn(str(global_data.player_num))
self.fade_out = tex.get_animation(0)
self.coin_overlay = CoinOverlay()
self.allnet_indicator = AllNetIcon()
self.start_ms = get_current_ms()
self.is_skipped = False
self.background = Background(str(global_data.player_num), 1280)
self.player_1 = ResultPlayer(str(global_data.player_num), False, False)
def on_screen_end(self, next_screen: str):
self.screen_init = False
global_data.songs_played += 1
tex.unload_textures()
audio.stop_sound('bgm')
audio.unload_all_sounds()
audio.unload_all_music()
reset_session()
return next_screen
return super().on_screen_end(next_screen)
def handle_input(self):
if is_r_don_pressed() or is_l_don_pressed():
@@ -63,7 +51,7 @@ class ResultScreen:
audio.play_sound('don', 'sound')
def update(self):
self.on_screen_start()
super().update()
current_time = get_current_ms()
self.fade_in.update(current_time)
self.player_1.update(current_time, self.fade_in.is_finished, self.is_skipped)
@@ -78,7 +66,7 @@ class ResultScreen:
def draw_overlay(self):
self.fade_in.draw()
ray.draw_rectangle(0, 0, self.width, self.height, ray.fade(ray.BLACK, self.fade_out.attribute))
ray.draw_rectangle(0, 0, 1280, 720, ray.fade(ray.BLACK, self.fade_out.attribute))
self.coin_overlay.draw()
self.allnet_indicator.draw()

View File

@@ -1,6 +1,8 @@
import logging
import pyray as ray
from libs.audio import audio
from libs.screen import Screen
from libs.utils import (
global_data,
is_l_don_pressed,
@@ -10,10 +12,12 @@ from libs.utils import (
save_config,
)
logger = logging.getLogger(__name__)
class SettingsScreen:
def __init__(self):
self.screen_init = False
class SettingsScreen(Screen):
def on_screen_start(self):
super().on_screen_start()
self.config = global_data.config
self.headers = list(self.config.keys())
self.headers.append('Exit')
@@ -23,13 +27,7 @@ class SettingsScreen:
self.editing_key = False
self.editing_gamepad = False
def on_screen_start(self):
if not self.screen_init:
audio.list_host_apis()
self.screen_init = True
def on_screen_end(self):
self.screen_init = False
def on_screen_end(self, next_screen: str):
save_config(self.config)
global_data.config = self.config
audio.close_audio_device()
@@ -41,7 +39,8 @@ class SettingsScreen:
audio.buffer_size = global_data.config["audio"]["buffer_size"]
audio.volume_presets = global_data.config["volume"]
audio.init_audio_device()
return "ENTRY"
logger.info("Settings saved and audio device re-initialized")
return next_screen
def get_current_settings(self):
"""Get the current section's settings as a list"""
@@ -53,6 +52,7 @@ class SettingsScreen:
def handle_boolean_toggle(self, section, key):
"""Toggle boolean values"""
self.config[section][key] = not self.config[section][key]
logger.info(f"Toggled boolean setting: {section}.{key} -> {self.config[section][key]}")
def handle_numeric_change(self, section, key, increment):
"""Handle numeric value changes"""
@@ -81,6 +81,7 @@ class SettingsScreen:
new_value = valid_sizes[new_idx]
self.config[section][key] = new_value
logger.info(f"Changed numeric setting: {section}.{key} -> {new_value}")
def handle_string_cycle(self, section, key):
"""Cycle through predefined string values"""
@@ -98,10 +99,12 @@ class SettingsScreen:
self.config[section][key] = values[new_idx]
except ValueError:
self.config[section][key] = values[0]
logger.info(f"Cycled string setting: {section}.{key} -> {self.config[section][key]}")
def handle_key_binding(self, section, key):
"""Handle key binding changes"""
self.editing_key = True
logger.info(f"Started key binding edit for: {section}.{key}")
def update_key_binding(self):
"""Update key binding based on input"""
@@ -116,11 +119,14 @@ class SettingsScreen:
setting_key, _ = settings[self.setting_index]
self.config[current_header][setting_key] = [new_key]
self.editing_key = False
logger.info(f"Key binding updated: {current_header}.{setting_key} -> {new_key}")
elif key_pressed == ray.KeyboardKey.KEY_ESCAPE:
self.editing_key = False
logger.info("Key binding edit cancelled")
def handle_gamepad_binding(self, section, key):
self.editing_gamepad = True
logger.info(f"Started gamepad binding edit for: {section}.{key}")
def update_gamepad_binding(self):
"""Update gamepad binding based on input"""
@@ -132,11 +138,13 @@ class SettingsScreen:
setting_key, _ = settings[self.setting_index]
self.config[current_header][setting_key] = [button_pressed]
self.editing_gamepad = False
logger.info(f"Gamepad binding updated: {current_header}.{setting_key} -> {button_pressed}")
if ray.is_key_pressed(ray.KeyboardKey.KEY_ESCAPE):
self.editing_gamepad = False
logger.info("Gamepad binding edit cancelled")
def update(self):
self.on_screen_start()
super().update()
# Handle key binding editing
if self.editing_key:
@@ -151,18 +159,22 @@ class SettingsScreen:
# Exit handling
if current_header == 'Exit' and (is_l_don_pressed() or is_r_don_pressed()):
return self.on_screen_end()
logger.info("Exiting settings screen")
return self.on_screen_end("ENTRY")
# Navigation between sections
if not self.in_setting_edit:
if is_r_kat_pressed():
self.header_index = (self.header_index + 1) % len(self.headers)
self.setting_index = 0
logger.info(f"Navigated to next section: {self.headers[self.header_index]}")
elif is_l_kat_pressed():
self.header_index = (self.header_index - 1) % len(self.headers)
self.setting_index = 0
logger.info(f"Navigated to previous section: {self.headers[self.header_index]}")
elif (is_l_don_pressed() or is_r_don_pressed()) and current_header != 'Exit':
self.in_setting_edit = True
logger.info(f"Entered section edit: {current_header}")
else:
# Navigation within settings
settings = self.get_current_settings()
@@ -172,8 +184,10 @@ class SettingsScreen:
if is_r_kat_pressed():
self.setting_index = (self.setting_index + 1) % len(settings)
logger.info(f"Navigated to next setting: {settings[self.setting_index][0]}")
elif is_l_kat_pressed():
self.setting_index = (self.setting_index - 1) % len(settings)
logger.info(f"Navigated to previous setting: {settings[self.setting_index][0]}")
elif is_r_don_pressed():
# Modify setting value
setting_key, setting_value = settings[self.setting_index]
@@ -209,6 +223,7 @@ class SettingsScreen:
elif ray.is_key_pressed(ray.KeyboardKey.KEY_ESCAPE):
self.in_setting_edit = False
logger.info("Exited section edit")
def draw(self):
# Draw title
@@ -237,7 +252,8 @@ class SettingsScreen:
display_value = ', '.join(map(str, value))
else:
display_value = str(value)
if key == 'device_type':
display_value = f'{display_value} ({audio.get_host_api_name(value)})'
ray.draw_text(f'{key}: {display_value}', 250, i*25 + 70, 20, color)
# Draw instructions
@@ -255,5 +271,3 @@ class SettingsScreen:
else:
# Draw exit instruction
ray.draw_text("Press Don to exit settings", 250, 100, 20, ray.GREEN)
def draw_3d(self):
pass

View File

@@ -3,6 +3,7 @@ from dataclasses import fields
from pathlib import Path
import pyray as ray
import logging
from libs.file_navigator import navigator
from libs.audio import audio
@@ -10,6 +11,7 @@ from libs.chara_2d import Chara2D
from libs.file_navigator import Directory, SongBox, SongFile
from libs.global_data import Modifiers
from libs.global_objects import AllNetIcon, CoinOverlay, Nameplate, Indicator, Timer
from libs.screen import Screen
from libs.texture import tex
from libs.transition import Transition
from libs.utils import (
@@ -22,63 +24,59 @@ from libs.utils import (
is_r_kat_pressed,
)
logger = logging.getLogger(__name__)
class State:
BROWSING = 0
SONG_SELECTED = 1
DIFF_SORTING = 2
class SongSelectScreen:
class SongSelectScreen(Screen):
BOX_CENTER = 444
def __init__(self, screen_width: int = 1280):
self.screen_init = False
self.screen_width = screen_width
self.indicator = Indicator(Indicator.State.SELECT)
self.navigator = navigator
def on_screen_start(self):
if not self.screen_init:
tex.load_screen_textures('song_select')
audio.load_screen_sounds('song_select')
audio.set_sound_volume('ura_switch', 0.25)
audio.set_sound_volume('add_favorite', 3.0)
audio.play_sound('bgm', 'music')
audio.play_sound('voice_enter', 'voice')
self.background_move = tex.get_animation(0)
self.move_away = tex.get_animation(1)
self.diff_fade_out = tex.get_animation(2)
self.text_fade_out = tex.get_animation(3)
self.text_fade_in = tex.get_animation(4)
self.background_fade_change = tex.get_animation(5)
self.blue_arrow_fade = tex.get_animation(29)
self.blue_arrow_move = tex.get_animation(30)
self.blue_arrow_fade.start()
self.blue_arrow_move.start()
self.state = State.BROWSING
self.game_transition = None
self.demo_song = None
self.diff_sort_selector = None
self.coin_overlay = CoinOverlay()
self.allnet_indicator = AllNetIcon()
self.texture_index = SongBox.DEFAULT_INDEX
self.last_texture_index = SongBox.DEFAULT_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()
super().on_screen_start()
audio.set_sound_volume('ura_switch', 0.25)
audio.set_sound_volume('add_favorite', 3.0)
audio.play_sound('bgm', 'music')
audio.play_sound('voice_enter', 'voice')
self.navigator = navigator
self.background_move = tex.get_animation(0)
self.move_away = tex.get_animation(1)
self.diff_fade_out = tex.get_animation(2)
self.text_fade_out = tex.get_animation(3)
self.text_fade_in = tex.get_animation(4)
self.background_fade_change = tex.get_animation(5)
self.blue_arrow_fade = tex.get_animation(29)
self.blue_arrow_move = tex.get_animation(30)
self.blue_arrow_fade.start()
self.blue_arrow_move.start()
self.state = State.BROWSING
self.game_transition = None
self.demo_song = None
self.diff_sort_selector = None
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.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.player_1 = SongSelectPlayer(str(global_data.player_num), self.text_fade_in)
self.player_1 = SongSelectPlayer(str(global_data.player_num), self.text_fade_in)
if self.navigator.items == []:
return self.on_screen_end("ENTRY")
if self.navigator.items == []:
logger.warning("No navigator items found, returning to ENTRY screen")
return self.on_screen_end("ENTRY")
if str(global_data.selected_song) in self.navigator.all_song_files:
self.navigator.mark_crowns_dirty_for_song(self.navigator.all_song_files[str(global_data.selected_song)])
if str(global_data.selected_song) in self.navigator.all_song_files:
self.navigator.mark_crowns_dirty_for_song(self.navigator.all_song_files[str(global_data.selected_song)])
self.navigator.reset_items()
curr_item = self.navigator.get_current_item()
curr_item.box.get_scores()
self.navigator.add_recent()
curr_item = self.navigator.get_current_item()
curr_item.box.get_scores()
self.navigator.add_recent()
def finalize_song(self):
global_data.selected_song = self.navigator.get_current_item().path
@@ -88,13 +86,10 @@ class SongSelectScreen:
def on_screen_end(self, next_screen):
self.screen_init = False
self.reset_demo_music()
self.navigator.reset_items()
self.finalize_song()
audio.unload_all_sounds()
audio.unload_all_music()
tex.unload_textures()
self.player_1.nameplate.unload()
return next_screen
self.navigator.get_current_item().box.yellow_box.create_anim()
return super().on_screen_end(next_screen)
def reset_demo_music(self):
"""Reset the preview music to the song select bgm."""
@@ -199,7 +194,7 @@ class SongSelectScreen:
"""Wrapper for timer callback"""
self._confirm_selection()
def _confirm_selection(self):
def _confirm_selection(self, player_selected: int = 1):
"""Confirm song selection and create game transition"""
audio.play_sound('don', 'sound')
audio.play_sound(f'voice_start_song_{global_data.player_num}p', 'voice')
@@ -214,7 +209,8 @@ class SongSelectScreen:
self.player_1.update(current_time)
if self.text_fade_out.is_finished:
self.player_1.selected_song = True
return "GAME"
next_screen = "GAME"
return next_screen
def check_for_selection(self):
if self.player_1.selected_diff_highlight_fade.is_finished and not audio.is_sound_playing(f'voice_start_song_{global_data.player_num}p') and self.game_transition is None:
@@ -230,7 +226,7 @@ class SongSelectScreen:
self.game_transition.start()
def update(self):
ret_val = self.on_screen_start()
ret_val = super().update()
if ret_val is not None:
return ret_val
current_time = get_current_ms()
@@ -288,12 +284,14 @@ class SongSelectScreen:
audio.play_music_stream(self.demo_song, 'music')
audio.seek_music_stream(self.demo_song, song.tja.metadata.demostart)
audio.stop_sound('bgm')
logger.info(f"Demo song loaded and playing for {song.tja.metadata.title}")
if song.box.is_open:
current_box = song.box
if not current_box.is_back and get_current_ms() >= song.box.wait + (83.33*3):
self.texture_index = current_box.texture_index
if ray.is_key_pressed(ray.KeyboardKey.KEY_ESCAPE):
logger.info("Escape key pressed, returning to ENTRY screen")
return self.on_screen_end('ENTRY')
def draw_background_diffs(self):
@@ -315,7 +313,7 @@ class SongSelectScreen:
for item in self.navigator.items:
box = item.box
if -156 <= box.position <= self.screen_width + 144:
if -156 <= box.position <= 1280 + 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)
else:
@@ -534,6 +532,7 @@ class SongSelectPlayer:
raise Exception("Directory was chosen instead of song")
diffs = sorted(selected_song.tja.metadata.course_data)
prev_diff = self.selected_difficulty
ret_val = None
if is_l_kat_pressed(self.player_num):
ret_val = self._navigate_difficulty_left(diffs)
@@ -616,7 +615,7 @@ class SongSelectPlayer:
self.selected_difficulty = 7 - self.selected_difficulty
return "ura_toggle"
def draw_selector(self, state: State, is_half: bool):
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
if self.selected_difficulty <= -1 or self.prev_diff == -1:
@@ -660,7 +659,7 @@ class SongSelectPlayer:
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))
def draw_background_diffs(self, state: State):
def draw_background_diffs(self, state: int):
if (self.selected_song and state == State.SONG_SELECTED and self.selected_difficulty >= 0):
if self.player_num == '2':
tex.draw_texture('global', 'background_diff', frame=self.selected_difficulty, fade=min(0.5, self.selected_diff_fadein.attribute), x=1025, y=-self.selected_diff_bounce.attribute, y2=self.selected_diff_bounce.attribute)
@@ -677,9 +676,9 @@ class SongSelectPlayer:
tex.draw_texture('global', 'bg_diff_text_bg', fade=min(0.5, self.selected_diff_text_fadein.attribute), scale=self.selected_diff_text_resize.attribute, center=True)
tex.draw_texture('global', 'bg_diff_text', frame=min(3, self.selected_difficulty), fade=self.selected_diff_text_fadein.attribute, scale=self.selected_diff_text_resize.attribute, center=True)
def draw(self, state: State, is_half: bool = False):
def draw(self, state: int, is_half: bool = False):
if (self.selected_song and state == State.SONG_SELECTED):
self.draw_selector(state, is_half)
self.draw_selector(is_half)
offset = 0
if self.neiro_selector is not None:

View File

@@ -1,3 +1,4 @@
import logging
import random
from pathlib import Path
@@ -12,15 +13,18 @@ from libs.utils import (
is_r_don_pressed,
)
from libs.video import VideoPlayer
from libs.screen import Screen
logger = logging.getLogger(__name__)
class State:
OP_VIDEO = 0
WARNING = 1
ATTRACT_VIDEO = 2
class TitleScreen:
def __init__(self):
class TitleScreen(Screen):
def __init__(self, name: str):
super().__init__(name)
#normalize to accept both stings and lists in toml
#maybe normalize centrally? but it's used only here
vp = global_data.config["paths"]["video_path"]
@@ -31,33 +35,27 @@ class TitleScreen:
base = Path(base)
self.op_video_list += list((base/"op_videos").glob("**/*.mp4"))
self.attract_video_list += list((base/"attract_videos").glob("**/*.mp4"))
self.screen_init = False
self.coin_overlay = CoinOverlay()
self.allnet_indicator = AllNetIcon()
self.entry_overlay = EntryOverlay()
def on_screen_start(self):
if not self.screen_init:
self.screen_init = True
tex.load_screen_textures('title')
audio.load_screen_sounds('title')
self.state = State.OP_VIDEO
self.op_video = None
self.attract_video = None
self.warning_board = None
self.fade_out = tex.get_animation(13)
self.text_overlay_fade = tex.get_animation(14)
super().on_screen_start()
self.state = State.OP_VIDEO
self.op_video = None
self.attract_video = None
self.warning_board = None
self.fade_out = tex.get_animation(13)
self.text_overlay_fade = tex.get_animation(14)
def on_screen_end(self) -> str:
def on_screen_end(self, next_screen) -> str:
if self.op_video is not None:
self.op_video.stop()
logger.info("OP video stopped")
if self.attract_video is not None:
self.attract_video.stop()
audio.unload_all_sounds()
audio.unload_all_music()
tex.unload_textures()
self.screen_init = False
return "ENTRY"
logger.info("Attract video stopped")
return super().on_screen_end(next_screen)
def scene_manager(self, current_time):
"""Manage the scene transitions"""
@@ -65,38 +63,44 @@ class TitleScreen:
if self.op_video is None:
self.op_video = VideoPlayer(random.choice(self.op_video_list))
self.op_video.start(current_time)
logger.info("Started OP video")
self.op_video.update()
if self.op_video.is_finished():
self.op_video.stop()
self.op_video = None
self.state = State.WARNING
logger.info("OP video finished, transitioning to WARNING state")
elif self.state == State.WARNING:
if self.warning_board is None:
self.warning_board = WarningScreen(current_time)
logger.info("Warning screen started")
self.warning_board.update(current_time)
if self.warning_board.is_finished:
self.state = State.ATTRACT_VIDEO
self.warning_board = None
logger.info("Warning finished, transitioning to ATTRACT_VIDEO state")
elif self.state == State.ATTRACT_VIDEO:
if self.attract_video is None:
self.attract_video = VideoPlayer(random.choice(self.attract_video_list))
self.attract_video.start(current_time)
logger.info("Started attract video")
self.attract_video.update()
if self.attract_video.is_finished():
self.attract_video.stop()
self.attract_video = None
self.state = State.OP_VIDEO
logger.info("Attract video finished, transitioning to OP_VIDEO state")
def update(self):
self.on_screen_start()
super().update()
current_time = get_current_ms()
self.text_overlay_fade.update(current_time)
self.fade_out.update(current_time)
if self.fade_out.is_finished:
self.fade_out.update(current_time)
return self.on_screen_end()
return self.on_screen_end("ENTRY")
self.scene_manager(current_time)
if is_l_don_pressed() or is_r_don_pressed():
@@ -120,9 +124,6 @@ class TitleScreen:
global_tex.draw_texture('overlay', 'hit_taiko_to_start', index=0, fade=self.text_overlay_fade.attribute)
global_tex.draw_texture('overlay', 'hit_taiko_to_start', index=1, fade=self.text_overlay_fade.attribute)
def draw_3d(self):
pass
class WarningScreen:
"""Warning screen for the game"""
class X:

View File

@@ -1,3 +1,4 @@
import logging
import copy
from pathlib import Path
from libs.tja import TJAParser
@@ -8,15 +9,16 @@ from libs.video import VideoPlayer
import pyray as ray
from scenes.game import ClearAnimation, FCAnimation, FailAnimation, GameScreen, Player, Background, SCREEN_WIDTH, ResultTransition
logger = logging.getLogger(__name__)
class TwoPlayerGameScreen(GameScreen):
def on_screen_start(self):
if not self.screen_init:
super().on_screen_start()
scene_preset = self.tja.metadata.scene_preset
if self.background is not None:
self.background.unload()
self.background = Background(3, self.bpm, scene_preset=scene_preset)
self.result_transition = ResultTransition(3)
super().on_screen_start()
scene_preset = self.tja.metadata.scene_preset
if self.background is not None:
self.background.unload()
self.background = Background(3, self.bpm, scene_preset=scene_preset)
self.result_transition = ResultTransition(3)
def load_hitsounds(self):
"""Load the hit sounds"""
@@ -26,12 +28,15 @@ class TwoPlayerGameScreen(GameScreen):
if global_data.hit_sound[0] == -1:
audio.load_sound(Path('none.wav'), 'hitsound_don_1p')
audio.load_sound(Path('none.wav'), 'hitsound_kat_1p')
logger.info("Loaded default (none) hit sounds for 1P")
elif global_data.hit_sound[0] == 0:
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound[0]) / "don.wav", 'hitsound_don_1p')
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound[0]) / "ka.wav", 'hitsound_kat_1p')
logger.info("Loaded wav hit sounds for 1P")
else:
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound[0]) / "don.ogg", 'hitsound_don_1p')
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound[0]) / "ka.ogg", 'hitsound_kat_1p')
logger.info("Loaded ogg hit sounds for 1P")
audio.set_sound_pan('hitsound_don_1p', 1.0)
audio.set_sound_pan('hitsound_kat_1p', 1.0)
@@ -39,12 +44,15 @@ class TwoPlayerGameScreen(GameScreen):
if global_data.hit_sound[1] == -1:
audio.load_sound(Path('none.wav'), 'hitsound_don_2p')
audio.load_sound(Path('none.wav'), 'hitsound_kat_2p')
logger.info("Loaded default (none) hit sounds for 2P")
elif global_data.hit_sound[1] == 0:
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound[1]) / "don_2p.wav", 'hitsound_don_2p')
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound[1]) / "ka_2p.wav", 'hitsound_kat_2p')
logger.info("Loaded wav hit sounds for 2P")
else:
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound[1]) / "don.ogg", 'hitsound_don_2p')
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound[1]) / "ka.ogg", 'hitsound_kat_2p')
logger.info("Loaded ogg hit sounds for 2P")
audio.set_sound_pan('hitsound_don_2p', 0.0)
audio.set_sound_pan('hitsound_kat_2p', 0.0)
@@ -55,10 +63,12 @@ class TwoPlayerGameScreen(GameScreen):
self.init_tja(global_data.selected_song)
audio.play_sound('restart', 'sound')
self.song_started = False
logger.info("F1 pressed: song restarted")
if ray.is_key_pressed(ray.KeyboardKey.KEY_ESCAPE):
if self.song_music is not None:
audio.stop_music_stream(self.song_music)
logger.info("Escape pressed: returning to SONG_SELECT_2P")
return self.on_screen_end('SONG_SELECT_2P')
def init_tja(self, song: Path):
@@ -77,6 +87,7 @@ class TwoPlayerGameScreen(GameScreen):
self.player_1 = Player(self.tja, 1, global_data.session_data[0].selected_difficulty, False, global_data.modifiers[0])
self.player_2 = Player(tja_copy, 2, global_data.session_data[1].selected_difficulty, True, global_data.modifiers[1])
self.start_ms = (get_current_ms() - self.tja.metadata.offset*1000)
logger.info(f"TJA initialized for two-player song: {song}")
def spawn_ending_anims(self):
if global_data.session_data[0].result_bad == 0:
@@ -94,7 +105,7 @@ class TwoPlayerGameScreen(GameScreen):
self.player_2.ending_anim = FailAnimation(self.player_2.is_2p)
def update(self):
self.on_screen_start()
super(GameScreen, self).update()
current_time = get_current_ms()
self.transition.update(current_time)
self.current_ms = current_time - self.start_ms

View File

@@ -1,20 +1,20 @@
import logging
from libs.utils import get_current_ms
from scenes.result import Background, FadeIn, ResultPlayer, ResultScreen
class TwoPlayerResultScreen(ResultScreen):
def __init__(self):
super().__init__()
logger = logging.getLogger(__name__)
class TwoPlayerResultScreen(ResultScreen):
def on_screen_start(self):
if not self.screen_init:
super().on_screen_start()
self.background = Background('3', self.width)
self.fade_in = FadeIn('3')
self.player_1 = ResultPlayer('1', True, False)
self.player_2 = ResultPlayer('2', True, True)
super().on_screen_start()
self.background = Background('3', 1280)
self.fade_in = FadeIn('3')
self.player_1 = ResultPlayer('1', True, False)
self.player_2 = ResultPlayer('2', True, True)
logger.info("TwoPlayerResultScreen started, background and players initialized")
def update(self):
self.on_screen_start()
super(ResultScreen, self).update()
current_time = get_current_ms()
self.fade_in.update(current_time)
self.player_1.update(current_time, self.fade_in.is_finished, self.is_skipped)

View File

@@ -1,19 +1,22 @@
import logging
from libs.file_navigator import SongFile
from libs.transition import Transition
from scenes.song_select import DiffSortSelect, SongSelectPlayer, SongSelectScreen, State
from libs.utils import get_current_ms, global_data
from libs.audio import audio
logger = logging.getLogger(__name__)
class TwoPlayerSongSelectScreen(SongSelectScreen):
def on_screen_start(self):
if not self.screen_init:
super().on_screen_start()
self.player_1 = SongSelectPlayer('1', self.text_fade_in)
self.player_2 = SongSelectPlayer('2', self.text_fade_in)
super().on_screen_start()
self.player_1 = SongSelectPlayer('1', self.text_fade_in)
self.player_2 = SongSelectPlayer('2', self.text_fade_in)
def finalize_song(self):
global_data.selected_song = self.navigator.get_current_item().path
global_data.session_data[0].genre_index = self.navigator.get_current_item().box.name_texture_index
logger.info(f"Finalized song selection: {global_data.selected_song}")
def handle_input_browsing(self):
"""Handle input for browsing songs."""
@@ -79,24 +82,25 @@ class TwoPlayerSongSelectScreen(SongSelectScreen):
return
p2_result = True
if result is not None:
print(result, p2_result)
logger.info(f"Difficulty selection result: {result}, p2_result={p2_result}")
if result == "cancel":
self._cancel_selection()
logger.info("Selection cancelled")
elif result == "confirm":
if p2_result:
self._confirm_selection(2)
else:
self._confirm_selection(1)
logger.info("Selection confirmed")
elif result == "ura_toggle":
if p2_result:
self.ura_switch_animation.start(not self.player_2.is_ura)
else:
self.ura_switch_animation.start(not self.player_1.is_ura)
logger.info("Ura toggled")
def handle_input_diff_sort(self):
"""
Handle input for sorting difficulty.
"""
"""Handle input for sorting difficulty."""
if self.diff_sort_selector is None:
raise Exception("Diff sort selector was not able to be created")
@@ -108,6 +112,7 @@ class TwoPlayerSongSelectScreen(SongSelectScreen):
self.state = State.BROWSING
self.text_fade_out.reset()
self.text_fade_in.reset()
logger.info(f"Diff sort selected: diff={diff}, level={level}")
if diff != -1:
if level != -1:
self.navigator.diff_sort_diff = diff
@@ -117,7 +122,7 @@ class TwoPlayerSongSelectScreen(SongSelectScreen):
def _cancel_selection(self):
"""Reset to browsing state"""
super()._cancel_selection()
self.player_2.selected_song = None
self.player_2.selected_song = False
def _confirm_selection(self, player_selected: int):
"""Confirm song selection and create game transition"""
@@ -133,6 +138,7 @@ class TwoPlayerSongSelectScreen(SongSelectScreen):
self.player_2.selected_diff_highlight_fade.start()
self.player_2.selected_diff_text_resize.start()
self.player_2.selected_diff_text_fadein.start()
logger.info(f"Confirmed selection for player {player_selected}")
def check_for_selection(self):
if (self.player_1.selected_diff_highlight_fade.is_finished and
@@ -148,6 +154,7 @@ class TwoPlayerSongSelectScreen(SongSelectScreen):
global_data.config['general']['language'], '')
self.game_transition = Transition(title, subtitle)
self.game_transition.start()
logger.info(f"Game transition started for song: {title} - {subtitle}")
def update_players(self, current_time):
self.player_1.update(current_time)
@@ -155,7 +162,8 @@ class TwoPlayerSongSelectScreen(SongSelectScreen):
if self.text_fade_out.is_finished:
self.player_1.selected_song = True
self.player_2.selected_song = True
return "GAME_2P"
next_screen = "GAME_2P"
return next_screen
def draw_background_diffs(self):
self.player_1.draw_background_diffs(self.state)