@@ -28,28 +28,102 @@ local AUTHOR = 'Sudospective'
28
28
29
29
30
30
local POptions = {}
31
+ local POToLower = {}
31
32
local plrcount = 0
32
33
for i , v in ipairs ( GAMESTATE :GetEnabledPlayers () ) do
33
- POptions [i ] = GAMESTATE :GetPlayerState (v ):GetPlayerOptions (' ModsLevel_Current' )
34
+ POptions [i ] = GAMESTATE :GetPlayerState (v ):GetPlayerOptions (' ModsLevel_Song' )
35
+ end
36
+ for k , v in pairs (PlayerOptions ) do
37
+ POToLower [k :lower ()] = v
34
38
end
35
39
36
40
local instant_tween = function (t ) return 1 end
41
+
42
+ -- From the Mirin template. It just works? Fuck me... ~Sudo
43
+ local function insertion_sort (t , l , h , c )
44
+ for i = l + 1 , h do
45
+ local k = l
46
+ local v = t [i ]
47
+ for j = i , l + 1 , - 1 do
48
+ if c (v , t [j - 1 ]) then
49
+ t [j ] = t [j - 1 ]
50
+ else
51
+ k = j
52
+ break
53
+ end
54
+ end
55
+ t [k ] = v
56
+ end
57
+ end
58
+ local function merge (t , b , l , m , h , c )
59
+ if c (t [m ], t [m + 1 ]) then
60
+ return
61
+ end
62
+ local i , j , k
63
+ i = 1
64
+ for j = l , m do
65
+ b [i ] = t [j ]
66
+ i = i + 1
67
+ end
68
+ i , j , k = 1 , m + 1 , l
69
+ while k < j and j <= h do
70
+ if c (t [j ], b [i ]) then
71
+ t [k ] = t [j ]
72
+ j = j + 1
73
+ else
74
+ t [k ] = b [i ]
75
+ i = i + 1
76
+ end
77
+ k = k + 1
78
+ end
79
+ for k = k , j - 1 do
80
+ t [k ] = b [i ]
81
+ i = i + 1
82
+ end
83
+ end
84
+ local magic_number = 12
85
+ local function merge_sort (t , b , l , h , c )
86
+ if h - l < magic_number then
87
+ insertion_sort (t , l , h , c )
88
+ else
89
+ local m = math.floor ((l + h ) / 2 )
90
+ merge_sort (t , b , l , m , c )
91
+ merge_sort (t , b , m + 1 , h , c )
92
+ merge (t , b , l , m , h , c )
93
+ end
94
+ end
95
+ local function default_comparator (a , b ) return a < b end
96
+ local function flip_comparator (c ) return function (a , b ) return c (b , a ) end end
97
+ local function stable_sort (t , c )
98
+ if not t [2 ] then return t end
99
+ c = c or default_comparator
100
+ local n = t .n
101
+ local b = {}
102
+ b [math.floor ((n + 1 ) / 2 )] = t [1 ]
103
+ merge_sort (t , b , 1 , n , c )
104
+ return t
105
+ end
106
+
37
107
local modlist = {}
38
108
local mod_percents = {}
39
109
local note_percents = {}
40
110
local custom_mods = {}
41
111
local default_mods = {}
42
112
local active = {}
113
+ local active_mods = {}
43
114
44
115
local function PlayerCount (num )
116
+ if not num then return plrcount end
45
117
plrcount = num
46
118
for pn = 1 , plrcount do
47
119
mod_percents [pn ] = {}
120
+ mod_percents [pn ].start_index = 0
48
121
note_percents [pn ] = {}
49
122
custom_mods [pn ] = {}
50
123
default_mods [pn ] = {}
51
124
active [pn ] = {}
52
125
end
126
+ return Mods
53
127
end
54
128
55
129
PlayerCount (2 )
@@ -79,43 +153,42 @@ local function ApplyMods(mod, percent, pn)
79
153
end
80
154
--]]
81
155
-- TODO: Make sure this doesn't run on unnecessary frames. ~Sudo
156
+ -- NOTE: Due to the nature of ModsLevel_Current, EVERY frame is a necessary frame. ~Sudo
157
+ local function resolve_customs (mods , pn )
158
+ for mod , percent in pairs (mods ) do
159
+ if custom_mods [pn ][mod ] ~= nil then
160
+ local new_percs = {custom_mods [pn ][mod ].Function (percent , pn )}
161
+ local new_mods = custom_mods [pn ][mod ].Return
162
+ local t = {}
163
+ for i = 1 , # new_mods do
164
+ t [new_mods [i ]] = new_percs [i ]
165
+ end
166
+ resolve_customs (t , pn )
167
+ else
168
+ mod_percents [pn ][mod ] = percent
169
+ end
170
+ end
171
+ end
82
172
local function ApplyMods ()
83
173
for pn = 1 , plrcount do
84
174
local modstring = ' '
175
+ resolve_customs (mod_percents [pn ], pn )
85
176
for mod , percent in pairs (mod_percents [pn ]) do
86
- if custom_mods [pn ][mod ] ~= nil then
87
- local new_perc = {custom_mods [pn ][mod ].Function (percent , pn )}
88
- local new_mod = custom_mods [pn ][mod ].Return
89
- for i = 1 , # new_perc do
90
- percent = new_perc [i ]
91
- mod = new_mod [i ]
92
- if POptions [pn ][mod ] then
93
- if mod :sub (2 ) == ' Mod' then
94
- POptions [pn ][mod ](POptions [pn ], percent , 9e9 )
95
- else
96
- POptions [pn ][mod ](POptions [pn ], percent * 0.01 , 9e9 )
97
- end
98
- elseif mod :lower () == ' xmod' then
99
- modstring = modstring .. ' *-1 ' .. percent .. mod :sub (1 , 1 ):lower ().. ' ,'
100
- elseif mod :sub (2 ):lower () == ' mod' then
101
- modstring = modstring .. ' *-1 ' .. mod :sub (1 , 1 ):lower ().. percent .. ' ,'
102
- else
103
- modstring = modstring .. ' *-1 ' .. (percent ).. ' ' .. mod :lower ().. ' ,'
104
- end
105
- end
106
- elseif mod then
177
+ if mod_percents [pn ][mod ] and not custom_mods [pn ][mod ] then
107
178
if POptions [pn ][mod ] then
108
- if mod :sub (2 ) == ' Mod ' then
179
+ if mod :sub (2 , - 1 ): lower () == ' mod ' then
109
180
POptions [pn ][mod ](POptions [pn ], percent , 9e9 )
110
181
else
111
182
POptions [pn ][mod ](POptions [pn ], percent * 0.01 , 9e9 )
112
183
end
184
+ elseif POToLower [mod :lower ()] and not mod :find (' mod' ) then
185
+ POToLower [mod ](POptions [pn ], percent * 0.01 , 9e9 )
113
186
elseif mod :lower () == ' xmod' then
114
187
modstring = modstring .. ' *-1 ' .. percent .. mod :sub (1 , 1 ):lower ().. ' ,'
115
188
elseif mod :sub (2 ):lower () == ' mod' then
116
189
modstring = modstring .. ' *-1 ' .. mod :sub (1 , 1 ):lower ().. percent .. ' ,'
117
190
else
118
- modstring = modstring .. ' *-1 ' .. ( percent ) .. ' ' .. mod :lower ().. ' ,'
191
+ modstring = modstring .. ' *-1 ' .. percent .. ' ' .. mod :lower ().. ' ,'
119
192
end
120
193
end
121
194
end
@@ -136,13 +209,13 @@ local function ApplyNotes(beat, col, mod, percent, pn)
136
209
end
137
210
138
211
local function UpdateMods ()
139
- for i , m in ipairs (modlist ) do
212
+ for i , m in ipairs (active_mods ) do
140
213
for j , v in ipairs (m .Modifiers ) do
141
214
-- If the player where we're trying to access is not available, then don't even update.
142
215
if m .Player and not POptions [m .Player ] then break end
143
216
local BEAT = std .BEAT
144
217
local pn = m .Player
145
- if ( BEAT >= m .Start and BEAT <= (m .Start + m .Length ) ) then
218
+ if BEAT >= m .Start and BEAT < (m .Start + m .Length ) then
146
219
if m .Type == ' Blend' then
147
220
-- Ease blending is a work in progress. Try to make sure two eases don't use the same mod.
148
221
v [3 ] = v [3 ] or mod_percents [pn ][v [2 ]] or 0
@@ -151,24 +224,25 @@ local function UpdateMods()
151
224
active [pn ][v [2 ]][v [4 ]] = m
152
225
local perc = 0
153
226
for n = 1 , v [4 ] do
154
- local offset = (n > v [4 ]) and 1 or 0
155
- local cur_m = active [pn ][v [2 ]][n ]
156
- local cur_v1 = cur_m .Modifiers [j ][1 ]
157
- local cur_v3 = cur_m .Modifiers [j ][3 ]
158
- local cur_ease = cur_m .Ease ((BEAT - cur_m .Start ) / cur_m .Length ) - offset
159
- if m .Length == 0 then cur_ease = cur_m .Ease (1 ) - offset end
160
- local cur_perc = cur_ease * (cur_v1 - cur_v3 )
161
- if # active [pn ][v [2 ]] == n then
162
- perc = perc + (cur_v3 + cur_perc )
227
+ if n <= v [4 ] then
228
+ local offset = (n < v [4 ]) and 1 or 0
229
+ local cur_m = active [pn ][v [2 ]][n ]
230
+ local cur_v1 = cur_m .Modifiers [j ][1 ]
231
+ local cur_v3 = cur_m .Modifiers [j ][3 ]
232
+ local cur_off = offset
233
+ local cur_ease = cur_m .Ease ((BEAT - cur_m .Start ) * OFMath .oneoverx (cur_m .Length )) - cur_off
234
+ if m .Length == 0 then cur_ease = cur_m .Ease (1 ) - cur_off end
235
+ local cur_perc = cur_ease * (cur_v1 - cur_v3 ) + cur_v3
236
+ perc = perc + cur_perc
163
237
end
164
238
end
165
239
mod_percents [pn ][v [2 ]] = perc
166
240
elseif m .Type == ' Player' then
167
241
v [3 ] = v [3 ] or mod_percents [pn ][v [2 ]] or default_mods [pn ][v [2 ]] or 0
168
- local ease = m .Ease ((BEAT - m .Start ) / m .Length )
242
+ local ease = m .Ease ((BEAT - m .Start ) * OFMath . oneoverx ( m .Length ) )
169
243
if m .Length == 0 then ease = m .Ease (1 ) end
170
- local perc = ease * (v [1 ] - v [3 ])
171
- mod_percents [pn ][v [2 ]] = perc + v [ 3 ]
244
+ local perc = ease * (v [1 ] - v [3 ]) + v [ 3 ]
245
+ mod_percents [pn ][v [2 ]] = perc
172
246
elseif m .Type == ' Note' then
173
247
local notemod = v [4 ].. ' |' .. v [1 ].. ' |' .. v [2 ]
174
248
v [5 ] = v [5 ] or note_percents [pn ][notemod ] or default_mods [pn ][notemod ] or 0
@@ -181,7 +255,7 @@ local function UpdateMods()
181
255
local cur_m = active [pn ][notemod ][n ]
182
256
local cur_v3 = cur_m .Modifiers [j ][3 ]
183
257
local cur_v5 = cur_m .Modifiers [j ][5 ]
184
- local cur_ease = cur_m .Ease ((BEAT - cur_m .Start ) / cur_m .Length ) - offset
258
+ local cur_ease = cur_m .Ease ((BEAT - cur_m .Start ) * OFMath . oneoverx ( cur_m .Length ) ) - offset
185
259
if m .Length == 0 then cur_ease = 1 end
186
260
local cur_perc = cur_ease * (cur_v3 - cur_v5 )
187
261
if # active [pn ][notemod ] == n then
@@ -190,12 +264,13 @@ local function UpdateMods()
190
264
end
191
265
note_percents [pn ][notemod ] = perc
192
266
end
193
- elseif BEAT > (m .Start + m .Length ) then
267
+ elseif BEAT >= (m .Start + m .Length ) then
194
268
if m .Type == ' Player' then
195
269
v [3 ] = v [3 ] or mod_percents [pn ][v [2 ]] or 0
196
270
mod_percents [pn ][v [2 ]] = m .Ease (1 ) * (v [1 ] - v [3 ]) + v [3 ]
197
- if v [4 ] and active [pn ][v [2 ]] then
198
- -- active[pn][v[2]][v[4]] = nil
271
+ if v [4 ] and active [pn ][v [2 ]][v [4 ]] then
272
+ table.remove (active [pn ][v [2 ]], v [4 ])
273
+ v [4 ] = v [4 ] - 1
199
274
end
200
275
elseif m .Type == ' Note' then
201
276
v [5 ] = v [5 ] or note_percents [pn ][notemod ] or 0
@@ -205,9 +280,6 @@ local function UpdateMods()
205
280
-- active[pn][notemod][v[6]] = nil
206
281
end
207
282
end
208
- if j == # m .Modifiers then
209
- -- m.Modifiers = {}
210
- end
211
283
end
212
284
end
213
285
end
@@ -228,11 +300,13 @@ local function Default(self, modtable)
228
300
-- printerr('Mods:Default')
229
301
for pn = 1 , plrcount do
230
302
for i = 1 , # modtable do
231
- table.insert (default_mods [pn ], modtable [i ])
303
+ default_mods [pn ][modtable [i ][2 ]] = modtable [i ][1 ]
304
+ mod_percents [pn ][modtable [i ][2 ]] = modtable [i ][1 ]
232
305
end
233
306
end
234
- local res = self :Insert (std .START , 0 , instant_tween , modtable )
235
- return res
307
+ -- local res = self:Insert(std.START, 0, instant_tween, modtable)
308
+ -- return res
309
+ return self
236
310
end
237
311
-- Define a new mod.
238
312
local function Define (self , name , func , ret )
@@ -386,17 +460,43 @@ local function Mirin(self, t, offset, pn)
386
460
return res
387
461
end
388
462
463
+ local function GetPercents ()
464
+ return mod_percents
465
+ end
466
+
389
467
FG [# FG + 1 ] = Def .Actor {
390
468
ReadyCommand = function (self )
391
469
for pn = 1 , plrcount do
392
470
if POptions [pn ] then POptions [pn ]:FromString (' *-1 clearall' ) end
471
+ for mod , percent in pairs (default_mods [pn ]) do
472
+ mod_percents [pn ][mod ] = percent
473
+ end
474
+ end
475
+ self :sleep (self :GetEffectDelta ()):queuecommand (' Sort' )
476
+ end ,
477
+ SortCommand = function (self )
478
+ local function compare (a , b )
479
+ return a .Start < b .Start
393
480
end
394
- -- Before we include the Note function, make sure we can actually call the required command.
395
- if std . PL [ 1 ]. Player . AddNoteMod then Mods . Note = Note end
481
+ modlist . n = # modlist
482
+ stable_sort ( modlist , compare )
396
483
end ,
397
484
UpdateCommand = function (self )
485
+ active_mods = {}
486
+ for i = 1 , # modlist do
487
+ local mod = modlist [i ]
488
+ if std .BEAT >= mod .Start then
489
+ table.insert (active_mods , mod )
490
+ end
491
+ end
398
492
UpdateMods ()
399
493
ApplyMods ()
494
+ for i = # modlist , 1 , - 1 do
495
+ local mod = modlist [i ]
496
+ if std .BEAT > mod .Start + mod .Length then
497
+ table.remove (modlist , i )
498
+ end
499
+ end
400
500
end
401
501
}
402
502
@@ -408,9 +508,11 @@ Mods = {
408
508
FromFile = FromFile ,
409
509
Define = Define ,
410
510
Insert = Insert ,
511
+ -- Note = Note,
411
512
Mirin = Mirin ,
412
513
Exsch = Exsch ,
413
514
Default = Default ,
515
+ GetPercents = GetPercents ,
414
516
}
415
517
Mods .__index = Mods
416
518
0 commit comments