mirror of
https://github.com/Yonokid/PyTaiko.git
synced 2026-02-04 19:50:12 +01:00
feat: implement #DELAY for BMSCROLL, HBSCROLL
This commit is contained in:
32
libs/tja.py
32
libs/tja.py
@@ -62,6 +62,7 @@ class Note:
|
||||
is_branch_start (bool): Whether the note is the start of a branch.
|
||||
branch_params (str): The parameters (requirements) of the branch.
|
||||
bpmchange (float): If it exists, the bpm will be multiplied by it when the note passes the judgement circle
|
||||
delay (float): Milliseconds, if it exists, the delay will be added when the note passes the judgement circle
|
||||
"""
|
||||
type: int = field(init=False)
|
||||
hit_ms: float = field(init=False)
|
||||
@@ -81,6 +82,7 @@ class Note:
|
||||
judge_pos_x: float = field(init=False)
|
||||
judge_pos_y: float = field(init=False)
|
||||
bpmchange: float = field(init=False)
|
||||
delay: float = field(init=False)
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.hit_ms < other.hit_ms
|
||||
@@ -905,8 +907,6 @@ class TJAParser:
|
||||
bpmchange_bar.bpmchange = bpmchange
|
||||
|
||||
bisect.insort(curr_bar_list, bpmchange_bar, key=lambda x: x.load_ms)
|
||||
|
||||
bpmchange = None
|
||||
else:
|
||||
bpm = parsed_bpm
|
||||
continue
|
||||
@@ -923,7 +923,33 @@ class TJAParser:
|
||||
gogo_time = False
|
||||
continue
|
||||
elif part.startswith("#DELAY"):
|
||||
self.current_ms += float(part[6:]) * 1000
|
||||
delay_ms = float(part[6:]) * 1000
|
||||
if scroll_type == ScrollType.BMSCROLL or scroll_type == ScrollType.HBSCROLL:
|
||||
if delay_ms < 0:
|
||||
# No changes if negative
|
||||
pass
|
||||
else:
|
||||
# Do not modify current_ms, it will be modified live
|
||||
delay_bar = Note()
|
||||
delay_bar.pixels_per_frame_x = get_pixels_per_frame(bpm * time_signature * x_scroll_modifier, time_signature*4, self.distance)
|
||||
delay_bar.pixels_per_frame_y = get_pixels_per_frame(bpm * time_signature * y_scroll_modifier, time_signature*4, self.distance)
|
||||
pixels_per_ms = get_pixels_per_ms(delay_bar.pixels_per_frame_x)
|
||||
|
||||
delay_bar.hit_ms = self.current_ms
|
||||
if pixels_per_ms == 0:
|
||||
delay_bar.load_ms = delay_bar.hit_ms
|
||||
else:
|
||||
delay_bar.load_ms = delay_bar.hit_ms - (self.distance / pixels_per_ms)
|
||||
delay_bar.type = 0
|
||||
delay_bar.display = False
|
||||
delay_bar.gogo_time = gogo_time
|
||||
delay_bar.bpm = bpm
|
||||
|
||||
delay_bar.delay = delay_ms
|
||||
|
||||
bisect.insort(curr_bar_list, delay_bar, key=lambda x: x.load_ms)
|
||||
else:
|
||||
self.current_ms += delay_ms
|
||||
continue
|
||||
elif part.startswith("#SUDDEN"):
|
||||
# Parse #SUDDEN command
|
||||
|
||||
@@ -362,6 +362,9 @@ class Player:
|
||||
self.score_counter = ScoreCounter(self.score, self.is_2p)
|
||||
self.gogo_time: Optional[GogoTime] = None
|
||||
self.bpmchanges: deque[BPMChange]
|
||||
self.delays: deque[Delay]
|
||||
self.delay_start: Optional[float] = None
|
||||
self.delay_end: Optional[float] = None
|
||||
self.combo_announce = ComboAnnounce(self.combo, 0, player_num, self.is_2p)
|
||||
self.branch_indicator = BranchIndicator(self.is_2p) if tja and tja.metadata.course_data[self.difficulty].is_branching else None
|
||||
self.ending_anim: Optional[FailAnimation | ClearAnimation | FCAnimation] = None
|
||||
@@ -406,10 +409,33 @@ class Player:
|
||||
self.other_notes = deque([note for note in self.play_notes if note.type not in {NoteType.DON, NoteType.DON_L, NoteType.KAT, NoteType.KAT_L}])
|
||||
self.total_notes = len([note for note in self.play_notes if 0 < note.type < 5])
|
||||
|
||||
# Collect bpmchange, delay (remove from bars, and pre-adjust their hit_ms)
|
||||
self.bpmchanges = deque()
|
||||
self.delays = deque()
|
||||
new_draw_bar_list: deque[Note] = deque()
|
||||
special_bars: deque[Note] = deque()
|
||||
for note in self.draw_bar_list:
|
||||
if hasattr(note, 'bpmchange') or hasattr(note, 'delay'):
|
||||
special_bars.append(note)
|
||||
else:
|
||||
new_draw_bar_list.append(note)
|
||||
self.draw_bar_list = new_draw_bar_list
|
||||
|
||||
special_bars_len = len(special_bars)
|
||||
for i, note in enumerate(special_bars):
|
||||
if hasattr(note, 'bpmchange'):
|
||||
self.bpmchanges.append(BPMChange(note.hit_ms, note.bpmchange))
|
||||
bpmchange = BPMChange(note.hit_ms, note.bpmchange)
|
||||
self.bpmchanges.append(bpmchange)
|
||||
for i2 in range(i + 1, special_bars_len):
|
||||
bar = special_bars[i2]
|
||||
bar.hit_ms = (bar.hit_ms - bpmchange.hit_ms) / bpmchange.bpmchange + bpmchange.hit_ms
|
||||
if hasattr(note, 'delay'):
|
||||
delay = Delay(note.hit_ms, note.delay)
|
||||
self.delays.append(delay)
|
||||
for i2 in range(i + 1, special_bars_len):
|
||||
bar = special_bars[i2]
|
||||
bar.hit_ms += delay.delay
|
||||
|
||||
|
||||
total_notes = notes
|
||||
if self.branch_m:
|
||||
@@ -454,11 +480,17 @@ class Player:
|
||||
|
||||
def get_position_x(self, width: int, current_ms: float, load_ms: float, pixels_per_frame: float) -> int:
|
||||
"""Calculates the x-coordinate of a note based on its load time and current time"""
|
||||
# Override if delay active
|
||||
if self.delay_end:
|
||||
current_ms = self.delay_end
|
||||
time_diff = load_ms - current_ms
|
||||
return int(width + pixels_per_frame * 0.06 * time_diff - (tex.textures["notes"]["1"].width//2)) - self.visual_offset
|
||||
|
||||
def get_position_y(self, current_ms: float, load_ms: float, pixels_per_frame: float, pixels_per_frame_x) -> int:
|
||||
"""Calculates the y-coordinate of a note based on its load time and current time"""
|
||||
# Override if delay active
|
||||
if self.delay_end:
|
||||
current_ms = self.delay_end
|
||||
time_diff = load_ms - current_ms
|
||||
return int((pixels_per_frame * 0.06 * time_diff) + ((self.tja.distance * pixels_per_frame) / pixels_per_frame_x))
|
||||
|
||||
@@ -967,12 +999,29 @@ class Player:
|
||||
|
||||
note.pixels_per_frame_x *= bpmchange.bpmchange
|
||||
note.pixels_per_frame_y *= bpmchange.bpmchange
|
||||
|
||||
self.bpm *= bpmchange.bpmchange
|
||||
self.bpmchanges.popleft()
|
||||
# Adjust later bpmchanges too
|
||||
for bpmchange_bar in self.bpmchanges:
|
||||
bpmchange_bar.hit_ms = (bpmchange_bar.hit_ms - bpmchange.hit_ms) / bpmchange.bpmchange + bpmchange.hit_ms
|
||||
if len(self.delays) != 0:
|
||||
if self.delay_start is not None and self.delay_end is not None:
|
||||
# Currently, a delay is active: notes should be frozen at ms = delay_start
|
||||
# Check if it ended
|
||||
if ms_from_start >= self.delay_end:
|
||||
self.delay_start = None
|
||||
self.delay_end = None
|
||||
# else:
|
||||
delay = self.delays[0]
|
||||
delay_success = delay.is_ready(ms_from_start)
|
||||
if delay_success:
|
||||
# Turn on delay visual
|
||||
self.delay_start = delay.hit_ms
|
||||
self.delay_end = delay.hit_ms + delay.delay
|
||||
# Adjust notes
|
||||
for note in chain(self.play_notes, self.current_bars, self.draw_bar_list):
|
||||
# time_diff must be the same throughout the delay
|
||||
# time_diff = note.load_ms - delay.hit_ms
|
||||
note.hit_ms += delay.delay
|
||||
note.load_ms += delay.delay
|
||||
self.delays.popleft()
|
||||
if self.lane_hit_effect is not None:
|
||||
self.lane_hit_effect.update(current_time)
|
||||
self.animation_manager(self.draw_drum_hit_list, current_time)
|
||||
@@ -1970,6 +2019,14 @@ class BPMChange:
|
||||
def is_ready(self, ms_from_start: float):
|
||||
return ms_from_start >= self.hit_ms
|
||||
|
||||
class Delay:
|
||||
"""For delay during HBSCROLL or BMSCROLL"""
|
||||
def __init__(self, hit_ms: float, delay: float):
|
||||
self.hit_ms = hit_ms
|
||||
self.delay = delay
|
||||
def is_ready(self, ms_from_start: float):
|
||||
return ms_from_start >= self.hit_ms
|
||||
|
||||
class ComboAnnounce:
|
||||
"""Displays the combo every 100 combos"""
|
||||
def __init__(self, combo: int, current_time_ms: float, player_num: PlayerNum, is_2p: bool):
|
||||
|
||||
Reference in New Issue
Block a user