mirror of
https://github.com/Yonokid/PyTaiko.git
synced 2026-02-04 11:40:13 +01:00
fix double free bug, add logging, update to python 3.14
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
143
scenes/game.py
143
scenes/game.py
@@ -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"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user