mirror of
https://github.com/Yonokid/PyTaiko.git
synced 2026-02-04 11:40:13 +01:00
improve results screen, fix many bugs
This commit is contained in:
14
README.md
14
README.md
@@ -11,20 +11,15 @@ Download for OS of choice on releases page
|
||||
How to run:
|
||||
Windows:
|
||||
```bash
|
||||
PyTaiko.exe {"Song Name"} {difficulty number 0-4}
|
||||
PyTaiko.exe
|
||||
```
|
||||
MacOS/Linux:
|
||||
```bash
|
||||
PyTaiko.bin {"Song Name"} {difficulty number 0-4}
|
||||
```
|
||||
PyTaiko.bin
|
||||
|
||||
## Roadmap
|
||||
|
||||
- Add Kusudama notes
|
||||
|
||||
- add basic song select
|
||||
|
||||
- add basic results screen
|
||||
https://linear.app/pytaiko
|
||||
|
||||
|
||||
## Known Issues
|
||||
@@ -64,7 +59,7 @@ Start the game
|
||||
|
||||
#### Keybinds?
|
||||
|
||||
EFJI
|
||||
see config.toml
|
||||
|
||||
#### Why does it look like Gen 3?
|
||||
|
||||
@@ -74,4 +69,3 @@ I like it
|
||||
## Contributing
|
||||
|
||||
I would highly suggest not contributing to this until sizeable progress has been made. The fabric of this code is ever changing and anything you write could disappear at any time
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ right_don = ['J','D']
|
||||
right_kat = ['I','U']
|
||||
|
||||
[audio]
|
||||
device_type = 'WASAPI'
|
||||
device_type = 'ASIO'
|
||||
asio_buffer = 6
|
||||
|
||||
[video]
|
||||
|
||||
115
libs/tja.py
115
libs/tja.py
@@ -65,8 +65,8 @@ class TJAParser:
|
||||
|
||||
def get_metadata(self):
|
||||
self._file_to_data()
|
||||
diff_index = 1
|
||||
highest_diff = -1
|
||||
current_diff = None # Track which difficulty we're currently processing
|
||||
|
||||
for item in self.data:
|
||||
if item[0] == '#':
|
||||
continue
|
||||
@@ -87,58 +87,88 @@ class TJAParser:
|
||||
elif 'DEMOSTART' in item:
|
||||
self.demo_start = float(item.split(':')[1])
|
||||
elif 'COURSE' in item:
|
||||
course = str(item.split(':')[1]).lower()
|
||||
# Determine which difficulty we're now processing
|
||||
course = str(item.split(':')[1]).lower().strip()
|
||||
|
||||
# Map the course string to its corresponding index
|
||||
if course == 'dan' or course == '6':
|
||||
current_diff = 6
|
||||
self.course_data[6] = []
|
||||
if course == 'tower' or course == '5':
|
||||
elif course == 'tower' or course == '5':
|
||||
current_diff = 5
|
||||
self.course_data[5] = []
|
||||
elif course == 'edit' or course == '4':
|
||||
current_diff = 4
|
||||
self.course_data[4] = []
|
||||
elif course == 'oni' or course == '3':
|
||||
current_diff = 3
|
||||
self.course_data[3] = []
|
||||
elif course == 'hard' or course == '2':
|
||||
current_diff = 2
|
||||
self.course_data[2] = []
|
||||
elif course == 'normal' or course == '1':
|
||||
current_diff = 1
|
||||
self.course_data[1] = []
|
||||
elif course == 'easy' or course == '0':
|
||||
current_diff = 0
|
||||
self.course_data[0] = []
|
||||
highest_diff = max(self.course_data)
|
||||
diff_index -= 1
|
||||
elif 'LEVEL' in item:
|
||||
item = int(item.split(':')[1])
|
||||
self.course_data[diff_index+highest_diff].append(item)
|
||||
elif 'BALLOON' in item:
|
||||
item = item.split(':')[1]
|
||||
if item == '':
|
||||
continue
|
||||
self.course_data[diff_index+highest_diff].append([int(x) for x in item.split(',')])
|
||||
elif 'SCOREINIT' in item:
|
||||
if item.split(':')[1] == '':
|
||||
continue
|
||||
item = item.split(':')[1]
|
||||
self.course_data[diff_index+highest_diff].append([int(x) for x in item.split(',')])
|
||||
elif 'SCOREDIFF' in item:
|
||||
if item.split(':')[1] == '':
|
||||
continue
|
||||
item = int(item.split(':')[1])
|
||||
self.course_data[diff_index+highest_diff].append(item)
|
||||
|
||||
# Only process these items if we have a current difficulty
|
||||
elif current_diff is not None:
|
||||
if 'LEVEL' in item:
|
||||
level = int(item.split(':')[1])
|
||||
self.course_data[current_diff].append(level)
|
||||
elif 'BALLOON' in item:
|
||||
balloon_data = item.split(':')[1]
|
||||
if balloon_data == '':
|
||||
continue
|
||||
self.course_data[current_diff].append([int(x) for x in balloon_data.split(',')])
|
||||
elif 'SCOREINIT' in item:
|
||||
score_init = item.split(':')[1]
|
||||
if score_init == '':
|
||||
continue
|
||||
self.course_data[current_diff].append([int(x) for x in score_init.split(',')])
|
||||
elif 'SCOREDIFF' in item:
|
||||
score_diff = item.split(':')[1]
|
||||
if score_diff == '':
|
||||
continue
|
||||
self.course_data[current_diff].append(int(score_diff))
|
||||
|
||||
return [self.title, self.title_ja, self.subtitle, self.subtitle_ja,
|
||||
self.bpm, self.wave, self.offset, self.demo_start, self.course_data]
|
||||
self.bpm, self.wave, self.offset, self.demo_start, self.course_data]
|
||||
|
||||
def data_to_notes(self, diff):
|
||||
self._file_to_data()
|
||||
#Get notes start and end
|
||||
note_start = -1
|
||||
note_end = -1
|
||||
diff_count = 0
|
||||
for i in range(len(self.data)):
|
||||
if self.data[i] == '#START':
|
||||
note_start = i+1
|
||||
elif self.data[i] == '#END':
|
||||
note_end = i
|
||||
diff_count += 1
|
||||
if diff_count == len(self.course_data) - diff:
|
||||
break
|
||||
target_found = False
|
||||
diffs = {0: "easy", 1: "normal", 2: "hard", 3: "oni", 4: "edit", 5: "tower", 6: "dan"}
|
||||
# Get the name corresponding to this difficulty number
|
||||
diff_name = diffs.get(diff, "").lower()
|
||||
|
||||
i = 0
|
||||
while i < len(self.data):
|
||||
line = self.data[i]
|
||||
|
||||
# Check if this is the start of a difficulty section
|
||||
if line.startswith("COURSE:"):
|
||||
course_value = line[7:].strip().lower()
|
||||
|
||||
# Match either the exact number or the name
|
||||
if (course_value.isdigit() and int(course_value) == diff) or course_value == diff_name:
|
||||
target_found = True
|
||||
else:
|
||||
target_found = False
|
||||
|
||||
# If we found our target section, look for START and END markers
|
||||
if target_found:
|
||||
if line == "#START":
|
||||
note_start = i + 1
|
||||
elif line == "#END" and note_start != -1:
|
||||
note_end = i
|
||||
break # We found our complete section
|
||||
|
||||
i += 1
|
||||
|
||||
notes = []
|
||||
bar = []
|
||||
@@ -148,19 +178,18 @@ class TJAParser:
|
||||
if line.startswith("#"):
|
||||
bar.append(line)
|
||||
else:
|
||||
item = line.strip(',')
|
||||
if item == '':
|
||||
if bar == []:
|
||||
bar.append(item)
|
||||
else:
|
||||
notes.append(bar)
|
||||
bar = []
|
||||
continue
|
||||
if line == ',':
|
||||
if len(bar) == 0 or all(item.startswith('#') for item in bar):
|
||||
bar.append('')
|
||||
notes.append(bar)
|
||||
bar = []
|
||||
else:
|
||||
item = line.strip(',')
|
||||
bar.append(item)
|
||||
if item != line:
|
||||
notes.append(bar)
|
||||
bar = []
|
||||
print(self.course_data)
|
||||
if len(self.course_data[diff]) < 2:
|
||||
return notes, None
|
||||
return notes, self.course_data[diff][1]
|
||||
|
||||
@@ -97,7 +97,6 @@ def get_config() -> dict[str, Any]:
|
||||
@dataclass
|
||||
class GlobalData:
|
||||
videos_cleared = False
|
||||
start_song: bool = False
|
||||
selected_song: str = '' #Path
|
||||
selected_difficulty: int = -1
|
||||
song_title: str = ''
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
raylib==5.0.0.3
|
||||
raylib_dynamic==5.0.0.3
|
||||
opencv-python==4.10.0.84
|
||||
numpy==2.2.5
|
||||
opencv_python==4.11.0.86
|
||||
pydub==0.25.1
|
||||
pyray==0.1.0
|
||||
raylib==5.5.0.2
|
||||
raylib_dynamic==5.5.0.2
|
||||
scipy==1.15.2
|
||||
sounddevice==0.5.1
|
||||
|
||||
3
requirements.txt.bak
Normal file
3
requirements.txt.bak
Normal file
@@ -0,0 +1,3 @@
|
||||
raylib==5.0.0.3
|
||||
raylib_dynamic==5.0.0.3
|
||||
opencv-python==4.10.0.84
|
||||
@@ -10,9 +10,20 @@ class EntryScreen:
|
||||
|
||||
self.texture_footer = load_texture_from_zip('Graphics\\lumendata\\entry.zip', 'entry_img00375.png')
|
||||
|
||||
self.screen_init = False
|
||||
|
||||
def on_screen_start(self):
|
||||
if not self.screen_init:
|
||||
self.screen_init = True
|
||||
|
||||
def on_screen_end(self):
|
||||
self.screen_init = False
|
||||
return "SONG_SELECT"
|
||||
|
||||
def update(self):
|
||||
self.on_screen_start()
|
||||
if ray.is_key_pressed(ray.KeyboardKey.KEY_ENTER):
|
||||
return "SONG_SELECT"
|
||||
return self.on_screen_end()
|
||||
|
||||
def draw(self):
|
||||
ray.draw_texture(self.texture_footer, 0, self.height - 151, ray.WHITE)
|
||||
|
||||
213
scenes/game.py
213
scenes/game.py
@@ -24,7 +24,12 @@ class GameScreen:
|
||||
self.height = height
|
||||
self.judge_x = 414
|
||||
self.current_ms = 0
|
||||
self.song_is_started = False
|
||||
|
||||
self.result_transition = None
|
||||
|
||||
self.song_info = None
|
||||
|
||||
self.screen_init = False
|
||||
|
||||
def load_textures(self):
|
||||
self.textures = load_all_textures_from_zip('Graphics\\lumendata\\enso_system\\common.zip')
|
||||
@@ -130,11 +135,16 @@ class GameScreen:
|
||||
self.textures.update(load_all_textures_from_zip('Graphics\\lumendata\\enso_system\\base1p.zip'))
|
||||
self.textures.update(load_all_textures_from_zip('Graphics\\lumendata\\enso_system\\don1p.zip'))
|
||||
|
||||
self.result_transition_1 = load_texture_from_zip('Graphics\\lumendata\\enso_result.zip', 'retry_game_img00125.png')
|
||||
self.result_transition_2 = load_texture_from_zip('Graphics\\lumendata\\enso_result.zip', 'retry_game_img00126.png')
|
||||
|
||||
def load_sounds(self):
|
||||
self.sound_don = audio.load_sound('Sounds\\inst_00_don.wav')
|
||||
self.sound_kat = audio.load_sound('Sounds\\inst_00_katsu.wav')
|
||||
self.sound_balloon_pop = audio.load_sound('Sounds\\balloon_pop.wav')
|
||||
|
||||
self.sound_result_transition = audio.load_sound('Sounds\\result\\VO_RESULT [1].ogg')
|
||||
|
||||
def init_tja(self, song: str, difficulty: int):
|
||||
self.load_textures()
|
||||
self.load_sounds()
|
||||
@@ -165,24 +175,46 @@ class GameScreen:
|
||||
|
||||
audio.play_sound(self.song_music)
|
||||
|
||||
def on_screen_start(self):
|
||||
if not self.screen_init:
|
||||
self.screen_init = True
|
||||
self.init_tja(global_data.selected_song, global_data.selected_difficulty)
|
||||
self.current_ms = get_current_ms() - self.start_ms
|
||||
self.song_info = SongInfo(self.current_ms, self.tja.title, 'TEST')
|
||||
self.result_transition = None
|
||||
|
||||
def on_screen_end(self):
|
||||
self.screen_init = False
|
||||
for zip in self.textures:
|
||||
for texture in self.textures[zip]:
|
||||
ray.unload_texture(texture)
|
||||
return 'RESULT'
|
||||
|
||||
def update(self):
|
||||
if global_data.start_song and not self.song_is_started:
|
||||
self.init_tja(global_data.selected_song, global_data.selected_difficulty)
|
||||
self.song_is_started = True
|
||||
|
||||
self.on_screen_start()
|
||||
|
||||
self.current_ms = get_current_ms() - self.start_ms
|
||||
self.player_1.update(self)
|
||||
|
||||
if len(self.player_1.play_note_list) == 0 and not audio.is_sound_playing(self.song_music):
|
||||
self.player_1.update(self)
|
||||
if self.song_info is not None:
|
||||
self.song_info.update(self.current_ms)
|
||||
|
||||
if self.result_transition is not None:
|
||||
self.result_transition.update(self.current_ms)
|
||||
if self.result_transition.is_finished:
|
||||
return self.on_screen_end()
|
||||
elif len(self.player_1.play_note_list) == 0 and (len(self.player_1.current_notes) == 0) and not audio.is_sound_playing(self.song_music):
|
||||
global_data.result_good, global_data.result_ok, global_data.result_bad, global_data.result_score = self.player_1.get_result_score()
|
||||
global_data.start_song = False
|
||||
self.song_is_started = False
|
||||
return 'RESULT'
|
||||
self.result_transition = ResultTransition(self.current_ms, self.height)
|
||||
audio.play_sound(self.sound_result_transition)
|
||||
|
||||
def draw(self):
|
||||
self.player_1.draw(self)
|
||||
if self.song_info is not None:
|
||||
self.song_info.draw(self)
|
||||
if self.result_transition is not None:
|
||||
self.result_transition.draw(self.width, self.height, self.result_transition_1, self.result_transition_2)
|
||||
|
||||
|
||||
class Player:
|
||||
def __init__(self, game_screen: GameScreen, player_number: int, difficulty: int, judge_offset: int):
|
||||
@@ -218,6 +250,7 @@ class Player:
|
||||
self.combo = 0
|
||||
self.score = 0
|
||||
self.max_combo = 0
|
||||
self.total_drumroll = 0
|
||||
|
||||
self.arc_points = 25
|
||||
|
||||
@@ -225,12 +258,11 @@ class Player:
|
||||
self.draw_effect_list: list[LaneHitEffect] = []
|
||||
self.draw_arc_list: list[NoteArc] = []
|
||||
self.draw_drum_hit_list: list[DrumHitEffect] = []
|
||||
self.drumroll_counter: list[DrumrollCounter] = []
|
||||
self.drumroll_counter: DrumrollCounter | None = None
|
||||
self.balloon_list: list[BalloonAnimation] = []
|
||||
self.base_score_list: list[ScoreCounterAnimation] = []
|
||||
self.combo_display = Combo(self.combo, game_screen.current_ms)
|
||||
self.score_counter = ScoreCounter(self.score, game_screen.current_ms)
|
||||
self.song_info = SongInfo(game_screen.current_ms, game_screen.tja.title, 'TEST')
|
||||
|
||||
self.input_log: dict[float, str] = dict()
|
||||
|
||||
@@ -304,7 +336,6 @@ class Player:
|
||||
else:
|
||||
bisect.insort_left(self.current_notes_draw, self.draw_note_list.popleft(), key=lambda x: x['index'])
|
||||
|
||||
#If a note is off screen, remove it
|
||||
if len(self.current_notes_draw) == 0:
|
||||
return
|
||||
|
||||
@@ -316,8 +347,6 @@ class Player:
|
||||
note = self.current_notes_draw[1]
|
||||
position = self.get_position(game_screen, note['ms'], note['ppf'])
|
||||
if position < game_screen.judge_x + 650:
|
||||
if note['note'] == '8':
|
||||
self.current_notes_draw.pop(0)
|
||||
self.current_notes_draw.pop(0)
|
||||
|
||||
def note_manager(self, game_screen: GameScreen):
|
||||
@@ -347,6 +376,7 @@ class Player:
|
||||
note_type = game_screen.note_type_dict[str(int(drum_type)+self.drumroll_big)]
|
||||
self.draw_arc_list.append(NoteArc(note_type, game_screen.current_ms, self.player_number))
|
||||
self.curr_drumroll_count += 1
|
||||
self.total_drumroll += 1
|
||||
self.score += 100
|
||||
self.base_score_list.append(ScoreCounterAnimation(game_screen.current_ms, 100))
|
||||
color = max(0, 255 - (self.curr_drumroll_count * 10))
|
||||
@@ -359,6 +389,7 @@ class Player:
|
||||
if len(self.balloon_list) < 1:
|
||||
self.balloon_list.append(BalloonAnimation(game_screen.current_ms, current_note['balloon']))
|
||||
self.curr_balloon_count += 1
|
||||
self.total_drumroll += 1
|
||||
self.score += 100
|
||||
self.base_score_list.append(ScoreCounterAnimation(game_screen.current_ms, 100))
|
||||
self.current_notes_draw[0]['popped'] = False
|
||||
@@ -367,8 +398,6 @@ class Player:
|
||||
self.current_notes_draw[0]['popped'] = True
|
||||
audio.play_sound(game_screen.sound_balloon_pop)
|
||||
self.note_correct(game_screen, self.current_notes[0])
|
||||
if len(self.current_notes) > 1 and self.current_notes[0]['note'] == '8':
|
||||
self.note_correct(game_screen, self.current_notes[0])
|
||||
|
||||
def check_note(self, game_screen: GameScreen, drum_type: str):
|
||||
if self.is_drumroll:
|
||||
@@ -421,17 +450,14 @@ class Player:
|
||||
self.current_notes.popleft()
|
||||
|
||||
def drumroll_counter_manager(self, game_screen: GameScreen):
|
||||
if self.is_drumroll and self.curr_drumroll_count > 0:
|
||||
if len(self.drumroll_counter) == 0 or self.drumroll_counter[-1].is_finished:
|
||||
self.drumroll_counter.append(DrumrollCounter(game_screen.current_ms))
|
||||
if self.is_drumroll and self.curr_drumroll_count > 0 and self.drumroll_counter is None:
|
||||
self.drumroll_counter = DrumrollCounter(game_screen.current_ms)
|
||||
|
||||
if len(self.drumroll_counter) <= 0:
|
||||
return
|
||||
|
||||
if self.drumroll_counter[0].is_finished and not self.is_drumroll:
|
||||
self.drumroll_counter.pop(0)
|
||||
else:
|
||||
self.drumroll_counter[0].update(game_screen, game_screen.current_ms, self.curr_drumroll_count)
|
||||
if self.drumroll_counter is not None:
|
||||
if self.drumroll_counter.is_finished and not self.is_drumroll:
|
||||
self.drumroll_counter = None
|
||||
else:
|
||||
self.drumroll_counter.update(game_screen, game_screen.current_ms, self.curr_drumroll_count)
|
||||
|
||||
def balloon_animation_manager(self, game_screen: GameScreen):
|
||||
if len(self.balloon_list) <= 0:
|
||||
@@ -489,7 +515,6 @@ class Player:
|
||||
self.animation_manager(game_screen, self.draw_drum_hit_list)
|
||||
self.animation_manager(game_screen, self.draw_arc_list)
|
||||
self.animation_manager(game_screen, self.base_score_list)
|
||||
self.song_info.update(game_screen.current_ms)
|
||||
self.score_counter.update(game_screen.current_ms, self.score)
|
||||
self.key_manager(game_screen)
|
||||
|
||||
@@ -499,10 +524,9 @@ class Player:
|
||||
|
||||
def draw_drumroll(self, game_screen: GameScreen, big: bool, position: int, index: int, color: int):
|
||||
drumroll_start_position = position
|
||||
i = 1
|
||||
while self.current_notes_draw[index+i]['note'] != '8':
|
||||
i += 1
|
||||
tail = self.current_notes_draw[index+i]
|
||||
tail = next((note for note in self.current_notes_draw[index+1:] if note['note'] == '8'), None)
|
||||
if tail is None:
|
||||
return
|
||||
if big:
|
||||
drumroll_body = 'dai_drumroll_body'
|
||||
drumroll_tail = 'dai_drumroll_tail'
|
||||
@@ -515,17 +539,16 @@ class Player:
|
||||
self.draw_note(game_screen, drumroll_tail, drumroll_end_position, color, 10, drumroll_length=None)
|
||||
|
||||
def draw_balloon(self, game_screen: GameScreen, note: dict, position: int, index: int):
|
||||
if self.current_notes_draw[0].get('popped', None):
|
||||
tail = next((note for note in self.current_notes_draw[index+1:] if note['note'] == '8'), None)
|
||||
if tail is None:
|
||||
return
|
||||
i = 1
|
||||
while self.current_notes_draw[index+i]['note'] != '8':
|
||||
i += 1
|
||||
end_time = self.current_notes_draw[index+i]
|
||||
end_time_position = self.get_position(game_screen, end_time['load_ms'], end_time['ppf'])
|
||||
if game_screen.current_ms >= end_time['ms']:
|
||||
position = end_time_position
|
||||
tail_position = self.get_position(game_screen, tail['load_ms'], tail['ppf'])
|
||||
if game_screen.current_ms >= tail['ms']:
|
||||
position = tail_position
|
||||
elif game_screen.current_ms >= note['ms']:
|
||||
position = 349
|
||||
if note.get('popped', None):
|
||||
return
|
||||
self.draw_note(game_screen, '7', position, 255, 9)
|
||||
|
||||
def draw_note(self, game_screen: GameScreen, note: str, position: int, color: int, se_note: int | None, drumroll_length: int | None=None):
|
||||
@@ -587,8 +610,6 @@ class Player:
|
||||
note = self.current_notes_draw[i]
|
||||
note_type, load_ms, pixels_per_frame = note['note'], note['load_ms'], note['ppf']
|
||||
position = self.get_position(game_screen, load_ms, pixels_per_frame)
|
||||
if 'popped' in note:
|
||||
continue
|
||||
if note_type == '5':
|
||||
self.draw_drumroll(game_screen, False, position, i, note['color'])
|
||||
elif note_type == '6':
|
||||
@@ -597,7 +618,7 @@ class Player:
|
||||
self.draw_balloon(game_screen, note, position, i)
|
||||
else:
|
||||
self.draw_note(game_screen, note_type, position, 255, note['se_note'])
|
||||
#ray.draw_text(str(i), position+64, 192, 25, ray.GREEN)
|
||||
ray.draw_text(str(i), position+64, 192, 25, ray.GREEN)
|
||||
|
||||
def draw_gauge(self, game_screen: GameScreen):
|
||||
ray.draw_texture(game_screen.textures['gage_don_1p_hard'][0], 327, 128, ray.WHITE)
|
||||
@@ -621,8 +642,8 @@ class Player:
|
||||
ray.draw_texture(game_screen.textures['lane_obi'][3], 0, 184, ray.WHITE)
|
||||
ray.draw_texture(game_screen.textures['lane_obi'][19], 0, 225, ray.WHITE)
|
||||
ray.draw_texture(game_screen.texture_difficulty[self.difficulty], 50, 222, ray.WHITE)
|
||||
self.song_info.draw(game_screen)
|
||||
self.draw_animation_list(game_screen, self.drumroll_counter)
|
||||
if self.drumroll_counter is not None:
|
||||
self.drumroll_counter.draw(game_screen)
|
||||
self.draw_animation_list(game_screen, self.draw_arc_list)
|
||||
self.draw_animation_list(game_screen, self.balloon_list)
|
||||
self.score_counter.draw(game_screen)
|
||||
@@ -787,6 +808,7 @@ class DrumrollCounter:
|
||||
|
||||
def update_count(self, current_ms: float, count: int, elapsed_time: float):
|
||||
self.total_duration = elapsed_time + 1349
|
||||
self.fade_animation.params['delay'] = self.total_duration - 166
|
||||
if self.drumroll_count != count:
|
||||
self.drumroll_count = count
|
||||
self.stretch_animation = Animation(current_ms, 50, 'text_stretch')
|
||||
@@ -1028,40 +1050,93 @@ class ScoreCounterAnimation:
|
||||
ray.draw_texture_pro(game_screen.textures['score_add_1p'][int(counter[i])], source_rect, dest_rect, ray.Vector2(0,0), 0, self.color)
|
||||
|
||||
class SongInfo:
|
||||
FADE_DURATION = 366
|
||||
DISPLAY_DURATION = 1666
|
||||
|
||||
def __init__(self, current_ms: float, song_name: str, genre: str):
|
||||
self.fade_in = Animation(current_ms, 366, 'fade')
|
||||
self.fade_in.params['initial_opacity'] = 0.0
|
||||
self.fade_in.params['final_opacity'] = 1.0
|
||||
self.fade_in = self._create_fade_in_animation(current_ms)
|
||||
self.fade_out = self._create_fade_out_animation(current_ms)
|
||||
|
||||
self.fade_out = Animation(current_ms, 366, 'fade')
|
||||
self.fade_out.params['initial_opacity'] = 1.0
|
||||
self.fade_out.params['final_opacity'] = 0.0
|
||||
self.fade_out.params['delay'] = 1666 + self.fade_in.duration
|
||||
self.song_name = song_name
|
||||
self.genre = genre
|
||||
|
||||
self.is_finished = False
|
||||
self.song_num_fade = ray.WHITE
|
||||
self.song_name_fade = ray.WHITE
|
||||
self.font = self._load_font_for_text(song_name)
|
||||
self.song_title = OutlinedText(
|
||||
self.font, song_name, 40, ray.WHITE, ray.BLACK, outline_thickness=5
|
||||
)
|
||||
|
||||
def _create_fade_in_animation(self, start_ms: float) -> Animation:
|
||||
fade_in = Animation(start_ms, self.FADE_DURATION, 'fade')
|
||||
fade_in.params['initial_opacity'] = 0.0
|
||||
fade_in.params['final_opacity'] = 1.0
|
||||
return fade_in
|
||||
|
||||
def _create_fade_out_animation(self, start_ms: float) -> Animation:
|
||||
fade_out = Animation(start_ms, self.FADE_DURATION, 'fade')
|
||||
fade_out.params['initial_opacity'] = 1.0
|
||||
fade_out.params['final_opacity'] = 0.0
|
||||
fade_out.params['delay'] = self.DISPLAY_DURATION + self.FADE_DURATION
|
||||
return fade_out
|
||||
|
||||
def _load_font_for_text(self, text: str) -> ray.Font:
|
||||
codepoint_count = ray.ffi.new('int *', 0)
|
||||
codepoints_no_dup = set()
|
||||
codepoints_no_dup.update(song_name)
|
||||
codepoints = ray.load_codepoints(''.join(codepoints_no_dup), codepoint_count)
|
||||
self.font = ray.load_font_ex('Graphics\\Modified-DFPKanteiryu-XB.ttf', 32, codepoints, 0)
|
||||
self.song_title = OutlinedText(self.font, song_name, 40, ray.WHITE, ray.BLACK, outline_thickness=5)
|
||||
unique_codepoints = set(text)
|
||||
codepoints = ray.load_codepoints(''.join(unique_codepoints), codepoint_count)
|
||||
return ray.load_font_ex('Graphics\\Modified-DFPKanteiryu-XB.ttf', 32, codepoints, 0)
|
||||
|
||||
def update(self, current_ms: float):
|
||||
self.fade_in.update(current_ms)
|
||||
self.fade_out.update(current_ms)
|
||||
self.song_num_fade = ray.fade(ray.WHITE, self.fade_in.attribute)
|
||||
self.song_name_fade = ray.fade(ray.WHITE, 1 - self.fade_in.attribute)
|
||||
if self.fade_in.is_finished:
|
||||
|
||||
if not self.fade_in.is_finished:
|
||||
self.song_num_fade = ray.fade(ray.WHITE, self.fade_in.attribute)
|
||||
self.song_name_fade = ray.fade(ray.WHITE, 1 - self.fade_in.attribute)
|
||||
else:
|
||||
self.song_num_fade = ray.fade(ray.WHITE, self.fade_out.attribute)
|
||||
self.song_name_fade = ray.fade(ray.WHITE, 1 - self.fade_out.attribute)
|
||||
|
||||
if self.fade_out.is_finished:
|
||||
self.fade_in.start_ms = current_ms + 1666
|
||||
self.fade_out.start_ms = current_ms + 1666
|
||||
self.fade_in.is_finished = False
|
||||
self.fade_out.is_finished = False
|
||||
self._reset_animations(current_ms)
|
||||
|
||||
def _reset_animations(self, current_ms: float):
|
||||
next_cycle_start = current_ms + self.DISPLAY_DURATION
|
||||
self.fade_in = self._create_fade_in_animation(next_cycle_start)
|
||||
self.fade_out = self._create_fade_out_animation(next_cycle_start)
|
||||
|
||||
def draw(self, game_screen: GameScreen):
|
||||
ray.draw_texture(game_screen.textures['song_info'][(global_data.songs_played % 4) + 8], 1132, 25, self.song_num_fade)
|
||||
self.song_title.draw(1252 - self.song_title.texture.width, int(50 - self.song_title.texture.height / 2), self.song_name_fade)
|
||||
song_texture_index = (global_data.songs_played % 4) + 8
|
||||
ray.draw_texture(
|
||||
game_screen.textures['song_info'][song_texture_index],
|
||||
1132, 25,
|
||||
self.song_num_fade
|
||||
)
|
||||
|
||||
text_x = 1252 - self.song_title.texture.width
|
||||
text_y = int(50 - self.song_title.texture.height / 2)
|
||||
self.song_title.draw(text_x, text_y, self.song_name_fade)
|
||||
|
||||
class ResultTransition:
|
||||
def __init__(self, current_ms: float, screen_height: int):
|
||||
self.move = Animation(current_ms, 983.33, 'move')
|
||||
self.move.params['start_position'] = 0.0
|
||||
self.move.params['total_distance'] = screen_height//2
|
||||
|
||||
self.is_finished = False
|
||||
|
||||
def update(self, current_ms: float):
|
||||
self.move.update(current_ms)
|
||||
|
||||
if self.move.is_finished:
|
||||
self.is_finished = True
|
||||
|
||||
def draw(self, screen_width: int, screen_height: int, texture_1: ray.Texture, texture_2: ray.Texture):
|
||||
x = 0
|
||||
while x < screen_width:
|
||||
ray.draw_texture(texture_1, x, (0 - texture_1.height) + int(self.move.attribute), ray.WHITE)
|
||||
ray.draw_texture(texture_1, x, (screen_height) - int(self.move.attribute), ray.WHITE)
|
||||
x += texture_1.width
|
||||
x = 0
|
||||
while x < screen_width:
|
||||
ray.draw_texture(texture_2, x, (0 - texture_2.height) + int(self.move.attribute / ((screen_height//2) / (texture_2.height//2))), ray.WHITE)
|
||||
ray.draw_texture(texture_2, x, (screen_height) - int(self.move.attribute / ((screen_height//2) / (texture_2.height//2))), ray.WHITE)
|
||||
x += texture_2.width
|
||||
|
||||
110
scenes/result.py
110
scenes/result.py
@@ -1,11 +1,12 @@
|
||||
import pyray as ray
|
||||
|
||||
from libs.animation import Animation
|
||||
from libs.audio import audio
|
||||
from libs.utils import (
|
||||
OutlinedText,
|
||||
get_current_ms,
|
||||
global_data,
|
||||
load_all_textures_from_zip,
|
||||
load_image_from_zip,
|
||||
)
|
||||
|
||||
|
||||
@@ -17,31 +18,61 @@ def draw_scaled_texture(texture, x: int, y: int, scale: float, color: ray.Color)
|
||||
ray.draw_texture_pro(texture, src_rect, dst_rect, ray.Vector2(0, 0), 0, color)
|
||||
|
||||
class ResultScreen:
|
||||
def __init__(self, width, height):
|
||||
def __init__(self, width: int, height: int):
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.sound_don = audio.load_sound('Sounds\\inst_00_don.wav')
|
||||
self.bgm = audio.load_sound('Sounds\\result\\JINGLE_SEISEKI [1].ogg')
|
||||
|
||||
zip_file = 'Graphics\\lumendata\\enso_result.zip'
|
||||
self.textures = load_all_textures_from_zip(zip_file)
|
||||
|
||||
ray.unload_texture(self.textures['result'][327])
|
||||
image = load_image_from_zip(zip_file, 'result_img00327.png')
|
||||
ray.image_resize(image, 1280, 144)
|
||||
self.textures['result'][327] = ray.load_texture_from_image(image)
|
||||
|
||||
self.text_generated = False
|
||||
self.song_info = FontText(global_data.song_title, 40).texture
|
||||
self.screen_init = False
|
||||
|
||||
self.fade_in = None
|
||||
|
||||
self.bgm_volume = 1.0
|
||||
|
||||
def on_screen_start(self):
|
||||
if not self.screen_init:
|
||||
self.textures = load_all_textures_from_zip('Graphics\\lumendata\\enso_result.zip')
|
||||
self.screen_init = True
|
||||
self.song_info = FontText(global_data.song_title, 40).texture
|
||||
self.bgm_volume = 1.0
|
||||
audio.play_sound(self.bgm)
|
||||
self.fade_in = FadeIn(get_current_ms())
|
||||
|
||||
def on_screen_end(self):
|
||||
self.screen_init = False
|
||||
global_data.songs_played += 1
|
||||
audio.play_sound(self.sound_don)
|
||||
for zip in self.textures:
|
||||
for texture in self.textures[zip]:
|
||||
ray.unload_texture(texture)
|
||||
audio.stop_sound(self.bgm)
|
||||
return "SONG_SELECT"
|
||||
|
||||
def update(self):
|
||||
self.on_screen_start()
|
||||
if ray.is_key_pressed(ray.KeyboardKey.KEY_ENTER):
|
||||
global_data.songs_played += 1
|
||||
audio.play_sound(self.sound_don)
|
||||
return "SONG_SELECT"
|
||||
return self.on_screen_end()
|
||||
|
||||
if not self.text_generated and global_data.song_title != '':
|
||||
self.song_info = FontText(global_data.song_title, 40).texture
|
||||
self.text_generated = True
|
||||
if self.fade_in is not None:
|
||||
self.fade_in.update(get_current_ms())
|
||||
|
||||
def draw_score_info(self):
|
||||
for i in range(len(str(global_data.result_good))):
|
||||
ray.draw_texture(self.textures['result'][int(str(global_data.result_good)[::-1][i]) + 136], 943-(i*24), 186, ray.WHITE)
|
||||
for i in range(len(str(global_data.result_ok))):
|
||||
ray.draw_texture(self.textures['result'][int(str(global_data.result_ok)[::-1][i]) + 136], 943-(i*24), 227, ray.WHITE)
|
||||
for i in range(len(str(global_data.result_bad))):
|
||||
ray.draw_texture(self.textures['result'][int(str(global_data.result_bad)[::-1][i]) + 136], 943-(i*24), 267, ray.WHITE)
|
||||
|
||||
def draw_total_score(self):
|
||||
ray.draw_texture(self.textures['result'][167], 554, 236, ray.WHITE)
|
||||
for i in range(len(str(global_data.result_score))):
|
||||
ray.draw_texture(self.textures['result'][int(str(global_data.result_score)[::-1][i]) + 156], 723-(i*21), 252, ray.WHITE)
|
||||
|
||||
def draw(self):
|
||||
x = 0
|
||||
@@ -49,15 +80,11 @@ class ResultScreen:
|
||||
ray.draw_texture(self.textures['result'][326], x, 0 - self.textures['result'][326].height//2, ray.WHITE)
|
||||
ray.draw_texture(self.textures['result'][326], x, self.height - self.textures['result'][326].height//2, ray.WHITE)
|
||||
x += self.textures['result'][326].width
|
||||
ray.draw_texture(self.textures['result'][327], 0, 0 - self.textures['result'][327].height//2, ray.WHITE)
|
||||
ray.draw_texture(self.textures['result'][327], 0, self.height - self.textures['result'][327].height + self.textures['result'][327].height//2, ray.WHITE)
|
||||
|
||||
|
||||
ray.draw_text(f"{global_data.selected_song}", 100, 60, 20, ray.BLACK)
|
||||
ray.draw_text(f"SCORE: {global_data.result_score}", 100, 80, 20, ray.BLACK)
|
||||
ray.draw_text(f"GOOD: {global_data.result_good}", 100, 100, 20, ray.BLACK)
|
||||
ray.draw_text(f"OK: {global_data.result_ok}", 100, 120, 20, ray.BLACK)
|
||||
ray.draw_text(f"BAD: {global_data.result_bad}", 100, 140, 20, ray.BLACK)
|
||||
x = 0
|
||||
while x < self.width:
|
||||
ray.draw_texture(self.textures['result'][327], x, 0 - self.textures['result'][327].height//2, ray.WHITE)
|
||||
ray.draw_texture(self.textures['result'][327], x, self.height - self.textures['result'][327].height + self.textures['result'][327].height//2, ray.WHITE)
|
||||
x += self.textures['result'][327].width
|
||||
|
||||
ray.draw_texture(self.textures['result'][330], -5, 3, ray.WHITE)
|
||||
ray.draw_texture(self.textures['result'][(global_data.songs_played % 4) + 331], 232, 4, ray.WHITE)
|
||||
@@ -71,6 +98,43 @@ class ResultScreen:
|
||||
draw_scaled_texture(self.textures['result'][187], 1058, 124, (10/11), ray.WHITE)
|
||||
draw_scaled_texture(self.textures['result'][188], 1182, 115, (10/11), ray.WHITE)
|
||||
|
||||
ray.draw_texture(self.textures['result'][170], 817, 186, ray.WHITE)
|
||||
ray.draw_texture(self.textures['result'][171], 817, 227, ray.WHITE)
|
||||
ray.draw_texture(self.textures['result'][172], 817, 267, ray.WHITE)
|
||||
ray.draw_texture(self.textures['result'][173], 987, 186, ray.WHITE)
|
||||
ray.draw_texture(self.textures['result'][174], 981, 227, ray.WHITE)
|
||||
|
||||
self.draw_score_info()
|
||||
self.draw_total_score()
|
||||
|
||||
if self.fade_in is not None:
|
||||
self.fade_in.draw(self.width, self.height, self.textures['result'][326], self.textures['result'][327])
|
||||
|
||||
|
||||
class FadeIn:
|
||||
def __init__(self, current_ms: float):
|
||||
self.fadein = Animation(current_ms, 450, 'fade')
|
||||
self.fadein.params['initial_opacity'] = 1.0
|
||||
self.fadein.params['final_opacity'] = 0.0
|
||||
self.fadein.params['delay'] = 100
|
||||
self.fade = ray.fade(ray.WHITE, self.fadein.attribute)
|
||||
|
||||
def update(self, current_ms: float):
|
||||
self.fadein.update(current_ms)
|
||||
self.fade = ray.fade(ray.WHITE, self.fadein.attribute)
|
||||
|
||||
def draw(self, screen_width: int, screen_height: int, texture_1: ray.Texture, texture_2: ray.Texture):
|
||||
x = 0
|
||||
while x < screen_width:
|
||||
ray.draw_texture(texture_1, x, 0 - texture_1.height//2, self.fade)
|
||||
ray.draw_texture(texture_1, x, screen_height - texture_1.height//2, self.fade)
|
||||
x += texture_1.width
|
||||
x = 0
|
||||
while x < screen_width:
|
||||
ray.draw_texture(texture_2, x, 0 - texture_2.height//2, self.fade)
|
||||
ray.draw_texture(texture_2, x, screen_height - texture_2.height + texture_2.height//2, self.fade)
|
||||
x += texture_2.width
|
||||
|
||||
class FontText:
|
||||
def __init__(self, text, font_size):
|
||||
codepoint_count = ray.ffi.new('int *', 0)
|
||||
|
||||
@@ -3,6 +3,7 @@ import os
|
||||
import pyray as ray
|
||||
|
||||
from libs.audio import audio
|
||||
from libs.tja import TJAParser
|
||||
from libs.utils import get_config, global_data
|
||||
|
||||
|
||||
@@ -12,7 +13,7 @@ class SongSelectScreen:
|
||||
self.height = height
|
||||
self.is_song_select = True
|
||||
self.is_difficulty_select = False
|
||||
self.song_list: list[str] = []
|
||||
self.song_list: dict[str, list] = dict()
|
||||
self.selected_song = 0
|
||||
self.selected_difficulty = 0
|
||||
self.selected_index = 0
|
||||
@@ -21,7 +22,23 @@ class SongSelectScreen:
|
||||
for dirpath, dirnames, filenames in os.walk(f'{get_config()["paths"]["tja_path"]}'):
|
||||
for filename in filenames:
|
||||
if filename.endswith(".tja"):
|
||||
self.song_list.append(dirpath)
|
||||
self.song_list[dirpath] = TJAParser(dirpath).get_metadata()
|
||||
|
||||
self.screen_init = False
|
||||
|
||||
def on_screen_start(self):
|
||||
if not self.screen_init:
|
||||
self.screen_init = True
|
||||
|
||||
self.is_song_select = True
|
||||
self.is_difficulty_select = False
|
||||
|
||||
def on_screen_end(self):
|
||||
self.screen_init = False
|
||||
audio.play_sound(self.sound_don)
|
||||
global_data.selected_song = list(self.song_list.keys())[self.selected_song]
|
||||
global_data.selected_difficulty = self.selected_difficulty
|
||||
return "GAME"
|
||||
|
||||
def update_song_select(self):
|
||||
if ray.is_key_pressed(ray.KeyboardKey.KEY_ENTER):
|
||||
@@ -37,13 +54,7 @@ class SongSelectScreen:
|
||||
|
||||
def update_difficulty_select(self):
|
||||
if ray.is_key_pressed(ray.KeyboardKey.KEY_ENTER):
|
||||
audio.play_sound(self.sound_don)
|
||||
global_data.selected_song = self.song_list[self.selected_song]
|
||||
global_data.selected_difficulty = self.selected_difficulty
|
||||
global_data.start_song = True
|
||||
self.is_song_select = True
|
||||
self.is_difficulty_select = False
|
||||
return "GAME"
|
||||
return self.on_screen_end()
|
||||
elif ray.is_key_pressed(ray.KeyboardKey.KEY_BACKSPACE):
|
||||
self.is_song_select = True
|
||||
self.is_difficulty_select = False
|
||||
@@ -55,6 +66,7 @@ class SongSelectScreen:
|
||||
self.selected_difficulty = (self.selected_difficulty + 1) % 5
|
||||
|
||||
def update(self):
|
||||
self.on_screen_start()
|
||||
if self.is_song_select:
|
||||
self.update_song_select()
|
||||
elif self.is_difficulty_select:
|
||||
@@ -62,21 +74,23 @@ class SongSelectScreen:
|
||||
|
||||
def draw_song_select(self):
|
||||
visible_songs = 36
|
||||
total_songs = len(self.song_list)
|
||||
song_paths = list(self.song_list.keys()) # Get all paths as a list
|
||||
total_songs = len(song_paths)
|
||||
start_index = max(0, self.selected_song - visible_songs // 2)
|
||||
|
||||
if start_index + visible_songs > total_songs:
|
||||
start_index = max(0, total_songs - visible_songs)
|
||||
|
||||
for i in range(visible_songs):
|
||||
song_index = (start_index + i) % total_songs
|
||||
if start_index + i < total_songs: # Ensure we don't go out of bounds
|
||||
song_index = start_index + i
|
||||
current_path = song_paths[song_index]
|
||||
# Get display text from metadata, or use the path as fallback
|
||||
display_text = self.song_list[current_path][0]
|
||||
|
||||
if song_index == self.selected_song:
|
||||
color = ray.GREEN
|
||||
else:
|
||||
color = ray.BLACK
|
||||
|
||||
ray.draw_text(self.song_list[song_index], 20, (20*i), 20, color)
|
||||
if song_index == self.selected_song:
|
||||
color = ray.GREEN
|
||||
else:
|
||||
color = ray.BLACK
|
||||
ray.draw_text(display_text, 20, (20*i), 20, color)
|
||||
|
||||
def draw_difficulty_select(self):
|
||||
difficulties = ["Easy", "Normal", "Hard", "Oni", "Ura"]
|
||||
|
||||
@@ -28,6 +28,8 @@ class TitleScreen:
|
||||
self.load_textures()
|
||||
self.warning_board = WarningScreen(get_current_ms(), self)
|
||||
|
||||
self.screen_init = False
|
||||
|
||||
def get_videos(self):
|
||||
return self.op_video, self.attract_video
|
||||
|
||||
@@ -41,6 +43,17 @@ class TitleScreen:
|
||||
|
||||
self.texture_black = load_texture_from_zip('Graphics\\lumendata\\attract\\movie.zip', 'movie_img00000.png')
|
||||
|
||||
def on_screen_start(self):
|
||||
if not self.screen_init:
|
||||
self.screen_init = True
|
||||
|
||||
def on_screen_end(self) -> str:
|
||||
for zip in self.textures:
|
||||
for texture in self.textures[zip]:
|
||||
ray.unload_texture(texture)
|
||||
self.screen_init = False
|
||||
return "ENTRY"
|
||||
|
||||
def scene_manager(self):
|
||||
if self.scene == 'Opening Video':
|
||||
self.op_video.update()
|
||||
@@ -59,9 +72,11 @@ class TitleScreen:
|
||||
self.op_video = VideoPlayer(random.choice(self.op_video_list))
|
||||
|
||||
def update(self):
|
||||
self.on_screen_start()
|
||||
|
||||
self.scene_manager()
|
||||
if ray.is_key_pressed(ray.KeyboardKey.KEY_ENTER):
|
||||
return "ENTRY"
|
||||
return self.on_screen_end()
|
||||
|
||||
def draw(self):
|
||||
if self.scene == 'Opening Video':
|
||||
|
||||
Reference in New Issue
Block a user