This commit is contained in:
Anthony Samms
2025-10-24 00:08:53 -04:00
parent 21c90d8530
commit 8b7ba3acbd
13 changed files with 2597 additions and 2070 deletions

View File

@@ -20,11 +20,13 @@ from scenes.devtest import DevScreen
from scenes.entry import EntryScreen from scenes.entry import EntryScreen
from scenes.game import GameScreen from scenes.game import GameScreen
from scenes.two_player.game import TwoPlayerGameScreen from scenes.two_player.game import TwoPlayerGameScreen
from scenes.two_player.result import TwoPlayerResultScreen
from scenes.loading import LoadScreen from scenes.loading import LoadScreen
from scenes.result import ResultScreen from scenes.result import ResultScreen
from scenes.settings import SettingsScreen from scenes.settings import SettingsScreen
from scenes.song_select import SongSelectScreen from scenes.song_select import SongSelectScreen
from scenes.title import TitleScreen from scenes.title import TitleScreen
from scenes.two_player.song_select import TwoPlayerSongSelectScreen
class Screens: class Screens:
@@ -34,6 +36,8 @@ class Screens:
GAME = "GAME" GAME = "GAME"
GAME_2P = "GAME_2P" GAME_2P = "GAME_2P"
RESULT = "RESULT" RESULT = "RESULT"
RESULT_2P = "RESULT_2P"
SONG_SELECT_2P = "SONG_SELECT_2P"
SETTINGS = "SETTINGS" SETTINGS = "SETTINGS"
DEV_MENU = "DEV_MENU" DEV_MENU = "DEV_MENU"
LOADING = "LOADING" LOADING = "LOADING"
@@ -107,10 +111,12 @@ def main():
title_screen = TitleScreen() title_screen = TitleScreen()
entry_screen = EntryScreen() entry_screen = EntryScreen()
song_select_screen = SongSelectScreen() song_select_screen = SongSelectScreen()
load_screen = LoadScreen(song_select_screen) song_select_screen_2p = TwoPlayerSongSelectScreen()
load_screen = LoadScreen()
game_screen = GameScreen() game_screen = GameScreen()
game_screen_2p = TwoPlayerGameScreen() game_screen_2p = TwoPlayerGameScreen()
result_screen = ResultScreen() result_screen = ResultScreen()
result_screen_2p = TwoPlayerResultScreen()
settings_screen = SettingsScreen() settings_screen = SettingsScreen()
dev_screen = DevScreen() dev_screen = DevScreen()
@@ -118,9 +124,11 @@ def main():
Screens.ENTRY: entry_screen, Screens.ENTRY: entry_screen,
Screens.TITLE: title_screen, Screens.TITLE: title_screen,
Screens.SONG_SELECT: song_select_screen, Screens.SONG_SELECT: song_select_screen,
Screens.SONG_SELECT_2P: song_select_screen_2p,
Screens.GAME: game_screen, Screens.GAME: game_screen,
Screens.GAME_2P: game_screen_2p, Screens.GAME_2P: game_screen_2p,
Screens.RESULT: result_screen, Screens.RESULT: result_screen,
Screens.RESULT_2P: result_screen_2p,
Screens.SETTINGS: settings_screen, Screens.SETTINGS: settings_screen,
Screens.DEV_MENU: dev_screen, Screens.DEV_MENU: dev_screen,
Screens.LOADING: load_screen Screens.LOADING: load_screen

View File

@@ -8,12 +8,18 @@ touch_enabled = false
timer_frozen = true timer_frozen = true
judge_counter = false judge_counter = false
[nameplate] [nameplate_1p]
name = 'どんちゃん' name = 'どんちゃん'
title = 'ドンだーデビュー!' title = 'ドンだーデビュー!'
dan = -1 dan = -1
gold = false gold = false
[nameplate_2p]
name = 'かつちゃん'
title = 'ドンだーデビュー!'
dan = -1
gold = false
[paths] [paths]
tja_path = ['Songs'] tja_path = ['Songs']
video_path = ['Videos'] video_path = ['Videos']

1327
libs/file_navigator.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,32 @@ class Modifiers:
inverse: bool = False inverse: bool = False
random: int = 0 random: int = 0
@dataclass
class SessionData:
"""Data class for storing session data. Wiped after the result screen.
selected_difficulty: The difficulty level selected by the user.
song_title: The title of the song being played.
genre_index: The index of the genre being played.
result_score: The score achieved in the game.
result_good: The number of good notes achieved in the game.
result_ok: The number of ok notes achieved in the game.
result_bad: The number of bad notes achieved in the game.
result_max_combo: The maximum combo achieved in the game.
result_total_drumroll: The total drumroll achieved in the game.
result_gauge_length: The length of the gauge achieved in the game.
prev_score: The previous score pulled from the database."""
selected_difficulty: int = 0
song_title: str = ''
genre_index: int = 0
result_score: int = 0
result_good: int = 0
result_ok: int = 0
result_bad: int = 0
result_max_combo: int = 0
result_total_drumroll: int = 0
result_gauge_length: int = 0
prev_score: int = 0
@dataclass @dataclass
class GlobalData: class GlobalData:
""" """
@@ -26,10 +52,11 @@ class GlobalData:
song_paths (dict[Path, str]): A dictionary mapping song paths to their hashes. song_paths (dict[Path, str]): A dictionary mapping song paths to their hashes.
song_progress (float): The progress of the loading bar. song_progress (float): The progress of the loading bar.
total_songs (int): The total number of songs. total_songs (int): The total number of songs.
hit_sound (int): The index of the hit sound currently used. hit_sound (list[int]): The indices of the hit sounds currently used.
player_num (int): The player number. Either 1 or 2. player_num (int): The player number. Either 1 or 2.
input_locked (int): The input lock status. 0 means unlocked, 1 or greater means locked. input_locked (int): The input lock status. 0 means unlocked, 1 or greater means locked.
modifiers (Modifiers): The modifiers for the game. modifiers (list[Modifiers]): The modifiers for the game.
session_data (list[SessionData]): Session data for both players.
""" """
selected_song: Path = Path() selected_song: Path = Path()
songs_played: int = 0 songs_played: int = 0
@@ -38,9 +65,15 @@ class GlobalData:
song_paths: dict[Path, str] = field(default_factory=lambda: dict()) #path to hash song_paths: dict[Path, str] = field(default_factory=lambda: dict()) #path to hash
song_progress: float = 0.0 song_progress: float = 0.0
total_songs: int = 0 total_songs: int = 0
hit_sound: int = 0 hit_sound: list[int] = field(default_factory=lambda: [0, 0])
player_num: int = 1 player_num: int = 1
input_locked: int = 0 input_locked: int = 0
modifiers: Modifiers = field(default_factory=lambda: Modifiers()) modifiers: list[Modifiers] = field(default_factory=lambda: [Modifiers(), Modifiers()])
session_data: list[SessionData] = field(default_factory=lambda: [SessionData(), SessionData()])
global_data = GlobalData() global_data = GlobalData()
def reset_session():
"""Reset the session data."""
global_data.session_data[0] = SessionData()
global_data.session_data[1] = SessionData()

View File

@@ -4,7 +4,6 @@ import math
import sys import sys
import time import time
import json import json
from dataclasses import dataclass
from libs.global_data import global_data from libs.global_data import global_data
from functools import lru_cache from functools import lru_cache
from pathlib import Path from pathlib import Path
@@ -92,11 +91,16 @@ def save_config(config: dict[str, Any]) -> None:
with open(Path('config.toml'), "w", encoding="utf-8") as f: with open(Path('config.toml'), "w", encoding="utf-8") as f:
tomlkit.dump(config, f) tomlkit.dump(config, f)
def is_l_don_pressed() -> bool: def is_l_don_pressed(player_num: str = '0') -> bool:
"""Check if the left don button is pressed""" """Check if the left don button is pressed"""
if global_data.input_locked: if global_data.input_locked:
return False return False
if player_num == '0':
keys = global_data.config["keys_1p"]["left_don"] + global_data.config["keys_2p"]["left_don"]
elif player_num == '1':
keys = global_data.config["keys_1p"]["left_don"] keys = global_data.config["keys_1p"]["left_don"]
elif player_num == '2':
keys = global_data.config["keys_2p"]["left_don"]
gamepad_buttons = global_data.config["gamepad"]["left_don"] gamepad_buttons = global_data.config["gamepad"]["left_don"]
for key in keys: for key in keys:
if ray.is_key_pressed(ord(key)): if ray.is_key_pressed(ord(key)):
@@ -117,11 +121,16 @@ def is_l_don_pressed() -> bool:
return False return False
def is_r_don_pressed() -> bool: def is_r_don_pressed(player_num: str = '0') -> bool:
"""Check if the right don button is pressed""" """Check if the right don button is pressed"""
if global_data.input_locked: if global_data.input_locked:
return False return False
if player_num == '0':
keys = global_data.config["keys_1p"]["right_don"] + global_data.config["keys_2p"]["right_don"]
elif player_num == '1':
keys = global_data.config["keys_1p"]["right_don"] keys = global_data.config["keys_1p"]["right_don"]
elif player_num == '2':
keys = global_data.config["keys_2p"]["right_don"]
gamepad_buttons = global_data.config["gamepad"]["right_don"] gamepad_buttons = global_data.config["gamepad"]["right_don"]
for key in keys: for key in keys:
if ray.is_key_pressed(ord(key)): if ray.is_key_pressed(ord(key)):
@@ -144,11 +153,16 @@ def is_r_don_pressed() -> bool:
return False return False
def is_l_kat_pressed() -> bool: def is_l_kat_pressed(player_num: str = '0') -> bool:
"""Check if the left kat button is pressed""" """Check if the left kat button is pressed"""
if global_data.input_locked: if global_data.input_locked:
return False return False
if player_num == '0':
keys = global_data.config["keys_1p"]["left_kat"] + global_data.config["keys_2p"]["left_kat"]
elif player_num == '1':
keys = global_data.config["keys_1p"]["left_kat"] keys = global_data.config["keys_1p"]["left_kat"]
elif player_num == '2':
keys = global_data.config["keys_2p"]["left_kat"]
gamepad_buttons = global_data.config["gamepad"]["left_kat"] gamepad_buttons = global_data.config["gamepad"]["left_kat"]
for key in keys: for key in keys:
if ray.is_key_pressed(ord(key)): if ray.is_key_pressed(ord(key)):
@@ -171,11 +185,16 @@ def is_l_kat_pressed() -> bool:
return False return False
def is_r_kat_pressed() -> bool: def is_r_kat_pressed(player_num: str = '0') -> bool:
"""Check if the right kat button is pressed""" """Check if the right kat button is pressed"""
if global_data.input_locked: if global_data.input_locked:
return False return False
if player_num == '0':
keys = global_data.config["keys_1p"]["right_kat"] + global_data.config["keys_2p"]["right_kat"]
elif player_num == '1':
keys = global_data.config["keys_1p"]["right_kat"] keys = global_data.config["keys_1p"]["right_kat"]
elif player_num == '2':
keys = global_data.config["keys_2p"]["right_kat"]
gamepad_buttons = global_data.config["gamepad"]["right_kat"] gamepad_buttons = global_data.config["gamepad"]["right_kat"]
for key in keys: for key in keys:
if ray.is_key_pressed(ord(key)): if ray.is_key_pressed(ord(key)):
@@ -198,39 +217,8 @@ def is_r_kat_pressed() -> bool:
return False return False
@dataclass
class SessionData:
"""Data class for storing session data. Wiped after the result screen.
selected_difficulty: The difficulty level selected by the user.
song_title: The title of the song being played.
genre_index: The index of the genre being played.
result_score: The score achieved in the game.
result_good: The number of good notes achieved in the game.
result_ok: The number of ok notes achieved in the game.
result_bad: The number of bad notes achieved in the game.
result_max_combo: The maximum combo achieved in the game.
result_total_drumroll: The total drumroll achieved in the game.
result_gauge_length: The length of the gauge achieved in the game.
prev_score: The previous score pulled from the database."""
selected_difficulty: int = 0
song_title: str = ''
genre_index: int = 0
result_score: int = 0
result_good: int = 0
result_ok: int = 0
result_bad: int = 0
result_max_combo: int = 0
result_total_drumroll: int = 0
result_gauge_length: int = 0
prev_score: int = 0
session_data = SessionData()
global_tex = TextureWrapper() global_tex = TextureWrapper()
def reset_session():
"""Reset the session data."""
return SessionData()
text_cache = set() text_cache = set()
if not Path('cache/image').exists(): if not Path('cache/image').exists():
if not Path('cache').exists(): if not Path('cache').exists():

View File

@@ -29,11 +29,14 @@ class EntryScreen:
tex.load_screen_textures('entry') tex.load_screen_textures('entry')
audio.load_screen_sounds('entry') audio.load_screen_sounds('entry')
self.side = 1 self.side = 1
self.is_2p = False
self.box_manager = BoxManager() self.box_manager = BoxManager()
self.state = State.SELECT_SIDE self.state = State.SELECT_SIDE
plate_info = global_data.config['nameplate']
# 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.nameplate = Nameplate(plate_info['name'], plate_info['title'], -1, -1, False)
self.indicator = Indicator(Indicator.State.SELECT)
self.coin_overlay = CoinOverlay() self.coin_overlay = CoinOverlay()
self.allnet_indicator = AllNetIcon() self.allnet_indicator = AllNetIcon()
self.entry_overlay = EntryOverlay() self.entry_overlay = EntryOverlay()
@@ -41,93 +44,95 @@ class EntryScreen:
self.screen_init = True self.screen_init = True
self.side_select_fade = tex.get_animation(0) self.side_select_fade = tex.get_animation(0)
self.bg_flicker = tex.get_animation(1) self.bg_flicker = tex.get_animation(1)
self.drum_move_1 = tex.get_animation(2)
self.drum_move_2 = tex.get_animation(3)
self.drum_move_3 = tex.get_animation(4)
self.cloud_resize = tex.get_animation(5)
self.cloud_resize_loop = tex.get_animation(6)
self.cloud_texture_change = tex.get_animation(7)
self.cloud_fade = tex.get_animation(8)
self.nameplate_fadein = tex.get_animation(12)
self.side_select_fade.start() self.side_select_fade.start()
self.chara = Chara2D(0, 100) self.chara = Chara2D(0, 100)
self.announce_played = False self.announce_played = False
self.players = [None, None]
audio.play_sound('bgm', 'music') audio.play_sound('bgm', 'music')
def on_screen_end(self, next_screen: str): def on_screen_end(self, next_screen: str):
self.screen_init = False self.screen_init = False
audio.stop_sound('bgm') audio.stop_sound('bgm')
self.nameplate.unload() self.nameplate.unload()
for player in self.players:
if player:
player.unload()
tex.unload_textures() tex.unload_textures()
audio.unload_all_sounds() audio.unload_all_sounds()
audio.unload_all_music() audio.unload_all_music()
return next_screen return next_screen
def handle_input(self): def handle_input(self):
if self.box_manager.is_box_selected():
return
if self.state == State.SELECT_SIDE: if self.state == State.SELECT_SIDE:
if is_l_don_pressed() or is_r_don_pressed(): if is_l_don_pressed() or is_r_don_pressed():
if self.side == 1: if self.side == 1:
return self.on_screen_end("TITLE") return self.on_screen_end("TITLE")
global_data.player_num = round((self.side/3) + 1) global_data.player_num = round((self.side/3) + 1)
self.drum_move_1.start()
self.drum_move_2.start() if self.players[0]:
self.drum_move_3.start() self.players[1] = EntryPlayer(global_data.player_num, self.side, self.box_manager)
self.cloud_resize.start() self.players[1].start_animations()
self.cloud_resize_loop.start() global_data.player_num = 1
self.cloud_texture_change.start() self.is_2p = True
self.cloud_fade.start() else:
self.players[0] = EntryPlayer(global_data.player_num, self.side, self.box_manager)
self.players[0].start_animations()
self.is_2p = False
audio.play_sound('cloud', 'sound') audio.play_sound('cloud', 'sound')
audio.play_sound(f'entry_start_{global_data.player_num}p', 'voice') audio.play_sound(f'entry_start_{global_data.player_num}p', 'voice')
plate_info = global_data.config['nameplate']
self.nameplate.unload()
self.nameplate = Nameplate(plate_info['name'], plate_info['title'], global_data.player_num, plate_info['dan'], plate_info['gold'])
self.nameplate_fadein.start()
self.state = State.SELECT_MODE self.state = State.SELECT_MODE
if self.side == 2:
self.chara = Chara2D(1, 100)
else:
self.chara = Chara2D(0, 100)
audio.play_sound('don', 'sound') audio.play_sound('don', 'sound')
if is_l_kat_pressed(): if is_l_kat_pressed():
audio.play_sound('kat', 'sound') audio.play_sound('kat', 'sound')
if self.players[0] and self.players[0].player_num == 1:
self.side = 1
elif self.players[0] and self.players[0].player_num == 2:
self.side = 0
else:
self.side = max(0, self.side - 1) self.side = max(0, self.side - 1)
if is_r_kat_pressed(): if is_r_kat_pressed():
audio.play_sound('kat', 'sound') audio.play_sound('kat', 'sound')
if self.players[0] and self.players[0].player_num == 1:
self.side = 2
elif self.players[0] and self.players[0].player_num == 2:
self.side = 1
else:
self.side = min(2, self.side + 1) self.side = min(2, self.side + 1)
elif self.state == State.SELECT_MODE: elif self.state == State.SELECT_MODE:
if is_l_don_pressed() or is_r_don_pressed(): for player in self.players:
if player:
player.handle_input()
if self.players[0] and self.players[0].player_num == 1 and is_l_don_pressed('2') or is_r_don_pressed('2'):
audio.play_sound('don', 'sound') audio.play_sound('don', 'sound')
self.box_manager.select_box() self.state = State.SELECT_SIDE
if is_l_kat_pressed(): plate_info = global_data.config['nameplate_2p']
audio.play_sound('kat', 'sound') self.nameplate = Nameplate(plate_info['name'], plate_info['title'], -1, -1, False)
self.box_manager.move_left() self.chara = Chara2D(1, 100)
if is_r_kat_pressed(): self.side_select_fade.restart()
audio.play_sound('kat', 'sound') self.side = 1
self.box_manager.move_right() elif self.players[0] and self.players[0].player_num == 2 and is_l_don_pressed('1') or is_r_don_pressed('1'):
audio.play_sound('don', 'sound')
self.state = State.SELECT_SIDE
self.side_select_fade.restart()
self.side = 1
def update(self): def update(self):
self.on_screen_start() self.on_screen_start()
current_time = get_current_ms() current_time = get_current_ms()
self.side_select_fade.update(current_time) self.side_select_fade.update(current_time)
self.bg_flicker.update(current_time) self.bg_flicker.update(current_time)
self.drum_move_1.update(current_time) self.box_manager.update(current_time, self.is_2p)
self.drum_move_2.update(current_time)
self.drum_move_3.update(current_time)
self.cloud_resize.update(current_time)
self.cloud_texture_change.update(current_time)
self.cloud_fade.update(current_time)
self.cloud_resize_loop.update(current_time)
self.box_manager.update(current_time)
self.nameplate_fadein.update(current_time)
self.nameplate.update(current_time)
self.indicator.update(current_time)
self.timer.update(current_time) self.timer.update(current_time)
self.nameplate.update(current_time)
self.chara.update(current_time, 100, False, False) self.chara.update(current_time, 100, False, False)
for player in self.players:
if player:
player.update(current_time)
if self.box_manager.is_finished(): if self.box_manager.is_finished():
return self.on_screen_end(self.box_manager.selected_box()) return self.on_screen_end(self.box_manager.selected_box())
if self.cloud_fade.is_finished and not audio.is_sound_playing(f'entry_start_{global_data.player_num}p') and not self.announce_played: 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:
audio.play_sound('select_mode', 'voice') audio.play_sound('select_mode', 'voice')
self.announce_played = True self.announce_played = True
return self.handle_input() return self.handle_input()
@@ -141,12 +146,6 @@ class EntryScreen:
tex.draw_texture('background', 'shops_right') tex.draw_texture('background', 'shops_right')
tex.draw_texture('background', 'lights', scale=2.0, fade=self.bg_flicker.attribute) tex.draw_texture('background', 'lights', scale=2.0, fade=self.bg_flicker.attribute)
def draw_footer(self):
tex.draw_texture('side_select', 'footer')
if self.state == State.SELECT_SIDE or self.side != 0:
tex.draw_texture('side_select', 'footer_left')
if self.state == State.SELECT_SIDE or self.side != 2:
tex.draw_texture('side_select', 'footer_right')
def draw_side_select(self, fade): def draw_side_select(self, fade):
tex.draw_texture('side_select', 'box_top_left', fade=fade) tex.draw_texture('side_select', 'box_top_left', fade=fade)
@@ -180,46 +179,38 @@ class EntryScreen:
self.nameplate.draw(500, 185) self.nameplate.draw(500, 185)
def draw_player_drum(self): def draw_player_drum(self):
move_x = self.drum_move_3.attribute for player in self.players:
move_y = self.drum_move_1.attribute + self.drum_move_2.attribute if player:
if self.side == 0: player.draw_drum()
offset = 0
tex.draw_texture('side_select', 'red_drum', x=move_x, y=move_y)
else:
move_x *= -1
offset = 620
tex.draw_texture('side_select', 'blue_drum', x=move_x, y=move_y)
scale = self.cloud_resize.attribute
if self.cloud_resize.is_finished:
scale = max(1, self.cloud_resize_loop.attribute)
if self.side == 2:
self.chara.draw(move_x + offset + 130, 570 + move_y, mirror=True)
else:
self.chara.draw(move_x + offset + 170, 570 + move_y)
tex.draw_texture('side_select', 'cloud', x=move_x + offset, y=move_y, frame=self.cloud_texture_change.attribute, fade=self.cloud_fade.attribute, scale=scale, center=True)
def draw_mode_select(self): def draw_mode_select(self):
self.draw_player_drum() for player in self.players:
if not self.cloud_texture_change.is_finished: if player and not player.is_cloud_animation_finished():
return return
self.box_manager.draw() self.box_manager.draw()
def draw(self): def draw(self):
self.draw_background() self.draw_background()
self.draw_player_drum()
if self.state == State.SELECT_SIDE: if self.state == State.SELECT_SIDE:
self.draw_side_select(self.side_select_fade.attribute) self.draw_side_select(self.side_select_fade.attribute)
elif self.state == State.SELECT_MODE: elif self.state == State.SELECT_MODE:
self.draw_mode_select() self.draw_mode_select()
self.draw_footer() tex.draw_texture('side_select', 'footer')
if self.players[0] and self.players[1]:
pass
elif not self.players[0]:
tex.draw_texture('side_select', 'footer_left')
tex.draw_texture('side_select', 'footer_right')
elif self.players[0] and self.players[0].player_num == 1:
tex.draw_texture('side_select', 'footer_right')
elif self.players[0] and self.players[0].player_num == 2:
tex.draw_texture('side_select', 'footer_left')
if self.state == State.SELECT_MODE:
if self.side == 0: for player in self.players:
self.nameplate.draw(30, 640, fade=self.nameplate_fadein.attribute) if player:
self.indicator.draw(50, 575, fade=self.nameplate_fadein.attribute) player.draw_nameplate_and_indicator(fade=player.nameplate_fadein.attribute)
else:
self.nameplate.draw(950, 640, fade=self.nameplate_fadein.attribute)
self.indicator.draw(770, 575, fade=self.nameplate_fadein.attribute)
tex.draw_texture('global', 'player_entry') tex.draw_texture('global', 'player_entry')
@@ -231,8 +222,136 @@ class EntryScreen:
self.coin_overlay.draw() self.coin_overlay.draw()
self.allnet_indicator.draw() self.allnet_indicator.draw()
def draw_3d(self): class EntryPlayer:
pass """Player-specific state and rendering for the entry screen"""
def __init__(self, player_num: int, side: int, box_manager: 'BoxManager'):
"""
Initialize a player for the entry screen
Args:
player_num: 1 or 2 (player number)
side: 0 for left (1P), 2 for right (2P)
box_manager: Reference to the box manager for input handling
"""
self.player_num = player_num
self.side = side
self.box_manager = box_manager
# Load player-specific resources
plate_info = global_data.config[f'nameplate_{self.player_num}p']
self.nameplate = Nameplate(
plate_info['name'],
plate_info['title'],
player_num,
plate_info['dan'],
plate_info['gold']
)
self.indicator = Indicator(Indicator.State.SELECT)
# Character (0 for red/1P, 1 for blue/2P)
chara_id = 0 if side == 0 else 1
self.chara = Chara2D(chara_id, 100)
# Animations
self.drum_move_1 = tex.get_animation(2)
self.drum_move_2 = tex.get_animation(3)
self.drum_move_3 = tex.get_animation(4)
self.cloud_resize = tex.get_animation(5)
self.cloud_resize_loop = tex.get_animation(6)
self.cloud_texture_change = tex.get_animation(7)
self.cloud_fade = tex.get_animation(8)
self.nameplate_fadein = tex.get_animation(12)
def start_animations(self):
"""Start all player entry animations"""
self.drum_move_1.start()
self.drum_move_2.start()
self.drum_move_3.start()
self.cloud_resize.start()
self.cloud_resize_loop.start()
self.cloud_texture_change.start()
self.cloud_fade.start()
self.nameplate_fadein.start()
def update(self, current_time: float):
"""Update player animations and state"""
self.drum_move_1.update(current_time)
self.drum_move_2.update(current_time)
self.drum_move_3.update(current_time)
self.cloud_resize.update(current_time)
self.cloud_texture_change.update(current_time)
self.cloud_fade.update(current_time)
self.cloud_resize_loop.update(current_time)
self.nameplate_fadein.update(current_time)
self.nameplate.update(current_time)
self.indicator.update(current_time)
self.chara.update(current_time, 100, False, False)
def draw_drum(self):
"""Draw the player's drum with animations"""
move_x = self.drum_move_3.attribute
move_y = self.drum_move_1.attribute + self.drum_move_2.attribute
if self.side == 0: # Left side (1P/red)
offset = 0
tex.draw_texture('side_select', 'red_drum', x=move_x, y=move_y)
chara_x = move_x + offset + 170
chara_mirror = False
else: # Right side (2P/blue)
move_x *= -1
offset = 620
tex.draw_texture('side_select', 'blue_drum', x=move_x, y=move_y)
chara_x = move_x + offset + 130
chara_mirror = True
# Draw character
chara_y = 570 + move_y
self.chara.draw(chara_x, chara_y, mirror=chara_mirror)
# Draw cloud
scale = self.cloud_resize.attribute
if self.cloud_resize.is_finished:
scale = max(1, self.cloud_resize_loop.attribute)
tex.draw_texture(
'side_select', 'cloud',
x=move_x + offset,
y=move_y,
frame=self.cloud_texture_change.attribute,
fade=self.cloud_fade.attribute,
scale=scale,
center=True
)
def draw_nameplate_and_indicator(self, fade: float = 1.0):
"""Draw nameplate and indicator at player-specific position"""
if self.side == 0: # Left side
self.nameplate.draw(30, 640, fade=fade)
self.indicator.draw(50, 575, fade=fade)
else: # Right side
self.nameplate.draw(950, 640, fade=fade)
self.indicator.draw(770, 575, fade=fade)
def is_cloud_animation_finished(self) -> bool:
"""Check if cloud texture change animation is finished"""
return self.cloud_texture_change.is_finished
def unload(self):
"""Unload player resources"""
self.nameplate.unload()
def handle_input(self):
"""Handle player input for mode selection"""
if self.box_manager.is_box_selected():
return
if is_l_don_pressed(str(self.player_num)) or is_r_don_pressed(str(self.player_num)):
audio.play_sound('don', 'sound')
self.box_manager.select_box()
if is_l_kat_pressed(str(self.player_num)):
audio.play_sound('kat', 'sound')
self.box_manager.move_left()
if is_r_kat_pressed(str(self.player_num)):
audio.play_sound('kat', 'sound')
self.box_manager.move_right()
class Box: class Box:
"""Box class for the entry screen""" """Box class for the entry screen"""
@@ -330,6 +449,7 @@ class BoxManager:
self.boxes = [Box(self.box_titles[i], self.box_locations[i]) for i in range(len(self.box_titles))] self.boxes = [Box(self.box_titles[i], self.box_locations[i]) for i in range(len(self.box_titles))]
self.selected_box_index = 0 self.selected_box_index = 0
self.fade_out = tex.get_animation(9) self.fade_out = tex.get_animation(9)
self.is_2p = False
spacing = 80 spacing = 80
box_width = self.boxes[0].texture.width box_width = self.boxes[0].texture.width
@@ -380,7 +500,12 @@ class BoxManager:
self.boxes[self.selected_box_index-1].move_left() self.boxes[self.selected_box_index-1].move_left()
self.boxes[self.selected_box_index].move_left() self.boxes[self.selected_box_index].move_left()
def update(self, current_time_ms: float): def update(self, current_time_ms: float, is_2p: bool):
self.is_2p = is_2p
if self.is_2p:
self.box_locations = ["SONG_SELECT_2P", "SETTINGS"]
for i, box in enumerate(self.boxes):
box.location = self.box_locations[i]
self.fade_out.update(current_time_ms) self.fade_out.update(current_time_ms)
for i, box in enumerate(self.boxes): for i, box in enumerate(self.boxes):
is_selected = i == self.selected_box_index is_selected = i == self.selected_box_index

View File

@@ -33,8 +33,7 @@ from libs.utils import (
is_l_kat_pressed, is_l_kat_pressed,
is_r_don_pressed, is_r_don_pressed,
is_r_kat_pressed, is_r_kat_pressed,
rounded, rounded
session_data,
) )
from libs.video import VideoPlayer from libs.video import VideoPlayer
@@ -57,20 +56,14 @@ class GameScreen:
if global_data.hit_sound == -1: if global_data.hit_sound == -1:
audio.load_sound(Path('none.wav'), 'hitsound_don_1p') audio.load_sound(Path('none.wav'), 'hitsound_don_1p')
audio.load_sound(Path('none.wav'), 'hitsound_kat_1p') audio.load_sound(Path('none.wav'), 'hitsound_kat_1p')
audio.load_sound(Path('none.wav'), 'hitsound_don_2p')
audio.load_sound(Path('none.wav'), 'hitsound_kat_2p')
if global_data.hit_sound == 0: if global_data.hit_sound == 0:
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound) / "don.wav", 'hitsound_don_1p') 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) / "ka.wav", 'hitsound_kat_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) / "don.wav", 'hitsound_don_2p')
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound) / "ka.wav", 'hitsound_kat_2p')
else: else:
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound) / "don.ogg", 'hitsound_don_1p') 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) / "ka.ogg", 'hitsound_kat_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) / "don.ogg", 'hitsound_don_2p')
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound) / "ka.ogg", 'hitsound_kat_2p')
def init_tja(self, song: Path, difficulty: int): def init_tja(self, song: Path):
"""Initialize the TJA file""" """Initialize the TJA file"""
self.tja = TJAParser(song, start_delay=self.start_delay, distance=SCREEN_WIDTH - GameScreen.JUDGE_X) self.tja = TJAParser(song, start_delay=self.start_delay, distance=SCREEN_WIDTH - GameScreen.JUDGE_X)
if self.tja.metadata.bgmovie != Path() and self.tja.metadata.bgmovie.exists(): if self.tja.metadata.bgmovie != Path() and self.tja.metadata.bgmovie.exists():
@@ -78,11 +71,11 @@ class GameScreen:
self.movie.set_volume(0.0) self.movie.set_volume(0.0)
else: else:
self.movie = None self.movie = None
session_data.song_title = self.tja.metadata.title.get(global_data.config['general']['language'].lower(), self.tja.metadata.title['en']) global_data.session_data[0].song_title = self.tja.metadata.title.get(global_data.config['general']['language'].lower(), self.tja.metadata.title['en'])
if self.tja.metadata.wave.exists() and self.tja.metadata.wave.is_file() and self.song_music is None: if self.tja.metadata.wave.exists() and self.tja.metadata.wave.is_file() and self.song_music is None:
self.song_music = audio.load_music_stream(self.tja.metadata.wave, 'song') self.song_music = audio.load_music_stream(self.tja.metadata.wave, 'song')
self.player_1 = Player(self.tja, global_data.player_num, difficulty, False, global_data.modifiers) 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) self.start_ms = (get_current_ms() - self.tja.metadata.offset*1000)
def on_screen_start(self): def on_screen_start(self):
@@ -94,7 +87,8 @@ class GameScreen:
audio.load_screen_sounds('game') 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, "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) ray.set_shader_value_texture(self.mask_shader, ray.get_shader_location(self.mask_shader, "texture1"), tex.textures['balloon']['rainbow'].texture)
self.init_tja(global_data.selected_song, session_data.selected_difficulty) session_data = global_data.session_data[global_data.player_num-1]
self.init_tja(global_data.selected_song)
self.load_hitsounds() self.load_hitsounds()
self.song_info = SongInfo(session_data.song_title, session_data.genre_index) self.song_info = SongInfo(session_data.song_title, session_data.genre_index)
self.result_transition = ResultTransition(global_data.player_num) self.result_transition = ResultTransition(global_data.player_num)
@@ -126,9 +120,10 @@ class GameScreen:
"""Write the score to the database""" """Write the score to the database"""
if self.tja is None: if self.tja is None:
return return
if global_data.modifiers.auto: if global_data.modifiers[global_data.player_num-1].auto:
return return
with sqlite3.connect('scores.db') as con: with sqlite3.connect('scores.db') as con:
session_data = global_data.session_data[global_data.player_num-1]
cursor = con.cursor() cursor = con.cursor()
notes, _, _, _ = TJAParser.notes_to_position(TJAParser(self.tja.file_path), self.player_1.difficulty) notes, _, _, _ = TJAParser.notes_to_position(TJAParser(self.tja.file_path), self.player_1.difficulty)
hash = self.tja.hash_note_data(notes) hash = self.tja.hash_note_data(notes)
@@ -180,7 +175,7 @@ class GameScreen:
if ray.is_key_pressed(ray.KeyboardKey.KEY_F1): if ray.is_key_pressed(ray.KeyboardKey.KEY_F1):
if self.song_music is not None: if self.song_music is not None:
audio.stop_music_stream(self.song_music) audio.stop_music_stream(self.song_music)
self.init_tja(global_data.selected_song, session_data.selected_difficulty) self.init_tja(global_data.selected_song)
audio.play_sound('restart', 'sound') audio.play_sound('restart', 'sound')
self.song_started = False self.song_started = False
@@ -190,7 +185,7 @@ class GameScreen:
return self.on_screen_end('SONG_SELECT') return self.on_screen_end('SONG_SELECT')
def spawn_ending_anims(self): def spawn_ending_anims(self):
if session_data.result_bad == 0: if global_data.session_data[global_data.player_num-1].result_bad == 0:
self.player_1.ending_anim = FCAnimation(self.player_1.is_2p) self.player_1.ending_anim = FCAnimation(self.player_1.is_2p)
elif self.player_1.gauge.is_clear: elif self.player_1.gauge.is_clear:
self.player_1.ending_anim = ClearAnimation(self.player_1.is_2p) self.player_1.ending_anim = ClearAnimation(self.player_1.is_2p)
@@ -223,6 +218,7 @@ class GameScreen:
if self.result_transition.is_finished and not audio.is_sound_playing('result_transition'): if self.result_transition.is_finished and not audio.is_sound_playing('result_transition'):
return self.on_screen_end('RESULT') return self.on_screen_end('RESULT')
elif self.current_ms >= self.player_1.end_time: elif self.current_ms >= self.player_1.end_time:
session_data = global_data.session_data[global_data.player_num-1]
session_data.result_score, session_data.result_good, session_data.result_ok, session_data.result_bad, session_data.result_max_combo, session_data.result_total_drumroll = self.player_1.get_result_score() session_data.result_score, session_data.result_good, session_data.result_ok, session_data.result_bad, session_data.result_max_combo, session_data.result_total_drumroll = self.player_1.get_result_score()
session_data.result_gauge_length = self.player_1.gauge.gauge_length session_data.result_gauge_length = self.player_1.gauge.gauge_length
if self.end_ms != 0: if self.end_ms != 0:
@@ -338,7 +334,7 @@ class Player:
self.branch_indicator = BranchIndicator(self.is_2p) if tja and tja.metadata.course_data[self.difficulty].is_branching else None self.branch_indicator = BranchIndicator(self.is_2p) if tja and tja.metadata.course_data[self.difficulty].is_branching else None
self.ending_anim: Optional[FailAnimation | ClearAnimation | FCAnimation] = None self.ending_anim: Optional[FailAnimation | ClearAnimation | FCAnimation] = None
self.is_gogo_time = False self.is_gogo_time = False
plate_info = global_data.config['nameplate'] plate_info = global_data.config[f'nameplate_{self.is_2p+1}p']
self.nameplate = Nameplate(plate_info['name'], plate_info['title'], global_data.player_num, plate_info['dan'], plate_info['gold']) self.nameplate = Nameplate(plate_info['name'], plate_info['title'], global_data.player_num, plate_info['dan'], plate_info['gold'])
self.chara = Chara2D(player_number - 1, self.bpm) self.chara = Chara2D(player_number - 1, self.bpm)
if global_data.config['general']['judge_counter']: if global_data.config['general']['judge_counter']:
@@ -748,7 +744,7 @@ class Player:
(is_r_kat_pressed, 'KAT', 'R', f'hitsound_kat_{self.player_number}p') (is_r_kat_pressed, 'KAT', 'R', f'hitsound_kat_{self.player_number}p')
] ]
for check_func, note_type, side, sound in input_checks: for check_func, note_type, side, sound in input_checks:
if check_func(): if check_func(self.player_number):
self.lane_hit_effect = LaneHitEffect(note_type, self.is_2p) self.lane_hit_effect = LaneHitEffect(note_type, self.is_2p)
self.draw_drum_hit_list.append(DrumHitEffect(note_type, side, self.is_2p)) self.draw_drum_hit_list.append(DrumHitEffect(note_type, side, self.is_2p))
@@ -994,21 +990,21 @@ class Player:
modifiers_to_draw = ['mod_shinuchi'] modifiers_to_draw = ['mod_shinuchi']
# Speed modifiers # Speed modifiers
if global_data.modifiers.speed >= 4: if global_data.modifiers[int(self.player_number)-1].speed >= 4:
modifiers_to_draw.append('mod_yonbai') modifiers_to_draw.append('mod_yonbai')
elif global_data.modifiers.speed >= 3: elif global_data.modifiers[int(self.player_number)-1].speed >= 3:
modifiers_to_draw.append('mod_sanbai') modifiers_to_draw.append('mod_sanbai')
elif global_data.modifiers.speed > 1: elif global_data.modifiers[int(self.player_number)-1].speed > 1:
modifiers_to_draw.append('mod_baisaku') modifiers_to_draw.append('mod_baisaku')
# Other modifiers # Other modifiers
if global_data.modifiers.display: if global_data.modifiers[int(self.player_number)-1].display:
modifiers_to_draw.append('mod_doron') modifiers_to_draw.append('mod_doron')
if global_data.modifiers.inverse: if global_data.modifiers[int(self.player_number)-1].inverse:
modifiers_to_draw.append('mod_abekobe') modifiers_to_draw.append('mod_abekobe')
if global_data.modifiers.random == 2: if global_data.modifiers[int(self.player_number)-1].random == 2:
modifiers_to_draw.append('mod_detarame') modifiers_to_draw.append('mod_detarame')
elif global_data.modifiers.random == 1: elif global_data.modifiers[int(self.player_number)-1].random == 1:
modifiers_to_draw.append('mod_kimagure') modifiers_to_draw.append('mod_kimagure')
# Draw all modifiers in one batch # Draw all modifiers in one batch
@@ -1038,8 +1034,6 @@ class Player:
# Group 4: Lane covers and UI elements (batch similar textures) # Group 4: Lane covers and UI elements (batch similar textures)
tex.draw_texture('lane', f'{self.player_number}p_lane_cover', index=self.is_2p) tex.draw_texture('lane', f'{self.player_number}p_lane_cover', index=self.is_2p)
tex.draw_texture('lane', 'drum', index=self.is_2p) tex.draw_texture('lane', 'drum', index=self.is_2p)
if global_data.modifiers.auto:
tex.draw_texture('lane', 'auto_icon', index=self.is_2p)
if self.ending_anim is not None: if self.ending_anim is not None:
self.ending_anim.draw() self.ending_anim.draw()
@@ -1064,11 +1058,13 @@ class Player:
self.judge_counter.draw() self.judge_counter.draw()
# Group 7: Player-specific elements # Group 7: Player-specific elements
if not global_data.modifiers.auto: if not self.modifiers.auto:
if self.is_2p: if self.is_2p:
self.nameplate.draw(-62, 371) self.nameplate.draw(-62, 371)
else: else:
self.nameplate.draw(-62, 285) self.nameplate.draw(-62, 285)
else:
tex.draw_texture('lane', 'auto_icon', index=self.is_2p)
self.draw_modifiers() self.draw_modifiers()
self.chara.draw(y=(self.is_2p*536)) self.chara.draw(y=(self.is_2p*536))

View File

@@ -7,18 +7,18 @@ from libs.global_objects import AllNetIcon
from libs.song_hash import build_song_hashes from libs.song_hash import build_song_hashes
from libs.texture import tex from libs.texture import tex
from libs.utils import get_current_ms, global_data from libs.utils import get_current_ms, global_data
from scenes.song_select import SongSelectScreen from libs.file_navigator import navigator
class LoadScreen: class LoadScreen:
def __init__(self, song_select_screen: SongSelectScreen): def __init__(self):
self.width = 1280 self.width = 1280
self.height = 720 self.height = 720
self.screen_init = False self.screen_init = False
self.songs_loaded = False self.songs_loaded = False
self.navigator_started = False self.navigator_started = False
self.loading_complete = False self.loading_complete = False
self.song_select_screen = song_select_screen self.navigator = navigator
# Progress bar settings # Progress bar settings
self.progress_bar_width = self.width * 0.43 self.progress_bar_width = self.width * 0.43
@@ -40,7 +40,7 @@ class LoadScreen:
def _load_navigator(self): def _load_navigator(self):
"""Background thread function to load navigator""" """Background thread function to load navigator"""
self.song_select_screen.load_navigator() self.navigator.initialize(global_data.config["paths"]["tja_path"])
self.loading_complete = True self.loading_complete = True
def on_screen_start(self): def on_screen_start(self):

View File

@@ -1,6 +1,6 @@
import pyray as ray import pyray as ray
from libs import utils from libs.global_data import reset_session
from libs.audio import audio from libs.audio import audio
from libs.chara_2d import Chara2D from libs.chara_2d import Chara2D
from libs.global_objects import AllNetIcon, CoinOverlay, Nameplate from libs.global_objects import AllNetIcon, CoinOverlay, Nameplate
@@ -10,8 +10,7 @@ from libs.utils import (
get_current_ms, get_current_ms,
global_data, global_data,
is_l_don_pressed, is_l_don_pressed,
is_r_don_pressed, is_r_don_pressed
session_data,
) )
@@ -34,29 +33,108 @@ class ResultScreen:
tex.load_screen_textures('result') tex.load_screen_textures('result')
audio.load_screen_sounds('result') audio.load_screen_sounds('result')
self.screen_init = True self.screen_init = True
self.song_info = OutlinedText(session_data.song_title, 40, ray.WHITE, ray.BLACK, outline_thickness=5) self.song_info = OutlinedText(global_data.session_data[0].song_title, 40, ray.WHITE, ray.BLACK, outline_thickness=5)
audio.play_sound('bgm', 'music') audio.play_sound('bgm', 'music')
self.fade_in = FadeIn() self.fade_in = FadeIn(str(global_data.player_num))
self.fade_out = tex.get_animation(0) self.fade_out = tex.get_animation(0)
self.fade_in_bottom = tex.get_animation(1) 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)
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
def handle_input(self):
if is_r_don_pressed() or is_l_don_pressed():
if not self.is_skipped:
self.is_skipped = True
else:
self.fade_out.start()
audio.play_sound('don', 'sound')
def update(self):
self.on_screen_start()
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)
if current_time >= self.start_ms + 5000 and not self.fade_out.is_started:
self.handle_input()
self.fade_out.update(current_time)
if self.fade_out.is_finished:
self.fade_out.update(current_time)
return self.on_screen_end("SONG_SELECT")
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))
self.coin_overlay.draw()
self.allnet_indicator.draw()
def draw_song_info(self):
tex.draw_texture('song_info', 'song_num', frame=global_data.songs_played%4)
dest = ray.Rectangle(1252 - self.song_info.texture.width, 35 - self.song_info.texture.height / 2, self.song_info.texture.width, self.song_info.texture.height)
self.song_info.draw(self.song_info.default_src, dest, ray.Vector2(0, 0), 0, ray.WHITE)
def draw(self):
self.background.draw()
self.draw_song_info()
self.player_1.draw()
self.draw_overlay()
class Background:
def __init__(self, player_num: str, width: int):
self.player_num = player_num
self.width = width
def draw(self):
x = 0
if self.player_num == '3':
while x < self.width:
tex.draw_texture('background', 'background_1p', x=x, y=-360)
tex.draw_texture('background', 'background_2p', x=x, y=360)
tex.draw_texture('background', 'footer_1p', x=x, y=-72)
tex.draw_texture('background', 'footer_2p', x=x, y=648)
x += 256
else:
while x < self.width:
tex.draw_texture('background', f'background_{self.player_num}p', x=x, y=-360)
tex.draw_texture('background', f'background_{self.player_num}p', x=x, y=360)
tex.draw_texture('background', f'footer_{self.player_num}p', x=x, y=-72)
tex.draw_texture('background', f'footer_{self.player_num}p', x=x, y=648)
x += 256
tex.draw_texture('background', 'result_text')
class ResultPlayer:
def __init__(self, player_num: str, has_2p: bool, is_2p: bool):
self.player_num = player_num
self.has_2p = has_2p
self.is_2p = is_2p
self.fade_in_finished = False
self.fade_in_bottom = tex.get_animation(1, is_copy=True)
self.bottom_characters = BottomCharacters()
self.gauge = None self.gauge = None
self.score_delay = None self.score_delay = None
self.bottom_characters = BottomCharacters()
self.crown = None self.crown = None
self.state = None self.state = None
self.high_score_indicator = None self.high_score_indicator = None
plate_info = global_data.config['nameplate'] self.chara = Chara2D(int(self.player_num) - 1, 100)
self.nameplate = Nameplate(plate_info['name'], plate_info['title'], global_data.player_num, plate_info['dan'], plate_info['gold']) session_data = global_data.session_data[int(self.player_num)-1]
self.chara = Chara2D(global_data.player_num - 1, 100)
self.score_animator = ScoreAnimator(session_data.result_score) self.score_animator = ScoreAnimator(session_data.result_score)
self.coin_overlay = CoinOverlay() plate_info = global_data.config[f'nameplate_{self.player_num}p']
self.allnet_indicator = AllNetIcon() self.nameplate = Nameplate(plate_info['name'], plate_info['title'], int(self.player_num), plate_info['dan'], plate_info['gold'])
self.score = '' self.score, self.good, self.ok, self.bad, self.max_combo, self.total_drumroll= '', '', '', '', '', ''
self.good = ''
self.ok = ''
self.bad = ''
self.max_combo = ''
self.total_drumroll = ''
self.update_list = [['score', session_data.result_score], self.update_list = [['score', session_data.result_score],
['good', session_data.result_good], ['good', session_data.result_good],
['ok', session_data.result_ok], ['ok', session_data.result_ok],
@@ -64,8 +142,6 @@ class ResultScreen:
['max_combo', session_data.result_max_combo], ['max_combo', session_data.result_max_combo],
['total_drumroll', session_data.result_total_drumroll]] ['total_drumroll', session_data.result_total_drumroll]]
self.update_index = 0 self.update_index = 0
self.is_skipped = False
self.start_ms = get_current_ms()
if session_data.result_ok == 0 and session_data.result_bad == 0: if session_data.result_ok == 0 and session_data.result_bad == 0:
self.crown_type = 'crown_dfc' self.crown_type = 'crown_dfc'
elif session_data.result_bad == 0: elif session_data.result_bad == 0:
@@ -73,21 +149,11 @@ class ResultScreen:
else: else:
self.crown_type = 'crown_clear' self.crown_type = 'crown_clear'
def on_screen_end(self): def update_score_animation(self, is_skipped: bool):
self.screen_init = False
global_data.songs_played += 1
tex.unload_textures()
audio.stop_sound('bgm')
audio.unload_all_sounds()
audio.unload_all_music()
utils.session_data = utils.reset_session()
return "SONG_SELECT"
def update_score_animation(self):
""" """
Update the score animation if a high score is achieved. Update the score animation if a high score is achieved.
""" """
if self.is_skipped: if is_skipped:
if self.update_index == len(self.update_list) - 1: if self.update_index == len(self.update_list) - 1:
return return
setattr(self, self.update_list[self.update_index][0], self.update_list[self.update_index][1]) setattr(self, self.update_list[self.update_index][0], self.update_list[self.update_index][1])
@@ -109,146 +175,102 @@ class ResultScreen:
self.score_animator = ScoreAnimator(self.update_list[self.update_index][1]) self.score_animator = ScoreAnimator(self.update_list[self.update_index][1])
self.score_delay += 16.67 * 3 self.score_delay += 16.67 * 3
if self.update_index > 0 and self.high_score_indicator is None: if self.update_index > 0 and self.high_score_indicator is None:
session_data = global_data.session_data[int(self.player_num)-1]
if session_data.result_score > session_data.prev_score: if session_data.result_score > session_data.prev_score:
self.high_score_indicator = HighScoreIndicator(session_data.prev_score, session_data.result_score) self.high_score_indicator = HighScoreIndicator(session_data.prev_score, session_data.result_score, self.is_2p)
def handle_input(self): def update(self, current_ms: float, fade_in_finished: bool, is_skipped: bool):
if is_r_don_pressed() or is_l_don_pressed(): self.fade_in_finished = fade_in_finished
if not self.is_skipped: if self.fade_in_finished and self.gauge is None:
self.is_skipped = True self.gauge = Gauge(self.player_num, global_data.session_data[int(self.player_num)-1].result_gauge_length, self.is_2p)
else:
self.fade_out.start()
audio.play_sound('don', 'sound')
def update(self):
self.on_screen_start()
current_time = get_current_ms()
self.fade_in.update(current_time)
if self.fade_in.is_finished and self.gauge is None:
self.gauge = Gauge(str(global_data.player_num), session_data.result_gauge_length)
self.bottom_characters.start() self.bottom_characters.start()
self.bottom_characters.update(self.state) self.bottom_characters.update(self.state)
self.update_score_animation(is_skipped)
if self.bottom_characters.is_finished and self.crown is None: if self.bottom_characters.is_finished and self.crown is None:
if self.gauge is not None and self.gauge.gauge_length > 69: if self.gauge is not None and self.gauge.gauge_length > 69:
self.crown = Crown() self.crown = Crown(self.is_2p)
if self.high_score_indicator is not None:
self.high_score_indicator.update(current_ms)
self.fade_in_bottom.update(current_ms)
self.nameplate.update(current_ms)
if self.gauge is not None: if self.gauge is not None:
self.gauge.update(current_time) self.gauge.update(current_ms)
if self.gauge.is_finished and self.score_delay is None: if self.gauge.is_finished and self.score_delay is None:
self.score_delay = current_time + 1883 self.score_delay = current_ms + 1883
if self.score_delay is not None: if self.score_delay is not None:
if current_time > self.score_delay and not self.fade_in_bottom.is_started: if current_ms > self.score_delay and not self.fade_in_bottom.is_started:
self.fade_in_bottom.start() self.fade_in_bottom.start()
if self.gauge is not None: if self.gauge is not None:
self.state = self.gauge.state self.state = self.gauge.state
if self.high_score_indicator is not None:
self.high_score_indicator.update(current_time)
self.fade_in_bottom.update(current_time)
if current_time >= self.start_ms + 5000 and not self.fade_out.is_started:
self.handle_input()
self.update_score_animation()
self.fade_out.update(current_time)
if self.fade_out.is_finished:
self.fade_out.update(current_time)
return self.on_screen_end()
if self.crown is not None: if self.crown is not None:
self.crown.update(current_time) self.crown.update(current_ms)
self.nameplate.update(current_time) self.chara.update(current_ms, 100, False, False)
self.chara.update(current_time, 100, False, False)
def draw_score_info(self): def draw_score_info(self):
""" """Draw the score information."""
Draw the score information. for j, score in enumerate([self.good, self.ok, self.bad, self.max_combo, self.total_drumroll]):
""" if score == '':
if self.good != '': continue
for i in range(len(str(self.good))): score_str = str(score)[::-1]
tex.draw_texture('score', 'judge_num', frame=int(str(self.good)[::-1][i]), x=943-(i*24), y=186) for i, digit in enumerate(score_str):
if self.ok != '': tex.draw_texture('score', 'judge_num', frame=int(digit), x=-(i*24), index=j+(self.is_2p*5))
for i in range(len(str(self.ok))):
tex.draw_texture('score', 'judge_num', frame=int(str(self.ok)[::-1][i]), x=943-(i*24), y=227)
if self.bad != '':
for i in range(len(str(self.bad))):
tex.draw_texture('score', 'judge_num', frame=int(str(self.bad)[::-1][i]), x=943-(i*24), y=267)
if self.max_combo != '':
for i in range(len(str(self.max_combo))):
tex.draw_texture('score', 'judge_num', frame=int(str(self.max_combo)[::-1][i]), x=1217-(i*24), y=186)
if self.total_drumroll != '':
for i in range(len(str(self.total_drumroll))):
tex.draw_texture('score', 'judge_num', frame=int(str(self.total_drumroll)[::-1][i]), x=1217-(i*24), y=227)
def draw_total_score(self): def draw_total_score(self):
""" """
Draw the total score. Draw the total score.
""" """
if not self.fade_in.is_finished: if not self.fade_in_finished:
return return
tex.draw_texture('score', 'score_shinuchi') tex.draw_texture('score', 'score_shinuchi', index=self.is_2p)
if self.score != '': if self.score != '':
for i in range(len(str(self.score))): for i in range(len(str(self.score))):
tex.draw_texture('score', 'score_num', x=-(i*21), frame=int(str(self.score)[::-1][i])) tex.draw_texture('score', 'score_num', x=-(i*21), frame=int(str(self.score)[::-1][i]), index=self.is_2p)
def draw_bottom_textures(self):
"""Draw the bottom textures."""
if self.state == State.FAIL:
tex.draw_texture('background', 'gradient_fail', fade=min(0.4, self.fade_in_bottom.attribute))
else:
tex.draw_texture('background', 'gradient_clear', fade=min(0.4, self.fade_in_bottom.attribute))
self.bottom_characters.draw()
def draw_modifiers(self): def draw_modifiers(self):
"""Draw the modifiers if enabled.""" """Draw the modifiers if enabled."""
if global_data.modifiers.display: if global_data.modifiers[int(self.player_num)-1].display:
tex.draw_texture('score', 'mod_doron') tex.draw_texture('score', 'mod_doron', index=self.is_2p)
if global_data.modifiers.inverse: if global_data.modifiers[int(self.player_num)-1].inverse:
tex.draw_texture('score', 'mod_abekobe') tex.draw_texture('score', 'mod_abekobe', index=self.is_2p)
if global_data.modifiers.random == 1: if global_data.modifiers[int(self.player_num)-1].random == 1:
tex.draw_texture('score', 'mod_kimagure') tex.draw_texture('score', 'mod_kimagure', index=self.is_2p)
elif global_data.modifiers.random == 2: elif global_data.modifiers[int(self.player_num)-1].random == 2:
tex.draw_texture('score', 'mod_detarame') tex.draw_texture('score', 'mod_detarame', index=self.is_2p)
if global_data.modifiers.speed >= 4: if global_data.modifiers[int(self.player_num)-1].speed >= 4:
tex.draw_texture('score', 'mod_yonbai') tex.draw_texture('score', 'mod_yonbai', index=self.is_2p)
elif global_data.modifiers.speed >= 3: elif global_data.modifiers[int(self.player_num)-1].speed >= 3:
tex.draw_texture('score', 'mod_sanbai') tex.draw_texture('score', 'mod_sanbai', index=self.is_2p)
elif global_data.modifiers.speed > 1: elif global_data.modifiers[int(self.player_num)-1].speed > 1:
tex.draw_texture('score', 'mod_baisaku') tex.draw_texture('score', 'mod_baisaku', index=self.is_2p)
def draw(self): def draw(self):
x = 0 if self.is_2p:
while x < self.width: if self.state == State.FAIL:
tex.draw_texture('background', f'background_{str(global_data.player_num)}p', x=x, y=-360) tex.draw_texture('background', 'gradient_fail', fade=min(0.4, self.fade_in_bottom.attribute))
tex.draw_texture('background', f'background_{str(global_data.player_num)}p', x=x, y=360) elif self.state == State.CLEAR:
tex.draw_texture('background', f'footer_{str(global_data.player_num)}p', x=x, y=-72) tex.draw_texture('background', 'gradient_clear', fade=min(0.4, self.fade_in_bottom.attribute))
tex.draw_texture('background', f'footer_{str(global_data.player_num)}p', x=x, y=648) else:
x += 256 y = -288 if self.has_2p else 0
if self.state == State.FAIL:
tex.draw_texture('background', 'gradient_fail', fade=min(0.4, self.fade_in_bottom.attribute), y=y)
elif self.state == State.CLEAR:
tex.draw_texture('background', 'gradient_clear', fade=min(0.4, self.fade_in_bottom.attribute), y=y)
tex.draw_texture('score', 'overlay', color=ray.fade(ray.WHITE, 0.75), index=self.is_2p)
tex.draw_texture('score', 'difficulty', frame=global_data.session_data[int(self.player_num)-1].selected_difficulty, index=self.is_2p)
if not self.has_2p:
self.bottom_characters.draw()
tex.draw_texture('background', 'result_text') tex.draw_texture('score', 'judge_good', index=self.is_2p)
tex.draw_texture('song_info', 'song_num', frame=global_data.songs_played%4) tex.draw_texture('score', 'judge_ok', index=self.is_2p)
dest = ray.Rectangle(1252 - self.song_info.texture.width, 35 - self.song_info.texture.height / 2, self.song_info.texture.width, self.song_info.texture.height) tex.draw_texture('score', 'judge_bad', index=self.is_2p)
self.song_info.draw(self.song_info.default_src, dest, ray.Vector2(0, 0), 0, ray.WHITE) tex.draw_texture('score', 'max_combo', index=self.is_2p)
tex.draw_texture('score', 'drumroll', index=self.is_2p)
tex.draw_texture('score', 'overlay', color=ray.fade(ray.WHITE, 0.75))
tex.draw_texture('score', 'difficulty', frame=session_data.selected_difficulty)
self.draw_bottom_textures()
if self.gauge is not None:
self.gauge.draw()
tex.draw_texture('score', 'judge_good')
tex.draw_texture('score', 'judge_ok')
tex.draw_texture('score', 'judge_bad')
tex.draw_texture('score', 'max_combo')
tex.draw_texture('score', 'drumroll')
self.draw_score_info() self.draw_score_info()
self.draw_total_score() self.draw_total_score()
@@ -256,31 +278,25 @@ class ResultScreen:
if self.crown is not None: if self.crown is not None:
self.crown.draw(self.crown_type) self.crown.draw(self.crown_type)
self.nameplate.draw(265, 80)
self.draw_modifiers() self.draw_modifiers()
if self.high_score_indicator is not None: if self.high_score_indicator is not None:
self.high_score_indicator.draw() self.high_score_indicator.draw()
self.chara.draw(y=100) self.chara.draw(y=100+(self.is_2p*360))
if self.gauge is not None:
self.fade_in.draw() self.gauge.draw()
ray.draw_rectangle(0, 0, self.width, self.height, ray.fade(ray.BLACK, self.fade_out.attribute)) self.nameplate.draw(265, 80+(self.is_2p*300))
self.coin_overlay.draw()
self.allnet_indicator.draw()
def draw_3d(self):
pass
class Crown: class Crown:
"""Represents a crown animation""" """Represents a crown animation"""
def __init__(self): def __init__(self, is_2p: bool):
self.resize = tex.get_animation(2) self.is_2p = is_2p
self.resize_fix = tex.get_animation(3) self.resize = tex.get_animation(2, is_copy=True)
self.white_fadein = tex.get_animation(4) self.resize_fix = tex.get_animation(3, is_copy=True)
self.gleam = tex.get_animation(5) self.white_fadein = tex.get_animation(4, is_copy=True)
self.fadein = tex.get_animation(6) self.gleam = tex.get_animation(5, is_copy=True)
self.fadein = tex.get_animation(6, is_copy=True)
self.resize.start() self.resize.start()
self.resize_fix.start() self.resize_fix.start()
self.white_fadein.start() self.white_fadein.start()
@@ -302,10 +318,10 @@ class Crown:
scale = self.resize.attribute scale = self.resize.attribute
if self.resize.is_finished: if self.resize.is_finished:
scale = self.resize_fix.attribute scale = self.resize_fix.attribute
tex.draw_texture('crown', crown_name, scale=scale, center=True) tex.draw_texture('crown', crown_name, scale=scale, center=True, index=self.is_2p)
tex.draw_texture('crown', 'crown_fade', fade=self.white_fadein.attribute) tex.draw_texture('crown', 'crown_fade', fade=self.white_fadein.attribute, index=self.is_2p)
if self.gleam.attribute >= 0: if self.gleam.attribute >= 0:
tex.draw_texture('crown', 'gleam', frame=self.gleam.attribute) tex.draw_texture('crown', 'gleam', frame=self.gleam.attribute, index=self.is_2p)
class BottomCharacters: class BottomCharacters:
"""Represents the bottom characters animation""" """Represents the bottom characters animation"""
@@ -387,10 +403,11 @@ class BottomCharacters:
class FadeIn: class FadeIn:
"""A fade out disguised as a fade in""" """A fade out disguised as a fade in"""
def __init__(self): def __init__(self, player_num: str):
self.fadein = tex.get_animation(15) self.fadein = tex.get_animation(15)
self.fadein.start() self.fadein.start()
self.is_finished = False self.is_finished = False
self.player_num = player_num
def update(self, current_ms: float): def update(self, current_ms: float):
self.fadein.update(current_ms) self.fadein.update(current_ms)
@@ -398,11 +415,19 @@ class FadeIn:
def draw(self): def draw(self):
x = 0 x = 0
if self.player_num == '3':
while x < 1280: while x < 1280:
tex.draw_texture('background', f'background_{str(global_data.player_num)}p', x=x, y=-360, fade=self.fadein.attribute) tex.draw_texture('background', 'background_1p', x=x, y=-360, fade=self.fadein.attribute)
tex.draw_texture('background', f'background_{str(global_data.player_num)}p', x=x, y=360, fade=self.fadein.attribute) tex.draw_texture('background', 'background_2p', x=x, y=360, fade=self.fadein.attribute)
tex.draw_texture('background', f'footer_{str(global_data.player_num)}p', x=x, y=-72, fade=self.fadein.attribute) tex.draw_texture('background', 'footer_1p', x=x, y=-72, fade=self.fadein.attribute)
tex.draw_texture('background', f'footer_{str(global_data.player_num)}p', x=x, y=648, fade=self.fadein.attribute) tex.draw_texture('background', 'footer_2p', x=x, y=648, fade=self.fadein.attribute)
x += 256
else:
while x < 1280:
tex.draw_texture('background', f'background_{self.player_num}p', x=x, y=-360, fade=self.fadein.attribute)
tex.draw_texture('background', f'background_{self.player_num}p', x=x, y=360, fade=self.fadein.attribute)
tex.draw_texture('background', f'footer_{self.player_num}p', x=x, y=-72, fade=self.fadein.attribute)
tex.draw_texture('background', f'footer_{self.player_num}p', x=x, y=648, fade=self.fadein.attribute)
x += 256 x += 256
class ScoreAnimator: class ScoreAnimator:
@@ -434,7 +459,8 @@ class ScoreAnimator:
class HighScoreIndicator: class HighScoreIndicator:
"""Indicates the difference between the old and new high score""" """Indicates the difference between the old and new high score"""
def __init__(self, old_score: int, new_score: int): def __init__(self, old_score: int, new_score: int, is_2p: bool):
self.is_2p = is_2p
self.score_diff = new_score - old_score self.score_diff = new_score - old_score
self.move = tex.get_animation(18) self.move = tex.get_animation(18)
self.fade = tex.get_animation(19) self.fade = tex.get_animation(19)
@@ -446,16 +472,17 @@ class HighScoreIndicator:
self.fade.update(current_ms) self.fade.update(current_ms)
def draw(self): def draw(self):
tex.draw_texture('score', 'high_score', y=self.move.attribute, fade=self.fade.attribute) tex.draw_texture('score', 'high_score', y=self.move.attribute, fade=self.fade.attribute, index=self.is_2p)
for i in range(len(str(self.score_diff))): for i in range(len(str(self.score_diff))):
tex.draw_texture('score', 'high_score_num', x=-(i*14), frame=int(str(self.score_diff)[::-1][i]), y=self.move.attribute, fade=self.fade.attribute) tex.draw_texture('score', 'high_score_num', x=-(i*14), frame=int(str(self.score_diff)[::-1][i]), y=self.move.attribute, fade=self.fade.attribute, index=self.is_2p)
class Gauge: class Gauge:
"""The gauge from the game screen, at 0.9x scale""" """The gauge from the game screen, at 0.9x scale"""
def __init__(self, player_num: str, gauge_length: int): def __init__(self, player_num: str, gauge_length: int, is_2p: bool):
self.is_2p = is_2p
self.player_num = player_num self.player_num = player_num
self.difficulty = min(2, session_data.selected_difficulty) self.difficulty = min(2, global_data.session_data[int(player_num)-1].selected_difficulty)
self.gauge_length = gauge_length self.gauge_length = gauge_length
self.clear_start = [69, 69, 69] self.clear_start = [69, 69, 69]
self.gauge_max = 87 self.gauge_max = 87
@@ -488,37 +515,37 @@ class Gauge:
def draw(self): def draw(self):
scale = 10/11 scale = 10/11
tex.draw_texture('gauge', f'{self.player_num}p_unfilled' + self.string_diff, scale=scale, fade=self.gauge_fade_in.attribute) tex.draw_texture('gauge', f'{self.player_num}p_unfilled' + self.string_diff, scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p)
gauge_length = int(self.gauge_length) gauge_length = int(self.gauge_length)
if gauge_length == self.gauge_max: if gauge_length == self.gauge_max:
if 0 < self.rainbow_animation.attribute < 8: if 0 < self.rainbow_animation.attribute < 8:
tex.draw_texture('gauge', 'rainbow' + self.string_diff, frame=self.rainbow_animation.attribute-1, scale=scale, fade=self.gauge_fade_in.attribute) tex.draw_texture('gauge', 'rainbow' + self.string_diff, frame=self.rainbow_animation.attribute-1, scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p)
tex.draw_texture('gauge', 'rainbow' + self.string_diff, frame=self.rainbow_animation.attribute, scale=scale, fade=self.gauge_fade_in.attribute) tex.draw_texture('gauge', 'rainbow' + self.string_diff, frame=self.rainbow_animation.attribute, scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p)
else: else:
for i in range(gauge_length+1): for i in range(gauge_length+1):
width = int(i * 7.2) width = int(i * 7.2)
if i == self.clear_start[self.difficulty] - 1: if i == self.clear_start[self.difficulty] - 1:
tex.draw_texture('gauge', 'bar_clear_transition', x=width, scale=scale, fade=self.gauge_fade_in.attribute) tex.draw_texture('gauge', 'bar_clear_transition', x=width, scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p)
elif i > self.clear_start[self.difficulty] - 1: elif i > self.clear_start[self.difficulty] - 1:
if i % 5 == 0: if i % 5 == 0:
tex.draw_texture('gauge', 'bar_clear_top', x=width, scale=scale, fade=self.gauge_fade_in.attribute) tex.draw_texture('gauge', 'bar_clear_top', x=width, scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p)
tex.draw_texture('gauge', 'bar_clear_bottom', x=width, scale=scale, fade=self.gauge_fade_in.attribute) tex.draw_texture('gauge', 'bar_clear_bottom', x=width, scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p)
tex.draw_texture('gauge', 'bar_clear_top', x=width+1, scale=scale, fade=self.gauge_fade_in.attribute) tex.draw_texture('gauge', 'bar_clear_top', x=width+1, scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p)
tex.draw_texture('gauge', 'bar_clear_bottom', x=width+1, scale=scale, fade=self.gauge_fade_in.attribute) tex.draw_texture('gauge', 'bar_clear_bottom', x=width+1, scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p)
else: else:
if i % 5 == 0: if i % 5 == 0:
tex.draw_texture('gauge', f'{self.player_num}p_bar', x=width, scale=scale, fade=self.gauge_fade_in.attribute) tex.draw_texture('gauge', f'{self.player_num}p_bar', x=width, scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p)
tex.draw_texture('gauge', f'{self.player_num}p_bar', x=width+1, scale=scale, fade=self.gauge_fade_in.attribute) tex.draw_texture('gauge', f'{self.player_num}p_bar', x=width+1, scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p)
tex.draw_texture('gauge', 'overlay' + self.string_diff, scale=scale, fade=min(0.15, self.gauge_fade_in.attribute)) tex.draw_texture('gauge', 'overlay' + self.string_diff, scale=scale, fade=min(0.15, self.gauge_fade_in.attribute), index=self.is_2p)
tex.draw_texture('gauge', 'footer', scale=scale, fade=self.gauge_fade_in.attribute) tex.draw_texture('gauge', 'footer', scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p)
if gauge_length >= self.clear_start[self.difficulty]: if gauge_length >= self.clear_start[self.difficulty]:
tex.draw_texture('gauge', 'clear', scale=scale, fade=self.gauge_fade_in.attribute, index=self.difficulty) tex.draw_texture('gauge', 'clear', scale=scale, fade=self.gauge_fade_in.attribute, index=self.difficulty+(self.is_2p*3))
if self.state == State.RAINBOW: if self.state == State.RAINBOW:
tex.draw_texture('gauge', 'tamashii_fire', scale=0.75 * scale, center=True, frame=self.tamashii_fire_change.attribute, fade=self.gauge_fade_in.attribute) tex.draw_texture('gauge', 'tamashii_fire', scale=0.75 * scale, center=True, frame=self.tamashii_fire_change.attribute, fade=self.gauge_fade_in.attribute, index=self.is_2p)
tex.draw_texture('gauge', 'tamashii', scale=scale, fade=self.gauge_fade_in.attribute) tex.draw_texture('gauge', 'tamashii', scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p)
if self.state == State.RAINBOW and self.tamashii_fire_change.attribute in (0, 1, 4, 5): if self.state == State.RAINBOW and self.tamashii_fire_change.attribute in (0, 1, 4, 5):
tex.draw_texture('gauge', 'tamashii_overlay', scale=scale, fade=min(0.5, self.gauge_fade_in.attribute)) tex.draw_texture('gauge', 'tamashii_overlay', scale=scale, fade=min(0.5, self.gauge_fade_in.attribute), index=self.is_2p)
else: else:
tex.draw_texture('gauge', 'clear_dark', scale=scale, fade=self.gauge_fade_in.attribute, index=self.difficulty) tex.draw_texture('gauge', 'clear_dark', scale=scale, fade=self.gauge_fade_in.attribute, index=self.difficulty+(self.is_2p*3))
tex.draw_texture('gauge', 'tamashii_dark', scale=scale, fade=self.gauge_fade_in.attribute) tex.draw_texture('gauge', 'tamashii_dark', scale=scale, fade=self.gauge_fade_in.attribute, index=self.is_2p)

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,12 @@
import copy import copy
from pathlib import Path from pathlib import Path
from libs.global_data import Modifiers
from libs.tja import TJAParser from libs.tja import TJAParser
from libs.utils import get_current_ms from libs.utils import get_current_ms
from libs.audio import audio from libs.audio import audio
from libs.utils import global_data, session_data from libs.utils import global_data
from libs.video import VideoPlayer from libs.video import VideoPlayer
from scenes.game import ClearAnimation, FCAnimation, FailAnimation, GameScreen, Player, Background, SCREEN_WIDTH import pyray as ray
from scenes.game import ClearAnimation, FCAnimation, FailAnimation, GameScreen, Player, Background, SCREEN_WIDTH, ResultTransition
class TwoPlayerGameScreen(GameScreen): class TwoPlayerGameScreen(GameScreen):
def on_screen_start(self): def on_screen_start(self):
@@ -16,27 +16,52 @@ class TwoPlayerGameScreen(GameScreen):
if self.background is not None: if self.background is not None:
self.background.unload() self.background.unload()
self.background = Background(3, self.bpm, scene_preset=scene_preset) self.background = Background(3, self.bpm, scene_preset=scene_preset)
self.result_transition = ResultTransition(3)
def load_hitsounds(self): def load_hitsounds(self):
"""Load the hit sounds""" """Load the hit sounds"""
sounds_dir = Path("Sounds") sounds_dir = Path("Sounds")
if global_data.hit_sound == -1:
# Load hitsounds for 1P
if global_data.hit_sound[0] == -1:
audio.load_sound(Path('none.wav'), 'hitsound_don_1p') audio.load_sound(Path('none.wav'), 'hitsound_don_1p')
audio.load_sound(Path('none.wav'), 'hitsound_kat_1p') audio.load_sound(Path('none.wav'), 'hitsound_kat_1p')
if global_data.hit_sound == 0: elif global_data.hit_sound[0] == 0:
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound) / "don.wav", 'hitsound_don_1p') 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) / "ka.wav", 'hitsound_kat_1p') audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound[0]) / "ka.wav", 'hitsound_kat_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')
audio.set_sound_pan('hitsound_don_1p', 1.0) audio.set_sound_pan('hitsound_don_1p', 1.0)
audio.set_sound_pan('hitsound_kat_1p', 1.0) audio.set_sound_pan('hitsound_kat_1p', 1.0)
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound) / "don_2p.wav", 'hitsound_don_2p')
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound) / "ka_2p.wav", 'hitsound_kat_2p') # Load hitsounds for 2P
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')
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')
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')
audio.set_sound_pan('hitsound_don_2p', 0.0) audio.set_sound_pan('hitsound_don_2p', 0.0)
audio.set_sound_pan('hitsound_kat_2p', 0.0) audio.set_sound_pan('hitsound_kat_2p', 0.0)
else:
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound) / "don.ogg", 'hitsound_don_1p')
audio.load_sound(sounds_dir / "hit_sounds" / str(global_data.hit_sound) / "ka.ogg", 'hitsound_kat_1p')
def init_tja(self, song: Path, difficulty: int): def global_keys(self):
if ray.is_key_pressed(ray.KeyboardKey.KEY_F1):
if self.song_music is not None:
audio.stop_music_stream(self.song_music)
self.init_tja(global_data.selected_song)
audio.play_sound('restart', 'sound')
self.song_started = False
if ray.is_key_pressed(ray.KeyboardKey.KEY_ESCAPE):
if self.song_music is not None:
audio.stop_music_stream(self.song_music)
return self.on_screen_end('SONG_SELECT_2P')
def init_tja(self, song: Path):
"""Initialize the TJA file""" """Initialize the TJA file"""
self.tja = TJAParser(song, start_delay=self.start_delay, distance=SCREEN_WIDTH - GameScreen.JUDGE_X) self.tja = TJAParser(song, start_delay=self.start_delay, distance=SCREEN_WIDTH - GameScreen.JUDGE_X)
if self.tja.metadata.bgmovie != Path() and self.tja.metadata.bgmovie.exists(): if self.tja.metadata.bgmovie != Path() and self.tja.metadata.bgmovie.exists():
@@ -44,24 +69,28 @@ class TwoPlayerGameScreen(GameScreen):
self.movie.set_volume(0.0) self.movie.set_volume(0.0)
else: else:
self.movie = None self.movie = None
session_data.song_title = self.tja.metadata.title.get(global_data.config['general']['language'].lower(), self.tja.metadata.title['en']) global_data.session_data[0].song_title = self.tja.metadata.title.get(global_data.config['general']['language'].lower(), self.tja.metadata.title['en'])
if self.tja.metadata.wave.exists() and self.tja.metadata.wave.is_file() and self.song_music is None: if self.tja.metadata.wave.exists() and self.tja.metadata.wave.is_file() and self.song_music is None:
self.song_music = audio.load_music_stream(self.tja.metadata.wave, 'song') self.song_music = audio.load_music_stream(self.tja.metadata.wave, 'song')
tja_copy = copy.deepcopy(self.tja) tja_copy = copy.deepcopy(self.tja)
self.player_1 = Player(self.tja, 1, difficulty, False, global_data.modifiers) 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, difficulty-1, True, Modifiers()) 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) self.start_ms = (get_current_ms() - self.tja.metadata.offset*1000)
def spawn_ending_anims(self): def spawn_ending_anims(self):
if session_data.result_bad == 0: if global_data.session_data[0].result_bad == 0:
self.player_1.ending_anim = FCAnimation(self.player_1.is_2p) self.player_1.ending_anim = FCAnimation(self.player_1.is_2p)
self.player_2.ending_anim = FCAnimation(self.player_2.is_2p)
elif self.player_1.gauge.is_clear: elif self.player_1.gauge.is_clear:
self.player_1.ending_anim = ClearAnimation(self.player_1.is_2p) self.player_1.ending_anim = ClearAnimation(self.player_1.is_2p)
self.player_2.ending_anim = ClearAnimation(self.player_2.is_2p)
elif not self.player_1.gauge.is_clear: elif not self.player_1.gauge.is_clear:
self.player_1.ending_anim = FailAnimation(self.player_1.is_2p) self.player_1.ending_anim = FailAnimation(self.player_1.is_2p)
if global_data.session_data[1].result_bad == 0:
self.player_2.ending_anim = FCAnimation(self.player_2.is_2p)
elif self.player_2.gauge.is_clear:
self.player_2.ending_anim = ClearAnimation(self.player_2.is_2p)
elif not self.player_2.gauge.is_clear:
self.player_2.ending_anim = FailAnimation(self.player_2.is_2p) self.player_2.ending_anim = FailAnimation(self.player_2.is_2p)
def update(self): def update(self):
@@ -80,10 +109,14 @@ class TwoPlayerGameScreen(GameScreen):
self.song_info.update(current_time) self.song_info.update(current_time)
self.result_transition.update(current_time) self.result_transition.update(current_time)
if self.result_transition.is_finished and not audio.is_sound_playing('result_transition'): if self.result_transition.is_finished and not audio.is_sound_playing('result_transition'):
return self.on_screen_end('RESULT') return self.on_screen_end('RESULT_2P')
elif self.current_ms >= self.player_1.end_time: elif self.current_ms >= self.player_1.end_time:
session_data = global_data.session_data[0]
session_data.result_score, session_data.result_good, session_data.result_ok, session_data.result_bad, session_data.result_max_combo, session_data.result_total_drumroll = self.player_1.get_result_score() session_data.result_score, session_data.result_good, session_data.result_ok, session_data.result_bad, session_data.result_max_combo, session_data.result_total_drumroll = self.player_1.get_result_score()
session_data.result_gauge_length = self.player_1.gauge.gauge_length session_data.result_gauge_length = self.player_1.gauge.gauge_length
session_data = global_data.session_data[1]
session_data.result_score, session_data.result_good, session_data.result_ok, session_data.result_bad, session_data.result_max_combo, session_data.result_total_drumroll = self.player_2.get_result_score()
session_data.result_gauge_length = self.player_2.gauge.gauge_length
if self.end_ms != 0: if self.end_ms != 0:
if current_time >= self.end_ms + 1000: if current_time >= self.end_ms + 1000:
if self.player_1.ending_anim is None: if self.player_1.ending_anim is None:

View File

@@ -0,0 +1,36 @@
from libs.utils import get_current_ms
from scenes.result import Background, FadeIn, ResultPlayer, ResultScreen
class TwoPlayerResultScreen(ResultScreen):
def __init__(self):
super().__init__()
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)
def update(self):
self.on_screen_start()
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)
self.player_2.update(current_time, self.fade_in.is_finished, self.is_skipped)
if current_time >= self.start_ms + 5000 and not self.fade_out.is_started:
self.handle_input()
self.fade_out.update(current_time)
if self.fade_out.is_finished:
self.fade_out.update(current_time)
return self.on_screen_end("SONG_SELECT_2P")
def draw(self):
self.background.draw()
self.draw_song_info()
self.player_1.draw()
self.player_2.draw()
self.draw_overlay()

View File

@@ -0,0 +1,166 @@
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
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)
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
def handle_input_browsing(self):
"""Handle input for browsing songs."""
action = self.player_1.handle_input_browsing(self.last_moved, self.navigator.items[self.navigator.selected_index] if self.navigator.items else None)
if action is None:
action = self.player_2.handle_input_browsing(self.last_moved, self.navigator.items[self.navigator.selected_index] if self.navigator.items else None)
if action is None:
return
current_time = get_current_ms()
if action == "skip_left":
self.reset_demo_music()
for _ in range(10):
self.navigator.navigate_left()
self.last_moved = current_time
elif action == "skip_right":
self.reset_demo_music()
for _ in range(10):
self.navigator.navigate_right()
self.last_moved = current_time
elif action == "navigate_left":
self.reset_demo_music()
self.navigator.navigate_left()
self.last_moved = current_time
elif action == "navigate_right":
self.reset_demo_music()
self.navigator.navigate_right()
self.last_moved = current_time
elif action == "go_back":
self.navigator.go_back()
elif action == "diff_sort":
self.state = State.DIFF_SORTING
self.diff_sort_selector = DiffSortSelect(self.navigator.diff_sort_statistics, self.navigator.diff_sort_diff, self.navigator.diff_sort_level)
self.text_fade_in.start()
self.text_fade_out.start()
elif action == "select_song":
selected_song = self.navigator.select_current_item()
if selected_song:
self.state = State.SONG_SELECTED
self.player_1.on_song_selected(selected_song)
audio.play_sound('don', 'sound')
audio.play_sound('voice_select_diff', 'voice')
self.move_away.start()
self.diff_fade_out.start()
self.text_fade_out.start()
self.text_fade_in.start()
self.player_1.selected_diff_bounce.start()
self.player_1.selected_diff_fadein.start()
self.player_2.selected_diff_bounce.start()
self.player_2.selected_diff_fadein.start()
elif action == "add_favorite":
self.navigator.add_favorite()
current_box = self.navigator.get_current_item().box
current_box.is_favorite = not current_box.is_favorite
def handle_input_selected(self):
"""Handle input for selecting difficulty."""
p2_result = False
result = self.player_1.handle_input_selected(self.navigator.get_current_item())
if result is None:
result = self.player_2.handle_input_selected(self.navigator.get_current_item())
if result is None:
return
p2_result = True
if result is not None:
print(result, p2_result)
if result == "cancel":
self._cancel_selection()
elif result == "confirm":
if p2_result:
self._confirm_selection(2)
else:
self._confirm_selection(1)
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)
def handle_input_diff_sort(self):
"""
Handle input for sorting difficulty.
"""
if self.diff_sort_selector is None:
raise Exception("Diff sort selector was not able to be created")
result = self.player_1.handle_input_diff_sort(self.diff_sort_selector)
if result is not None:
diff, level = result
self.diff_sort_selector = None
self.state = State.BROWSING
self.text_fade_out.reset()
self.text_fade_in.reset()
if diff != -1:
if level != -1:
self.navigator.diff_sort_diff = diff
self.navigator.diff_sort_level = level
self.navigator.select_current_item()
def _cancel_selection(self):
"""Reset to browsing state"""
super()._cancel_selection()
self.player_2.selected_song = None
def _confirm_selection(self, player_selected: int):
"""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')
if player_selected == 1:
global_data.session_data[0].selected_difficulty = self.player_1.selected_difficulty
self.player_1.selected_diff_highlight_fade.start()
self.player_1.selected_diff_text_resize.start()
self.player_1.selected_diff_text_fadein.start()
elif player_selected == 2:
global_data.session_data[1].selected_difficulty = self.player_2.selected_difficulty
self.player_2.selected_diff_highlight_fade.start()
self.player_2.selected_diff_text_resize.start()
self.player_2.selected_diff_text_fadein.start()
def check_for_selection(self):
if (self.player_1.selected_diff_highlight_fade.is_finished and
self.player_2.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):
selected_song = self.navigator.get_current_item()
if not isinstance(selected_song, SongFile):
raise Exception("picked directory")
title = selected_song.tja.metadata.title.get(
global_data.config['general']['language'], '')
subtitle = selected_song.tja.metadata.subtitle.get(
global_data.config['general']['language'], '')
self.game_transition = Transition(title, subtitle)
self.game_transition.start()
def update_players(self, current_time):
self.player_1.update(current_time)
self.player_2.update(current_time)
if self.text_fade_out.is_finished:
self.player_1.selected_song = True
self.player_2.selected_song = True
return "GAME_2P"
def draw_background_diffs(self):
self.player_1.draw_background_diffs(self.state)
self.player_2.draw_background_diffs(self.state)
def draw_players(self):
self.player_1.draw(self.state)
self.player_2.draw(self.state)