-- 5.1/itgm/outfox compat only!
if not PlayerOptions.ConfusionOffset then 
	SCREENMAN:SystemMessage('This file has mods that are not compatible with your version of StepMania! Please play on ITGMania/5.1/OutFox.')
	return {} 
end

local showAFTs = DISPLAY:SupportsRenderToTexture()

local sw = SCREEN_WIDTH
local sh = SCREEN_HEIGHT
local scx = SCREEN_CENTER_X
local scy = SCREEN_CENTER_Y
local dw = PREFSMAN:GetPreference('DisplayWidth')
local dh = PREFSMAN:GetPreference('DisplayHeight')
local dcx = dw/2
local dcy = dh/2
local zr = sw/dw
local rz = dw/sw

local comboProxies = {}
local pp = {}

local checked = false
local mod_plr = {}

local FRAME_RATE = 60

-- EASING --
local path = GAMESTATE:GetCurrentSong():GetSongDir()..'lua/'
loadfile(path..'easing.lua')()
loadfile(path..'modhelpers.lua')()

flicker_value = 1

local function Plr(pn)
	return mod_plr[pn]
end
function GetJud(pn)
	return _G['jud'..pn]
end
function GetCom(pn)
	return _G['com'..pn]
end

local po = {GAMESTATE:GetPlayerState(0):GetPlayerOptions'ModsLevel_Song', GAMESTATE:GetPlayerState(1):GetPlayerOptions'ModsLevel_Song'}
local function mod_do(str, pn)
	if pn then
		po[pn]:FromString(str)
	else
		for pn = 1, 2 do
			po[pn]:FromString(str)
		end
	end
end

-- ALIASES --
--These make it so the syntax for the table entries matches the syntax for the nITG template.
--If you want to prefix all your calls with "modhelpers.", "ease.", etc., then you won't need this and can remove this section.
local linear, inQuad, outQuad, inOutQuad, outInQuad, inCubic, outCubic, inOutCubic, outInCubic, inQuart,outQuart, inOutQuart, outInQuart, inQuint, outQuint, inOutQuint, outInQuint, inSine, outSine, inOutSine,outInSine, inExpo, outExpo, inOutExpo, outInExpo, inCirc, outCirc, inOutCirc, outInCirc, inElastic,outElastic, inOutElastic, outInElastic, inBack, outBack, inOutBack, outInBack, inBounce, outBounce,inOutBounce, outInBounce = ease.linear, ease.inQuad, ease.outQuad, ease.inOutQuad, ease.outInQuad,ease.inCubic, ease.outCubic, ease.inOutCubic, ease.outInCubic, ease.inQuart, ease.outQuart,ease.inOutQuart, ease.outInQuart, ease.inQuint, ease.outQuint, ease.inOutQuint, ease.outInQuint,ease.inSine, ease.outSine, ease.inOutSine, ease.outInSine, ease.inExpo, ease.outExpo, ease.inOutExpo,ease.outInExpo, ease.inCirc, ease.outCirc, ease.inOutCirc, ease.outInCirc, ease.inElastic,ease.outElastic, ease.inOutElastic, ease.outInElastic, ease.inBack, ease.outBack, ease.inOutBack,ease.outInBack, ease.inBounce, ease.outBounce, ease.inOutBounce, ease.outInBounce

local perframe, mod_insert, mod2_insert, mod_ease, mod_perframe, mod_message, mod_blacklist, simple_m0d, simple_m0d2, simple_m0d3, mod_wiggle, mod_spring, mod_springt, mod_springt2, mod_spring_adjustable, mod_onebeat, switcfino_add, switcfino_reset, mod_sugarkiller, modulo, reverseRotation, randomXD, mod_bounce, ease_wiggle, ease_wiggleAbs, mod_flicker = modhelpers.perframe, modhelpers.mod_insert, modhelpers.mod2_insert, modhelpers.mod_ease, modhelpers.mod_perframe, modhelpers.mod_message, modhelpers.mod_blacklist, modhelpers.simple_m0d, modhelpers.simple_m0d2, modhelpers.simple_m0d3, modhelpers.mod_wiggle, modhelpers.mod_spring, modhelpers.mod_springt, modhelpers.mod_springt2, modhelpers.mod_spring_adjustable, modhelpers.mod_onebeat, modhelpers.switcfino_add, modhelpers.switcfino_reset, modhelpers.mod_sugarkiller, modhelpers.modulo,modhelpers.reverseRotation, modhelpers.randomXD, modhelpers.mod_bounce, modhelpers.ease_wiggle, modhelpers.ease_wiggleAbs, modhelpers.mod_flicker

local m = mod_insert
local m2 = mod_message
local me = mod_ease
local mb = mod_bounce
local mf = mod_flicker


local l = 'len'
local e = 'end'

mod_firstSeenBeat = GAMESTATE:GetSongBeat() --necessary to prevent long freezes

-- thank u ky dashiel
local isusingreverse = {false, false}
for pn = 1, 2 do
	if po[pn] then
		isusingreverse[pn] = po[pn]:Reverse() ~= 0
	end
end
		
			
---------------------------------------------------------------------------------------
----------------------Begin tables 'n stuff--------------------------------------------
---------------------------------------------------------------------------------------

--beat based mods
--{beat_start, beat_end, mods, len_or_end, player_number}
mods = {
	{0,9999,'*1000 no beat, *1000 no confusionoffset, *1000 no tipsy, *1000 no drunk, *1000 no tornado, *1000 no invert, *1000 no flip, *1000 no tiny','end'},
}

--this is both a message broadcaster and a function runner
--if you put {beat,'String'}, then 'String' is broadcast as a message on that beat
--if you put {beat,function() somecode end}, then function() is run at that beat
--see example on beat 32

curaction = 1
--{beat,thing,persists}
mod_actions = {}

--beat-based ease mods
--{time_start, time_end, mod_start, mod_end, mod, len_or_end, ease_type, player_number, sustaintime, optional_param1, optional_param2}
--valid ease types are:
	--linear
	--inQuad    outQuad    inOutQuad    outInQuad
	--inCubic   outCubic   inOutCubic   outInCubic
	--inQuart   outQuart   inOutQuart   outInQuart
	--inQuint   outQuint   inOutQuint   outInQuint
	--inSine    outSine    inOutSine    outInSine
	--inExpo    outExpo    inOutExpo    outInExpo
	--inCirc    outCirc    inOutCirc    outInCirc
	--inElastic outElastic inOutElastic outInElastic    --can take 2 optional parameters - amplitude & period
	--inBack    outBack    inOutBack    outInBack       --can take 1 optional parameter  - spring amount
	--inBounce  outBounce  inOutBounce  outInBounce
	
mods_ease = {
	-- EXAMPLE TWEEN: from beat 0 to 4, make rotationz go from 360 to 0 with the inOutBack tween
	--{0,4,360,0,'rotationz','end',inOutBack},
}

mod_perframes = {	
}

function mpf(s,e,f)
	table.insert(mod_perframes,{s,e,f})
end

local h = SCREEN_HEIGHT / 480

-- garbage implementation of tweens as mods
local function rotx(a)
	for pn = 1, 2 do
		local b = Plr(pn)
		if b then
			b:rotationx(a)
		end
	end
end 
local function roty(a)
	for pn = 1, 2 do
		local b = Plr(pn)
		if b then
			b:rotationy(a)
		end
	end
end
local function rotz(a)
	for pn = 1, 2 do
		local b = Plr(pn)
		if b then
			b:rotationz(a)
		end
	end
end
local function zoom(a)
	for pn = 1, 2 do
		local b = Plr(pn)
		if b then
			b:zoom(a * h)
		end
	end
end
local function zoomx(a)
	for pn = 1, 2 do
		local b = Plr(pn)
		if b then
			b:zoomx(a * h)
		end
	end
end
local function zoomy(a)
	for pn = 1, 2 do
		local b = Plr(pn)
		if b then
			b:zoomy(a * h)
		end
	end
end
local function zoomz(a)
	for pn = 1, 2 do
		local b = Plr(pn)
		if b then
			b:zoomz(a)
		end
	end
end
-- probably not actually necessary
local function movez(a)
	for pn = 1, 2 do
		local b = Plr(pn)
		if b then
			b:z(a)
		end
	end
end
local function movey(a)
	for pn = 1, 2 do
		local b = Plr(pn)
		if b then
			b:y(a)
		end
	end
end
local function skewx(a)
	for pn = 1, 2 do
		local b = Plr(pn)
		if b then
			b:skewx(a)
		end
	end
end

function sor_vibro(a)
	for pn = 1,2 do
		local b = Plr(pn)
		local m = pn * 2 - 3
		if b then
			b:vibrate(1)
			b:effectmagnitude(a * m, a/8, 0)
		end
	end
end


-- misc functions
function modulo(a, b)
	return a - math.floor(a/b)*b;
end
function randomXD(t) -- getting a ton of mileage out of this function for 2 years thanks exschwasion
	if t == 0 then return 0.5 else
	return modulo(math.sin(t * 3229.3) * 43758.5453, 1) end
end

local function m_reverse(beat, dur, val)
	for pn = 1, 2 do
		local valn = isusingreverse[pn] and 100 - val or val
		m(beat, dur, '*10000 '..valn..' reverse', l, pn)
	end
end
local function me_reverse(beat, dur, start, stop, ease)
	for pn = 1, 2 do
		local startn = isusingreverse[pn] and 100 - start or start
		local stopn = isusingreverse[pn] and 100 - stop or stop
		me(beat, dur, startn, stopn, 'reverse', l, ease, pn)
	end
end
local function mb_reverse(beat, dur, start, stop, easeIn, easeOut)
	for pn = 1, 2 do
		local startn = isusingreverse[pn] and 100 - start or start
		local stopn = isusingreverse[pn] and 100 - stop or stop
		mb(beat, dur, startn, stopn, 'reverse', easeIn, easeOut, pn)
	end
end

local function spike(beat, dur, pre, start, stop, mod, ease, preEase)
	preEase = preEase or inExpo
	me(beat - pre, pre, start, stop, mod, l, preEase)
	me(beat, dur, stop, start, mod, l, ease)
end
local function sprike(beat, dur, pre, start, stop, realstop, mod, ease, preEase)
	preEase = preEase or inExpo
	me(beat - pre, pre, start, stop, mod, l, preEase)
	me(beat, dur, stop, realstop, mod, l, ease)
end
local function spikef(beat, dur, pre, start, stop, mod, ease, preEase)
	preEase = preEase or inExpo
	mf(beat - pre, pre, start, stop, mod, l, preEase)
	mf(beat, dur, stop, start, mod, l, ease)
end

-- DO NOT FORGET THE #FGCHANGES:0.000=lua=1.000=0=0=1; IF NOTHING APPEARS

-- difficulty assignment/mods scaling
local multipliers = {
    ['Difficulty_Beginner'] = 0.3,
    ['Difficulty_Easy'] = 0.45,
    ['Difficulty_Medium'] = 0.6,
    ['Difficulty_Hard'] = 0.75,
    ['Difficulty_Challenge'] = 1,
}

local curdiff = {'Difficulty_Challenge', 'Difficulty_Challenge'}
local curmult = {1, 1}

for pn = 1, 2 do
    local steps = GAMESTATE:GetCurrentSteps(pn - 1)
    if steps then
        curdiff[pn] = steps:GetDifficulty()
		curmult[pn] = multipliers[steps:GetDifficulty()]
    end
end

for pn = 1, 2 do
	-- universal difficulty mods

	-- ending hit (ripped from flying castle, thanks sorae/tetaes)
	me(384, 3, 0, -50*curmult[pn], 'drunk', l, inOutCubic)
	me(384, 3, 0, 20*curmult[pn], 'tipsy', l, inOutCubic)
	me(387, 2, 20*curmult[pn], 0, 'tipsy', l, outExpo)
	me(387, 2, -50*curmult[pn], 0, 'drunk', l, outExpo)
	me(385, 3, 0, 50, 'flip', l, inCubic)
	me(384, 4, 0, 50*curmult[pn], 'brake', l, inQuad)
	me(384, 4, 0, 50*curmult[pn], 'stealth', l, inSine)
	me(387, 1, 1, 1 + 0.2*curmult[pn], function(a) zoom(a) end, l, inExpo)
	me(387.5, 0.5, 0, -100*curmult[pn], 'tiny', l, inExpo)
			
	me(388, 2, 50, -10, 'flip', l, outExpo, nil, 10)
	me(388, 2, 50, 100, 'stealth', l, outExpo, nil, 10)
	me(388, 4, 1.4, 0.9, function(a) zoom(a) end, l, outExpo)
	me(388, 4, -200, 0, 'tiny', l, outExpo)
	me(388, 2, 0, 20, 'centered', l, outExpo, nil, 10)
	for c = 0, 3 do
		local inv = (c % 3) * 2 - 1
		for pn = 1, 2 do
			if GAMESTATE:IsPlayerEnabled(pn-1) then
				me(388, 8, 0, -314*2*inv*randomXD(c*pn), 'confusionoffset'..c, l, outExpo, pn)
				me(388, 8, 0, 100*(randomXD(c*pn*12) - 0.5)*2, 'movex'..c, l, outExpo, pn)
				me(388, 8, 0, 250*(randomXD(c*pn*12) - 0.5)*2, 'movey'..c, l, outExpo, pn)
			end
		end
	end
	for i = 388, 392, 0.2 do
		me(i, 0.1, 70, 70, 'dark', l, linear) 
		me(i+0.1, 0.1, 90, 90, 'dark', l, linear) 
	end
    
    if curdiff[pn] == 'Difficulty_Medium' or curdiff[pn] == 'Difficulty_Hard' or curdiff[pn] == 'Difficulty_Challenge' then
		-- 16-19 specific mods

		-- first drum hits before first break

		local kick = {32, 32.75, 33.5, 34.25, 35}
		local togg = 1
		local kicks = {316, 317.5, 319, 320, 321.5}
		local toggs = 1

		for i, v in ipairs(kick) do
			me(v, 1.5, 20*curmult[pn], 0, 'brake', l, inOutExpo)
			me(v, 1.5, 70*togg*curmult[pn], 0, 'drunk', l, outCubic)
			me(v, 1.5, -60*curmult[pn], 0, 'tiny', l, outCubic)
			me(v-0.25, 0.25, 1, 1 + 0.1*curmult[pn], function(a) zoomx(a) end, l, inExpo)
			me(v, 1, 1 + 0.1*curmult[pn], 1, function(a) zoomx(a) end, l, outExpo)
			me(v-0.25, 0.25, 1, 1 - 0.1*curmult[pn], function(a) zoomy(a) end, l, inExpo)
			me(v, 1, 1 - 0.1*curmult[pn], 1, function(a) zoomy(a) end, l, outExpo)
			togg = -togg
		end

		-- lead up into first drop

		-- lead up into first streamy section

		-- end up first section

		-- lead up into 2nd section
		mb_reverse(284, 6, 0, 25, outCubic, inCubic)	
		mb(284, 6, 0, 10, 'tornado', outCubic, inCubic)
		me(284, 6, 628, 0, 'confusionoffset', l, outCubic)

		-- light mods during SDVX boss

		me(292, 22, 0, 40*curmult[pn], 'tiny', l, linear)
		me(292, 22, 0, 30*curmult[pn], 'tipsy', l, linear)
		me(314, 1.9, 40*curmult[pn], 0, 'tiny', l, linear)
		me(314, 1.9, 30*curmult[pn], 0, 'tipsy', l, linear)

		me(325, 15, 0, 40*22/15*curmult[pn], 'tiny', l, linear)
		me(325, 15, 0, 30*22/15*curmult[pn], 'tipsy', l, linear)
		me(340, 8, 40*22/15*curmult[pn], 0, 'tiny', l, linear)
		me(340, 8, 30*22/15*curmult[pn], 0, 'tipsy', l, linear)

		-- bounce during big hit 2nd drop
		
		for j, w in ipairs(kicks) do
			me(w, 1.5, 20*curmult[pn], 0, 'brake', l, inOutExpo)
			me(w, 1.5, 70*toggs*curmult[pn], 0, 'drunk', l, outCubic)
			me(w, 1.5, -60*curmult[pn], 0, 'tiny', l, outCubic)
			me(w-0.25, 0.25, 1, 1 + 0.1*curmult[pn], function(a) zoomx(a) end, l, inExpo)
			me(w, 1, 1 + 0.1*curmult[pn], 1, function(a) zoomx(a) end, l, outExpo)
			me(w-0.25, 0.25, 1, 1 - 0.1*curmult[pn], function(a) zoomy(a) end, l, inExpo)
			me(w, 1, 1 - 0.1*curmult[pn], 1, function(a) zoomy(a) end, l, outExpo)
			toggs = -toggs
		end

		-- bounce during ending section

		for i, v in ipairs({357, 358, 359, 360, 361, 362, 363, 365, 366, 367}) do
			me(v, 1, 10*curmult[pn], 0, 'tornado', l, outCubic)
			me(v, 1, -30*curmult[pn], 0, 'tiny', l, outCubic)
			me(v, 1, -5*curmult[pn], 0, 'flip', l, outCubic)
			me(v, 1, -50*curmult[pn], 0, 'tipsy', l, outCubic)
		end

		me(364, 1, -10*curmult[pn], 0, 'tornado', l, outCubic)
		me(364, 1, 30*curmult[pn], 0, 'tiny', l, outCubic)
		me(364, 1, 5*curmult[pn], 0, 'flip', l, outCubic)
		me(364, 1, 50*curmult[pn], 0, 'tipsy', l, outCubic)

		-- shake during ending

    end

	-- challenge specific mods
	if curdiff[pn] == Difficulty_Challenge then
        -- idk fuck em up!
    end
end

function modtable_compare(a,b)
	return a[1] < b[1]
end

if table.getn(mod_actions) > 1 then
	table.sort(mod_actions, modtable_compare)
end

local ac = Def.ActorFrame{
	Name= "xtl_actor_d",
	InitCommand=function(self)
		self:x(scx)
		self:y(scy)
	end,
}

if showAFTs then
	local aft_screenTex = Def.ActorFrameTexture{
		Name = "ScreenTex",
		InitCommand=function(self)
			self:SetTextureName( "ScreenTex" )
			self:SetWidth(dw);
			self:SetHeight(dh);
			self:EnableAlphaBuffer(false);
			self:Create();
		end,
		OnCommand=function(self)
			self:visible(true);
			self:Draw();
		end,
		Def.Quad{ OnCommand=cmd(x,dw*0.5;y,dh*0.5;zoomto,dw,dh;diffuse,0,0,0,1), },
		--Def.ActorProxy{ Name = "ProxyUL"; OnCommand=function(self) self:x(0); self:y(0) self:SetTarget(SCREENMAN:GetTopScreen():GetChild('Underlay')); self:zoom(rz); end },
		Def.ActorFrame{
			Name = "FrameProxyP1";
			OnCommand=function(self)
				local p = mod_plr[1]
				if p then
					self:x(p:GetX()/sw*dw);
					self:y(dh*.5);
				end
			end --[[,
			
			Def.ActorProxy{ Name = "ProxyP1C"; OnCommand=function(self)
				self:x(0);
				self:y(0);
				self:zoom(rz);
				local p = mod_plr[1]
				if p then
					self:SetTarget(p:GetChild('Combo'));
					comboProxies[1] = self;
				end
			end } --]],
			Def.ActorProxy{ Name = "ProxyP1N"; OnCommand=function(self)
				self:x(0);
				self:y(0);
				self:zoom(rz);
				local p = mod_plr[1]
				if p then
					self:SetTarget(p:GetChild('NoteField'));
					pp[1] = self;
				end
			end } --[[,
			Def.ActorProxy{ Name = "ProxyP1J"; OnCommand=function(self)
				self:x(0);
				self:y(0);
				self:zoom(rz);
				local p = mod_plr[1]
				if p then
					self:SetTarget(p:GetChild('Judgment'));
				end
			end } --]]
		},
		Def.ActorFrame{
			Name = "FrameProxyP2";
			OnCommand=function(self)
				local p = mod_plr[2]
				if p then
					self:x(p:GetX()/sw*dw);
					self:y(dh*.5);
				end
			end,
			Def.ActorProxy{ Name = "ProxyP2C"; OnCommand=function(self)
				self:x(0);
				self:y(0);
				self:zoom(rz);
				local p = mod_plr[2];
				if p then
					self:SetTarget(p:GetChild('Combo'));
					comboProxies[2] = self;
				end
			end },
			Def.ActorProxy{ Name = "ProxyP2N"; OnCommand=function(self)
				self:x(0);
				self:y(0);
				self:zoom(rz);
				local p = mod_plr[2];
				if p then
					self:SetTarget(p:GetChild('NoteField'));
					pp[2] = self;
				end
			end },
			Def.ActorProxy{ Name = "ProxyP2J"; OnCommand=function(self)
				self:x(0);
				self:y(0);
				self:zoom(rz);
				local p = mod_plr[2];
				if p then
					self:SetTarget(p:GetChild('Judgment'));
				end
			end },
		},
		Def.ActorProxy{ Name = "ProxyOL"; OnCommand=function(self) self:x(0); self:y(0) self:SetTarget(SCREENMAN:GetTopScreen():GetChild('Overlay')); self:zoom(rz); end },
	};
	ac[#ac+1] = aft_screenTex
	local fuckTex = Def.ActorFrameTexture{
		Name = "FuckTex",
		InitCommand=function(self)
			self:SetTextureName( "FuckTex" )
			self:SetWidth(dw);
			self:SetHeight(dh);
			self:EnableAlphaBuffer(true);
			self:Create();
		end,
		OnCommand=function(self)
			self:visible(true);
			self:Draw();
		end,
	};
	ac[#ac+1] = Def.ActorFrame{
		Name = "SpriteOuterFrame",
		OnCommand = cmd(zoom,zr;x,SCREEN_CENTER_X;),

		-- sargasso blue color during break
		Def.Sprite{
			Texture = "ScreenTex";
			OnCommand = function(self)
				spectrum_blue = self
				self:blend('add')
				self:diffuse(0, 0.7, 1, 0)
				self:visible(false);
			end,
		},
	}
	ac[#ac+1] = Def.ActorFrame{
		Name = "toiletcontainer",
		-- SDVX toilet boss energy
		-- sleep until 74.409
		-- fully fade in at 75.780 (+1.371)
		-- start fading out at 84.009 (+8.229)
		-- fully fade out at 85.380 (+1.371)
		-- start fading in at 96.352 (+11.143)
		-- fully fade in at 97.723 (+1.371)
		-- start fading out at 119.495 (+21.772)
		-- fully fade out at 120.866 (+1.371)
		Def.Sprite{
			InitCommand= function(self) _blue = self; self:SetTextureFiltering(false) end,
			OnCommand= cmd(basezoom,3;diffusealpha,0;visible,1;zoomto,SCREEN_WIDTH,SCREEN_HEIGHT;
			hibernate,75.409;
			linear,1.371;diffusealpha,0.375;
			sleep,8.229;
			linear,1.371;diffusealpha,0;
			sleep,11.143;
			linear,1.371;diffusealpha,0.375;
			sleep,21.772;
			linear,1.371;diffusealpha,0;),
			Texture='blue.png',
		},
		-- sleep until 99.095
		-- fully fade in at 100.466 (+1.371)
		-- start fading out at 119.495 (+19.029)
		-- fully fade out at 120.866 (+1.371)
		Def.Sprite{
			InitCommand= function(self) _toilettu = self; self:SetTextureFiltering(false); self:blend('add') end,
			OnCommand= cmd(diffusealpha,0;y,-7200;visible,1;
			hibernate,99.095;
			linear,1.15;diffusealpha,0.5;y,0;
			zoom,2;sleep,19.250;
			linear,1.371;diffusealpha,0;),
			Texture='toilettu.mp4',
		},
	}
end


ac[#ac+1] = Def.Actor{
	OnCommand= function(self)
		mod_firstSeenBeat = GAMESTATE:GetSongBeat()
		self:sleep(9e9)
	end
}
ac[#ac+1] = Def.Actor{
		
		OnCommand=function(self)
			self:queuecommand'Ready'
		end,
		ReadyCommand=function(self)
			screen = SCREENMAN:GetTopScreen()

			local editorhasunnamedplayer = false
			if string.find(screen:GetName(),"^ScreenEdit") and (not screen:GetChild'PlayerP1') then
				editorhasunnamedplayer = true
			end
			if editorhasunnamedplayer then
				for i, v in pairs(screen:GetChild'') do
					if _G.tostring(v):find'Player' then
						mod_plr[1] = v
					end
				end
			else
				for pn = 1, 2 do
					mod_plr[pn] = screen:GetChild('PlayerP' .. pn)
					if mod_plr[pn] then
						_G['jud'..pn] = mod_plr[pn]:GetChild('Judgment')
						_G['com'..pn] = mod_plr[pn]:GetChild('Combo')
					end
				end
			end
			
			self:queuecommand'Update'
		end,
		UpdateCommand=function(self)
			local beat = GAMESTATE:GetSongBeat()
			
			
---------------------------------------------------------------------------------------
----------------------Begin table handlers---------------------------------------------
---------------------------------------------------------------------------------------
			
			disable = false
			if disable ~= true and beat > mod_firstSeenBeat+0.1 then
				
				------------------------------------------------------------------------------
				-- custom mod reader by TaroNuke edited by WinDEU and re-stolen by TaroNuke --
				------------------------------------------------------------------------------
				for i,v in pairs(mods) do
					if v and table.getn(v) > 3 and v[1] and v[2] and v[3] and v[4] then
						if beat >=v[1] then
							if (v[4] == 'len' and beat <=v[1]+v[2]) or (v[4] == 'end' and beat <=v[2]) then
								if table.getn(v) == 5 then
									mod_do(v[3],v[5])
								else
									mod_do(v[3])
								end
							end
						end
					else
						v[1] = 0
						v[2] = 0
						v[3] = ''
						v[4] = 'error'
						SCREENMAN:SystemMessage('Bad mod in beat-based table (line '..i..')')
					end
				end
				
				--------------------------------------------------------------------------------------
				-- i dont know who this reader is but he looks like he is made out of EASE HAHAHAHA --
				-- original code by exschwasion, bastardized by taro for cmod support and less 'if' --
				--------------------------------------------------------------------------------------
				for i,v in pairs(mods_ease) do
					if v and table.getn(v) > 6 and v[1] and v[2] and v[3] and v[4] and v[5] and v[6] and v[7] then
						if beat >=v[1] then
							if (v[6] == 'len' and beat <=v[1]+v[2]) or (v[6] == 'end' and beat <=v[2]) then
								
								local duration = v[2]
								if v[6] == 'end' then duration = v[2] - v[1] end
								local curtime = beat - v[1]
								local diff = v[4] - v[3]
								local startstrength = v[3]
								local curve = v[7]
								local mod = v[5]
								
								local strength = curve(curtime, startstrength, diff, duration, v[10], v[11]) --extra parameters for back and elastic eases :eyes:
								if v.flicker then strength = strength * flicker_value end
								
								if type(mod) == 'function' then
									mod(strength)
								else
									local modstr = v[5] == 'xmod' and strength..'x' or (v[5] == 'cmod' and 'C'..strength or strength..' '..v[5])
									mod_do('*10000 '..modstr,v[8]);
								end
								
							elseif (v[9] and ((v[6] == 'len' and beat <=v[1]+v[2]+v[9]) or (v[6] == 'end' and beat <=v[9]))) then
							
								local strength = v[4]
								if v.flicker then strength = strength * flicker_value end
								if type(mod) == 'function' then
									mod(strength)
								else
									local modstr = v[5] == 'xmod' and strength..'x' or (v[5] == 'cmod' and 'C'..strength or strength..' '..v[5])
									mod_do('*10000 '..modstr,v[8]);
								end
								
							end
						end
					else
						SCREENMAN:SystemMessage('Bad mod in beat-based ease table (line '..i..')')
					end
				end
				
				--------------------
				-- Perframe stuff --
				--------------------
				
				----------------------------------------
				-- HBLBHCBLBJGBHL DO THIS EVERY FRAME --
				----------------------------------------
				
				if table.getn(mod_perframes)>0 then
					for i=1,table.getn(mod_perframes) do
						local a = mod_perframes[i]
						if beat > a[1] and beat < a[2] then
							a[3](beat)
						end
					end
				end
				
				---------------------------------------
				-- ACTION RPGS AINT GOT SHIT ON THIS --
				---------------------------------------
				while curaction<=table.getn(mod_actions) and GAMESTATE:GetSongBeat()>=mod_actions[curaction][1] do
					if mod_actions[curaction][3] or GAMESTATE:GetSongBeat() < mod_actions[curaction][1]+2 then
						if type(mod_actions[curaction][2]) == 'function' then
							mod_actions[curaction][2]()
						elseif type(mod_actions[curaction][2]) == 'string' then
							MESSAGEMAN:Broadcast(mod_actions[curaction][2])
						end
					end
					curaction = curaction+1
				end
				
			end
			
			flicker_value = flicker_value * -1
			
			self:sleep(1/FRAME_RATE)
			self:queuecommand('Update')

---------------------------------------------------------------------------------------
----------------------END DON'T TOUCH IT KIDDO-----------------------------------------
---------------------------------------------------------------------------------------
			
		end,
	
	}

return ac