freeslot("MT_WARPFX", "sfx_shadst")

local function ToggleCC(player) //Toggle Chaos Control on or off.
	if player.spectator or player.homing or not player.mo then return false end
//if the player is spectating or homing in on an enemy, or on the title screen, then turn off chaos control.
		local effect = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_WARPFX)
//spawns the warp effect
		S_StartSound(effect, sfx_prloop)
//starts the sound effect
		effect.destscale = 3*FRACUNIT
//this is the stretch that happens on the warp hole
		player.mo.momx = 0
		player.mo.momy = 0
		player.mo.momz = 0
//these stop the player in place
		if (player.mo.flags & MF_SHOOTABLE)
	//if the player is allowed to be hit by an enemy or ring.
			player.mo.flags = $1|MF_NOGRAVITY|MF_NOCLIPTHING
//add no gravity and noclipping to shadow's current list of flags
			player.mo.flags = $1 & !MF_SHOOTABLE
//remove the flag that allows shadow to be hit from the current list of his flags
		else
	//otherwise, if he is not allowed to be hit
			player.mo.flags = $1|MF_SHOOTABLE|MF_NOGRAVITY
//add the flags that enable his hitbox and allow gravity to affect him back to his list of flags
			player.mo.flags = $1 & !MF_NOCLIPTHING
//remove the noclip flag
			if not P_IsObjectOnGround(player.mo)
		//and if the player is not on the ground while still unable to be hit
				player.mo.state = S_PLAY_FALL
		//fall state
				player.mo.falldelay = 20
		//set a 20 frame timer for the state to be active?
			end
		end
	end

local function ChaosControl(player, dir)
	if player.spectator or not player.mo then return false end
//if the player is a spectator, or on the title screen, then skip this loop
		if player.cctimer == 0 and player.consecutiveccs < 2
	//if the chaoscontrol timer is 0 and you can still perform cc
			player.cctimer = 8
	//set the timer to 8 ingame tics
			player.weapondelay = 10
	//set our weapondelay to 10 tics
			ToggleCC(player)
	//call the above cctoggle function
			if dir == "up"
	//if the direction is up
				A_ZThrust(player.mo, 25)
		//send shadow up
			elseif dir == "down"
	//or if the direction is down
				A_ZThrust(player.mo, -25)
		//send him down
				else P_InstaThrust(player.mo, dir, 72*FRACUNIT)
		//otherwise just send him forwards
			end
		player.consecutiveccs = $1+1
	//use up one of his chaos controls
		end
	end

addHook("AbilitySpecial", function(player)
//this only runs when using your ability
	if not (player.mo and player.mo.skin == "legacyshadow") then return false end
//if you're on the title screen or not shadow, then  skip this loop
		if not P_LookForEnemies(player) and not P_SuperReady(player) and player.consecutiveccs < 2
	//if you're not in range of an object that shadow can homing attack, and you don't have all the chaos emeralds, and you haven't done your two cc's yet
			local dir
		//let's set up a new variable called "direction"
			if ((player.cmd.buttons & BT_USE) and not (twodlevel))
		//if the player is pressing spin and not in a 2D level
			or (player.cmd.forwardmove < 0 and (twodlevel))
		//or the player is holding backwards (down) in a 2D level
				dir = "down"
			//the direction is now set to down
			elseif ((player.cmd.forwardmove or player.cmd.sidemove) and not (twodlevel))
		//otherwise, if you're holding foward, backwards, or strafe in either direction and not in 2D mode
				dir = player.cmd.angleturn<<16 + R_PointToAngle2(0, 0, player.cmd.forwardmove*FRACUNIT, -player.cmd.sidemove*FRACUNIT)
			//this is where the old script messed up, the direction is now set to whereever in 360 degrees relative to the camera, the player is pointing, accounting for analog controllers too!
			elseif (player.cmd.forwardmove > 0 and (twodlevel))
		//otherwise otherwise if the player is holding forward (up) in a 2d level
				dir = "up"
			//going up!
			elseif (player.cmd.sidemove and (twodlevel))
		//otherwise otherwise otherwise if we're holding left or right in a 2d level
				dir = -player.cmd.sidemove+player.mo.angle
			//our direction is equal to what direction we're holding, but we're gonna add that direction to the player's angle so it actually makes him go backwards when needed. it's weird i know but otherwise he just goes forwards
			else dir = "up"
		//otherwise otherwise otherwise otherwise, if nothing is pressed, then the direction is set to up
			end
			ChaosControl(player, dir)
		//call that chaos control function that we just set up
			return true
		//the information is now true, you've just warped one time
		else
	//otherwise, if the player IS in range of an enemy or anything listed above
			player.mo.falldelay = 1
		//set the falldelay timer to 1 tic
		end
	end)

addHook("JumpSpinSpecial", function(player)
//this only runs when you're pressing jump AND spin
	player.mo.flags = $1 & !MF_NOGRAVITY
//remove the no gravity flag from shadow's list of active flags
	player.mo.falldelay = 0
//and set his falldelay timer to 0 tics
end)

addHook("ThinkFrame", do
//this will run at the very last bit of each frame after most everything else has run
	for player in players.iterate do
//this is only for players, thank you very much, and it will always run as long as there's a player in the game
		if (player.mo and player.mo.skin == "legacyshadow")
			//if you are not on the title screen, and you are a shadow
			player.charflags = SF_SUPER
			//set the following flags: shadow can turn super, shadow has his own custom super animations, and he uses the spin animations while being super
			if player.mo.color == SKINCOLOR_BLACK
		//if the player is using the color black as their color of choice
				player.revitem = MT_SHADORB
			//set the player's spin charge orb to the yellow spin item
				player.spinitem = MT_SHADORB
			//same with the spin trail
				player.thokitem = MT_SHADORB
			//same with the thok trail
			end
			//this is how the animations and the sounds work for skating
			if player.mo.state == S_SHADSKAT3 
			//if the player is currently in the 3rd frame of their skating anim
				S_StartSound(player.mo, sfx_shadst)
			//play the skate sound
			elseif player.mo.state == S_SHADSKAT9
			//otherwise, if they're on the 9th frame (they could've just used an or statment here, would probably be faster but it's pretty good already so *shrug*)
				S_StartSound(player.mo, sfx_shadst)
			//play the sound again
			end
			if (player.mo.state >= S_PLAY_RUN and player.mo.state <= S_PLAY_RUN)
		//if the player is in their running animation
			//and they're not in the special stage, that'd be weird
				and not (player.mo.tracer and player.mo.tracer.type == MT_TUBEWAYPOINT)
			//and they're not in a zoom tube
					player.mo.state = S_SHADSKAT1
				//use our custom skating animations
				end    
			end
			if (player.mo.state >= S_SHADSKAT1 and player.mo.state <= S_SHADSKAT12)
		//if we're in our custom skating animations
				player.panim = PA_RUN
			//act like the player is currently in their running animations
				if player.cctimer == 0
			//if the chaos control timer is set to 0 tics (what does this have to do with friction?)
					player.mo.friction = FRACUNIT-(FRACUNIT/100)
				//set the player's friction to 90 percent of whatever fracunit means in this case
				end
			end
			if P_IsObjectOnGround(player.mo) and player.mo.falldelay and player.mo.falldelay > 0
		//if the player is on the ground, and fall delay is somewhere above zero (the first one is redundant)
				P_ResetPlayer(player)
			//reset the player's everything, states, stats, and flags
				player.mo.state = S_PLAY_STND
			//set the player's state to standing
				player.mo.flags = $1 & !MF_NOGRAVITY
			//remove that pesky no gravity flag
		end
	end
end)

//surprisingly, this is where jay decided to dump all their custom variable settings rather than at the top like normal people
addHook("ThinkFrame", do
//runs every frame, after everything else
	for player in players.iterate do
		if player.mo and player.mo.skin == "legacyshadow" then
		//if you're not on the title screen and you're a shadow of your former self
			if player.moving == nil or (not player.cmd.forwardmove and not player.cmd.sidemove)
			//if this variable is not set or you're not touching any directions
				player.moving = false
			//set this to false
			end
			
			if player.consecutiveccs == nil or P_IsObjectOnGround(player.mo) or player.powers[pw_super] > 0
		//if this variable is not set, or you're on the ground, or you've been super for any length of time
				player.consecutiveccs = 0
		//set the number of chaos controls you've done to zero
			end
			
			if player.mo.falldelay == nil
		//if this variable has not been set
				player.mo.falldelay = 0
			//set it to 0
			elseif player.mo.falldelay > 0
		//otherwise if this value is greater than zero
				player.mo.falldelay = $1-1
			//tic the timer down by one each time this runs (which is every frame)
			end
			
			if player.mo.falldelay > 1
		//if falldelay is greater than 1 tic
				player.mo.state = S_PLAY_FALL
			//send the player into their fall state
			else player.mo.flags = $1 & !MF_NOGRAVITY
		//otherwise, remove the nogravity flag
			end
			
			//proposed fix, if the player is homing in on an enemy, in a ball, or standing on the ground, reset most of the flags associated with the teleport state
			if player.homing
			or P_IsObjectOnGround(player.mo)
			or (player.mo.state >= S_PLAY_ROLL and player.mo.state <= S_PLAY_ROLL and player.pflags & PF_THOKKED)
				player.mo.flags = $1 & !MF_NOCLIPTHING
				player.mo.flags = $1|MF_SHOOTABLE
			end
			
			if player.mo.falldelay == 1
		//if the falldelay timer is on exactly one tic
				player.mo.state = S_PLAY_ROLL
			//set the player's state to rolling			
				player.pflags = $1|PF_JUMPED
			//add the jumping flag
			end
			
			if player.cctimer == nil
		//if your chaos control timer has not been set yet
				player.cctimer = 0
			//set this to 0 tics
			elseif player.cctimer > 0
		//otherwise, if this is greater than 0
				player.cctimer = $1-1
			//lower the timer by 1 each tic
				player.mo.flags2 = $1|MF2_DONTDRAW 
			//during chaos control, you are invisible
				player.mo.friction = FRACUNIT 
			//there's also no slowing down due to friction
				player.powers[pw_nocontrol] = 1 
			//and you are locked into whatever movement you chose
				player.pflags = $1|PF_FULLSTASIS
			//you also can not move or jump
			end
			
			if player.cctimer == 1
		//if the chaos control timer is exactly on the last frame
				ToggleCC(player)
			//call that chaos control toggle function  from earlier
			end
			
			if (player.pflags & PF_SPINNING) and not (player.mo.state >= S_PLAY_ROLL and player.mo.state <= S_PLAY_ROLL)
		//if you have the spinning flag, but you're not in the spinning animation
				player.mo.state = S_PLAY_ROLL
			//display the spin animation
			end
			
		//from here, i believe this is pretty much the FSonic spindash, so i'll leave this as is.
			if (player.pflags & PF_STARTDASH)
				and (player.mo.state >= S_PLAY_ROLL and player.mo.state <= S_PLAY_ROLL) then
				player.mo.state = S_CSTMSPIN1 //Change state
			end
			if (player.mo.state == S_CSTMSPIN1)
				and (player.pflags & PF_STARTDASH) then
				player.panim = PA_ROLL
				
				if not player.mo.sonicspindashframe then
					player.mo.sonicspindashframe = 0
				end
				
				player.mo.sonicspindashframe = ($1 + (player.dashspeed/90)) % (4*FRACUNIT)
				
				if (player.mo.eflags & MFE_UNDERWATER)
				for i=1,8 do
					local bubble = P_SpawnMobj(player.mo.x, player.mo.y,
						player.mo.z + ((player.mo.eflags & MFE_VERTICALFLIP)/MFE_VERTICALFLIP * (player.mo.height - mobjinfo[MT_SMALLBUBBLE].height)),
						MT_SMALLBUBBLE)
					
					bubble.tics = 8
					bubble.eflags = $1 | (player.mo.eflags & MFE_VERTICALFLIP)
					bubble.scale = player.mo.scale >> 1
					bubble.destscale = player.mo.scale << 1
					bubble.fuse = 10
					bubble.scalespeed = FixedMul(bubble.scalespeed, player.mo.scale) -- scale the scaling speed!
					P_SetObjectMomZ(bubble, player.dashspeed/30+P_RandomByte()<<10, false)
				   
					P_InstaThrust(bubble, player.mo.angle+FixedAngle(P_RandomRange(-15,15)*FRACUNIT), -(30*FRACUNIT)/9)
					P_TryMove(bubble, bubble.x+bubble.momx, bubble.y+bubble.momy, true)
				end
				end
				if (player.powers[pw_shield] == SH_ELEMENTAL) and not (player.mo.eflags & MFE_UNDERWATER) and not (player.mo.eflags & MFE_GOOWATER)
				for i=1,8 do
					local flame = P_SpawnMobj(player.mo.x, player.mo.y,
						player.mo.z + ((player.mo.eflags & MFE_VERTICALFLIP)/MFE_VERTICALFLIP * (player.mo.height - mobjinfo[MT_FIREDUST].height)),
						MT_FIREDUST)
					
					flame.tics = 8
					flame.eflags = $1 | (player.mo.eflags & MFE_VERTICALFLIP)
					flame.scale = player.mo.scale >> 1
					flame.destscale = player.mo.scale << 1
					flame.fuse = 10
					flame.scalespeed = FixedMul(flame.scalespeed, player.mo.scale) -- scale the scaling speed!
					P_SetObjectMomZ(flame, player.dashspeed/30+P_RandomByte()<<10, false)
				   
					P_InstaThrust(flame, player.mo.angle+FixedAngle(P_RandomRange(-15,15)*FRACUNIT), -(30*FRACUNIT)/9)
					P_TryMove(flame, flame.x+flame.momx, flame.y+flame.momy, true)
				end
				end
				if not (player.powers[pw_shield] == SH_ELEMENTAL) and not (player.mo.eflags & MFE_UNDERWATER) and not (player.mo.eflags & MFE_GOOWATER)
				for i=1,8 do
					local particle = P_SpawnMobj(player.mo.x, player.mo.y,
						player.mo.z + ((player.mo.eflags & MFE_VERTICALFLIP)/MFE_VERTICALFLIP * (player.mo.height - mobjinfo[MT_PARTICLE].height)),
						MT_PARTICLE)
					
					particle.tics = 8
					particle.eflags = $1 | (player.mo.eflags & MFE_VERTICALFLIP)
					particle.scale = player.mo.scale >> 1
					particle.destscale = player.mo.scale << 3
					particle.scalespeed = FixedMul(particle.scalespeed, player.mo.scale) -- scale the scaling speed!
					P_SetObjectMomZ(particle, player.dashspeed/30+P_RandomByte()<<10, false)
				   
					P_InstaThrust(particle, player.mo.angle+FixedAngle(P_RandomRange(-15,15)*FRACUNIT), -(30*FRACUNIT)/9)
					P_TryMove(particle, particle.x+particle.momx, particle.y+particle.momy, true)
				end
				end
				player.mo.frame = player.mo.sonicspindashframe/FRACUNIT
				
			end
		end
	end
end)

addHook("MobjThinker", function(char)
//this runs for each mobile object in the map
local player = char.player
//this just makes it easier to read this hook like a thinkframe
	if (player.mo and player.mo.skin == "legacyshadow")
//if you're not on the title and you're searching for that damn fourth chaos emerald (i can only make this so interesting)
		player.charflags = $1|SF_SUPER
	//add the skin flag that allows you to go super
		if (player.mo.state == S_PLAY_DEAD)
	//if you're dying
			if (player.mo.eflags & MFE_UNDERWATER)
		//if you're underwater
			and player.powers[pw_underwater] <= 0
		//and your drowning timer is less than 0
				player.mo.frame = n
			//use the drowning sprite instead
			end
		end
	end
end, MT_PLAYER)