Skip to content

Commit a1ea24f

Browse files
committed
Refatoração
1 parent 34efa24 commit a1ea24f

File tree

8 files changed

+1906
-1659
lines changed

8 files changed

+1906
-1659
lines changed

OsuLearn.ipynb

+1,599-1,311
Large diffs are not rendered by default.

osu/preview/__main__.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,17 @@
1515
BEATMAPS_FOLDER = 'C:\\Program Files (x86)\\Jogos\\osu!\\Songs\\'
1616
BEATMAP = glob(BEATMAPS_FOLDER + "\\**\\*Uta*Himei*.osu")[0]
1717
#BEATMAP = glob(BEATMAPS_FOLDER + "\\**\\*Burnt Rice*ScubDomino*Lemon*.osu")[0]
18-
#BEATMAP = glob(BEATMAPS_FOLDER + "\\**\\*Kami no Kotoba*byfar*Voice of God*.osu")[0]
18+
#BEATMAP = glob(BEATMAPS_FOLDER + "\\**\\*Quantum Entanglement*.osu")[0]
1919
#BEATMAP = glob(BEATMAPS_FOLDER + "\\**\\*Imprinting*9.5*.osu")[0]
2020
#BEATMAP = glob(BEATMAPS_FOLDER + "\\**\\*DAYBREAK*Horizon[]]*.osu")[0]
2121
beatmap = _beatmap.load(BEATMAP)
2222

2323
AUDIO = os.path.dirname(BEATMAP) + "\\" + beatmap["AudioFilename"]
2424

25+
#REPLAY = glob('C:\\Program Files (x86)\\Jogos\\osu!\\Replays\\BzMasked -*Quantum Entanglement*.osr')[0]
2526
REPLAY = 'C:\\Program Files (x86)\\Jogos\\osu!\\Replays\\BzMasked - Imperial Circus Dead Decadence - Uta [Himei] (2018-04-29) Osu.osr'
2627
REPLAY_DATA = 'osu\\replay.npy'
27-
REPLAY_SAMPLING_RATE = 16
28+
REPLAY_SAMPLING_RATE = 32
2829

2930
my_replay = _replay.load(REPLAY)
3031

osu/preview/beatmap.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
from . import hitobjects
33

44
COLOR_PALLETE = [
5-
( 255, 0, 0 ),
5+
#( 255, 0, 0 ),
6+
( 255, 0, 255 ),
67
( 255, 255, 0 ),
7-
( 0, 255, 0 ),
8+
#( 0, 255, 0 ),
9+
( 0, 255, 255 ),
810
( 0, 0, 255 ),
911
]
1012

@@ -28,14 +30,15 @@ def render(self, screen, time):
2830
self.last_new_combo = visible_objects[0].time
2931

3032
color_index = self.color_index
33+
3134
circle_radius = int(self.beatmap.circle_radius())
3235
beat_duration = self.beatmap.beat_duration(time)
3336

3437
for i in range(len(visible_objects)):
3538
obj = visible_objects[i]
3639

37-
if i > 0 and obj.new_combo:
38-
color_index += visible_objects[0].combo_skip + 1
40+
if obj.new_combo and obj.time > self.last_new_combo:
41+
color_index += obj.combo_skip + 1
3942
color_index %= len(COLOR_PALLETE)
4043

4144
hitobjects.render(

osu/rulesets/_util/binfile.py

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
def read_byte(file):
2+
return ord(file.read(1))
3+
4+
def read_short(file):
5+
return read_byte(file) + (read_byte(file) << 8)
6+
7+
def read_int(file):
8+
return read_short(file) + (read_short(file) << 16)
9+
10+
def read_long(file):
11+
return read_int(file) + (read_int(file) << 32)
12+
13+
def read_uleb128(file):
14+
n = 0
15+
i = 0
16+
while True:
17+
byte = read_byte(file)
18+
n += (byte & 0x7F) << i
19+
if byte & 0x80 != 0:
20+
i += 7
21+
else:
22+
return n
23+
24+
def read_binary_string(file):
25+
while True:
26+
flag = read_byte(file)
27+
if flag == 0x00:
28+
return ""
29+
elif flag == 0x0b:
30+
length = read_uleb128(file)
31+
return file.read(length).decode('utf8')
32+
else:
33+
raise RuntimeError("Invalid file")

osu/rulesets/beatmap.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ def visible_objects(self, time, count=None):
167167
preempt, _ = self.approach_rate()
168168

169169
i = bsearch(self.hit_objects, time, lambda obj: obj.time + preempt - obj.duration(self.beat_duration(obj.time), self['SliderMultiplier']))
170-
i -= 1
170+
i -= 5
171171
if i < 0:
172172
i = 0
173173

osu/rulesets/replay.py

+16-230
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,24 @@
44
from matplotlib.path import Path
55
import matplotlib.patches as mpatches
66

7+
from enum import IntFlag
8+
79
from . import beatmap as osu_beatmap
810
from ._util.bsearch import bsearch
11+
from ._util.binfile import *
12+
13+
from functools import reduce
14+
15+
class Mod(IntFlag):
16+
DT = 0x40
17+
HR = 0x10
918

1019
class Replay:
1120
def __init__(self, file):
1221
self.game_mode = read_byte(file)
1322

1423
# Sem minigames
15-
if self.game_mode != 0:
16-
raise RuntimeError("Not a osu!std replay")
24+
assert self.game_mode == 0, "Not a osu!std replay"
1725

1826
# Versão do osu! e hash do mapa. A gente ignora.
1927
self.osu_version = read_int(file)
@@ -43,7 +51,7 @@ def __init__(self, file):
4351
self.accuracy = (self.n_300s + self.n_100s / 3 + self.n_50s / 6) / total
4452

4553
# Mods (ignora)
46-
self.mods = read_int(file)
54+
self.mods = Mod(read_int(file))
4755

4856
# Gráfico de vida. Vide site para o formato.
4957
life_graph = read_binary_string(file)
@@ -70,6 +78,10 @@ def __init__(self, file):
7078
# Não usado
7179
_ = read_long(file)
7280

81+
def has_mods(self, *mods):
82+
mask = reduce(lambda x, y: x|y, mods)
83+
return bool(self.mods & mask)
84+
7385
def frame(self, time):
7486
index = bsearch(self.data, time, lambda f: f[0])
7587

@@ -84,232 +96,6 @@ def frame(self, time):
8496

8597
return self.data[index][1:]
8698

87-
def read_byte(file):
88-
return ord(file.read(1))
89-
90-
def read_short(file):
91-
return read_byte(file) + (read_byte(file) << 8)
92-
93-
def read_int(file):
94-
return read_short(file) + (read_short(file) << 16)
95-
96-
def read_long(file):
97-
return read_int(file) + (read_int(file) << 32)
98-
99-
def read_uleb128(file):
100-
n = 0
101-
i = 0
102-
while True:
103-
byte = read_byte(file)
104-
n += (byte & 0x7F) << i
105-
if byte & 0x80 != 0:
106-
i += 7
107-
else:
108-
return n
109-
110-
def read_binary_string(file):
111-
while True:
112-
flag = read_byte(file)
113-
if flag == 0x00:
114-
return ""
115-
elif flag == 0x0b:
116-
length = read_uleb128(file)
117-
return file.read(length).decode('utf8')
118-
else:
119-
raise RuntimeError("Invalid file")
120-
12199
def load(filename):
122100
with open(filename, "rb") as file:
123-
return Replay(file)
124-
125-
def draw_cursor(ax, x, y, z):
126-
keys = int(z)
127-
if keys & 0x01:
128-
cursor = 'ro'
129-
elif keys & 0x02:
130-
cursor = 'go'
131-
else:
132-
cursor = 'y+'
133-
ax.plot([x], [384 - y], cursor)
134-
135-
def draw_slider(ax, beatmap, radius, t, obj):
136-
path = obj[5].split("|")
137-
stype = path.pop(0)
138-
139-
points = [(obj[0], 384 - obj[1])] + [(int(x), 384 - int(y)) for x, y in [t.split(':') for t in path]]
140-
codes = []
141-
142-
if stype == 'L':
143-
codes.append(Path.MOVETO)
144-
for i in range(1, len(points)):
145-
codes.append(Path.LINETO)
146-
147-
elif stype == 'P': # Círculo perfeito
148-
codes.append(Path.MOVETO)
149-
codes.append(Path.CURVE3)
150-
codes.append(Path.CURVE3)
151-
152-
else: # Bezier
153-
n = 0
154-
for i in range(0, len(points)):
155-
n += 1
156-
repeated = i + 1 < len(points) and points[i] == points[i + 1]
157-
if i + 1 == len(points) or n == 4 or repeated:
158-
if n == 2:
159-
codes.append(Path.MOVETO)
160-
codes.append(Path.LINETO)
161-
elif n == 3:
162-
codes.append(Path.MOVETO)
163-
codes.append(Path.CURVE3)
164-
codes.append(Path.CURVE3)
165-
elif n == 4:
166-
codes.append(Path.MOVETO)
167-
codes.append(Path.CURVE4)
168-
codes.append(Path.CURVE4)
169-
codes.append(Path.CURVE4)
170-
n = 0
171-
172-
preempt, fade = beatmap.approach_rate()
173-
duration = beatmap.slider_duration(obj)
174-
if t < obj[2] + preempt + duration:
175-
alpha = 1
176-
else:
177-
alpha = 1 - (t - (obj[2] + preempt + duration)) / osu_beatmap.SLIDER_FADEOUT
178-
179-
slider = mpatches.PathPatch(
180-
Path(points, codes),
181-
fc="none", capstyle='round', transform=ax.transData, fill=False,
182-
edgecolor=(1.0, 1.0, 1.0, alpha), linewidth=radius, joinstyle='bevel', zorder=-2
183-
)
184-
ax.add_patch(slider)
185-
186-
slider = mpatches.PathPatch(
187-
Path(points, codes),
188-
fc="none", capstyle='round', transform=ax.transData, fill=False,
189-
edgecolor="#333333", linewidth=radius-4, joinstyle='bevel', zorder=-1
190-
)
191-
ax.add_patch(slider)
192-
193-
last_nc = 0
194-
current_color = 0
195-
196-
def draw_hit_objects(ax, beatmap, t, objs):
197-
global current_color, last_nc
198-
199-
circles = ([], [], [])
200-
radius = (27.2 - 2.24 * beatmap['CircleSize'])
201-
combo_colors = [(1.0, 0, 0), (0, 1.0, 0), (0, 1.0, 0), (1.0, 1.0, 0)]
202-
203-
# Sliders
204-
for obj in objs:
205-
color = current_color
206-
if obj[3] & 4 and obj[2] > last_nc:
207-
last_nc = obj[2]
208-
current_color += 1
209-
current_color %= len(combo_colors)
210-
color = current_color
211-
elif obj[2] < last_nc:
212-
color = (len(combo_colors) + current_color - 1) % len(combo_colors)
213-
214-
# Spinner
215-
if obj[3] & 8:
216-
ax.plot([obj[0]], [obj[1]], 'yo', fillstyle='none', markersize=128)
217-
218-
else:
219-
# Slider
220-
if obj[3] & 2:
221-
draw_slider(ax, beatmap, radius, t, obj)
222-
223-
# Círculo (Slider também tem)
224-
circles[0].append(obj[0])
225-
circles[1].append(384 - obj[1])
226-
227-
preempt, fade = beatmap.approach_rate()
228-
229-
if t > obj[2] + preempt:
230-
alpha = 1 - (t - obj[2] - preempt) / 100
231-
elif t < obj[2] + fade:
232-
alpha = 1 - ((obj[2] + fade) - t) / fade
233-
else:
234-
alpha = 1
235-
alpha = max([0, min(1, alpha)])
236-
237-
238-
r, g, b = combo_colors[color]
239-
circles[2].append((r, g, b, alpha))
240-
241-
ax.scatter(circles[0], circles[1], color=circles[2], s=radius ** 2)
242-
243-
def cursor_state(replay, time):
244-
tt = 0
245-
for t in replay:
246-
w, x, y, z = t
247-
248-
if w > 0:
249-
tt += w
250-
else:
251-
continue
252-
253-
if tt > time:
254-
break
255-
256-
return x, y, z
257-
258-
def preview(beatmap, replay_data):
259-
plt.ion()
260-
261-
fig = plt.figure(facecolor='#333333')
262-
ax = fig.add_subplot(1, 1, 1)
263-
264-
ax.clear()
265-
ax.axis('off')
266-
fig.canvas.draw()
267-
268-
plt.show()
269-
270-
delta = 0
271-
tt = 0
272-
for t in replay_data[0]:
273-
w, x, y, z = t
274-
275-
if w > 0:
276-
tt += w
277-
278-
sec = w / 1000
279-
280-
# Frame skip
281-
if delta > sec:
282-
delta -= sec
283-
continue
284-
285-
# Espera o tempo especificado entre o estado atual e o anterior
286-
# no arquivo de replay
287-
if sec > 0:
288-
time.sleep(sec - delta)
289-
delta = 0
290-
291-
start = time.time()
292-
293-
# Limpa tudo
294-
ax.clear()
295-
ax.axis('off')
296-
ax.set_xlim((-32, 512 + 32))
297-
ax.set_ylim((-32, 384 + 32))
298-
fig.canvas.draw_idle()
299-
300-
# Desenha o objeto do mapa
301-
objs = beatmap.visible_objects(tt)
302-
303-
if len(objs) > 0:
304-
draw_hit_objects(ax, beatmap, tt, objs)
305-
306-
draw_cursor(ax, x, y, z)
307-
308-
for d in replay_data[1:]:
309-
x, y, z = cursor_state(d, tt)
310-
draw_cursor(ax, x, y, z)
311-
312-
fig.canvas.draw()
313-
314-
# Calcula o atraso
315-
delta += time.time() - start
101+
return Replay(file)

0 commit comments

Comments
 (0)