Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added event support (except for storyboards) #69

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 164 additions & 3 deletions slider/beatmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,125 @@ def _get(cs, ix, default=no_default):
return default


class EventType(IntEnum):
Background = 0
Video = 1
Break = 2
Sprite = 3
Animation = 4


class Event:
def __init__(self, event_type, start_time):
self.event_type = event_type
self.start_time = timedelta(milliseconds=start_time)

@classmethod
def parse(cls, data):
event_type, start_time_or_layer, *event_params = data.split(',')
event_type = EventType(int(event_type))

# TODO implement storyboarding events
if event_type is EventType.Sprite:
pass
if event_type is EventType.Animation:
pass

try:
start_time = int(start_time_or_layer)
except ValueError:
raise ValueError(f'Invalid start_time provided, got {start_time}')

if event_type is EventType.Background:
return Background.parse(event_params)
if event_type is EventType.Video:
return Video.parse(start_time, event_params)
if event_type is EventType.Break:
return Break.parse(start_time, event_params)

# make sure we've handled all event types.
raise ValueError(f'Unimplemented event type: {event_type}')


class Background(Event):

def __init__(self, filename, x_offset, y_offset):
super().__init__(EventType.Background, 0)
self.filename = filename
self.x_offset = x_offset
self.y_offset = y_offset

@classmethod
def parse(cls, event_params):
try:
filename, x_offset, y_offset = event_params
filename = filename.strip('"')
except ValueError:
raise ValueError(
f'Missing param for Background, received {event_params}')
try:
x_offset = int(x_offset)
except ValueError:
raise ValueError(f'x_offset is invalid, got {x_offset}')

try:
y_offset = int(y_offset)
except ValueError:
raise ValueError(f'y_offset is invalid, got {y_offset}')

return cls(filename, x_offset, y_offset)


class Break(Event):
def __init__(self, start_time, end_time):
super().__init__(EventType.Break, start_time)
self.end_time = timedelta(milliseconds=end_time)

@classmethod
def parse(cls, start_time, event_params):
if not event_params:
raise ValueError('expected end_time paramter for Break')

try:
end_time = int(event_params[0])
except ValueError:
raise ValueError(f'Invalid end_time provided, got {end_time}')

return cls(start_time, end_time)



class Video(Event):
def __init__(self, start_time, filename, x_offset, y_offset):
super().__init__(EventType.Video, start_time)
self.filename = filename
self.x_offset = x_offset
self.y_offset = y_offset

@classmethod
def parse(cls, start_time, event_params):
try:
filename, x_offset, y_offset = event_params
filename = filename.strip('"')
except ValueError:
raise ValueError(
f'Missing param for video, received {event_params}')

try:
x_offset = int(x_offset)
except ValueError:
raise ValueError(f'x_offset is invalid, got {x_offset}')

try:
y_offset = int(y_offset)
except ValueError:
raise ValueError(f'y_offset is invalid, got {y_offset}')

return cls(start_time, filename, x_offset, y_offset)




class TimingPoint:
"""A timing point assigns properties to an offset into a beatmap.

Expand Down Expand Up @@ -60,6 +179,7 @@ class TimingPoint:
kiai_mode : bool
Wheter or not kiai time effects are active.
"""

def __init__(self,
offset,
ms_per_beat,
Expand Down Expand Up @@ -1611,6 +1731,8 @@ class Beatmap:
The timing points the the map.
hit_objects : list[HitObject]
The hit objects in the map.
events : list[Event]
The events in the map.

Notes
-----
Expand Down Expand Up @@ -1652,7 +1774,8 @@ def __init__(self,
slider_multiplier,
slider_tick_rate,
timing_points,
hit_objects):
hit_objects,
events):
self.format_version = format_version
self.audio_filename = audio_filename
self.audio_lead_in = audio_lead_in
Expand Down Expand Up @@ -1685,6 +1808,8 @@ def __init__(self,
self.slider_multiplier = slider_multiplier
self.slider_tick_rate = slider_tick_rate
self.timing_points = timing_points
self.events = events

self._hit_objects = hit_objects
# cache hit object stacking at different ar and cs values
self._hit_objects_with_stacking = {}
Expand Down Expand Up @@ -1871,6 +1996,35 @@ def ar(self,

return ar

@lazyval
def breaks(self):
"""The breaks of this beatmap.
"""
return tuple(e for e in self.events if isinstance(e, Break))

@lazyval
def backgrounds(self):
"""The backgrounds of this beatmap.
"""
return tuple(e for e in self.events if isinstance(e, Background))
tybug marked this conversation as resolved.
Show resolved Hide resolved

@lazyval
def videos(self):
"""The videos of this beatmap.
"""
return tuple(e for e in self.events if isinstance(e, Video))

def sprites(self):
"""The sprites of this beatmap.
"""
return tuple(e for e in self.events if isinstance(e, Sprite))

def animations(self):
"""The animations of this beatmap.
"""
return tuple(e for e in self.events if isinstance(e, Animation))


def hit_objects(self,
*,
circles=True,
Expand Down Expand Up @@ -2399,7 +2553,9 @@ def commit_group():
commit_group()
current_group = line[1:-1]
else:
group_buffer.append(line)
is_storyboard_param = line[0] == '_' or line[0] == ' '
if not is_storyboard_param:
group_buffer.append(line)
tybug marked this conversation as resolved.
Show resolved Hide resolved

# commit the final group
commit_group()
Expand Down Expand Up @@ -2454,6 +2610,11 @@ def parse(cls, data):
parent = timing_point
timing_points.append(timing_point)

events = []
for raw_event in groups['Events']:
event = Event.parse(raw_event)
events.append(event)

slider_multiplier = _get_as_float(
groups,
'Difficulty',
Expand Down Expand Up @@ -2566,7 +2727,7 @@ def parse(cls, data):
),
groups['HitObjects'],
)),

events=events,
)

def pack(self):
Expand Down
11 changes: 10 additions & 1 deletion slider/tests/test_beatmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,8 @@ def test_hit_objects_stacking():
slider_multiplier=1,
slider_tick_rate=1,
timing_points=[],
hit_objects=hit_objects
hit_objects=hit_objects,
events=[]
)
radius = slider.beatmap.circle_radius(5)
stack_offset = radius / 10
Expand Down Expand Up @@ -239,6 +240,14 @@ def test_od(beatmap):
assert beatmap.od() == 9


def test_background(beatmap):
background = beatmap.backgrounds[0]
assert background.filename == 'miiro_no_scenario.png'
assert background.x_offset == 0
assert background.y_offset == 0
assert background.start_time == timedelta(milliseconds=0)


def test_pack(beatmap):
# Pack the beatmap and parse it again to see if there is difference.
packed_str = beatmap.pack()
Expand Down