mirror of
https://github.com/Yonokid/PyTaiko.git
synced 2026-02-04 11:40:13 +01:00
Add #SUDDEN, #DELAY
This commit is contained in:
83
docs/tja_compat.csv
Normal file
83
docs/tja_compat.csv
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
TITLE,Supported
|
||||||
|
SUBTITLE,Supported
|
||||||
|
WAVE,Supported
|
||||||
|
DEMOSTART,Supported
|
||||||
|
OFFSET,Supported
|
||||||
|
BPM,Supported
|
||||||
|
SCENEPRESET,Supported
|
||||||
|
BGMOVIE,Supported
|
||||||
|
COURSE,Supported
|
||||||
|
LEVEL,Supported
|
||||||
|
BALLOON,Supported
|
||||||
|
TOTAL,Supported
|
||||||
|
|
||||||
|
#START / #END,Supported
|
||||||
|
#BPMCHANGE,Supported
|
||||||
|
#MEASURE,Supported
|
||||||
|
#GOGOSTART / #GOGOEND,Supported
|
||||||
|
#SCROLL,Supported
|
||||||
|
#BARLINEOFF / #BARLINEON,Supported
|
||||||
|
#LYRIC,Supported
|
||||||
|
#SECTION,Supported
|
||||||
|
#BRANCHSTART / #BRANCHEND,Supported
|
||||||
|
#N / #E / #M,Supported
|
||||||
|
#DELAY,Supported
|
||||||
|
#SUDDEN,Supported
|
||||||
|
|
||||||
|
ARTIST,Unsupported
|
||||||
|
MAKER,Unsupported
|
||||||
|
NOTESDESIGNER,Unsupported
|
||||||
|
AUTHOR,Unsupported
|
||||||
|
GENRE,Unsupported
|
||||||
|
SIDE,Unsupported
|
||||||
|
SIDEREV,Unsupported
|
||||||
|
SONGVOL,Unsupported
|
||||||
|
SEVOL,Unsupported
|
||||||
|
HEADSCROLL,Unsupported
|
||||||
|
PREIMAGE,Unsupported
|
||||||
|
TAIKOWEBSKIN,Unsupported
|
||||||
|
TOWERTYPE,Unsupported
|
||||||
|
DANTICK,Unsupported
|
||||||
|
DANTICKCOLOR,Unsupported
|
||||||
|
SELECTBG,Unsupported
|
||||||
|
BGIMAGE,Unsupported
|
||||||
|
BGOFFSET,Unsupported
|
||||||
|
MOVIEOFFSET,Unsupported
|
||||||
|
BGA Headers,Unsupported
|
||||||
|
LYRICS: / LYRICFILE,Unsupported
|
||||||
|
EXPLICIT,Unsupported
|
||||||
|
GAME,Unsupported
|
||||||
|
STYLE,Unsupported
|
||||||
|
LIFE,Unsupported
|
||||||
|
GAUGEINCR,Unsupported
|
||||||
|
EXAM,Unsupported
|
||||||
|
SCOREMODE,Unsupported
|
||||||
|
SCOREINIT,Unsupported
|
||||||
|
SCOREDIFF,Unsupported
|
||||||
|
HIDDENBRANCH,Unsupported
|
||||||
|
|
||||||
|
#BMSCROLL / #HBSCROLL / #NMSCROLL,Unsupported
|
||||||
|
#PAPAMAMA,Unsupported
|
||||||
|
#DUMMYSTART / #DUMMYEND,Unsupported
|
||||||
|
#BARLINESCROLL,Unsupported
|
||||||
|
#HISPEED,Unsupported
|
||||||
|
#DIRECTION,Unsupported
|
||||||
|
#BARLINE,Unsupported
|
||||||
|
#JPOSSCROLL,Unsupported
|
||||||
|
#JUDGEDELAY,Unsupported
|
||||||
|
#NOTESPAWN,Unsupported
|
||||||
|
#SENOTECHANGE,Unsupported
|
||||||
|
#NOTESCHANGE,Unsupported
|
||||||
|
#LEVELHOLD,Unsupported
|
||||||
|
#NEXTSONG,Unsupported
|
||||||
|
#GAMETYPE,Unsupported
|
||||||
|
#HIDDEN,Unsupported
|
||||||
|
#NOTE / #BARLINE,Unsupported
|
||||||
|
#GRADATION,Unsupported
|
||||||
|
#INCLUDE,Unsupported
|
||||||
|
#SPLITLANE / #MERGELANE,Unsupported
|
||||||
|
#OBJ / #CAM,Unsupported
|
||||||
|
#BORDERCOLOR,Unsupported
|
||||||
|
#CHANGETEXTURE / #RESETTEXTURE,Unsupported
|
||||||
|
#SETCONFIG,Unsupported
|
||||||
|
#BGAON / #BGAOFF,Unsupported
|
||||||
|
29
libs/tja.py
29
libs/tja.py
@@ -70,6 +70,8 @@ class Note:
|
|||||||
is_branch_start: bool = field(init=False)
|
is_branch_start: bool = field(init=False)
|
||||||
branch_params: str = field(init=False)
|
branch_params: str = field(init=False)
|
||||||
lyric: str = field(init=False)
|
lyric: str = field(init=False)
|
||||||
|
sudden_appear_ms: float = field(init=False)
|
||||||
|
sudden_moving_ms: float = field(init=False)
|
||||||
|
|
||||||
def __lt__(self, other):
|
def __lt__(self, other):
|
||||||
return self.hit_ms < other.hit_ms
|
return self.hit_ms < other.hit_ms
|
||||||
@@ -623,6 +625,8 @@ class TJAParser:
|
|||||||
balloon = self.metadata.course_data[diff].balloon.copy()
|
balloon = self.metadata.course_data[diff].balloon.copy()
|
||||||
count = 0
|
count = 0
|
||||||
index = 0
|
index = 0
|
||||||
|
sudden_appear = 0
|
||||||
|
sudden_moving = 0
|
||||||
time_signature = 4/4
|
time_signature = 4/4
|
||||||
bpm = self.metadata.bpm
|
bpm = self.metadata.bpm
|
||||||
x_scroll_modifier = 1
|
x_scroll_modifier = 1
|
||||||
@@ -788,6 +792,26 @@ class TJAParser:
|
|||||||
elif '#GOGOEND' in part:
|
elif '#GOGOEND' in part:
|
||||||
gogo_time = False
|
gogo_time = False
|
||||||
continue
|
continue
|
||||||
|
elif part.startswith("#DELAY"):
|
||||||
|
self.current_ms += float(part[6:]) * 1000
|
||||||
|
continue
|
||||||
|
elif part.startswith("#SUDDEN"):
|
||||||
|
# Parse #SUDDEN command
|
||||||
|
parts = part.split()
|
||||||
|
if len(parts) >= 3:
|
||||||
|
appear_duration = float(parts[1])
|
||||||
|
moving_duration = float(parts[2])
|
||||||
|
|
||||||
|
# Convert to milliseconds
|
||||||
|
sudden_appear = appear_duration * 1000
|
||||||
|
sudden_moving = moving_duration * 1000
|
||||||
|
|
||||||
|
# Handle special case: if value is 0, treat as infinity
|
||||||
|
if sudden_appear == 0:
|
||||||
|
sudden_appear = float('inf')
|
||||||
|
if sudden_moving == 0:
|
||||||
|
sudden_moving = float('inf')
|
||||||
|
continue
|
||||||
#Unrecognized commands will be skipped for now
|
#Unrecognized commands will be skipped for now
|
||||||
elif len(part) > 0 and not part[0].isdigit():
|
elif len(part) > 0 and not part[0].isdigit():
|
||||||
logger.warning(f"Unrecognized command: {part}")
|
logger.warning(f"Unrecognized command: {part}")
|
||||||
@@ -856,6 +880,11 @@ class TJAParser:
|
|||||||
note.gogo_time = gogo_time
|
note.gogo_time = gogo_time
|
||||||
note.moji = -1
|
note.moji = -1
|
||||||
note.lyric = lyric
|
note.lyric = lyric
|
||||||
|
|
||||||
|
if sudden_appear > 0 or sudden_moving > 0:
|
||||||
|
note.sudden_appear_ms = sudden_appear
|
||||||
|
note.sudden_moving_ms = sudden_moving
|
||||||
|
|
||||||
if item in {'5', '6'}:
|
if item in {'5', '6'}:
|
||||||
note = Drumroll(note)
|
note = Drumroll(note)
|
||||||
note.color = 255
|
note.color = 255
|
||||||
|
|||||||
@@ -1015,8 +1015,6 @@ class Player:
|
|||||||
if not self.current_bars:
|
if not self.current_bars:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Batch bar draws by pre-calculating positions
|
|
||||||
bar_draws = []
|
|
||||||
for bar in reversed(self.current_bars):
|
for bar in reversed(self.current_bars):
|
||||||
if not bar.display:
|
if not bar.display:
|
||||||
continue
|
continue
|
||||||
@@ -1026,17 +1024,17 @@ class Player:
|
|||||||
frame = 1
|
frame = 1
|
||||||
else:
|
else:
|
||||||
frame = 0
|
frame = 0
|
||||||
bar_draws.append((str(bar.type), frame, x_position+60, y_position+190+(self.is_2p*176)))
|
if y_position != 0:
|
||||||
|
angle = math.degrees(math.atan2(bar.pixels_per_frame_y, bar.pixels_per_frame_x))
|
||||||
|
else:
|
||||||
|
angle = 0
|
||||||
|
tex.draw_texture('notes', str(bar.type), frame=frame, x=x_position+60, y=y_position+190+(self.is_2p*176), rotation=angle)
|
||||||
|
|
||||||
# Draw all bars in one batch
|
|
||||||
for bar_type, frame, x, y in bar_draws:
|
|
||||||
tex.draw_texture('notes', bar_type, frame=frame, x=x, y=y)
|
|
||||||
|
|
||||||
def draw_notes(self, current_ms: float, start_ms: float):
|
def draw_notes(self, current_ms: float, start_ms: float):
|
||||||
"""Draw notes in the player's lane"""
|
"""Draw notes in the player's lane"""
|
||||||
if not self.current_notes_draw:
|
if not self.current_notes_draw:
|
||||||
return
|
return
|
||||||
|
|
||||||
eighth_in_ms = 0 if self.bpm == 0 else (60000 * 4 / self.bpm) / 8
|
eighth_in_ms = 0 if self.bpm == 0 else (60000 * 4 / self.bpm) / 8
|
||||||
current_eighth = 0
|
current_eighth = 0
|
||||||
if self.combo >= 50 and eighth_in_ms != 0:
|
if self.combo >= 50 and eighth_in_ms != 0:
|
||||||
@@ -1048,19 +1046,34 @@ class Player:
|
|||||||
if note.type == NoteType.TAIL:
|
if note.type == NoteType.TAIL:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if isinstance(note, Drumroll):
|
if hasattr(note, 'sudden_appear_ms') and hasattr(note, 'sudden_moving_ms'):
|
||||||
self.draw_drumroll(current_ms, note, current_eighth)
|
appear_ms = note.hit_ms - note.sudden_appear_ms
|
||||||
elif isinstance(note, Balloon) and not note.is_kusudama:
|
moving_start_ms = note.hit_ms - note.sudden_moving_ms
|
||||||
x_position = self.get_position_x(SCREEN_WIDTH, current_ms, note.load_ms, note.pixels_per_frame_x)
|
|
||||||
y_position = self.get_position_y(current_ms, note.load_ms, note.pixels_per_frame_y, note.pixels_per_frame_x)
|
if current_ms < appear_ms:
|
||||||
self.draw_balloon(current_ms, note, current_eighth)
|
continue
|
||||||
tex.draw_texture('notes', 'moji', frame=note.moji, x=x_position - (168//2) + 64, y=323 + y_position+(self.is_2p*176))
|
|
||||||
|
if current_ms < moving_start_ms:
|
||||||
|
effective_ms = moving_start_ms
|
||||||
|
else:
|
||||||
|
effective_ms = current_ms
|
||||||
|
|
||||||
|
x_position = self.get_position_x(SCREEN_WIDTH, effective_ms, note.load_ms, note.pixels_per_frame_x)
|
||||||
|
y_position = self.get_position_y(effective_ms, note.load_ms, note.pixels_per_frame_y, note.pixels_per_frame_x)
|
||||||
else:
|
else:
|
||||||
x_position = self.get_position_x(SCREEN_WIDTH, current_ms, note.load_ms, note.pixels_per_frame_x)
|
x_position = self.get_position_x(SCREEN_WIDTH, current_ms, note.load_ms, note.pixels_per_frame_x)
|
||||||
y_position = self.get_position_y(current_ms, note.load_ms, note.pixels_per_frame_y, note.pixels_per_frame_x)
|
y_position = self.get_position_y(current_ms, note.load_ms, note.pixels_per_frame_y, note.pixels_per_frame_x)
|
||||||
|
|
||||||
|
if isinstance(note, Drumroll):
|
||||||
|
self.draw_drumroll(current_ms, note, current_eighth)
|
||||||
|
elif isinstance(note, Balloon) and not note.is_kusudama:
|
||||||
|
self.draw_balloon(current_ms, note, current_eighth)
|
||||||
|
tex.draw_texture('notes', 'moji', frame=note.moji, x=x_position - (168//2) + 64, y=323 + y_position+(self.is_2p*176))
|
||||||
|
else:
|
||||||
if note.display:
|
if note.display:
|
||||||
tex.draw_texture('notes', str(note.type), frame=current_eighth % 2, x=x_position, y=y_position+192+(self.is_2p*176), center=True)
|
tex.draw_texture('notes', str(note.type), frame=current_eighth % 2, x=x_position, y=y_position+192+(self.is_2p*176), center=True)
|
||||||
tex.draw_texture('notes', 'moji', frame=note.moji, x=x_position - (168//2) + 64, y=323 + y_position+(self.is_2p*176))
|
tex.draw_texture('notes', 'moji', frame=note.moji, x=x_position - (168//2) + 64, y=323 + y_position+(self.is_2p*176))
|
||||||
|
|
||||||
ray.draw_text(self.current_notes_draw[0].lyric, SCREEN_WIDTH//2 - (ray.measure_text(self.current_notes_draw[0].lyric, 40)//2), SCREEN_HEIGHT - 50, 40, ray.BLUE)
|
ray.draw_text(self.current_notes_draw[0].lyric, SCREEN_WIDTH//2 - (ray.measure_text(self.current_notes_draw[0].lyric, 40)//2), SCREEN_HEIGHT - 50, 40, ray.BLUE)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -131,7 +131,10 @@ class PracticeGameScreen(GameScreen):
|
|||||||
self.transition.update(current_time)
|
self.transition.update(current_time)
|
||||||
if not self.paused:
|
if not self.paused:
|
||||||
self.current_ms = current_time - self.start_ms
|
self.current_ms = current_time - self.start_ms
|
||||||
self.start_song(current_time)
|
if self.transition.is_finished:
|
||||||
|
self.start_song(self.current_ms)
|
||||||
|
else:
|
||||||
|
self.start_ms = current_time - self.tja.metadata.offset*1000
|
||||||
self.update_background(current_time)
|
self.update_background(current_time)
|
||||||
|
|
||||||
if self.song_music is not None:
|
if self.song_music is not None:
|
||||||
|
|||||||
Reference in New Issue
Block a user