mirror of
https://github.com/Yonokid/PyTaiko.git
synced 2026-02-04 11:40:13 +01:00
Merge pull request #167 from splitlane/main
Game: Add BMSCROLL, HBSCROLL
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,3 +6,5 @@ cache
|
|||||||
dev-config.toml
|
dev-config.toml
|
||||||
libaudio.so
|
libaudio.so
|
||||||
latest.log
|
latest.log
|
||||||
|
libaudio.dll
|
||||||
|
libaudio.dylib
|
||||||
|
|||||||
83
libs/tja.py
83
libs/tja.py
@@ -41,6 +41,11 @@ class NoteType(IntEnum):
|
|||||||
TAIL = 8
|
TAIL = 8
|
||||||
KUSUDAMA = 9
|
KUSUDAMA = 9
|
||||||
|
|
||||||
|
class ScrollType(IntEnum):
|
||||||
|
NMSCROLL = 0
|
||||||
|
BMSCROLL = 1
|
||||||
|
HBSCROLL = 2
|
||||||
|
|
||||||
@dataclass()
|
@dataclass()
|
||||||
class TimelineObject:
|
class TimelineObject:
|
||||||
hit_ms: float = field(init=False)
|
hit_ms: float = field(init=False)
|
||||||
@@ -57,6 +62,8 @@ class TimelineObject:
|
|||||||
cam_rotation: float = field(init=False)
|
cam_rotation: float = field(init=False)
|
||||||
|
|
||||||
bpm: float = field(init=False)
|
bpm: float = field(init=False)
|
||||||
|
bpmchange: float = field(init=False)
|
||||||
|
delay: float = field(init=False)
|
||||||
'''
|
'''
|
||||||
gogo_time: bool = field(init=False)
|
gogo_time: bool = field(init=False)
|
||||||
branch_params: str = field(init=False)
|
branch_params: str = field(init=False)
|
||||||
@@ -64,6 +71,8 @@ class TimelineObject:
|
|||||||
is_section_marker: bool = False
|
is_section_marker: bool = False
|
||||||
sudden_appear_ms: float = 0
|
sudden_appear_ms: float = 0
|
||||||
sudden_moving_ms: float = 0
|
sudden_moving_ms: float = 0
|
||||||
|
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
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __lt__(self, other):
|
def __lt__(self, other):
|
||||||
@@ -540,6 +549,7 @@ class TJAParser:
|
|||||||
# Use enumerate for single iteration
|
# Use enumerate for single iteration
|
||||||
note_start = note_end = -1
|
note_start = note_end = -1
|
||||||
target_found = False
|
target_found = False
|
||||||
|
scroll_type = ScrollType.NMSCROLL
|
||||||
|
|
||||||
# Find the section boundaries
|
# Find the section boundaries
|
||||||
for i, line in enumerate(self.data):
|
for i, line in enumerate(self.data):
|
||||||
@@ -552,6 +562,15 @@ class TJAParser:
|
|||||||
elif line == "#END" and note_start != -1:
|
elif line == "#END" and note_start != -1:
|
||||||
note_end = i
|
note_end = i
|
||||||
break
|
break
|
||||||
|
elif '#NMSCROLL' in line:
|
||||||
|
scroll_type = ScrollType.NMSCROLL
|
||||||
|
continue
|
||||||
|
elif '#BMSCROLL' in line:
|
||||||
|
scroll_type = ScrollType.BMSCROLL
|
||||||
|
continue
|
||||||
|
elif '#HBSCROLL' in line:
|
||||||
|
scroll_type = ScrollType.HBSCROLL
|
||||||
|
continue
|
||||||
|
|
||||||
if note_start == -1 or note_end == -1:
|
if note_start == -1 or note_end == -1:
|
||||||
return []
|
return []
|
||||||
@@ -561,6 +580,14 @@ class TJAParser:
|
|||||||
bar = []
|
bar = []
|
||||||
section_data = self.data[note_start:note_end]
|
section_data = self.data[note_start:note_end]
|
||||||
|
|
||||||
|
# Prepend scroll type
|
||||||
|
if scroll_type == ScrollType.NMSCROLL:
|
||||||
|
bar.append('#NMSCROLL')
|
||||||
|
elif scroll_type == ScrollType.BMSCROLL:
|
||||||
|
bar.append('#BMSCROLL')
|
||||||
|
elif scroll_type == ScrollType.HBSCROLL:
|
||||||
|
bar.append('#HBSCROLL')
|
||||||
|
|
||||||
for line in section_data:
|
for line in section_data:
|
||||||
if line.startswith("#"):
|
if line.startswith("#"):
|
||||||
bar.append(line)
|
bar.append(line)
|
||||||
@@ -775,6 +802,18 @@ class TJAParser:
|
|||||||
is_section_start = False
|
is_section_start = False
|
||||||
section_bar = None
|
section_bar = None
|
||||||
lyric = ""
|
lyric = ""
|
||||||
|
scroll_type = ScrollType.NMSCROLL
|
||||||
|
|
||||||
|
# Only used during BMSCROLL or HBSCROLL
|
||||||
|
bpmchange_last_bpm = bpm
|
||||||
|
delay_current = 0
|
||||||
|
delay_last_note_ms = self.current_ms
|
||||||
|
|
||||||
|
def add_delay_bar(hit_ms: float, delay: float):
|
||||||
|
delay_timeline = TimelineObject()
|
||||||
|
delay_timeline.hit_ms = hit_ms
|
||||||
|
delay_timeline.delay = delay
|
||||||
|
bisect.insort(curr_timeline, delay_timeline, key=lambda x: x.hit_ms)
|
||||||
|
|
||||||
for bar in notes:
|
for bar in notes:
|
||||||
bar_length = sum(len(part) for part in bar if '#' not in part)
|
bar_length = sum(len(part) for part in bar if '#' not in part)
|
||||||
@@ -1186,12 +1225,20 @@ class TJAParser:
|
|||||||
judge_pos_y = judge_target_y
|
judge_pos_y = judge_target_y
|
||||||
continue
|
continue
|
||||||
elif '#NMSCROLL' in part:
|
elif '#NMSCROLL' in part:
|
||||||
|
scroll_type = ScrollType.NMSCROLL
|
||||||
|
continue
|
||||||
|
elif '#BMSCROLL' in part:
|
||||||
|
scroll_type = ScrollType.BMSCROLL
|
||||||
|
continue
|
||||||
|
elif '#HBSCROLL' in part:
|
||||||
|
scroll_type = ScrollType.HBSCROLL
|
||||||
continue
|
continue
|
||||||
elif '#MEASURE' in part:
|
elif '#MEASURE' in part:
|
||||||
divisor = part.find('/')
|
divisor = part.find('/')
|
||||||
time_signature = float(part[9:divisor]) / float(part[divisor+1:])
|
time_signature = float(part[9:divisor]) / float(part[divisor+1:])
|
||||||
continue
|
continue
|
||||||
elif '#SCROLL' in part:
|
elif '#SCROLL' in part:
|
||||||
|
if scroll_type != ScrollType.BMSCROLL:
|
||||||
scroll_value = part[7:]
|
scroll_value = part[7:]
|
||||||
if 'i' in scroll_value:
|
if 'i' in scroll_value:
|
||||||
normalized = scroll_value.replace('.i', 'j').replace('i', 'j')
|
normalized = scroll_value.replace('.i', 'j').replace('i', 'j')
|
||||||
@@ -1204,10 +1251,20 @@ class TJAParser:
|
|||||||
y_scroll_modifier = 0.0
|
y_scroll_modifier = 0.0
|
||||||
continue
|
continue
|
||||||
elif '#BPMCHANGE' in part:
|
elif '#BPMCHANGE' in part:
|
||||||
bpm = float(part[11:])
|
parsed_bpm = float(part[11:])
|
||||||
|
if scroll_type == ScrollType.BMSCROLL or scroll_type == ScrollType.HBSCROLL:
|
||||||
|
# Do not modify bpm, it needs to be changed live by bpmchange
|
||||||
|
bpmchange = parsed_bpm / bpmchange_last_bpm
|
||||||
|
bpmchange_last_bpm = parsed_bpm
|
||||||
|
|
||||||
|
bpmchange_timeline = TimelineObject()
|
||||||
|
bpmchange_timeline.hit_ms = self.current_ms
|
||||||
|
bpmchange_timeline.bpmchange = bpmchange
|
||||||
|
bisect.insort(curr_timeline, bpmchange_timeline, key=lambda x: x.hit_ms)
|
||||||
|
else:
|
||||||
timeline_obj = TimelineObject()
|
timeline_obj = TimelineObject()
|
||||||
timeline_obj.hit_ms = self.current_ms
|
timeline_obj.hit_ms = self.current_ms
|
||||||
timeline_obj.bpm = bpm
|
timeline_obj.bpm = parsed_bpm
|
||||||
bisect.insort(curr_timeline, timeline_obj, key=lambda x: x.hit_ms)
|
bisect.insort(curr_timeline, timeline_obj, key=lambda x: x.hit_ms)
|
||||||
continue
|
continue
|
||||||
elif '#BARLINEOFF' in part:
|
elif '#BARLINEOFF' in part:
|
||||||
@@ -1223,7 +1280,18 @@ class TJAParser:
|
|||||||
gogo_time = False
|
gogo_time = False
|
||||||
continue
|
continue
|
||||||
elif part.startswith("#DELAY"):
|
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 not positive
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# Do not modify current_ms, it will be modified live
|
||||||
|
delay_current += delay_ms
|
||||||
|
|
||||||
|
# Delays will be combined between notes, and attached to previous note
|
||||||
|
else:
|
||||||
|
self.current_ms += delay_ms
|
||||||
continue
|
continue
|
||||||
elif part.startswith("#SUDDEN"):
|
elif part.startswith("#SUDDEN"):
|
||||||
parts = part.split()
|
parts = part.split()
|
||||||
@@ -1282,13 +1350,22 @@ class TJAParser:
|
|||||||
if item == '.':
|
if item == '.':
|
||||||
continue
|
continue
|
||||||
if item == '0' or (not item.isdigit()):
|
if item == '0' or (not item.isdigit()):
|
||||||
|
delay_last_note_ms = self.current_ms
|
||||||
self.current_ms += increment
|
self.current_ms += increment
|
||||||
continue
|
continue
|
||||||
if item == '9' and curr_note_list and curr_note_list[-1].type == 9:
|
if item == '9' and curr_note_list and curr_note_list[-1].type == 9:
|
||||||
|
delay_last_note_ms = self.current_ms
|
||||||
self.current_ms += increment
|
self.current_ms += increment
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if delay_current != 0:
|
||||||
|
# logger.debug(delay_current)
|
||||||
|
add_delay_bar(delay_last_note_ms, delay_current)
|
||||||
|
delay_current = 0
|
||||||
|
|
||||||
|
|
||||||
note = Note()
|
note = Note()
|
||||||
|
delay_last_note_ms = self.current_ms
|
||||||
note.hit_ms = self.current_ms
|
note.hit_ms = self.current_ms
|
||||||
note.display = True
|
note.display = True
|
||||||
note.pixels_per_frame_x = bar_line.pixels_per_frame_x
|
note.pixels_per_frame_x = bar_line.pixels_per_frame_x
|
||||||
|
|||||||
149
scenes/game.py
149
scenes/game.py
@@ -6,6 +6,7 @@ import sqlite3
|
|||||||
from collections import deque
|
from collections import deque
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
import pyray as ray
|
import pyray as ray
|
||||||
|
|
||||||
@@ -360,6 +361,8 @@ class Player:
|
|||||||
self.combo_display = Combo(self.combo, 0, self.is_2p)
|
self.combo_display = Combo(self.combo, 0, self.is_2p)
|
||||||
self.score_counter = ScoreCounter(self.score, self.is_2p)
|
self.score_counter = ScoreCounter(self.score, self.is_2p)
|
||||||
self.gogo_time: Optional[GogoTime] = None
|
self.gogo_time: Optional[GogoTime] = None
|
||||||
|
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.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.branch_indicator = BranchIndicator(self.is_2p) if tja and tja.metadata.course_data[self.difficulty].is_branching else None
|
||||||
self.ending_anim: Optional[FailAnimation | ClearAnimation | FCAnimation] = None
|
self.ending_anim: Optional[FailAnimation | ClearAnimation | FCAnimation] = None
|
||||||
@@ -383,6 +386,57 @@ class Player:
|
|||||||
def reset_chart(self):
|
def reset_chart(self):
|
||||||
notes, self.branch_m, self.branch_e, self.branch_n = self.tja.notes_to_position(self.difficulty)
|
notes, self.branch_m, self.branch_e, self.branch_n = self.tja.notes_to_position(self.difficulty)
|
||||||
self.play_notes, self.draw_note_list, self.draw_bar_list = apply_modifiers(notes, self.modifiers)
|
self.play_notes, self.draw_note_list, self.draw_bar_list = apply_modifiers(notes, self.modifiers)
|
||||||
|
|
||||||
|
self.don_notes = deque([note for note in self.play_notes if note.type in {NoteType.DON, NoteType.DON_L}])
|
||||||
|
self.kat_notes = deque([note for note in self.play_notes if note.type in {NoteType.KAT, NoteType.KAT_L}])
|
||||||
|
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])
|
||||||
|
total_notes = notes
|
||||||
|
if self.branch_m:
|
||||||
|
for section in self.branch_m:
|
||||||
|
self.total_notes += len([note for note in section.play_notes if 0 < note.type < 5])
|
||||||
|
total_notes += section
|
||||||
|
self.base_score = calculate_base_score(total_notes)
|
||||||
|
|
||||||
|
#Note management
|
||||||
|
self.timeline = notes.timeline
|
||||||
|
self.timeline_index = 0 # Range: [0, len(timeline)]
|
||||||
|
self.current_bars: list[Note] = []
|
||||||
|
self.current_notes_draw: list[Note | Drumroll | Balloon] = []
|
||||||
|
self.is_drumroll = False
|
||||||
|
self.curr_drumroll_count = 0
|
||||||
|
self.is_balloon = False
|
||||||
|
self.curr_balloon_count = 0
|
||||||
|
self.is_branch = False
|
||||||
|
self.curr_branch_reqs = []
|
||||||
|
self.branch_condition_count = 0
|
||||||
|
self.branch_condition = ''
|
||||||
|
self.balloon_index = 0
|
||||||
|
self.bpm = 120
|
||||||
|
if self.timeline and hasattr(self.timeline[self.timeline_index], 'bpm'):
|
||||||
|
self.bpm = self.timeline[self.timeline_index].bpm
|
||||||
|
# Handle HBSCROLL, BMSCROLL (pre-modify hit_ms, so that notes can't be literally hit, but are still visually different) - basically it applies the transformations of #BPMCHANGE and #DELAY to hit_ms, so that notes can't be hit even if its visaulyl
|
||||||
|
for i, o in enumerate(self.timeline):
|
||||||
|
if hasattr(o, 'bpmchange'):
|
||||||
|
hit_ms = o.hit_ms
|
||||||
|
bpmchange = o.bpmchange
|
||||||
|
for note in chain(self.play_notes, self.current_bars, self.draw_bar_list):
|
||||||
|
if note.hit_ms > hit_ms:
|
||||||
|
note.hit_ms = (note.hit_ms - hit_ms) / bpmchange + hit_ms
|
||||||
|
for i2 in range(i + 1, len(self.timeline)):
|
||||||
|
o2 = self.timeline[i2]
|
||||||
|
o2.hit_ms = (o2.hit_ms - hit_ms) / bpmchange + hit_ms
|
||||||
|
elif hasattr(o, 'delay'):
|
||||||
|
hit_ms = o.hit_ms
|
||||||
|
delay = o.delay
|
||||||
|
for note in chain(self.play_notes, self.current_bars, self.draw_bar_list):
|
||||||
|
if note.hit_ms > hit_ms:
|
||||||
|
note.hit_ms += delay
|
||||||
|
for i2 in range(i + 1, len(self.timeline)):
|
||||||
|
o2 = self.timeline[i2]
|
||||||
|
o2.hit_ms += delay
|
||||||
|
|
||||||
|
# Decide end_time after all transforms have been applied
|
||||||
self.end_time = 0
|
self.end_time = 0
|
||||||
if self.play_notes:
|
if self.play_notes:
|
||||||
self.end_time = self.play_notes[-1].hit_ms
|
self.end_time = self.play_notes[-1].hit_ms
|
||||||
@@ -399,35 +453,6 @@ class Player:
|
|||||||
if section.play_notes:
|
if section.play_notes:
|
||||||
self.end_time = max(self.end_time, section.play_notes[-1].hit_ms)
|
self.end_time = max(self.end_time, section.play_notes[-1].hit_ms)
|
||||||
|
|
||||||
self.don_notes = deque([note for note in self.play_notes if note.type in {NoteType.DON, NoteType.DON_L}])
|
|
||||||
self.kat_notes = deque([note for note in self.play_notes if note.type in {NoteType.KAT, NoteType.KAT_L}])
|
|
||||||
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])
|
|
||||||
total_notes = notes
|
|
||||||
if self.branch_m:
|
|
||||||
for section in self.branch_m:
|
|
||||||
self.total_notes += len([note for note in section.play_notes if 0 < note.type < 5])
|
|
||||||
total_notes += section
|
|
||||||
self.base_score = calculate_base_score(total_notes)
|
|
||||||
|
|
||||||
#Note management
|
|
||||||
self.timeline = notes.timeline
|
|
||||||
self.timeline_index = 0
|
|
||||||
self.current_bars: list[Note] = []
|
|
||||||
self.current_notes_draw: list[Note | Drumroll | Balloon] = []
|
|
||||||
self.is_drumroll = False
|
|
||||||
self.curr_drumroll_count = 0
|
|
||||||
self.is_balloon = False
|
|
||||||
self.curr_balloon_count = 0
|
|
||||||
self.is_branch = False
|
|
||||||
self.curr_branch_reqs = []
|
|
||||||
self.branch_condition_count = 0
|
|
||||||
self.branch_condition = ''
|
|
||||||
self.balloon_index = 0
|
|
||||||
self.bpm = 120
|
|
||||||
if self.timeline and hasattr(self.timeline[self.timeline_index], 'bpm'):
|
|
||||||
self.bpm = self.timeline[self.timeline_index].bpm
|
|
||||||
|
|
||||||
def merge_branch_section(self, branch_section: NoteList, current_ms: float):
|
def merge_branch_section(self, branch_section: NoteList, current_ms: float):
|
||||||
"""Merges the branch notes into the current notes"""
|
"""Merges the branch notes into the current notes"""
|
||||||
self.play_notes.extend(branch_section.play_notes)
|
self.play_notes.extend(branch_section.play_notes)
|
||||||
@@ -450,18 +475,24 @@ class Player:
|
|||||||
|
|
||||||
def get_position_x(self, width: int, current_ms: float, load_ms: float, pixels_per_frame: float) -> int:
|
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"""
|
"""Calculates the x-coordinate of a note based on its load time and current time"""
|
||||||
|
# Override if delay active
|
||||||
|
if self.delay_start:
|
||||||
|
current_ms = self.delay_start
|
||||||
time_diff = load_ms - current_ms
|
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
|
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:
|
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"""
|
"""Calculates the y-coordinate of a note based on its load time and current time"""
|
||||||
|
# Override if delay active
|
||||||
|
if self.delay_start:
|
||||||
|
current_ms = self.delay_start
|
||||||
time_diff = load_ms - current_ms
|
time_diff = load_ms - current_ms
|
||||||
if pixels_per_frame_x == 0:
|
if pixels_per_frame_x == 0:
|
||||||
return int(pixels_per_frame * 0.06 * time_diff)
|
return int(pixels_per_frame * 0.06 * time_diff)
|
||||||
return int((pixels_per_frame * 0.06 * time_diff) + ((self.tja.distance * pixels_per_frame) / pixels_per_frame_x))
|
return int((pixels_per_frame * 0.06 * time_diff) + ((self.tja.distance * pixels_per_frame) / pixels_per_frame_x))
|
||||||
|
|
||||||
def handle_tjap3_extended_commands(self, current_ms: float):
|
def handle_tjap3_extended_commands(self, current_ms: float):
|
||||||
if not self.timeline:
|
if not self.timeline or self.timeline_index >= len(self.timeline):
|
||||||
return
|
return
|
||||||
|
|
||||||
timeline_object = self.timeline[self.timeline_index]
|
timeline_object = self.timeline[self.timeline_index]
|
||||||
@@ -497,27 +528,65 @@ class Player:
|
|||||||
global_data.camera.rotation = timeline_object.cam_rotation
|
global_data.camera.rotation = timeline_object.cam_rotation
|
||||||
should_advance = True
|
should_advance = True
|
||||||
|
|
||||||
if should_advance and self.timeline_index < len(self.timeline) - 1:
|
if should_advance:
|
||||||
self.timeline_index += 1
|
self.timeline_index += 1
|
||||||
|
|
||||||
def get_judge_position(self, current_ms: float):
|
def get_judge_position(self, current_ms: float):
|
||||||
"""Get the current judgment circle position based on bar data"""
|
"""Get the current judgment circle position based on bar data"""
|
||||||
if not self.timeline:
|
if not self.timeline or self.timeline_index >= len(self.timeline):
|
||||||
return
|
return
|
||||||
timeline_object = self.timeline[self.timeline_index]
|
timeline_object = self.timeline[self.timeline_index]
|
||||||
if hasattr(timeline_object, 'judge_pos_x') and timeline_object.hit_ms <= current_ms:
|
if hasattr(timeline_object, 'judge_pos_x') and timeline_object.hit_ms <= current_ms:
|
||||||
self.judge_x = timeline_object.judge_pos_x * tex.screen_scale
|
self.judge_x = timeline_object.judge_pos_x * tex.screen_scale
|
||||||
self.judge_y = timeline_object.judge_pos_y * tex.screen_scale
|
self.judge_y = timeline_object.judge_pos_y * tex.screen_scale
|
||||||
if self.timeline_index < len(self.timeline) - 1:
|
self.timeline_index += 1
|
||||||
|
|
||||||
|
def handle_scroll_type_commands(self, current_ms: float):
|
||||||
|
if not self.timeline or self.timeline_index >= len(self.timeline):
|
||||||
|
return
|
||||||
|
|
||||||
|
timeline_object = self.timeline[self.timeline_index]
|
||||||
|
should_advance = False
|
||||||
|
|
||||||
|
if hasattr(timeline_object, 'bpmchange') and timeline_object.hit_ms <= current_ms:
|
||||||
|
hit_ms = timeline_object.hit_ms
|
||||||
|
bpmchange = timeline_object.bpmchange
|
||||||
|
# Adjust notes (visually)
|
||||||
|
for note in chain(self.play_notes, self.current_bars, self.draw_bar_list):
|
||||||
|
# Already modified
|
||||||
|
# note.hit_ms = (note.hit_ms - hit_ms) / bpmchange + hit_ms
|
||||||
|
# time_diff * note.pixels_per_frame need to be the same before and after the adjustment
|
||||||
|
# that means time_diff should be divided by self.bpmchange.bpmchange
|
||||||
|
# current_ms = self.bpmchange.hit_ms
|
||||||
|
time_diff = note.load_ms - hit_ms
|
||||||
|
note.load_ms = time_diff / bpmchange + hit_ms
|
||||||
|
|
||||||
|
note.pixels_per_frame_x *= bpmchange
|
||||||
|
note.pixels_per_frame_y *= bpmchange
|
||||||
|
self.bpm *= bpmchange
|
||||||
|
should_advance = True
|
||||||
|
|
||||||
|
if hasattr(timeline_object, 'delay') and timeline_object.hit_ms <= current_ms:
|
||||||
|
hit_ms = timeline_object.hit_ms
|
||||||
|
delay = timeline_object.delay
|
||||||
|
if self.delay_start is not None:
|
||||||
|
logger.error('Needs fix: delay is currently active, but another delay is being activated')
|
||||||
|
else:
|
||||||
|
# Turn on delay visual
|
||||||
|
self.delay_start = hit_ms
|
||||||
|
self.delay_end = hit_ms + delay
|
||||||
|
|
||||||
|
should_advance = True
|
||||||
|
|
||||||
|
if should_advance:
|
||||||
self.timeline_index += 1
|
self.timeline_index += 1
|
||||||
|
|
||||||
def update_bpm(self, current_ms: float):
|
def update_bpm(self, current_ms: float):
|
||||||
if not self.timeline:
|
if not self.timeline or self.timeline_index >= len(self.timeline):
|
||||||
return
|
return
|
||||||
timeline_object = self.timeline[self.timeline_index]
|
timeline_object = self.timeline[self.timeline_index]
|
||||||
if hasattr(timeline_object, 'bpm') and timeline_object.hit_ms <= current_ms:
|
if hasattr(timeline_object, 'bpm') and timeline_object.hit_ms <= current_ms:
|
||||||
self.bpm = timeline_object.bpm
|
self.bpm = timeline_object.bpm
|
||||||
if self.timeline_index < len(self.timeline) - 1:
|
|
||||||
self.timeline_index += 1
|
self.timeline_index += 1
|
||||||
|
|
||||||
def animation_manager(self, animation_list: list, current_time: float):
|
def animation_manager(self, animation_list: list, current_time: float):
|
||||||
@@ -992,6 +1061,16 @@ class Player:
|
|||||||
self.animation_manager(self.draw_drum_hit_list, current_time)
|
self.animation_manager(self.draw_drum_hit_list, current_time)
|
||||||
self.get_judge_position(ms_from_start)
|
self.get_judge_position(ms_from_start)
|
||||||
self.handle_tjap3_extended_commands(ms_from_start)
|
self.handle_tjap3_extended_commands(ms_from_start)
|
||||||
|
self.handle_scroll_type_commands(ms_from_start)
|
||||||
|
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:
|
||||||
|
delay = self.delay_end - self.delay_start
|
||||||
|
for note in chain(self.play_notes, self.current_bars, self.draw_bar_list):
|
||||||
|
note.load_ms += delay
|
||||||
|
self.delay_start = None
|
||||||
|
self.delay_end = None
|
||||||
self.update_bpm(ms_from_start)
|
self.update_bpm(ms_from_start)
|
||||||
|
|
||||||
# More efficient arc management
|
# More efficient arc management
|
||||||
|
|||||||
Reference in New Issue
Block a user