@@ -28,28 +28,102 @@ local AUTHOR = 'Sudospective'
2828
2929
3030local POptions = {}
31+ local POToLower = {}
3132local plrcount = 0
3233for 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
3438end
3539
3640local 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+
37107local modlist = {}
38108local mod_percents = {}
39109local note_percents = {}
40110local custom_mods = {}
41111local default_mods = {}
42112local active = {}
113+ local active_mods = {}
43114
44115local function PlayerCount (num )
116+ if not num then return plrcount end
45117 plrcount = num
46118 for pn = 1 , plrcount do
47119 mod_percents [pn ] = {}
120+ mod_percents [pn ].start_index = 0
48121 note_percents [pn ] = {}
49122 custom_mods [pn ] = {}
50123 default_mods [pn ] = {}
51124 active [pn ] = {}
52125 end
126+ return Mods
53127end
54128
55129PlayerCount (2 )
@@ -79,43 +153,42 @@ local function ApplyMods(mod, percent, pn)
79153end
80154--]]
81155-- 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
82172local function ApplyMods ()
83173 for pn = 1 , plrcount do
84174 local modstring = ' '
175+ resolve_customs (mod_percents [pn ], pn )
85176 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
107178 if POptions [pn ][mod ] then
108- if mod :sub (2 ) == ' Mod ' then
179+ if mod :sub (2 , - 1 ): lower () == ' mod ' then
109180 POptions [pn ][mod ](POptions [pn ], percent , 9e9 )
110181 else
111182 POptions [pn ][mod ](POptions [pn ], percent * 0.01 , 9e9 )
112183 end
184+ elseif POToLower [mod :lower ()] and not mod :find (' mod' ) then
185+ POToLower [mod ](POptions [pn ], percent * 0.01 , 9e9 )
113186 elseif mod :lower () == ' xmod' then
114187 modstring = modstring .. ' *-1 ' .. percent .. mod :sub (1 , 1 ):lower ().. ' ,'
115188 elseif mod :sub (2 ):lower () == ' mod' then
116189 modstring = modstring .. ' *-1 ' .. mod :sub (1 , 1 ):lower ().. percent .. ' ,'
117190 else
118- modstring = modstring .. ' *-1 ' .. ( percent ) .. ' ' .. mod :lower ().. ' ,'
191+ modstring = modstring .. ' *-1 ' .. percent .. ' ' .. mod :lower ().. ' ,'
119192 end
120193 end
121194 end
@@ -136,13 +209,13 @@ local function ApplyNotes(beat, col, mod, percent, pn)
136209end
137210
138211local function UpdateMods ()
139- for i , m in ipairs (modlist ) do
212+ for i , m in ipairs (active_mods ) do
140213 for j , v in ipairs (m .Modifiers ) do
141214 -- If the player where we're trying to access is not available, then don't even update.
142215 if m .Player and not POptions [m .Player ] then break end
143216 local BEAT = std .BEAT
144217 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
146219 if m .Type == ' Blend' then
147220 -- Ease blending is a work in progress. Try to make sure two eases don't use the same mod.
148221 v [3 ] = v [3 ] or mod_percents [pn ][v [2 ]] or 0
@@ -151,24 +224,25 @@ local function UpdateMods()
151224 active [pn ][v [2 ]][v [4 ]] = m
152225 local perc = 0
153226 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
163237 end
164238 end
165239 mod_percents [pn ][v [2 ]] = perc
166240 elseif m .Type == ' Player' then
167241 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 ) )
169243 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
172246 elseif m .Type == ' Note' then
173247 local notemod = v [4 ].. ' |' .. v [1 ].. ' |' .. v [2 ]
174248 v [5 ] = v [5 ] or note_percents [pn ][notemod ] or default_mods [pn ][notemod ] or 0
@@ -181,7 +255,7 @@ local function UpdateMods()
181255 local cur_m = active [pn ][notemod ][n ]
182256 local cur_v3 = cur_m .Modifiers [j ][3 ]
183257 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
185259 if m .Length == 0 then cur_ease = 1 end
186260 local cur_perc = cur_ease * (cur_v3 - cur_v5 )
187261 if # active [pn ][notemod ] == n then
@@ -190,12 +264,13 @@ local function UpdateMods()
190264 end
191265 note_percents [pn ][notemod ] = perc
192266 end
193- elseif BEAT > (m .Start + m .Length ) then
267+ elseif BEAT >= (m .Start + m .Length ) then
194268 if m .Type == ' Player' then
195269 v [3 ] = v [3 ] or mod_percents [pn ][v [2 ]] or 0
196270 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
199274 end
200275 elseif m .Type == ' Note' then
201276 v [5 ] = v [5 ] or note_percents [pn ][notemod ] or 0
@@ -205,9 +280,6 @@ local function UpdateMods()
205280 -- active[pn][notemod][v[6]] = nil
206281 end
207282 end
208- if j == # m .Modifiers then
209- -- m.Modifiers = {}
210- end
211283 end
212284 end
213285 end
@@ -228,11 +300,13 @@ local function Default(self, modtable)
228300 -- printerr('Mods:Default')
229301 for pn = 1 , plrcount do
230302 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 ]
232305 end
233306 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
236310end
237311-- Define a new mod.
238312local function Define (self , name , func , ret )
@@ -386,17 +460,43 @@ local function Mirin(self, t, offset, pn)
386460 return res
387461end
388462
463+ local function GetPercents ()
464+ return mod_percents
465+ end
466+
389467FG [# FG + 1 ] = Def .Actor {
390468 ReadyCommand = function (self )
391469 for pn = 1 , plrcount do
392470 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
393480 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 )
396483 end ,
397484 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
398492 UpdateMods ()
399493 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
400500 end
401501}
402502
@@ -408,9 +508,11 @@ Mods = {
408508 FromFile = FromFile ,
409509 Define = Define ,
410510 Insert = Insert ,
511+ -- Note = Note,
411512 Mirin = Mirin ,
412513 Exsch = Exsch ,
413514 Default = Default ,
515+ GetPercents = GetPercents ,
414516}
415517Mods .__index = Mods
416518
0 commit comments