From deb4e84e583b254a0ce11c4ca31a9ffd72486765 Mon Sep 17 00:00:00 2001 From: Anthony Samms Date: Fri, 31 Oct 2025 14:55:46 -0400 Subject: [PATCH] add failure noise and some more TJA metadata handling --- libs/icon.png | Bin 4403 -> 7333 bytes libs/tja.py | 59 +++++++++++++++--- scenes/game_dan.py | 149 ++++++++++++++++++++++++++++++++------------- 3 files changed, 157 insertions(+), 51 deletions(-) diff --git a/libs/icon.png b/libs/icon.png index 7927eb55cce5e010c20683f9fda471b0484eab3e..2da7ea0eea7033e2bb0418ecb1f0d12d0c65cbb8 100644 GIT binary patch literal 7333 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 0.5: + bar_texture = 'exam_gold' + else: + bar_texture = 'exam_red' + + exam_data.append({ + 'exam': exam, + 'progress': progress, + 'bar_texture': bar_texture, + 'counter_value': counter_value, + 'red_value': exam.red + }) + + return { + 'remaining_notes': remaining_notes, + 'exam_data': exam_data + } + + def _get_exam_progress(self, exam: Exam) -> int: + """Get progress value based on exam type""" + type_mapping = { + 'gauge': (self.player_1.gauge.gauge_length / self.player_1.gauge.gauge_max) * 100, + 'judgeperfect': self.player_1.good_count, + 'judgegood': self.player_1.ok_count + self.player_1.bad_count, + 'judgebad': self.player_1.bad_count, + 'score': self.player_1.score, + 'combo': self.player_1.max_combo + } + return int(type_mapping.get(exam.type, 0)) + def update(self): super(GameScreen, self).update() current_time = get_current_ms() @@ -109,6 +165,10 @@ class DanGameScreen(GameScreen): self.player_1.update(self.current_ms, current_time, self.background) self.song_info.update(current_time) self.result_transition.update(current_time) + + self.dan_info_cache = self._calculate_dan_info() + self._check_exam_failures() + if self.result_transition.is_finished and not audio.is_sound_playing('result_transition'): logger.info("Result transition finished, moving to RESULT screen") return self.on_screen_end('RESULT') @@ -133,40 +193,71 @@ class DanGameScreen(GameScreen): return self.global_keys() + def _check_exam_failures(self): + for i, exam in enumerate(self.exams): + progress_value = self._get_exam_progress(exam) + + if self.exam_failed[i]: + continue + + if exam.range == 'more': + if progress_value < exam.red and self.end_ms != 0: + self.exam_failed[i] = True + audio.play_sound('exam_failed', 'sound') + logger.info(f"Exam {i} ({exam.type}) failed: {progress_value} < {exam.red}") + elif exam.range == 'less': + counter_value = max(0, exam.red - progress_value) + if counter_value == 0: + self.exam_failed[i] = True + audio.play_sound('dan_failed', 'sound') + logger.info(f"Exam {i} ({exam.type}) failed: counter reached 0") + def draw_dan_info(self): + if self.dan_info_cache is None: + return + + cache = self.dan_info_cache + + # Draw total notes counter tex.draw_texture('dan_info', 'total_notes') - counter = str(self.total_notes - self.player_1.good_count - self.player_1.ok_count - self.player_1.bad_count) + counter = str(cache['remaining_notes']) self._draw_counter(counter, margin=45, texture='total_notes_counter') - for i, exam in enumerate(self.exams): + # Draw exam info + for i, exam_info in enumerate(cache['exam_data']): y_offset = i * 94 + exam = exam_info['exam'] + tex.draw_texture('dan_info', 'exam_bg', y=y_offset) tex.draw_texture('dan_info', 'exam_overlay_1', y=y_offset) - # Get progress based on exam type - progress = self._get_exam_progress(exam) / exam.red - if exam.range == 'less': - progress = 1 - progress - self._draw_progress_bar(progress, y_offset) - # Draw exam type and counter - counter = str(exam.red) - self._draw_counter(counter, margin=22, texture='value_counter', index=0, y=y_offset) - tex.draw_texture('dan_info', f'exam_{exam.type}', y=y_offset, x=-len(counter)*20) + # Draw progress bar + tex.draw_texture('dan_info', exam_info['bar_texture'], x2=940*exam_info['progress'], y=y_offset) + # Draw exam type and red value counter + red_counter = str(exam_info['red_value']) + self._draw_counter(red_counter, margin=22, texture='value_counter', index=0, y=y_offset) + tex.draw_texture('dan_info', f'exam_{exam.type}', y=y_offset, x=-len(red_counter)*20) + + # Draw range indicator if exam.range == 'less': tex.draw_texture('dan_info', 'exam_less', y=y_offset) elif exam.range == 'more': tex.draw_texture('dan_info', 'exam_more', y=y_offset) + # Draw current value counter tex.draw_texture('dan_info', 'exam_overlay_2', y=y_offset) - if exam.range == 'less': - counter = str(max(0, exam.red - self._get_exam_progress(exam))) - elif exam.range == 'more': - counter = str(max(0, self._get_exam_progress(exam))) - self._draw_counter(counter, margin=22, texture='value_counter', index=1, y=y_offset) + value_counter = str(exam_info['counter_value']) + self._draw_counter(value_counter, margin=22, texture='value_counter', index=1, y=y_offset) + if exam.type == 'gauge': tex.draw_texture('dan_info', 'exam_percent', y=y_offset, index=1) + if self.exam_failed[i]: + tex.draw_texture('dan_info', 'exam_bg', fade=0.5, y=y_offset) + tex.draw_texture('dan_info', 'exam_failed', y=y_offset) + + # Draw frame and title tex.draw_texture('dan_info', 'frame', frame=self.color) if self.hori_name is not None: self.hori_name.draw(outline_color=ray.BLACK, x=154 - (self.hori_name.texture.width//2), @@ -180,32 +271,6 @@ class DanGameScreen(GameScreen): kwargs['index'] = index tex.draw_texture('dan_info', texture, **kwargs) - def _get_exam_progress(self, exam: Exam) -> int: - """Get progress value based on exam type""" - type_mapping = { - 'gauge': (self.player_1.gauge.gauge_length / self.player_1.gauge.gauge_max) * 100, - 'judgeperfect': self.player_1.good_count, - 'judgegood': self.player_1.ok_count, - 'judgebad': self.player_1.bad_count, - 'score': self.player_1.score, - 'combo': self.player_1.max_combo - } - return int(type_mapping.get(exam.type, 0)) - - def _draw_progress_bar(self, progress, y_offset): - """Draw the progress bar with appropriate color""" - progress = max(0, progress) # Clamp to 0 minimum - progress = min(progress, 1) # Clamp to 1 maximum - - if progress == 1: - texture = 'exam_max' - elif progress >= 0.5: - texture = 'exam_gold' - else: - texture = 'exam_red' - - tex.draw_texture('dan_info', texture, x2=940*progress, y=y_offset) - @override def draw(self): self.background.draw()