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

LIST_ACTORS = false

checked = false

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

	sw = SCREEN_WIDTH
	sh = SCREEN_HEIGHT --shortcuts
	
	scx = sw/2
	scy = sh/2
	
local po = {GAMESTATE:GetPlayerState(0):GetPlayerOptions'ModsLevel_Song', GAMESTATE:GetPlayerState(1):GetPlayerOptions'ModsLevel_Song'}
function mod_do(str,pn)
	--if pn then if GAMESTATE:IsPlayerEnabled(pn-1) then handle_mods(str,pn) end
	--else for pn=1,2 do if GAMESTATE:IsPlayerEnabled(pn-1) then handle_mods(str,pn) end end end
	
    if pn then
        po[pn]:FromString(str)
    else
        po[1]:FromString(str)
        po[2]:FromString(str)
    end
end

_numplayers = 0
local _diff = {0,0}
if GAMESTATE:IsHumanPlayer(PLAYER_1) then
	local stepsr = (GAMESTATE:GetCurrentSteps(PLAYER_1):GetDifficulty())
	if stepsr == "Difficulty_Beginner" then _diff[1] = 0 end
	if stepsr == "Difficulty_Easy" then _diff[1] = 1 end
	if stepsr == "Difficulty_Medium" then _diff[1] = 2 end
	if stepsr == "Difficulty_Hard" then _diff[1] = 3 end
	if stepsr == "Difficulty_Challenge" then _diff[1] = 4 end
	_numplayers = _numplayers+1
end
if GAMESTATE:IsHumanPlayer(PLAYER_2) then
	local stepsr = (GAMESTATE:GetCurrentSteps(PLAYER_2):GetDifficulty())
	if stepsr == "Difficulty_Beginner" then _diff[2] = 0 end
	if stepsr == "Difficulty_Easy" then _diff[2] = 1 end
	if stepsr == "Difficulty_Medium" then _diff[2] = 2 end
	if stepsr == "Difficulty_Hard" then _diff[2] = 3 end
	if stepsr == "Difficulty_Challenge" then _diff[2] = 4 end
	_numplayers = _numplayers+1
end

--print("tech num players: ".._numplayers)

-- thank u ky dashiel
local poptions = {GAMESTATE:GetPlayerState(0):GetPlayerOptions('ModsLevel_Song'), GAMESTATE:GetPlayerState(1):GetPlayerOptions('ModsLevel_Song')}
_isusingreverse = {false, false}
_miniscale = {1,1}
for pn = 1, 2 do
	if poptions[pn] then
		_isusingreverse[pn] = poptions[pn]:Reverse() ~= 0
		_miniscale[pn] = (200 - poptions[pn]:Mini()) * 0.005
	end
end

			sw,sh = SCREEN_WIDTH,SCREEN_HEIGHT
			
			curmod = 1
			m_current = {{},{}}
			m_lastframe = {{},{}}
			m_startv = {{},{}}
			m_startt = {{},{}}
			m_endv = {{},{}}
			m_len = {{},{}}
			m_ease = {{},{}}
			m_opt1 = {{},{}}
			m_opt2 = {{},{}}
			m_tweening = {{},{}}
			modnames = {}
			m_isreal = {} --REAL mods that are part of the engine
			
			function definemod(name,initv,real)
				if not initv then initv = 0 end
				if real == nil then real = true end
				table.insert(modnames,name)
				m_isreal[name] = real
				for pn=1,2 do
					m_tweening[pn][name] = false
					m_current[pn][name] = initv
					m_lastframe[pn][name] = -1
					m_startv[pn][name] = initv
					m_endv[pn][name] = initv
					m_startt[pn][name] = 0
					m_len[pn][name] = 0
					m_opt1[pn][name] = 0
					m_opt2[pn][name] = 0
					m_ease[pn][name] = linear
				end
			end
			
			function modulo(a, b)
				return a - math.floor(a/b)*b;
			end
			
			function randomXD(t)
				if t == 0 then return 0.5 else
				return modulo(math.sin(t * 3229.3) * 43758.5453, 1) end
			end
			
			--regular ass mods
			definemod('drunk')
			definemod('tipsy')
			definemod('stealth')
			definemod('sudden')
			definemod('suddenoffset')
			definemod('brake')
			definemod('beat')
			definemod('tiny')
			definemod('dark')
			definemod('flip')
			definemod('invert')
			definemod('centered')
			definemod('split')
			definemod('cross')
			definemod('alternate')
			definemod('reverse')
			definemod('hallway')
			definemod('distant')
			definemod('confusionoffset')
			definemod('static',0,false)
			for i=1,8 do
				definemod('movex'..i)
				definemod('movey'..i)
				definemod('dark'..i)
				definemod('tiny'..i)
				definemod('confusionoffset'..i)
			end
			
			--tweens as mods lol
			definemod('x',0,false)
			definemod('y',0,false)
			definemod('z',0,false)
			definemod('rotationx',0,false)
			definemod('rotationy',0,false)
			definemod('rotationz',0,false)
			definemod('zoomx',1,false)
			definemod('zoomy',1,false)
			definemod('zoomz',1,false)
			definemod('zoom',1,false)
			
			mods, ma, ca, mfsb = {}, {}, 1, GAMESTATE:GetSongBeat();
			function mod_do(str,pn) if pn then GAMESTATE:ApplyGameCommand('mod,'..str,pn) else GAMESTATE:ApplyGameCommand('mod,'..str) end end
			local me, m2 = function (t)
				local beat,len,ease,str1,mod = unpack(t)
				for pn=1,2 do
					--print(_diff[pn],t.min)
					if not t.min and not t.only or t.only and _diff[pn] == t.only or t.min and _diff[pn] >= t.min then
						if not t.plr or pn == t.plr then
							table.insert(mods,{beat,len,ease,str1,mod,startVal=t.startVal,opt1=t.opt1,opt2=t.opt2,plr=t.plr})
						end
					end
				end
			end, function (beat,fun,per) table.insert(ma,{beat,fun,per}) end
			function ome(t)
				me{t[1],t[2],t[3],startVal=t[4],t[5],t[6],min=t.min,only=t.only,opt1=t.opt1,opt2=t.opt2,plr=t.plr}
			end
			
			capturedOrig = false
			origx = {SCREEN_WIDTH*0.25,SCREEN_WIDTH*0.75}
			origy = {SCREEN_HEIGHT*0.5,SCREEN_HEIGHT*0.5}
			origz = {0,0}
			
			function sm2(t)
				local b,l,e,v2,v1,mo = unpack(t)
				me{b-.05,.1,linear,v2,mo,min=t.min,plr=t.plr,only=t.only,opt1=t.opt1,opt2=t.opt2}
				me{b+.05,l-.05,e,v1,mo,min=t.min,plr=t.plr,only=t.only,opt1=t.opt1,opt2=t.opt2}
			end
			
			function set(t)
				local b,v1,mo = unpack(t)
				me{b,0,linear,v1,mo,min=t.min,plr=t.plr,only=t.only}
				return set
			end
			
			_anyplayers = false
			_firstplayer = -1
			
			m2(0,function()
				for pn=1,2 do
					local a = SCREENMAN:GetTopScreen():GetChild('PlayerP'..pn)
					
					local darkSize = math.min((SCREEN_WIDTH*.5)/320,SCREEN_HEIGHT/480)
					
					if a then
						origx[pn] = a:GetX()
						origy[pn] = a:GetY()
						origz[pn] = a:GetZ()
						
						_anyplayers = true
						if _firstplayer == -1 then
							_firstplayer = pn
						end
						
					end
					
				end
				if _firstplayer == -1 then
					_firstplayer = 1
				end
				capturedOrig = true
			end,true)
			
			
			--thanks mr thatkid!!!
			local log0_5 = 1/math.log(0.5)
			--Save this so we do one less math.log & division every call, maybe.
			
			function SM5Tiny(amt)
				--takes a nitg tiny% and returns a sm5 tiny%
				--return amt --in nitg, just return base val
				return (math.log(1-0.5*(amt*0.01))*log0_5)*100
			end
			
			function spinny(t)
				local b,d,cd,l = unpack(t)
				if not l then l=1.5 end
				local maxc = 3
				local del = .1
				if _doubles then
					maxc = 7
					del = .166*.5
				end
				for i=0,maxc do
					local wc = i
					if cd == -1 then
						wc = maxc-i
					end
					wc = wc+1
					ome{b-l+del*i,l,inExpo,0,314*d,'confusionoffset'..wc,min=t.min,plr=t.plr,only=t.only}
					ome{b+del*i,l,outExpo,-314*d,0,'confusionoffset'..wc,min=t.min,plr=t.plr,only=t.only}
				end
			end
			
			ome{12,.75,outCubic,SM5Tiny(-150),0,"tiny"}
			ome{12.75,.75,outCubic,SM5Tiny(-150),0,"tiny"}
			sm2{12,.75,outCubic,-150,0,"drunk"}
			sm2{12.75,.75,outCubic,150,0,"tipsy"}
			
			for pn=1,2 do
				for i=13.5,13.999,.125 do
					if _isusingreverse[pn] then
						ome{i,.125,linear,95,100,"reverse",plr=pn}
					else
						ome{i,.125,linear,5,0,"reverse",plr=pn}
					end
				end
			end
			sm2{14.25,.5,outCubic,100,0,"tipsy"}
			sm2{14.75,.5,outCubic,-100,0,"tipsy"}
			
			sm2{16,3.5,linear,200,0,"distant"}

			me{21.5,.166,outCubic,-80,"drunk"}
			me{21.5+.166,.166,outCubic,80,"drunk"}
			me{21.5+.333,.166,outCubic,-80,"drunk"}
			me{22,.166,outCubic,0,"drunk"}
			me{22,.25,outCubic,100,"tipsy"}
			me{22.25,.25,outCubic,-100,"tipsy"}
			me{22.5,.25,outCubic,100,"tipsy"}
			me{22.75,1,outCubic,0,"tipsy"}
			
			local scars = {
				24,
				24.25,
				24.75,
				25.25,
				25.5,
				26,
				26.25,
				26.75,
				27,
				27.25,
				27.5,
			}
			for i=1,#scars do
				local v=scars[i]
				local b=v
				local mul = 1
				if i == #scars-4 then mul = .8 end
				if i == #scars-3 then mul = .6 end
				if i == #scars-2 then mul = .4 end
				if i == #scars-1 then mul = .2 end
				if i == #scars then mul = 0 end
				for j=1,4 do
					me{b,.25,linear,(-80+160*randomXD(v*100+j))*mul,"movey"..j}
				end
			end
			
			ome{19.5,.5,outCubic,SM5Tiny(-150),0,"tiny"}
			ome{20,.75,outCubic,SM5Tiny(-150),0,"tiny"}
			ome{20.75,.75,outCubic,SM5Tiny(-150),0,"tiny"}
			sm2{19.5,.5,outCubic,150,0,"drunk"}
			sm2{20,.75,outCubic,-150,0,"drunk"}
			sm2{20.75,.75,outCubic,150,0,"tipsy"}
			
			me{67.0,.25,outCubic,70,"brake"}
			me{67.25,.75,inCubic,0,"brake"}
			me{99.0,.25,outCubic,70,"brake"}
			me{99.25,.75,inCubic,0,"brake"}
			
			set{71,.3,"static"}
			set{72,0,"static"}
			
			local f = 1
			for i=0,1 do
				ome{52+32*i,1,outCubic,SM5Tiny(-100),0,"tiny"}
				ome{53+32*i,.75,outCubic,SM5Tiny(-100),0,"tiny"}
				ome{53.75+32*i,.75,outCubic,SM5Tiny(-100),0,"tiny"}
				sm2{52+32*i,1,outCubic,-100,0,"drunk"}
				sm2{53+32*i,.75,outCubic,100,0,"drunk"}
				sm2{53.75+32*i,.75,outCubic,100,0,"tipsy"}
				
				spinny{62+32*i,1,f,2,min=2}
				f=-f
			end
			
			for i=0,2 do
				sm2{40+i,1,outCubic,SM5Tiny(-100),0,"tiny"}
				sm2{40+i,1,outCubic,30,0,"brake"}
				
				sm2{137+i*.75,.75,outCubic,SM5Tiny(-100),0,"tiny"}
				sm2{137+i*.75,.75,outCubic,40,0,"brake"}
				sm2{137+i*.75,.75,outCubic,100*f,0,"drunk"}
				f=-f
			end
			
			for i=0,16 do
				local mu = i==16 and 0 or 1
				--me{155+.125*i,.125,outCubic,20*f*mu,"drunk"}
				me{155+(1/16)*i,1/16,outCubic,4*f*mu,"brake"}
				f=-f
			end
			
			for i=172,186,1 do
				sm2{i,1,outCubic,20,0,"brake"}
			end
			for i=188,195.5,.5 do
				sm2{i,.5,outCubic,20,0,"brake"}
			end
			
			set{195.5,200,"beat"}
			set{197.5,0,"beat"}
			
			for i=0,1 do
				sm2{170+i,1,outCubic,SM5Tiny(-100),0,"tiny"}
				sm2{170+i,1,outCubic,30,0,"brake"}
				sm2{170+i,1,outCubic,80*f,0,"drunk"}
				f=-f
			end
			for i=0,2 do
				sm2{200+i,1,outCubic,SM5Tiny(-100),0,"tiny"}
				sm2{200+i,1,outCubic,30,0,"brake"}
			end
			
			me{228,1,outCubic,.1,"static"}
			me{234,2,linear,0,"static"}
			me{260,1,outCubic,.1,"static"}
			me{266,2,linear,0,"static"}
			me{316,1,outCubic,.1,"static"}
			me{322,2,linear,0,"static"}
			
			me{332,12,linear,100,"stealth"}

			--scry

			me{0,0.1,linear,100,"stealth"}
			me{4,6,linear,0,"stealth"}
			me{36,4,inQuad,-628*2,"confusionoffset"}
			
			local dontdoit = 0
			local function cheeky(beat)
				me{beat,0.5,outCirc,50,"drunk"}
				me{beat+.5,0.5,outCirc,0,"drunk"}
				me{beat+1,0.5,outCirc,-50,"drunk"}
				me{beat+1.5,0.5,outCirc,0,"drunk"}
				if dontdoit == 0 then
					me{beat+2,0.5,outCirc,-50,"tipsy"}
					me{beat+2.5,0.5,outCirc,0,"tipsy"}
				end
				dontdoit = dontdoit + 1
			end

			cheeky(56)
			cheeky(88)

			me{75,0.5,outCirc,-10,"flip"}
			me{75.5,0.5,outCirc,0,"flip"}
			me{76,0.5,outCirc,5,"rotationz"}
			me{76.5,0.5,outCirc,0,"rotationz"}
			
			ome{107,1,outCirc,-5,0,"rotationz"}

			me{139,1,outCirc,25,"stealth"}
			me{155,1,linear,0,"stealth"}

			me{186.5,0.5,outCirc,50,"stealth"}
			me{188,0.5,outCirc,0,"stealth"}
			ome{188,1,outCirc,SM5Tiny(-150),0,"tiny"}

			-- the rest
			
			function cmp(a,b)
				return a[1] < b[1]
			end
			if table.getn(ma) > 1 then
				table.sort(ma, cmp)
			end
			if table.getn(mods) > 1 then
				table.sort(mods, cmp)
			end

local ac = Def.ActorFrame{
	
	Def.Actor{
		OnCommand=function(s)
			s:queuecommand('Loop')
		end,
		LoopCommand=function(self)
			local beat = GAMESTATE:GetSongBeat()
			if beat > mfsb+0.1 then
				
				while curmod <= #mods and beat > mods[curmod][1] do
					local v = mods[curmod]
					
					local mn = v[5]
					local dur = v[2]
					if v.timing and v.timing == 'end' then
						dur = v[2]-v[1]
					end
					
					print('launch attack '..v[4]..' '..mn..' at beat '..v[1])
					
					if v.plr and not v.pn then v.pn = v.plr end
					
					for pn=1,2 do
						if not v.pn or pn == v.pn then
							m_startt[pn][mn] = v[1]
							m_len[pn][mn] = dur
							m_ease[pn][mn] = v[3]
							if v.startVal then
								m_startv[pn][mn] = v.startVal
							else
								m_startv[pn][mn] = m_current[pn][mn]
							end
							if v.add then
								m_endv[pn][mn] = m_current[pn][mn]+v[4]
							else
								m_endv[pn][mn] = v[4]
							end
							m_opt1[pn][mn] = v.opt1
							m_opt2[pn][mn] = v.opt2
							m_tweening[pn][mn] = true
						end
					end
					
					curmod = curmod+1
				end
				
				for pn=1,2 do
					for _,v in pairs(modnames) do
						if m_tweening[pn][v] then
							local curtime = beat - m_startt[pn][v]
							local duration = m_len[pn][v]
							local startstrength = m_startv[pn][v]
							local diff = m_endv[pn][v] - startstrength
							local curve = m_ease[pn][v]
							local strength = curve(curtime, startstrength, diff, duration, m_opt1[pn][v], m_opt2[pn][v])
							m_current[pn][v] = strength
							if beat > m_startt[pn][v]+duration then
								m_tweening[pn][v] = false
							end
						else
							m_current[pn][v] = m_endv[pn][v]
						end
					end
				end
				
				--ONLY apply mods if there were changes THIS frame
				for pn=1,2 do
					local mstr = ''
					for _,v in pairs(modnames) do
						if m_isreal[v] and m_lastframe[pn][v] ~= m_current[pn][v] then
							mstr = mstr..'*10000 '..m_current[pn][v]..' '..v..','
							m_lastframe[pn][v] = m_current[pn][v]
						end
					end
					if mstr ~= '' then
						mod_do(mstr,pn)
					end
				end
				
				if capturedOrig then
					for pn=1,2 do
						local a = SCREENMAN:GetTopScreen():GetChild('PlayerP'..pn)
						if a then
						
							a:x(origx[pn]+m_current[pn].x*SCREEN_HEIGHT/480)
							if _isusingreverse[pn] then
								a:y(origy[pn]-m_current[pn].y*SCREEN_HEIGHT/480)
							else
								a:y(origy[pn]+m_current[pn].y*SCREEN_HEIGHT/480)
							end
							a:z(origz[pn]+m_current[pn].z)
							if _isusingreverse[pn] then
								a:rotationx(-m_current[pn].rotationx)
							else
								a:rotationx(m_current[pn].rotationx)
							end
							a:rotationy(m_current[pn].rotationy)
							a:rotationz(m_current[pn].rotationz)
							a:basezoomx(m_current[pn].zoomx*m_current[pn].zoom)
							a:basezoomy(m_current[pn].zoomy*m_current[pn].zoom)
							a:zoomz(m_current[pn].zoomz*m_current[pn].zoom)

						end
					end
				
					--print(_static)
					--print(m_current[_firstplayer])
					--print(m_current[_firstplayer].static)
					_static:diffusealpha(m_current[_firstplayer].static)
					
				end
				
				while ca<=table.getn(ma) and GAMESTATE:GetSongBeat()>=ma[ca][1] do
					if ma[ca][3] or GAMESTATE:GetSongBeat() < ma[ca][1]+2 then
						ma[ca][2]()
					end
					ca = ca+1
				end
			end
			self:sleep(1/60) self:queuecommand('Loop')
		end
	},
	
	Def.Sprite{
		Delay0000= 0.034,
		Delay0001= 0.034,
		Delay0002= 0.034,
		Delay0003= 0.034,
		Frame0000= 0,
		Frame0001= 1,
		Frame0002= 2,
		Frame0003= 3,
		InitCommand= function(self) _static = self; self:SetTextureFiltering(false) end,
		OnCommand= cmd(x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;diffusealpha,0;scaletocover,0,0,SCREEN_WIDTH,SCREEN_HEIGHT),
		Texture= "_static 4x1.png",
	},

	Def.Quad{ InitCommand=cmd(visible,false), OnCommand=cmd(sleep,9E9) },
	
	--RECURSIVE DIGGER -Used to obtain all accessible screen elements
	Def.Quad{
		OnCommand=cmd(visible,false;sleep,1;queuecommand,"ListActors"),
		ListActorsCommand=function(self)
			
			if LIST_ACTORS then
				local file = RageFileUtil.CreateRageFile()
				local path=GAMESTATE:GetCurrentSong():GetSongDir();
				file:Open( path.."lua/TopscreenActorList.txt", 2 ) --2 = write
				
				file:PutLine("Actors part of TopScreen in "..THEME:GetCurThemeName())
				
				function listall(name, actor, level)
					--[[
					if name == "SongForeground" then
						local tabs = ''
						for i=1,level do
							tabs = tabs..'\t'
						end
						file:PutLine(tabs.."(SongForeground contains every actor placed into the fgchanges layer,")
						file:PutLine(tabs.."But that information is not relevant to this list)")
					end
					]]
					if string.sub(tostring(actor),1,10) == "ActorFrame" then
						local tabs = ''
						for i=1,level do
							tabs = tabs..'\t'
						end
						local str = tabs.."Actors part of "..name.." in "..THEME:GetCurThemeName()
						print(str)
						file:PutLine(str)
						for i,v in pairs(actor:GetChildren()) do
							if i and i~= "" then
								if string.sub(name,1,7) == "unnamed" then
									local str2 = tabs.."\tunnamed (within "..name.."):GetChild('"..i.."'); ("..tostring(v)..")"
									file:PutLine(str2);
									listall("unnamed (within "..name..")",v,level+1);
								else
									local str2 = tabs.."\tGetChild('"..name.."'):GetChild('"..i.."'); ("..tostring(v)..")"
									file:PutLine(str2);
									listall(i,v,level+1);
								end
							else
								local str3 = tabs.."\tunnamed actor in "..name.."... ("..tostring(v)..")"
								file:PutLine(str3);
								listall("unnamed (within "..name..")",v,level+1);
							end
						end
					end
				end
				
				local level = 1
				for i,v in pairs(SCREENMAN:GetTopScreen():GetChildren()) do
					if i and i~= "" then
						local str = "\tSCREENMAN:GetTopScreen():GetChild('"..i.."'); ("..tostring(v)..")"
						print(str)
						file:PutLine(str);
						listall(i,v,level+1);
					else
						local str = "\tunnamed actor... ("..tostring(v)..")"
						print(str)
						file:PutLine(str);
						listall("unnamed (top level)",v,level+1);
					end
				end
				
				file:Close();
			
			end
			
		end
	},
	
	Name= "xtl_actor_d",
	
}
return ac