Files
PyTaiko/scenes/song_select.py
2025-11-14 15:20:35 -05:00

1317 lines
63 KiB
Python

import random
from dataclasses import fields
from pathlib import Path
import pyray as ray
import logging
from libs.file_navigator import DanCourse, navigator
from libs.audio import audio
from libs.chara_2d import Chara2D
from libs.file_navigator import Directory, SongBox, SongFile
from libs.global_data import Difficulty, Modifiers, PlayerNum
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 (
OutlinedText,
get_current_ms,
get_key_code,
global_data,
is_l_don_pressed,
is_l_kat_pressed,
is_r_don_pressed,
is_r_kat_pressed,
)
logger = logging.getLogger(__name__)
class State:
BROWSING = 0
SONG_SELECTED = 1
DIFF_SORTING = 2
class SongSelectScreen(Screen):
BOX_CENTER = 444
def on_screen_start(self):
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.dan_transition = DanTransition()
session_data = global_data.session_data[global_data.player_num]
self.player_1 = SongSelectPlayer(global_data.player_num, self.text_fade_in)
if self.navigator.items == []:
logger.warning("No navigator items found, returning to ENTRY screen")
return self.on_screen_end("ENTRY")
if str(session_data.selected_song) in self.navigator.all_song_files:
selected_song = self.navigator.all_song_files[str(session_data.selected_song)]
if not isinstance(selected_song, DanCourse):
self.navigator.mark_crowns_dirty_for_song(selected_song)
curr_item = self.navigator.get_current_item()
curr_item.box.get_scores()
self.navigator.add_recent()
def finalize_song(self):
global_data.session_data[global_data.player_num].selected_song = self.navigator.get_current_item().path
global_data.session_data[global_data.player_num].selected_difficulty = self.player_1.selected_difficulty
global_data.session_data[global_data.player_num].genre_index = self.navigator.get_current_item().box.name_texture_index
def on_screen_end(self, next_screen):
self.screen_init = False
self.reset_demo_music()
self.finalize_song()
self.player_1.nameplate.unload()
current_item = self.navigator.get_current_item()
if current_item.box.yellow_box is not None:
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."""
if self.demo_song is not None:
audio.stop_music_stream(self.demo_song)
audio.unload_music_stream(self.demo_song)
audio.play_sound('bgm', 'music')
self.demo_song = None
self.navigator.get_current_item().box.wait = get_current_ms()
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)
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":
current_song = self.navigator.get_current_item()
if isinstance(current_song, Directory) and current_song.box.texture_index == 13:
self.dan_transition.start()
audio.stop_sound('bgm')
return
selected_song = self.navigator.select_current_item()
if isinstance(selected_song, SongFile):
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()
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."""
result = self.player_1.handle_input_selected(self.navigator.get_current_item())
if result == "cancel":
self._cancel_selection()
elif result == "confirm":
self._confirm_selection()
elif result == "ura_toggle":
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"""
self.player_1.selected_song = False
self.move_away.reset()
self.diff_fade_out.reset()
self.text_fade_out.reset()
self.text_fade_in.reset()
self.state = State.BROWSING
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.navigator.reset_items()
def _confirm_selection_wrapper(self):
"""Wrapper for timer callback"""
self._confirm_selection()
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')
self.player_1.selected_diff_highlight_fade.start()
self.player_1.selected_diff_text_resize.start()
self.player_1.selected_diff_text_fadein.start()
def handle_input(self):
self.player_1.handle_input(self.state, self)
def update_players(self, current_time) -> str:
self.player_1.update(current_time)
if self.text_fade_out.is_finished:
self.player_1.selected_song = True
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:
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(self):
ret_val = super().update()
if ret_val is not None:
return ret_val
current_time = get_current_ms()
self.background_move.update(current_time)
self.move_away.update(current_time)
self.diff_fade_out.update(current_time)
self.background_fade_change.update(current_time)
self.text_fade_out.update(current_time)
self.text_fade_in.update(current_time)
self.ura_switch_animation.update(current_time)
self.indicator.update(current_time)
self.blue_arrow_fade.update(current_time)
self.blue_arrow_move.update(current_time)
self.dan_transition.update(current_time)
if self.dan_transition.is_finished:
self.navigator.select_current_item()
return self.on_screen_end('DAN_SELECT')
next_screen = self.update_players(current_time)
if self.state == State.BROWSING or self.state == State.DIFF_SORTING:
self.timer_browsing.update(current_time)
elif self.state == State.SONG_SELECTED:
self.timer_selected.update(current_time)
if self.last_texture_index != self.texture_index:
if not self.background_fade_change.is_started:
self.background_fade_change.start()
if self.background_fade_change.is_finished:
self.last_texture_index = self.texture_index
self.background_fade_change.reset()
if self.game_transition is not None:
self.game_transition.update(current_time)
if self.game_transition.is_finished:
return self.on_screen_end(next_screen)
else:
self.handle_input()
if self.demo_song is not None:
audio.update_music_stream(self.demo_song)
if self.navigator.genre_bg is not None:
self.navigator.genre_bg.update(get_current_ms())
if self.diff_sort_selector is not None:
self.diff_sort_selector.update(get_current_ms())
self.check_for_selection()
for song in self.navigator.items:
song.box.update(self.state == State.SONG_SELECTED)
song.box.is_open = song.box.position == SongSelectScreen.BOX_CENTER + 150
if not isinstance(song, Directory) and song.box.is_open:
if self.demo_song is None and get_current_ms() >= song.box.wait + (83.33*3):
song.box.get_scores()
if song.tja.metadata.wave.exists() and song.tja.metadata.wave.is_file():
self.demo_song = audio.load_music_stream(song.tja.metadata.wave, 'demo_song')
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(get_key_code(global_data.config["keys"]["back_key"])):
logger.info("Back key pressed, returning to ENTRY screen")
return self.on_screen_end('ENTRY')
def draw_background_diffs(self):
self.player_1.draw_background_diffs(self.state)
def draw_players(self):
self.player_1.draw(self.state)
def draw(self):
width = tex.textures['box']['background'].width
for i in range(0, width * 4, width):
tex.draw_texture('box', 'background', frame=self.last_texture_index, x=i-self.background_move.attribute)
tex.draw_texture('box', 'background', frame=self.texture_index, x=i-self.background_move.attribute, fade=1 - self.background_fade_change.attribute)
self.draw_background_diffs()
if self.navigator.genre_bg is not None and self.state == State.BROWSING:
self.navigator.genre_bg.draw(95)
for item in self.navigator.items:
box = item.box
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:
box.draw(box.position + int(self.move_away.attribute), 95, self.player_1.is_ura, fade_override=self.diff_fade_out.attribute)
if self.state == State.BROWSING:
tex.draw_texture('global', 'arrow', index=0, x=-(self.blue_arrow_move.attribute*2), fade=self.blue_arrow_fade.attribute)
tex.draw_texture('global', 'arrow', index=1, mirror='horizontal', x=self.blue_arrow_move.attribute*2, fade=self.blue_arrow_fade.attribute)
tex.draw_texture('global', 'footer')
self.ura_switch_animation.draw()
if self.diff_sort_selector is not None:
self.diff_sort_selector.draw()
if (self.player_1.selected_song and self.state == State.SONG_SELECTED):
tex.draw_texture('global', 'difficulty_select', fade=self.text_fade_in.attribute)
elif self.state == State.DIFF_SORTING:
tex.draw_texture('global', 'difficulty_select', fade=self.text_fade_in.attribute)
else:
tex.draw_texture('global', 'song_select', fade=self.text_fade_out.attribute)
self.draw_players()
if self.state == State.BROWSING and self.navigator.items != []:
self.navigator.get_current_item().box.draw_score_history()
self.indicator.draw(410, 575)
tex.draw_texture('global', 'song_num_bg', fade=0.75)
tex.draw_texture('global', 'song_num', frame=global_data.songs_played % 4)
if self.state == State.BROWSING or self.state == State.DIFF_SORTING:
self.timer_browsing.draw()
elif self.state == State.SONG_SELECTED:
self.timer_selected.draw()
self.coin_overlay.draw()
if self.game_transition is not None:
self.game_transition.draw()
if self.dan_transition.is_started:
self.dan_transition.draw()
self.allnet_indicator.draw()
class SongSelectPlayer:
def __init__(self, player_num: PlayerNum, text_fade_in):
self.player_num = player_num
self.selected_difficulty = -3
self.prev_diff = -3
self.selected_song = False
self.is_ready = False
self.is_ura = False
self.ura_toggle = 0
self.diff_select_move_right = False
self.neiro_selector = None
self.modifier_selector = None
# References to shared animations
self.diff_selector_move_1 = tex.get_animation(26, is_copy=True)
self.diff_selector_move_2 = tex.get_animation(27, is_copy=True)
self.text_fade_in = text_fade_in
self.selected_diff_bounce = tex.get_animation(33, is_copy=True)
self.selected_diff_fadein = tex.get_animation(34, is_copy=True)
self.selected_diff_highlight_fade = tex.get_animation(35, is_copy=True)
self.selected_diff_text_resize = tex.get_animation(36, is_copy=True)
self.selected_diff_text_fadein = tex.get_animation(37, is_copy=True)
# Player-specific objects
self.chara = Chara2D(int(self.player_num) - 1, 100)
plate_info = global_data.config[f'nameplate_{self.player_num}p']
self.nameplate = Nameplate(plate_info['name'], plate_info['title'],
self.player_num, plate_info['dan'], plate_info['gold'], plate_info['rainbow'], plate_info['title_bg'])
def update(self, current_time):
"""Update player state"""
self.selected_diff_bounce.update(current_time)
self.selected_diff_fadein.update(current_time)
self.selected_diff_highlight_fade.update(current_time)
self.selected_diff_text_resize.update(current_time)
self.selected_diff_text_fadein.update(current_time)
self.diff_selector_move_1.update(current_time)
self.diff_selector_move_2.update(current_time)
self.nameplate.update(current_time)
self.chara.update(current_time, 100, False, False)
if self.neiro_selector is not None:
self.neiro_selector.update(current_time)
if self.neiro_selector.is_finished:
self.neiro_selector = None
if self.modifier_selector is not None:
self.modifier_selector.update(current_time)
if self.modifier_selector.is_finished:
self.modifier_selector = None
def is_voice_playing(self):
"""Check if player voice is playing"""
return audio.is_sound_playing(f'voice_start_song_{self.player_num}p')
def on_song_selected(self, selected_song):
"""Called when a song is selected"""
if 4 not in selected_song.tja.metadata.course_data:
self.is_ura = False
elif (4 in selected_song.tja.metadata.course_data and
3 not in selected_song.tja.metadata.course_data):
self.is_ura = True
def handle_input_browsing(self, last_moved, selected_item):
"""Handle input for browsing songs. Returns action string or None."""
current_time = get_current_ms()
# Skip left (fast navigate)
if ray.is_key_pressed(ray.KeyboardKey.KEY_LEFT_CONTROL) or (is_l_kat_pressed(self.player_num) and current_time <= last_moved + 50):
audio.play_sound('skip', 'sound')
return "skip_left"
# Skip right (fast navigate)
if ray.is_key_pressed(ray.KeyboardKey.KEY_RIGHT_CONTROL) or (is_r_kat_pressed(self.player_num) and current_time <= last_moved + 50):
audio.play_sound('skip', 'sound')
return "skip_right"
# Navigate left
if is_l_kat_pressed(self.player_num):
audio.play_sound('kat', 'sound')
return "navigate_left"
# Navigate right
if is_r_kat_pressed(self.player_num):
audio.play_sound('kat', 'sound')
return "navigate_right"
# Select/Enter
if is_l_don_pressed(self.player_num) or is_r_don_pressed(self.player_num):
if selected_item is not None and selected_item.box.is_back:
audio.play_sound('cancel', 'sound')
return "go_back"
elif isinstance(selected_item, Directory) and selected_item.collection == Directory.COLLECTIONS[3]:
return "diff_sort"
else:
return "select_song"
# Add favorite
if ray.is_key_pressed(ray.KeyboardKey.KEY_SPACE):
audio.play_sound('add_favorite', 'sound')
return "add_favorite"
return None
def handle_input_diff_sort(self, diff_sort_selector):
"""Handle input for difficulty sorting. Returns (diff, level) tuple or None."""
if is_l_kat_pressed(self.player_num):
diff_sort_selector.input_left()
audio.play_sound('kat', 'sound')
if is_r_kat_pressed(self.player_num):
diff_sort_selector.input_right()
audio.play_sound('kat', 'sound')
if is_l_don_pressed(self.player_num) or is_r_don_pressed(self.player_num):
result = diff_sort_selector.input_select()
audio.play_sound('don', 'sound')
return result
return None
def handle_input(self, state, screen):
"""Main input dispatcher. Delegates to state-specific handlers."""
if self.is_voice_playing() or self.is_ready:
return
if state == State.BROWSING:
screen.handle_input_browsing()
elif state == State.SONG_SELECTED:
screen.handle_input_selected()
elif state == State.DIFF_SORTING:
screen.handle_input_diff_sort()
def handle_input_selected(self, current_item):
"""Handle input for selecting difficulty. Returns 'cancel', 'confirm', or None"""
if self.neiro_selector is not None:
if is_l_kat_pressed(self.player_num):
self.neiro_selector.move_left()
elif is_r_kat_pressed(self.player_num):
self.neiro_selector.move_right()
if is_l_don_pressed(self.player_num) or is_r_don_pressed(self.player_num):
audio.play_sound('don', 'sound')
self.neiro_selector.confirm()
return None
if self.modifier_selector is not None:
if is_l_kat_pressed(self.player_num):
audio.play_sound('kat', 'sound')
self.modifier_selector.left()
elif is_r_kat_pressed(self.player_num):
audio.play_sound('kat', 'sound')
self.modifier_selector.right()
if is_l_don_pressed(self.player_num) or is_r_don_pressed(self.player_num):
audio.play_sound('don', 'sound')
self.modifier_selector.confirm()
return None
if is_l_don_pressed(self.player_num) or is_r_don_pressed(self.player_num):
if self.selected_difficulty == -3:
return "cancel"
elif self.selected_difficulty == -2:
audio.play_sound('don', 'sound')
self.modifier_selector = ModifierSelector(self.player_num)
return None
elif self.selected_difficulty == -1:
audio.play_sound('don', 'sound')
self.neiro_selector = NeiroSelector(self.player_num)
return None
else:
self.is_ready = True
return "confirm"
if is_l_kat_pressed(self.player_num) or is_r_kat_pressed(self.player_num):
audio.play_sound('kat', 'sound')
selected_song = current_item
if isinstance(selected_song, Directory):
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)
elif is_r_kat_pressed(self.player_num):
ret_val = self._navigate_difficulty_right(diffs)
if Difficulty.EASY <= self.selected_difficulty <= Difficulty.URA and self.selected_difficulty != prev_diff:
self.selected_diff_bounce.start()
self.selected_diff_fadein.start()
return ret_val
if (ray.is_key_pressed(ray.KeyboardKey.KEY_TAB) and
self.selected_difficulty in [Difficulty.ONI, Difficulty.URA]):
return self._toggle_ura_mode()
return None
def _navigate_difficulty_left(self, diffs):
"""Navigate difficulty selection leftward"""
self.diff_select_move_right = False
if self.is_ura and self.selected_difficulty == Difficulty.URA:
self.diff_selector_move_1.start()
self.prev_diff = self.selected_difficulty
if len(diffs) == 1:
self.selected_difficulty = -1
else:
self.selected_difficulty = diffs[-3]
elif self.selected_difficulty == -1 or self.selected_difficulty == -2:
self.diff_selector_move_2.start()
self.prev_diff = self.selected_difficulty
self.selected_difficulty -= 1
elif self.selected_difficulty == -3:
pass
elif self.selected_difficulty not in diffs:
self.prev_diff = self.selected_difficulty
self.diff_selector_move_1.start()
self.selected_difficulty = min(diffs)
elif self.selected_difficulty == min(diffs):
self.diff_selector_move_2.start()
self.prev_diff = self.selected_difficulty
self.selected_difficulty = -1
else:
self.diff_selector_move_1.start()
self.prev_diff = self.selected_difficulty
self.selected_difficulty = diffs[diffs.index(self.selected_difficulty) - 1]
def _navigate_difficulty_right(self, diffs):
"""Navigate difficulty selection rightward"""
self.diff_select_move_right = True
if self.is_ura and self.selected_difficulty == Difficulty.HARD:
self.prev_diff = self.selected_difficulty
self.selected_difficulty = Difficulty.URA
self.diff_selector_move_1.start()
if (self.selected_difficulty in [Difficulty.ONI, Difficulty.URA] and Difficulty.URA in diffs and Difficulty.ONI in diffs):
self.ura_toggle = (self.ura_toggle + 1) % 10
if self.ura_toggle == 0:
return self._toggle_ura_mode()
elif self.selected_difficulty == -1:
self.prev_diff = self.selected_difficulty
self.selected_difficulty = min(diffs)
self.diff_selector_move_2.start()
self.diff_selector_move_1.start()
elif self.selected_difficulty == -2 or self.selected_difficulty == -3:
self.prev_diff = self.selected_difficulty
self.selected_difficulty += 1
self.diff_selector_move_2.start()
elif self.selected_difficulty < max(diffs):
self.prev_diff = self.selected_difficulty
self.selected_difficulty = diffs[diffs.index(self.selected_difficulty) + 1]
self.diff_selector_move_1.start()
def _toggle_ura_mode(self):
"""Toggle between ura and normal mode. Returns 'ura_toggle' to signal screen to play animation"""
self.ura_toggle = 0
self.is_ura = not self.is_ura
audio.play_sound('ura_switch', 'sound')
self.selected_difficulty = 7 - self.selected_difficulty
return "ura_toggle"
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:
if self.prev_diff == -1 and self.selected_difficulty >= 0:
if not self.diff_selector_move_2.is_finished:
name = f'{self.player_num}p_balloon_half' if is_half else f'{self.player_num}p_balloon'
tex.draw_texture('diff_select', name, x=((self.prev_diff+3) * 70) - 220 + (self.diff_selector_move_2.attribute * direction), fade=fade)
name = f'{self.player_num}p_outline_back_half' if is_half else f'{self.player_num}p_outline_back'
tex.draw_texture('diff_select', name, x=((self.prev_diff+3) * 70) + (self.diff_selector_move_2.attribute * direction))
else:
difficulty = min(Difficulty.ONI, self.selected_difficulty)
name = f'{self.player_num}p_balloon_half' if is_half else f'{self.player_num}p_balloon'
tex.draw_texture('diff_select', name, x=(difficulty * 115), fade=fade)
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))
elif not self.diff_selector_move_2.is_finished:
name = f'{self.player_num}p_outline_back_half' if is_half else f'{self.player_num}p_outline_back'
tex.draw_texture('diff_select', name, x=((self.prev_diff+3) * 70) + (self.diff_selector_move_2.attribute * direction))
if self.selected_difficulty != -3:
name = f'{self.player_num}p_balloon_half' if is_half else f'{self.player_num}p_balloon'
tex.draw_texture('diff_select', name, x=((self.prev_diff+3) * 70) - 220 + (self.diff_selector_move_2.attribute * direction), fade=fade)
else:
name = f'{self.player_num}p_outline_back_half' if is_half else f'{self.player_num}p_outline_back'
tex.draw_texture('diff_select', name, x=((self.selected_difficulty+3) * 70))
if self.selected_difficulty != -3:
name = f'{self.player_num}p_balloon_half' if is_half else f'{self.player_num}p_balloon'
tex.draw_texture('diff_select', name, x=((self.selected_difficulty+3) * 70) - 220, fade=fade)
else:
if self.prev_diff == -1:
return
if not self.diff_selector_move_1.is_finished:
difficulty = min(Difficulty.ONI, self.prev_diff)
name = f'{self.player_num}p_balloon_half' if is_half else f'{self.player_num}p_balloon'
tex.draw_texture('diff_select', name, x=(difficulty * 115) + (self.diff_selector_move_1.attribute * direction), fade=fade)
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) + (self.diff_selector_move_1.attribute * direction))
else:
difficulty = min(Difficulty.ONI, self.selected_difficulty)
name = f'{self.player_num}p_balloon_half' if is_half else f'{self.player_num}p_balloon'
tex.draw_texture('diff_select', name, x=(difficulty * 115), fade=fade)
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: int):
if (self.selected_song and state == State.SONG_SELECTED and self.selected_difficulty >= Difficulty.EASY):
if self.player_num == PlayerNum.P2:
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)
if self.selected_diff_highlight_fade.is_reversing or self.selected_diff_highlight_fade.is_finished:
tex.draw_texture('global', 'background_diff', frame=self.selected_difficulty, x=1025, y=-self.selected_diff_bounce.attribute, y2=self.selected_diff_bounce.attribute)
tex.draw_texture('global', 'background_diff_highlight', frame=min(Difficulty.ONI, self.selected_difficulty), fade=self.selected_diff_highlight_fade.attribute, x=1025)
tex.draw_texture('global', 'bg_diff_text_bg', x=1025, 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(Difficulty.ONI, self.selected_difficulty), x=1025, fade=self.selected_diff_text_fadein.attribute, scale=self.selected_diff_text_resize.attribute, center=True)
else:
tex.draw_texture('global', 'background_diff', frame=self.selected_difficulty, fade=min(0.5, self.selected_diff_fadein.attribute), y=-self.selected_diff_bounce.attribute, y2=self.selected_diff_bounce.attribute)
if self.selected_diff_highlight_fade.is_reversing or self.selected_diff_highlight_fade.is_finished:
tex.draw_texture('global', 'background_diff', frame=self.selected_difficulty, y=-self.selected_diff_bounce.attribute, y2=self.selected_diff_bounce.attribute)
tex.draw_texture('global', 'background_diff_highlight', frame=min(Difficulty.ONI, self.selected_difficulty), fade=self.selected_diff_highlight_fade.attribute)
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(Difficulty.ONI, self.selected_difficulty), fade=self.selected_diff_text_fadein.attribute, scale=self.selected_diff_text_resize.attribute, center=True)
def draw(self, state: int, is_half: bool = False):
if (self.selected_song and state == State.SONG_SELECTED):
self.draw_selector(is_half)
offset = 0
if self.neiro_selector is not None:
offset = self.neiro_selector.move.attribute
if self.neiro_selector.is_confirmed:
offset += -370
else:
offset *= -1
if self.modifier_selector is not None:
offset = self.modifier_selector.move.attribute
if self.modifier_selector.is_confirmed:
offset += -370
else:
offset *= -1
if self.player_num == PlayerNum.P1:
self.nameplate.draw(30, 640)
self.chara.draw(x=-50, y=410 + (offset*0.6))
else:
self.nameplate.draw(950, 640)
self.chara.draw(mirror=True, x=950, y=410 + (offset*0.6))
if self.neiro_selector is not None:
self.neiro_selector.draw()
if self.modifier_selector is not None:
self.modifier_selector.draw()
class UraSwitchAnimation:
"""The animation for the ura switch."""
def __init__(self) -> None:
self.texture_change = tex.get_animation(7)
self.fade_out = tex.get_animation(8)
self.fade_out.attribute = 0
def start(self, is_backwards: bool):
if is_backwards:
self.texture_change = tex.get_animation(6)
self.texture_change.start()
self.fade_out.start()
def update(self, current_ms: float):
self.texture_change.update(current_ms)
self.fade_out.update(current_ms)
def draw(self):
tex.draw_texture('diff_select', 'ura_switch', frame=self.texture_change.attribute, fade=self.fade_out.attribute)
class DiffSortSelect:
"""The menu for selecting the difficulty sort and level sort."""
def __init__(self, statistics: dict[int, dict[int, list[int]]], prev_diff: int, prev_level: int):
self.selected_box = -1
self.selected_level = 1
self.in_level_select = False
self.confirmation = False
self.confirm_index = 1
self.num_boxes = 6
self.limits = [5, 7, 8, 10]
self.bg_resize = tex.get_animation(19)
self.diff_fade_in = tex.get_animation(20)
self.box_flicker = tex.get_animation(21)
self.bounce_up_1 = tex.get_animation(22)
self.bounce_down_1 = tex.get_animation(23)
self.bounce_up_2 = tex.get_animation(24)
self.bounce_down_2 = tex.get_animation(25)
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.bg_resize.start()
self.diff_fade_in.start()
self.prev_diff = prev_diff
self.prev_level = prev_level
self.diff_sort_statistics = statistics
self.diff_sort_sum_stat = {
course: [
sum(stats[0] for stats in levels.values()),
sum(stats[1] for stats in levels.values()),
sum(stats[2] for stats in levels.values())
]
for course, levels in self.diff_sort_statistics.items()
}
audio.play_sound('voice_diff_sort_enter', 'voice')
def update(self, current_ms):
self.bg_resize.update(current_ms)
self.diff_fade_in.update(current_ms)
self.box_flicker.update(current_ms)
self.bounce_up_1.update(current_ms)
self.bounce_down_1.update(current_ms)
self.bounce_up_2.update(current_ms)
self.bounce_down_2.update(current_ms)
def get_random_sort(self):
diff = random.randint(0, 4)
if diff == Difficulty.EASY:
level = random.randint(1, 5)
elif diff == Difficulty.NORMAL:
level = random.randint(1, 7)
elif diff == Difficulty.HARD:
level = random.randint(1, 8)
elif diff == Difficulty.ONI:
level = random.randint(1, 10)
else:
level = random.choice([1, 5, 6, 7, 8, 9, 10])
return diff, level
def input_select(self):
if self.confirmation:
if self.confirm_index == 0:
self.confirmation = False
return None
elif self.confirm_index == 1:
return self.selected_box, self.selected_level
elif self.confirm_index == 2:
self.confirmation = False
self.in_level_select = False
return None
elif self.in_level_select:
self.confirmation = True
self.bounce_up_1.start()
self.bounce_down_1.start()
self.bounce_up_2.start()
self.bounce_down_2.start()
self.confirm_index = 1
audio.play_sound('voice_diff_sort_confirm', 'voice')
return None
if self.selected_box == -1:
return (-1, -1)
elif self.selected_box == 5:
return (0, -1)
elif self.selected_box == 4:
return self.get_random_sort()
audio.play_sound('voice_diff_sort_level', 'voice')
self.in_level_select = True
self.bg_resize.start()
self.diff_fade_in.start()
self.selected_level = min(self.selected_level, self.limits[self.selected_box])
return None
def input_left(self):
if self.confirmation:
self.confirm_index = max(self.confirm_index - 1, 0)
elif self.in_level_select:
self.selected_level = max(self.selected_level - 1, 1)
else:
self.selected_box = max(self.selected_box - 1, -1)
def input_right(self):
if self.confirmation:
self.confirm_index = min(self.confirm_index + 1, 2)
elif self.in_level_select:
self.selected_level = min(self.selected_level + 1, self.limits[self.selected_box])
else:
self.selected_box = min(self.selected_box + 1, self.num_boxes - 1)
def draw_statistics(self):
tex.draw_texture('diff_sort', f'stat_bg_{global_data.player_num}p')
tex.draw_texture('diff_sort', 'stat_overlay')
tex.draw_texture('diff_sort', 'stat_diff', frame=min(self.selected_box, 3))
if self.in_level_select or self.selected_box == 5:
tex.draw_texture('diff_sort', 'stat_starx')
if self.selected_box == 5:
tex.draw_texture('diff_sort', 'stat_prev')
counter = str(self.prev_level)
else:
counter = str(self.selected_level)
margin = 25
total_width = len(counter) * margin
for i, digit in enumerate(counter):
tex.draw_texture('diff_sort', 'stat_num_star', frame=int(digit), x=70-(len(counter) - i) * margin, y=-108)
counter = str(self.diff_sort_statistics[self.selected_box][self.selected_level][0])
if self.selected_box == 5:
counter = str(self.diff_sort_statistics[self.prev_diff][self.prev_level][0])
margin = 23
total_width = len(counter) * margin
for i, digit in enumerate(counter):
tex.draw_texture('diff_sort', 'stat_num', frame=int(digit), x=-(total_width//2)+(i*margin))
for j in range(2):
if self.selected_box == 5:
counter = str(self.diff_sort_statistics[self.prev_diff][self.prev_level][0])
else:
counter = str(self.diff_sort_statistics[self.selected_box][self.selected_level][0])
margin = 10
total_width = len(counter) * margin
for i, digit in enumerate(counter):
tex.draw_texture('diff_sort', 'stat_num_small', index=j, frame=int(digit), x=-(total_width//2)+(i*margin))
for j in range(2):
if self.selected_box == 5:
counter = str(self.diff_sort_statistics[self.prev_diff][self.prev_level][j+1])
else:
counter = str(self.diff_sort_statistics[self.selected_box][self.selected_level][j+1])
margin = 25
total_width = len(counter) * margin
for i, digit in enumerate(counter):
tex.draw_texture('diff_sort', 'stat_num_star', index=j, frame=int(digit), x=-(len(counter) - i) * margin)
else:
counter = str(self.diff_sort_sum_stat[self.selected_box][0])
margin = 23
total_width = len(counter) * margin
for i, digit in enumerate(counter):
tex.draw_texture('diff_sort', 'stat_num', frame=int(digit), x=-(total_width//2)+(i*margin))
for j in range(2):
counter = str(self.diff_sort_sum_stat[self.selected_box][0])
margin = 10
total_width = len(counter) * margin
for i, digit in enumerate(counter):
tex.draw_texture('diff_sort', 'stat_num_small', index=j, frame=int(digit), x=-(total_width//2)+(i*margin))
for j in range(2):
counter = str(self.diff_sort_sum_stat[self.selected_box][j+1])
margin = 25
total_width = len(counter) * margin
for i, digit in enumerate(counter):
tex.draw_texture('diff_sort', 'stat_num_star', index=j, frame=int(digit), x=-(len(counter) - i) * margin)
def draw_diff_select(self):
tex.draw_texture('diff_sort', 'background', scale=self.bg_resize.attribute, center=True)
tex.draw_texture('diff_sort', 'back', fade=self.diff_fade_in.attribute)
for i in range(self.num_boxes):
if i == self.selected_box:
tex.draw_texture('diff_sort', 'box_highlight', x=(100*i), fade=self.diff_fade_in.attribute)
tex.draw_texture('diff_sort', 'box_text_highlight', x=(100*i), frame=i, fade=self.diff_fade_in.attribute)
else:
tex.draw_texture('diff_sort', 'box', x=(100*i), fade=self.diff_fade_in.attribute)
tex.draw_texture('diff_sort', 'box_text', x=(100*i), frame=i, fade=self.diff_fade_in.attribute)
if self.selected_box == -1:
tex.draw_texture('diff_sort', 'back_outline', fade=self.box_flicker.attribute)
else:
tex.draw_texture('diff_sort', 'box_outline', x=(100*self.selected_box), fade=self.box_flicker.attribute)
for i in range(self.num_boxes):
if i < 4:
tex.draw_texture('diff_sort', 'box_diff', x=(100*i), frame=i)
if Difficulty.EASY <= self.selected_box <= Difficulty.ONI or self.selected_box == 5:
self.draw_statistics()
def draw_level_select(self):
tex.draw_texture('diff_sort', 'background', scale=self.bg_resize.attribute, center=True)
if self.confirmation:
tex.draw_texture('diff_sort', 'star_select_prompt')
else:
tex.draw_texture('diff_sort', 'star_select_text', fade=self.diff_fade_in.attribute)
tex.draw_texture('diff_sort', 'star_limit', frame=self.selected_box, fade=self.diff_fade_in.attribute)
tex.draw_texture('diff_sort', 'level_box', fade=self.diff_fade_in.attribute)
tex.draw_texture('diff_sort', 'diff', frame=self.selected_box, fade=self.diff_fade_in.attribute)
tex.draw_texture('diff_sort', 'star_num', frame=self.selected_level, fade=self.diff_fade_in.attribute)
for i in range(self.selected_level):
tex.draw_texture('diff_sort', 'star', x=(i*40.5), fade=self.diff_fade_in.attribute)
if self.confirmation:
texture = tex.textures['diff_sort']['level_box']
ray.draw_rectangle(texture.x[0], texture.y[0], texture.x2[0], texture.y2[0], ray.fade(ray.BLACK, 0.5))
y = -self.bounce_up_1.attribute + self.bounce_down_1.attribute - self.bounce_up_2.attribute + self.bounce_down_2.attribute
for i in range(3):
if i == self.confirm_index:
tex.draw_texture('diff_sort', 'small_box_highlight', x=(i*245), y=y)
tex.draw_texture('diff_sort', 'small_box_text_highlight', x=(i*245), y=y, frame=i)
tex.draw_texture('diff_sort', 'small_box_outline', x=(i*245), y=y, fade=self.box_flicker.attribute)
else:
tex.draw_texture('diff_sort', 'small_box', x=(i*245), y=y)
tex.draw_texture('diff_sort', 'small_box_text', x=(i*245), y=y, frame=i)
else:
tex.draw_texture('diff_sort', 'pongos')
if self.selected_level != 1:
tex.draw_texture('diff_sort', 'arrow', index=0, x=-self.blue_arrow_move.attribute, fade=self.blue_arrow_fade.attribute)
if self.selected_level != self.limits[self.selected_box]:
tex.draw_texture('diff_sort', 'arrow', index=1, mirror='horizontal', x=self.blue_arrow_move.attribute, fade=self.blue_arrow_fade.attribute)
self.draw_statistics()
def draw(self):
ray.draw_rectangle(0, 0, 1280, 720, ray.fade(ray.BLACK, 0.6))
if self.in_level_select:
self.draw_level_select()
else:
self.draw_diff_select()
class NeiroSelector:
"""The menu for selecting the game hitsounds."""
def __init__(self, player_num: PlayerNum):
self.player_num = player_num
self.selected_sound = global_data.hit_sound[self.player_num]
with open(Path("Sounds") / 'hit_sounds' / 'neiro_list.txt', encoding='utf-8-sig') as neiro_list:
self.sounds = neiro_list.readlines()
self.sounds.append('無音')
self.load_sound()
audio.play_sound(f'voice_hitsound_select_{self.player_num}p', 'voice')
self.is_finished = False
self.is_confirmed = False
self.move = tex.get_animation(28, is_copy=True)
self.move.start()
self.blue_arrow_fade = tex.get_animation(29, is_copy=True)
self.blue_arrow_move = tex.get_animation(30, is_copy=True)
self.text = OutlinedText(self.sounds[self.selected_sound], 50, ray.WHITE)
self.text_2 = OutlinedText(self.sounds[self.selected_sound], 50, ray.WHITE)
self.move_sideways = tex.get_animation(31, is_copy=True)
self.fade_sideways = tex.get_animation(32, is_copy=True)
self.direction = -1
def load_sound(self):
if self.selected_sound == len(self.sounds):
return
if self.selected_sound == 0:
self.curr_sound = audio.load_sound(Path("Sounds") / "hit_sounds" / str(self.selected_sound) / "don.wav", 'hit_sound')
else:
self.curr_sound = audio.load_sound(Path("Sounds") / "hit_sounds" / str(self.selected_sound) / "don.ogg", 'hit_sound')
def move_left(self):
if self.move.is_started and not self.move.is_finished:
return
self.selected_sound = (self.selected_sound - 1) % len(self.sounds)
audio.unload_sound(self.curr_sound)
self.load_sound()
self.move_sideways.start()
self.fade_sideways.start()
self.text.unload()
self.text = self.text_2
self.text_2 = OutlinedText(self.sounds[self.selected_sound], 50, ray.WHITE)
self.direction = -1
if self.selected_sound == len(self.sounds):
return
audio.play_sound(self.curr_sound, 'hitsound')
def move_right(self):
if self.move.is_started and not self.move.is_finished:
return
self.selected_sound = (self.selected_sound + 1) % len(self.sounds)
audio.unload_sound(self.curr_sound)
self.load_sound()
self.move_sideways.start()
self.fade_sideways.start()
self.text.unload()
self.text = self.text_2
self.text_2 = OutlinedText(self.sounds[self.selected_sound], 50, ray.WHITE)
self.direction = 1
if self.selected_sound == len(self.sounds):
return
audio.play_sound(self.curr_sound, 'hitsound')
def confirm(self):
if self.move.is_started and not self.move.is_finished:
return
if self.selected_sound == len(self.sounds):
global_data.hit_sound[self.player_num] = -1
else:
global_data.hit_sound[self.player_num] = self.selected_sound
self.is_confirmed = True
self.move.restart()
def update(self, current_ms):
self.move.update(current_ms)
self.blue_arrow_fade.update(current_ms)
self.blue_arrow_move.update(current_ms)
self.move_sideways.update(current_ms)
self.fade_sideways.update(current_ms)
self.is_finished = self.move.is_finished and self.is_confirmed
def draw(self):
if self.is_confirmed:
y = -370 + self.move.attribute
else:
y = -self.move.attribute
x = (int(self.player_num) - 1) * 800
tex.draw_texture('neiro', 'background', x=x, y=y)
tex.draw_texture('neiro', f'{self.player_num}p', x=x, y=y)
tex.draw_texture('neiro', 'divisor', x=x, y=y)
tex.draw_texture('neiro', 'music_note', y=y, x=x+(self.move_sideways.attribute*self.direction), fade=self.fade_sideways.attribute)
tex.draw_texture('neiro', 'music_note', y=y, x=x+(self.direction*-100) + (self.move_sideways.attribute*self.direction), fade=1 - self.fade_sideways.attribute)
tex.draw_texture('neiro', 'blue_arrow', y=y, x=x-self.blue_arrow_move.attribute, fade=self.blue_arrow_fade.attribute)
tex.draw_texture('neiro', 'blue_arrow', y=y, x=x+200 + self.blue_arrow_move.attribute, mirror='horizontal', fade=self.blue_arrow_fade.attribute)
counter = str(self.selected_sound+1)
total_width = len(counter) * 20
for i in range(len(counter)):
tex.draw_texture('neiro', 'counter', frame=int(counter[i]), x=x-(total_width // 2) + (i * 20), y=y)
counter = str(len(self.sounds))
total_width = len(counter) * 20
for i in range(len(counter)):
tex.draw_texture('neiro', 'counter', frame=int(counter[i]), x=x-(total_width // 2) + (i * 20) + 60, y=y)
self.text.draw(outline_color=ray.BLACK, x=x+235 - (self.text.texture.width//2) + (self.move_sideways.attribute*self.direction), y=y+1000, color=ray.fade(ray.WHITE, self.fade_sideways.attribute))
self.text_2.draw(outline_color=ray.BLACK, x=x+(self.direction*-100) + 235 - (self.text_2.texture.width//2) + (self.move_sideways.attribute*self.direction), y=y+1000, color=ray.fade(ray.WHITE, 1 - self.fade_sideways.attribute))
class ModifierSelector:
"""The menu for selecting the game modifiers."""
TEX_MAP = {
"auto": "mod_auto",
"speed": "mod_baisaku",
"display": "mod_doron",
"inverse": "mod_abekobe",
"random": "mod_kimagure"
}
NAME_MAP = {
"auto": "オート",
"speed": "はやさ",
"display": "ドロン",
"inverse": "あべこべ",
"random": "ランダム"
}
def __init__(self, player_num: PlayerNum):
self.player_num = player_num
self.mods = fields(Modifiers)
self.current_mod_index = 0
self.is_confirmed = False
self.is_finished = False
self.blue_arrow_fade = tex.get_animation(29, is_copy=True)
self.blue_arrow_move = tex.get_animation(30, is_copy=True)
self.move = tex.get_animation(28, is_copy=True)
self.move.start()
self.move_sideways = tex.get_animation(31, is_copy=True)
self.fade_sideways = tex.get_animation(32, is_copy=True)
self.direction = -1
audio.play_sound(f'voice_options_{self.player_num}p', 'sound')
self.text_name = [OutlinedText(ModifierSelector.NAME_MAP[mod.name], 30, ray.WHITE, outline_thickness=3.5) for mod in self.mods]
self.text_true = OutlinedText('する', 30, ray.WHITE, outline_thickness=3.5)
self.text_false = OutlinedText('しない', 30, ray.WHITE, outline_thickness=3.5)
self.text_speed = OutlinedText(str(global_data.modifiers[self.player_num].speed), 30, ray.WHITE, outline_thickness=3.5)
self.text_kimagure = OutlinedText('きまぐれ', 30, ray.WHITE, outline_thickness=3.5)
self.text_detarame = OutlinedText('でたらめ', 30, ray.WHITE, outline_thickness=3.5)
# Secondary text objects for animation
self.text_true_2 = OutlinedText('する', 30, ray.WHITE, outline_thickness=3.5)
self.text_false_2 = OutlinedText('しない', 30, ray.WHITE, outline_thickness=3.5)
self.text_speed_2 = OutlinedText(str(global_data.modifiers[self.player_num].speed), 30, ray.WHITE, outline_thickness=3.5)
self.text_kimagure_2 = OutlinedText('きまぐれ', 30, ray.WHITE, outline_thickness=3.5)
self.text_detarame_2 = OutlinedText('でたらめ', 30, ray.WHITE, outline_thickness=3.5)
def update(self, current_ms):
self.is_finished = self.is_confirmed and self.move.is_finished
if self.is_finished:
for text in self.text_name:
text.unload()
self.move.update(current_ms)
self.blue_arrow_fade.update(current_ms)
self.blue_arrow_move.update(current_ms)
self.move_sideways.update(current_ms)
self.fade_sideways.update(current_ms)
# Removed conditional update - now handled immediately in left/right methods
def confirm(self):
if self.is_confirmed:
return
self.current_mod_index += 1
if self.current_mod_index == len(self.mods):
self.is_confirmed = True
self.move.restart()
def _start_text_animation(self, direction, old_value):
self.move_sideways.start()
self.fade_sideways.start()
self.direction = direction
# Update primary text objects immediately
current_mod = self.mods[self.current_mod_index]
current_value = getattr(global_data.modifiers[self.player_num], current_mod.name)
if current_mod.name == 'speed':
# text_speed_2 becomes the old value (fades out)
# text_speed becomes the new value (fades in)
self.text_speed_2.unload()
self.text_speed_2 = self.text_speed
self.text_speed = OutlinedText(str(current_value), 30, ray.WHITE, outline_thickness=3.5)
elif current_mod.type is bool:
if current_value:
self.text_true.unload()
self.text_true = self.text_true_2
self.text_true_2 = OutlinedText('する', 30, ray.WHITE, outline_thickness=3.5)
else:
self.text_false.unload()
self.text_false = self.text_false_2
self.text_false_2 = OutlinedText('しない', 30, ray.WHITE, outline_thickness=3.5)
elif current_mod.name == 'random':
if current_value == 1:
self.text_kimagure.unload()
self.text_kimagure = self.text_kimagure_2
self.text_kimagure_2 = OutlinedText('きまぐれ', 30, ray.WHITE, outline_thickness=3.5)
elif current_value == 2:
self.text_detarame.unload()
self.text_detarame = self.text_detarame_2
self.text_detarame_2 = OutlinedText('でたらめ', 30, ray.WHITE, outline_thickness=3.5)
def left(self):
if self.is_confirmed:
return
current_mod = self.mods[self.current_mod_index]
current_value = getattr(global_data.modifiers[self.player_num], current_mod.name)
if current_mod.type is bool:
setattr(global_data.modifiers[self.player_num], current_mod.name, not current_value)
self._start_text_animation(-1, current_value)
elif current_mod.name == 'speed':
new_value = max(0.1, (current_value*10 - 1))/10
setattr(global_data.modifiers[self.player_num], current_mod.name, new_value)
self._start_text_animation(-1, current_value)
elif current_mod.name == 'random':
new_value = max(0, current_value-1)
setattr(global_data.modifiers[self.player_num], current_mod.name, new_value)
self._start_text_animation(-1, current_value)
def right(self):
if self.is_confirmed:
return
current_mod = self.mods[self.current_mod_index]
current_value = getattr(global_data.modifiers[self.player_num], current_mod.name)
if current_mod.type is bool:
setattr(global_data.modifiers[self.player_num], current_mod.name, not current_value)
self._start_text_animation(1, current_value)
elif current_mod.name == 'speed':
new_value = (current_value*10 + 1)/10
setattr(global_data.modifiers[self.player_num], current_mod.name, new_value)
self._start_text_animation(1, current_value)
elif current_mod.name == 'random':
new_value = (current_value+1) % 3
setattr(global_data.modifiers[self.player_num], current_mod.name, new_value)
self._start_text_animation(1, current_value)
def _draw_animated_text(self, text_primary: OutlinedText, text_secondary: OutlinedText, x: float, y: float, should_animate: bool):
if should_animate and not self.move_sideways.is_finished:
# Draw primary text moving out
text_primary.draw(outline_color=ray.BLACK, x=x + (self.move_sideways.attribute * self.direction), y=y, color=ray.fade(ray.WHITE, self.fade_sideways.attribute))
# Draw secondary text moving in
text_secondary.draw(outline_color=ray.BLACK, x=(self.direction * -100) + x + (self.move_sideways.attribute * self.direction), y=y, color=ray.fade(ray.WHITE, 1 - self.fade_sideways.attribute))
else:
# Draw static text
text_primary.draw(outline_color=ray.BLACK, x=x, y=y)
def draw(self):
if self.is_confirmed:
move = self.move.attribute - 370
else:
move = -self.move.attribute
x = (int(self.player_num) - 1) * 800
tex.draw_texture('modifier', 'top', y=move, x=x)
tex.draw_texture('modifier', f'{self.player_num}p', y=move, x=x)
tex.draw_texture('modifier', 'bottom', y=move + (len(self.mods)*50), x=x)
for i in range(len(self.mods)):
tex.draw_texture('modifier', 'background', y=move + (i*50), x=x)
if i == self.current_mod_index:
tex.draw_texture('modifier', 'mod_bg_highlight', y=move + (i*50), x=x)
else:
tex.draw_texture('modifier', 'mod_bg', y=move + (i*50), x=x)
tex.draw_texture('modifier', 'mod_box', y=move + (i*50), x=x)
self.text_name[i].draw(outline_color=ray.BLACK, x=92 + x, y=819 + move + (i*50))
current_mod = self.mods[i]
current_value = getattr(global_data.modifiers[self.player_num], current_mod.name)
is_current_mod = (i == self.current_mod_index)
if current_mod.type is bool:
if current_value:
tex.draw_texture('modifier', ModifierSelector.TEX_MAP[self.mods[i].name], y=move + (i*50), x=x)
text_x = 330 - (self.text_true.texture.width//2)
text_y = 819 + move + (i*50)
self._draw_animated_text(self.text_true, self.text_true_2, text_x + x, text_y, is_current_mod)
else:
text_x = 330 - (self.text_false.texture.width//2)
text_y = 819 + move + (i*50)
self._draw_animated_text(self.text_false, self.text_false_2, text_x + x, text_y, is_current_mod)
elif current_mod.name == 'speed':
text_x = 330 - (self.text_speed.texture.width//2)
text_y = 819 + move + (i*50)
self._draw_animated_text(self.text_speed, self.text_speed_2, text_x + x, text_y, is_current_mod)
if current_value >= 4.0:
tex.draw_texture('modifier', 'mod_yonbai', x=x, y=move + (i*50))
elif current_value >= 3.0:
tex.draw_texture('modifier', 'mod_sanbai', x=x, y=move + (i*50))
elif current_value > 1.0:
tex.draw_texture('modifier', ModifierSelector.TEX_MAP[self.mods[i].name], x=x, y=move + (i*50))
elif current_mod.name == 'random':
if current_value == 1:
text_x = 330 - (self.text_kimagure.texture.width//2)
text_y = 819 + move + (i*50)
self._draw_animated_text(self.text_kimagure, self.text_kimagure_2, text_x + x, text_y, is_current_mod)
tex.draw_texture('modifier', ModifierSelector.TEX_MAP[self.mods[i].name], x=x, y=move + (i*50))
elif current_value == 2:
text_x = 330 - (self.text_detarame.texture.width//2)
text_y = 819 + move + (i*50)
self._draw_animated_text(self.text_detarame, self.text_detarame_2, text_x + x, text_y, is_current_mod)
tex.draw_texture('modifier', 'mod_detarame', x=x, y=move + (i*50))
else:
text_x = 330 - (self.text_false.texture.width//2)
text_y = 819 + move + (i*50)
self._draw_animated_text(self.text_false, self.text_false_2, text_x + x, text_y, is_current_mod)
if i == self.current_mod_index:
tex.draw_texture('modifier', 'blue_arrow', y=move + (i*50), x=x-self.blue_arrow_move.attribute, fade=self.blue_arrow_fade.attribute)
tex.draw_texture('modifier', 'blue_arrow', y=move + (i*50), x=x+110 + self.blue_arrow_move.attribute, mirror='horizontal', fade=self.blue_arrow_fade.attribute)
class DanTransition:
def __init__(self):
self.slide_in = tex.get_animation(38)
self.is_finished = False
self.is_started = False
def start(self):
self.slide_in.start()
self.is_started = True
def update(self, current_time_ms: float):
self.slide_in.update(current_time_ms)
if self.slide_in.is_finished:
self.is_finished = True
def draw(self):
tex.draw_texture('dan_transition', 'background', x2=self.slide_in.attribute)