--Here we keep all the objects dedicated to Yu Sonic's existence.

--SA-styled Jump Ball. Surrounds Sonic as he jumps, spins, and bounces!

addHook("MobjThinker", function(s)
	if s.target and s.target.valid
	local t = s.target
	A_CapeChase(s,1,0)
		if not s.valid --Cape Chase is dangerous....
		return
		end
		
		s.skin = "adventuresonic"

		if s.target.player
		local p = s.target.player
		s.angle = p.drawangle
			if p.yusonictable
			local yu = p.yusonictable
				if (yu.startdash or p.pflags & PF_STARTDASH)
					s.spriteyscale = FRACUNIT*100/(yu.yspinstretch)
					s.spritexscale = FRACUNIT*96/(yu.xspinstretch)
					s.spriteyoffset = -2*FRACUNIT
				end
				if p.powers[pw_carry] == CR_ZOOMTUBE --Match vertical offset in zoomtubes better.
					s.spriteyoffset = -10*FRACUNIT
					t.spriteyoffset = -8*FRACUNIT
					yu.resetspriteoffset = 2
				end	
			end
		end
	end
end, MT_YUJUMPBALL)

--SA2-styled Jumptrails. One of them hovers over Sonic at all times, we want Lua for that.

addHook("MobjThinker", function(s)
	if s.tracer and s.tracer.valid
		A_CapeChase(s, 1,0)
	end
end, MT_YUJUMPTRAIL)

--Enemies! How will they react to various levels of pain? ...shall we find out?

addHook("MobjDamage", function(s, i, so)
	if not (so and so.player and so.skin == "adventuresonic" and so.player.yusonictable)
	return
	end
local yu = so.player.yusonictable

  if (s.flags & (MF_ENEMY|MF_BOSS|MF_MONITOR))
		
	--Don't go skyrocketing off enemies/monitors when bouncing on them.
	
	if yu.bouncestate and i and i.skin == "adventuresonic" and i.player
	
		if ((so.state == S_PLAY_FALL) and yu.bouncestate == 2)
			YuResetBounce(so.player, so, yu)
			so.state = S_PLAY_ROLL
			so.player.pflags = $|PF_JUMPED & ~PF_THOKKED & ~PF_SPINNING
		else
			yu.skipgroundcheck = 1
			yu.bouncexstretch = 70
			yu.bounceystretch = 132
		
			so.state = S_PLAY_ROLL
			so.sprite2 = SPR2_ROLL
			
			local ghs = P_SpawnGhostMobj(so)
			ghs.z = s.z
			ghs.spritexscale = 115356

			if (so.eflags & MFE_UNDERWATER)
				yu.firstbounce = false
				P_SetObjectMomZ(so, 7*FRACUNIT)	
			else
				yu.firstbounce = false
				P_SetObjectMomZ(so, 16*FRACUNIT)			
			end
			so.player.pflags = $|PF_JUMPED & ~PF_THOKKED & ~PF_SPINNING
			so.eflags = $ & ~MFE_JUSTHITFLOOR
		
			S_StartSound(so, sfx_yusfx3)
			yu.bouncestate = 2
		end
		
	elseif yu.hominganimstart and (so.player.pflags & PF_JUMPED) and i and i.skin == "adventuresonic" and i.player
	and not i.player.homing and not yu.lsahoming
	
		--Don't fly sky-high when thokking out of a bounce.	
			if (so.eflags & MFE_UNDERWATER)
				P_SetObjectMomZ(so, 6*FRACUNIT)	
			else
				P_SetObjectMomZ(so, 14*FRACUNIT)			
			end		
			
	--Bounce a little further off bosses to make them a little harder to cheese with homing-spam
		
	elseif (s.flags & MF_BOSS) and (so.player.homing or yu.lsahoming) and so.skin == "adventuresonic"
	and i and i.skin == "adventuresonic" and i.player
	P_Thrust(so, R_PointToAngle2(so.x, so.y, s.x, s.y), -132*FRACUNIT)
	yu.nosarun4 = 70
		if not yu.lsahoming
		yu.stophomingtrick = true
		end
		if (s.type == MT_EGGMOBILE4) or yu.lsahoming --Bounce VERY far from this guy to avoid getting crushed.
		P_Thrust(so, R_PointToAngle2(so.x, so.y, s.x, s.y), -100*FRACUNIT)
		end
		return
	end
  end
end)

--Metal Sonic, ffs, do your stupid thing.

addHook("MobjDeath", function(s, i, so)
	if so and so.skin == "adventuresonic"
		if s.valid
			A_BossDeath(s,0,0)
		end
		if s.valid
	 		A_BossScream(s,0,0)
		end
	end
end, MT_METALSONIC_BATTLE)

--Collision detection stuff.

addHook("MobjMoveCollide", function(s, v)
if not (s.player or (v and v.valid))
return end
local p = s.player
	if s.skin == "adventuresonic" and p.yusonictable
	local yu = p.yusonictable
		if (v.flags & MF_SPRING)
			if not (s.z > v.z+v.height) and not (v.z > s.z+s.height)				
				if v.info.mass --If a spring has a mass value, it means vertical spring power!
				--Yes, that's confusing. But no, Sonic Team JR, don't nuke all backwards compatibility by renaming the value.
				yu.hitspring = 2
				yu.yteleport = nil
				yu.airleverage = 0
				yu.homingsetup = false
				yu.sa2jumpdone = false
				yu.airstart = 8
				yu.flighthoming = 0
				yu.superflight = 0
					if (yu.lsatarget or yu.lightdashing or yu.lsaattack)
						YuStopLSA(p, s, yu)
						if P_IsObjectOnGround(s)
						s.state = S_PLAY_FALL
						p.pflags = $ & ~PF_JUMPED & ~PF_THOKKED & ~PF_SPINNING & ~PF_STARTDASH
						end
					elseif yu.lsadash
					and not yu.lsaattack and not yu.lightdashing and not yu.lsahoming
					yu.lsadash = 0
					YuCancelLSA(p, s, yu)
					YuStopLSAdash(p, s, yu)
						if P_IsObjectOnGround(s)
						s.state = S_PLAY_FALL
						p.pflags = $ & ~PF_JUMPED & ~PF_THOKKED & ~PF_SPINNING & ~PF_STARTDASH
						end
					end
					if (v.type == MT_BLUESPRING) and yu.jump // and if not v.info.damage and p.homing 
						yu.strongbspring = true
						yu.strongspring = 27
					end
				else --Okay, so a horizontal spring, then. The thrust is determined by the damage value, btw.
					if (p.pflags & PF_SPINNING)
					yu.noubercontrol = 6 --Prevent the player from flying out of control
					end
					
					yu.yteleport = nil
					yu.nosarun4 = 14
					yu.flighthoming = 0
					
					if v.info.damage
					local noairdrag = v.info.damage/FRACUNIT
						yu.nospindrag = 60
						if v.info.damage > 45*FRACUNIT
						s.friction = FRACUNIT --Uphold some speed from the spring! Like...vanilla honestly shoulda been doing.
						yu.runfriction = FRACUNIT
						yu.frictionlock = (((v.info.damage/FRACUNIT)-45)/2)
							if yu.frictionlock > 23
							yu.frictionlock = 23
							elseif yu.frictionlock < 5
							yu.frictionlock = 5
							end
						end
						if noairdrag > 70 --Cap
						noairdrag = 70
						end

						yu.noairdrag = noairdrag

						if (yu.lsatarget or yu.lightdashing or yu.lsaattack)
							YuStopLSA(p, s, yu)
							if P_IsObjectOnGround(s)
							s.state = S_PLAY_FALL
							p.pflags = $ & ~PF_JUMPED & ~PF_THOKKED & ~PF_SPINNING & ~PF_STARTDASH
							end
						elseif yu.lsadash
						and not yu.lsaattack and not yu.lightdashing and not yu.lsahoming
						yu.lsadash = 0
						YuCancelLSA(p, s, yu)
						YuStopLSAdash(p, s, yu)
							if P_IsObjectOnGround(s)
							s.state = S_PLAY_FALL
							p.pflags = $ & ~PF_JUMPED & ~PF_THOKKED & ~PF_SPINNING & ~PF_STARTDASH
							end
						
						--Snap to the floor for smoother gameplay!
						elseif ((v.z < v.floorz+30*v.scale) and not (s.eflags & MFE_VERTICALFLIP))
						or ((v.z > v.ceilingz-60*v.scale) and (s.eflags & MFE_VERTICALFLIP))
							if p.homing 
								p.homing = 0
								if not P_IsObjectOnGround(s)
								p.pflags = $|PF_JUMPED & ~PF_THOKKED
								else 
								s.state = S_PLAY_FALL
								end
							elseif yu.bouncestate						
								YuResetBounce(p, s, yu)
							end				
							if (s.eflags & MFE_VERTICALFLIP) and (s.z > s.ceilingz-110*FRACUNIT)
							//	s.z = s.ceilingz
								if not (p.pflags & PF_SPINNING)
								s.state = S_PLAY_FALL
									if not P_IsObjectOnGround(s)
									p.pflags = $|PF_JUMPED & ~PF_THOKKED
									end
								end
							elseif (s.z < s.floorz+110*FRACUNIT)
							//	s.z = s.floorz
								if not (p.pflags & PF_SPINNING) --Allow continued spinning/charging.
								s.state = S_PLAY_FALL
									if not P_IsObjectOnGround(s)
									p.pflags = $|PF_JUMPED & ~PF_THOKKED
									end
								end
							end
						end
					end
				end
			end	
			return
		elseif (yu.bouncestate or (yu.superaura and yu.supersonic)) and (v.flags & MF_MONITOR and v.health)
			if not ((s.z > v.z+v.height+5*FRACUNIT) or ((s.z > v.z+v.height+40*FRACUNIT)))
			and not (v.z > s.z+s.height)
			P_KillMobj(v, s, s) --Kill boxes with the bounce without technically touching them.
			return false
			end
		elseif (yu.lsahoming or yu.lsadash or p.powers[pw_super])
		and (v.valid and ((v.type == MT_EGGSHIELD) or (v.type == MT_SPIKE) or (v.type == MT_WALLSPIKE) or (v.type == MT_TURRET) or (v.type == MT_EGGROBO1)
		or (v.type == MT_CYBRAKDEMON_ELECTRIC_BARRIER)) and v.health)
			if (v.type == MT_SPIKE) or (v.type == MT_WALLSPIKE) or (v.type == MT_TURRET)
				if not ((s.z > v.z+v.height+20*FRACUNIT) or (s.z+s.height+20*FRACUNIT < v.z))
				P_KillMobj(v, s, s)
				return false
				end
			elseif (v.type == MT_EGGROBO1)
				return false
			elseif (v.type == MT_EGGSHIELD)
				if v.target and v.target.valid and v.target.health and (v.target.type == MT_EGGGUARD)
				v.target.flags = $|MF_ENEMY|MF_SHOOTABLE
				P_KillMobj(v.target, s, s)
				yu.hadnotarget = 0
				end
				P_KillMobj(v, s, s)
				return false
			elseif (v.type == MT_CYBRAKDEMON_ELECTRIC_BARRIER)
				if v.target and v.target.valid and (v.target.type == MT_CYBRAKDEMON) and (v.target.health < 13)
					v.target.health = $+1 --Don't do damage to Brak if he has a shield.
					v.target.yulsaresist = true
				end
				P_KillMobj(v, s, s)
				return false
			end	
		elseif ((v.type == MT_SALOONDOOR) or (v.type == MT_SALOONDOORCENTER)) and (gametype == GT_COOP)
			if v.valid and v.health and ((p.pflags & PF_SPINNING) or p.powers[pw_super] or yu.lsahoming or yu.lsadash)
			and not (v.flags & MF_NOCLIP)
				S_StartSound(s, sfx_wbreak)
				for d = 0,12
					local debris = P_SpawnMobj(v.x+(P_RandomRange(-50,50)*v.scale), v.y+(P_RandomRange(-50,50)*v.scale), v.z+(P_RandomRange(5,80)*v.scale), MT_WOODDEBRIS)
					if debris and debris.valid
					debris.flags = $|MF_BOUNCE|MF_GRENADEBOUNCE|MF_NOCLIPTHING
					P_InstaThrust(debris, s.angle+(P_RandomRange(-20,20)*ANG1), p.speed+8*s.scale)
					P_SetObjectMomZ(debris, P_RandomRange(-4,8)*s.scale, false)
					end
				end
				if (v.type == MT_SALOONDOOR)
				v.flags = $|MF_NOCLIP
				P_KillMobj(v, s, s)
				elseif (v.type == MT_SALOONDOORCENTER)
				v.flags = $|MF_NOCLIP
				end
				return false
			end
		end
		--Because every character needs something ridiculous, right?
		if yu.superaura and p.powers[pw_super]
		and v and v.valid 
		and ( ((v.type >= MT_GFZTREE) and (v.type <= MT_THZTREEBRANCH) or (v.type == MT_PINETREE) or ((v.type >= MT_STALAGMITE0) and (v.type <= MT_ROSY)))
		or ((v.type >= MT_CACTI1) and (v.type <= MT_CACTISMALLSEG))
		or ((v.flags & MF_SOLID) and ((v.flags & MF_SCENERY) or (v.flags & MF_PUSHABLE))) or (v.flags == MF_SOLID) )
			if not (s.z > v.z+v.height) and not (v.z > s.z+s.height)		
			S_StartSound(s, sfx_wbreak)
			S_StartSound(s, sfx_wbreak)
			S_StartSound(v, sfx_kc40)

				local ghs = P_SpawnMobjFromMobj(v, 0,0,0, MT_YUTHOKDISP)
				ghs.flags = v.flags
				ghs.sprite = v.sprite
				ghs.frame = v.frame
				ghs.flags2 = v.flags2|MF2_DONTDRAW
				ghs.target = v
				ghs.iamatree = 200
				ghs.scale = v.scale/20
				ghs.scalespeed = $/4
			
				local num = 0
					if not ((v.type >= MT_GFZTREE) and (v.type <= MT_THZTREEBRANCH) or (v.type == MT_PINETREE) or (v.type >= MT_ARIDSIGN_CAUTION and v.type <= MT_ARIDSIGN_SHARPTURN))
					num = 12
					end
					for d = num,12
						local debris = P_SpawnMobj(v.x+(P_RandomRange(-50,50)*v.scale), v.y+(P_RandomRange(-50,50)*v.scale), v.z+(P_RandomRange(5,80)*v.scale), MT_WOODDEBRIS)
						if debris and debris.valid
						debris.flags = $|MF_BOUNCE|MF_GRENADEBOUNCE|MF_NOCLIPTHING
						P_InstaThrust(debris, s.angle+(P_RandomRange(-30,30)*ANG1), p.speed+8*s.scale)
						P_SetObjectMomZ(debris, P_RandomRange(3,8)*s.scale, false)
							if d == 12 --You are a tree.
							debris.sprite = v.sprite
							debris.frame = v.frame
							ghs.treedebris = debris
							ghs.treedebrisdir = P_RandomRange(1,2)
								if ghs.treedebrisdir == 2
								ghs.treedebrisdir = -1
								end
							end
						end
					end
				
				v.flags = $|MF_NOCLIPTHING
				v.flags2 = $|MF2_DONTDRAW
				
			return false
			end
		end
		
	end
end, MT_PLAYER)

//Ignore shields when lightspeed attacking.
local function ShieldNopeS(s, so)
	if so and so.player and so.player.yusonictable and (so.player.yusonictable.lsahoming or so.player.yusonictable.lsadash or so.player.powers[pw_super])
	return true
	end
end
local function ShieldNope(s, so)
	if so and so.player and so.player.yusonictable
	and (so.player.yusonictable.lsahoming or so.player.yusonictable.lsadash or so.player.powers[pw_super])
			if s.target and s.target.valid and s.target.health and (s.target.type == MT_EGGGUARD)
				s.target.flags = $|MF_ENEMY|MF_SHOOTABLE
				P_KillMobj(s.target, so, so)
			end
			YuLSAimpact(so.player, so, so.player.yusonictable, s)
			if s and s.valid
				s.state = S_NULL
			end
	return false
	end
end
addHook("TouchSpecial", ShieldNopeS,  MT_EGGSHIELD)
addHook("MobjCollide", ShieldNope,  MT_EGGSHIELD)

--Eject the passenger, we did NOT sign up for this!! Only in coop, though, we don't want to give SA-Sonic an advantage in races.

addHook("MobjThinker", function(s)
	if s.valid and s.health and s.target
		if s.target.valid and s.target.skin == "adventuresonic" and s.target.player and s.target.player.yusonictable
		local yu = s.target.player.yusonictable
		local p = s.target.player
			if (p.powers[pw_carry] == CR_MINECART) and (gametype == GT_COOP)
				if (yu.spin and yu.spin < 3)
				p.powers[pw_carry] = 0
				p.pflags = $|PF_JUMPED & ~PF_THOKKED
				s.target.momx = s.momx
				s.target.momy = s.momy
				P_DoJump(p, true)
				P_SetObjectMomZ(s.target, 8*FRACUNIT, true)
				s.target = nil
				return
				end
			end
		end
	end
end, MT_MINECART)

//The Vault move, jump off Sonic's spindash to leap to new heights!

addHook("MobjThinker", function(s)
	if s.valid and s.target and s.target.valid and s.target.skin == "adventuresonic"
	and s.target.player and s.target.player.yusonictable and s.health
		local t = s.target
			
		s.scale = t.scale
		P_TeleportMove(s, t.x, t.y, t.z-32*s.scale) --Invisible, so don't need to optimize for uncapped.
		
	elseif s.valid
		s.state = S_NULL
		return
	end
end, MT_YUVAULT)

local function YuVaultMove(s, v)
	if v and v.valid and v.player and not v.player.bot and s.target and s.target.valid and not (v == s.target)	
		if v.player.yusonictable and not v.player.yusonictable.vaultanim and not v.player.yusonictable.ignorevaults
		and not (s.z > v.z+v.height) and not (v.z > s.z+s.height)
		
			if not s.supervault
			v.player.yusonictable.vaultready = 3
			v.player.yusonictable.vaultreadytarget = s.target
			
			else
			
				if not v.player.powers[pw_shield] and s.target.player
				and s.target.player.powers[pw_super] and s.target.skin == "adventuresonic"
				and not (v.player.powers[pw_super] and v.skin == "adventuresonic")
				v.player.powers[pw_shield] = SH_YUSUPERSHIELD
				P_YuFlash(v.player, PAL_RECYCLE, 3)
				S_StartSound(v, sfx_yusfx9)
				v.player.yusonictable.superorigin = s.target
					for d = 1,18
						YuSuperSparkles(v.player, v, v.player.yusonictable, false, 4)
					end
				end
			end
			
		end
	end
	return false	
end

addHook("MobjCollide", YuVaultMove,  MT_YUVAULT)
addHook("MobjMoveCollide", YuVaultMove,  MT_YUVAULT)

--Not pushing Super Sonic or invincible Sonic, pals.
local function EggRoNope(s, v)
	if v and v.player and v.skin == "adventuresonic" and (v.player.powers[pw_super] or v.player.powers[pw_invulnerability])
	
			YuLSAimpact(v.player, v, v.player.yusonictable, s)
			
			local debris = P_SpawnMobj(s.x+(P_RandomRange(-50,50)*s.scale), s.y+(P_RandomRange(-50,50)*s.scale), s.z+(P_RandomRange(5,80)*s.scale), MT_BOSSJUNK)
				if debris and debris.valid
					debris.flags = $|MF_BOUNCE|MF_GRENADEBOUNCE|MF_NOCLIPTHING
					debris.scale = v.scale
					debris.angle = v.angle+(P_RandomRange(120,-120)*ANG1)
					debris.sprite = s.sprite
					debris.frame = s.frame
					debris.rollangle = ANG1*P_RandomRange(0,359)
					P_InstaThrust(debris, s.angle, 20*v.scale)
					P_SetObjectMomZ(debris, 4*v.scale, false)
					debris.momx = $+v.momx
					debris.momy = $+v.momy
					debris.momz = $+v.momz
				end
				
				if s and s.valid
					s.state = S_NULL
					return false
				end
	return false
	end
end

addHook("MobjCollide", EggRoNope,  MT_EGGROBO1)
addHook("MobjMoveCollide", EggRoNope,  MT_EGGROBO1)

//Jump back out of the death pit at the cost of your shield!

local function NegatePit(s, p)
	s.momx = $/4
	s.momy = $/4
	s.z = $+(100*s.scale*P_MobjFlip(s))
	S_StartSound(s, sfx_wirlsg)
	S_StartSound(s, sfx_shldls)
	S_StartSound(s, sfx_shrpgo)
	S_StartSound(s, sfx_kc5b)
	P_NukeEnemies(s, s, 180*FRACUNIT)
	P_YuFlash(p, PAL_RECYCLE, 4)
	
	local ghs = P_SpawnGhostMobj(s)
	ghs.destscale = $*99
	ghs.blendmode = AST_ADD
	
	P_SetObjectMomZ(s, 30*FRACUNIT)
	s.state = S_PLAY_ROLL
	p.pflags = $|PF_JUMPED & ~PF_THOKKED & ~PF_SPINNING & ~PF_STARTDASH & ~PF_STARTJUMP
	P_RemoveShield(p)
end

//Who killed ya?
local function CheckKillerAngle(s, p, yu, i, so)
	if so and so.valid
		if so.player
			P_YuQuake(so.player, 80*FRACUNIT, 5)
		end
		return R_PointToAngle2(s.x, s.y, so.x-so.momx, so.y-so.momy)
	elseif i and i.valid
		if i.player
			P_YuQuake(i.player, 80*FRACUNIT, 5)
		end	
		return R_PointToAngle2(s.x, s.y, i.x-i.momx, i.y-i.momy)
	else
		return nil
	end
end

//Yu Sonic does NOT get damaged when....

addHook("ShouldDamage", function(s, i, so, dmg, dmgtype)
if not s.player
return
end

if s.player.powers[pw_shield] == SH_YUSUPERSHIELD
		if ((dmgtype == DMG_ELECTRIC) or (dmgtype == DMG_WATER))
		return false
		elseif (dmgtype == DMG_DEATHPIT)
			NegatePit(s, s.player)
			return false		
		end
end

if not ((s.skin == "adventuresonic") and s.player.yusonictable)
return end

local p = s.player
local yu = s.player.yusonictable

	if dmgtype
		if (dmgtype == DMG_DEATHPIT)
			yu.deathangle = nil
			if p.powers[pw_super] --Fly back up!
				s.momx = $/4
				s.momy = $/4
				local ghs = P_SpawnGhostMobj(s)
				ghs.destscale = $*99
				ghs.blendmode = AST_ADD
				P_SetObjectMomZ(s, 30*FRACUNIT)
				p.pflags = $|PF_JUMPED & ~PF_THOKKED
				YuSuperFlyStart(p, s, yu, true)
				return false
				
			elseif s.health
			
				if (p.powers[pw_shield] == SH_WHIRLWIND) or (p.powers[pw_shield] == (SH_WHIRLWIND|SH_FIREFLOWER))				
					yu.startdash = 0
					NegatePit(s, p)
					return false
				else
					if ((s.state == S_PLAY_FALL) or (s.state == S_PLAY_ROLL) or (s.state == S_PLAY_JUMP))
					yu.downtheholestate = S_PLAY_FALL
					else
					yu.downtheholestate = S_PLAY_PAIN
					end
				end
			end

		elseif (dmgtype == DMG_CRUSHED)  --Barely got clipped by a crush? We'll let you off with regular damage.
		and (p.rings or p.powers[pw_shield] or p.powers[pw_flashing] or p.powers[pw_invulnerability])
		local ogx = s.x
		local ogy = s.y
			for d = 0,7
			local ssector = R_PointInSubsectorOrNil(s.x+FixedMul(50*FRACUNIT, cos(s.angle+(ANGLE_45*d))), s.y+FixedMul(50*FRACUNIT, sin(s.angle+(ANGLE_45*d))))
				 if ssector != nil and P_CheckPosition(s, s.x+FixedMul(50*FRACUNIT, cos(s.angle+(ANGLE_45*d))), s.y+FixedMul(50*FRACUNIT, sin(s.angle+(ANGLE_45*d))))
				 and ssector.sector and (ssector.sector.floorheight+(48*s.scale) < ssector.sector.ceilingheight)
				 and not (yu.maxcrush > 15)
				 
				 s.spritexscale = FRACUNIT+22000
				 s.spriteyscale = FRACUNIT-22000
				 local ghs = P_SpawnGhostMobj(s)
				 ghs.spritexscale = 115356
				 P_TeleportMove(s, s.x+FixedMul(50*FRACUNIT, cos(s.angle+(ANGLE_45*d))), s.y+FixedMul(50*FRACUNIT, sin(s.angle+(ANGLE_45*d))), s.z)
				 p.pflags = $ & ~PF_SPINNING & ~PF_STARTDASH
				 yu.startdash = 0
				 
				 	if not p.powers[pw_flashing]
					S_StartSound(s, sfx_s3kae)
					YuVoice(p, s, yu, sfx_yuvoc3)
				 	end
					
				 	if s.z == s.floorz
				 	s.z = $+5*FRACUNIT
					end
				 
				 P_DamageMobj(s)
				 local ghs = P_SpawnGhostMobj(s)
				 ghs.spritexscale = 115356
				 P_DoPlayerPain(p)
				 p.drawangle = R_PointToAngle2(s.x, s.y, ogx, ogy)
				 yu.bouncexstretch = 70
				 yu.bounceystretch = 132	
				 yu.maxcrush = $+4
				 
				 return false
				 end
			end
		end
	end 

	--All these properties take priority over the next, hence the elseif chain.

	if yu.lsahoming or yu.supertransform --Total invincibility during this.
		return false
	elseif yu.ledgegrabcooldown >= 24 --Get up invincibility against enemies and hazards.
	and so and ((so.flags & MF_ENEMY) or (so.flags & MF_PAIN))
	and (s.state == S_PLAY_SPRING) and (p.pflags & PF_JUMPED)
		return false		
	elseif p.homing
		if i
			if (i.type == MT_TNTBARREL) or (i.type == MT_DRAGONTAIL)
				return false
			elseif ((i.type == MT_SNAPPER_LEG) or (i.type == MT_SNAPPER_HEAD) or (i.type == MT_GSNAPPER) or (i.type == MT_MINUS)
			or (i.type == MT_FACESTABBERSPEAR) or (i.type == MT_FANG))
				if (s.z > i.z+(i.height/5)) --Only let players take damage when it's very obvious they attacked from the wrong spot.
				return false --You don't want them to feel like they got clipped by glitchy hitboxes, even if it WAS technically their fault.
				end
			end
		end
	elseif yu.homingresist or yu.lightdashaftereffects --Note how this resistance only applies when p.homing is not active and thus has to be BELOW p.homing
		if so and ((so.flags & MF_ENEMY) or (so.flags & MF_BOSS)) or (dmgtype and dmgtype == DMG_SPIKE)
		return false
		end
	elseif yu.bouncestate
		if i and ((i.type == MT_SNAPPER_LEG) or (i.type == MT_SNAPPER_HEAD) or (i.type == MT_MINUS)
		or (i.type == MT_FACESTABBERSPEAR) or (i.type == MT_DRAGONTAIL) or (i.type == MT_CRUSHCLAW))
		return false --Ignore these when bouncing.
		end
	end
end, MT_PLAYER)

--Many special death animations in this MobjKill hook.

addHook("MobjDeath", function(s, i, so, dmgtype)
if not ((s.skin == "adventuresonic") and s.player and s.player.yusonictable)
return end
local p = s.player
local yu = s.player.yusonictable
	if yu.lsacharged
		YuCancelLSA(p, s, yu)
	end
	if yu.lsaattack or yu.lsadash
		YuStopLSA(p, s, yu)
	end
	if yu.bouncestate
	YuResetBounce(p, s, yu)
	end
	YuCancelTricks(p, s, yu)
	if dmgtype	
		if (dmgtype == DMG_DEATHPIT) --We fell into a pit!	
			if (mapheaderinfo[gamemap].skynum == 22)
			and s.subsector and s.subsector.sector
			and (((s.eflags & MFE_VERTICALFLIP) and ((s.subsector.sector.ceilingpic == "F_SKY1") or not s.subsector.sector.ceilingpic))
			or (((s.subsector.sector.floorpic == "F_SKY1") or not s.subsector.sector.floorpic) and not (s.eflags & MFE_VERTICALFLIP)))
			yu.downtheholefire = 60 --We're falling to earth!!
			else
			yu.downtheholefire = 0
			end
			yu.deathstart = 0
			yu.deathangle = nil
				yu.downtheholemomx = s.momx
				yu.downtheholemomy = s.momy
				yu.downtheholemomz = s.momz
				yu.downthehole = 2
				S_StopSoundByID(s, skins["adventuresonic"].soundsid[SKSPLDET1])
				S_StopSoundByID(s, skins["adventuresonic"].soundsid[SKSPLDET2])
				S_StopSoundByID(s, skins["adventuresonic"].soundsid[SKSPLDET3])
				S_StopSoundByID(s, skins["adventuresonic"].soundsid[SKSPLDET4])
				
		elseif (dmgtype == DMG_FIRE) --Fire death!
		
			yu.deathangle = CheckKillerAngle(s, p, yu, i, so)
			yu.deathstart = 2
			yu.burning = 200
			
		elseif (dmgtype == DMG_DROWNED)
		
			if not (s.eflags & MFE_UNDERWATER) and p.powers[pw_spacetime] --LIAR!! This is outter space!
				yu.deathstart = 2
				yu.spacedeath = true
			else
				yu.deathstart = 2
				yu.drowning = true
			end
		elseif (dmgtype == DMG_SPACEDROWN)
		
			yu.deathstart = 2
			yu.spacedeath = true
		else		
			if not yu.downthehole and not yu.drowning and not yu.spacedeath
			yu.deathangle = CheckKillerAngle(s, p, yu, i, so)
			yu.deathstart = 2
				if (dmgtype == DMG_ELECTRIC)
				yu.electricdeath = 65
				end
			 	if (dmgtype == DMG_NUKE)
				yu.bombdeath = true
				end
			end			
		end		
	else
		if not yu.downthehole and not yu.drowning and not yu.spacedeath
			if (so and so.player and so.player.hypermysticsonic)
			or ((i and i.valid) and ((i.type == MT_TNTBARREL) or (i.type == MT_PROXIMITYTNT) or (i.type == MT_DRAGONMINE) or (i.type == MT_EXPLOSIONRING)
			or (i.type == MT_FBOMB) or (i.type == MT_CYBRAKDEMON_VILE_EXPLOSION) or (i.type == MT_BOMBSPHERE) or (i.type == MT_CYBRAKDEMON_NAPALM_BOMB_LARGE)))
			yu.bombdeath = true
			end
		yu.deathangle = CheckKillerAngle(s, p, yu, i, so)
		yu.deathstart = 2
		s.sprite2 = SPR2_PAIN
		s.frame = A
		end	
	end
end, MT_PLAYER)

--Hurt enemies for a few frames after the homing attack.(also used for Lightdash/LSA)

addHook("PlayerCanDamage", function(p)
	if p.mo and p.mo.valid and (p.mo.skin == "adventuresonic") and p.yusonictable
		if (p.yusonictable.homingresist and (p.mo.momz == 0))
		or (p.yusonictable.ledgegrabcooldown > 24)
		or p.yusonictable.lsahoming or p.yusonictable.lightdashaftereffects
		or p.yusonictable.uncurlanim
		return true
		end
	end
end)

--Enter gaps always when jumping, even if not in rolling frames! (Mostly for SA2-jump style)

addHook("PlayerCanEnterSpinGaps", function(p)
	if p.mo and p.mo.valid and (p.mo.skin == "adventuresonic") and p.yusonictable
		if ((p.pflags & PF_JUMPED) or p.yusonictable.uncurlanim and P_IsObjectOnGround(p.mo) or p.yusonictable.ledgegrabcooldown > 24)
		or (p.mo.state == S_PLAY_DEAD)
		return true
		end
	end
end)

//The homing attack is meant as an aid, not a troll.
//Let's NOT punish players because the game intentionally targets hazards and won't let you thok until you're out of range.

addHook("MobjThinker", function(sp)
		if sp.tntdelayexp
			sp.tntdelayexp = $-1
			if sp.tntdelayexp == 1
			sp.flags = MF_MISSILE|MF_NOBLOCKMAP
			sp.momz = $-1
			sp.momy = $-1
			sp.momx = $-1
			end
		end
end, MT_TNTBARREL)

addHook("MobjDamage", function(s, i, so)
		if so and so.player and so.skin == "adventuresonic" and so.player.yusonictable
local yu = so.player.yusonictable
local p = so.player
			if (yu.lsaattack and not yu.lsadeadjump) and so.target and (so.target == s)
			or p.powers[pw_super]
				if (yu.lsaattack and not s.yulsaresist) or (yu.superaura and (p.speed > 100*so.scale))
				YuLSAimpact(p, so, yu, s)
				end
				if (s.health > 1)
				and not s.player
					if not (s.yulsaresist and not p.powers[pw_super])
					s.health = $-1
						P_YuQuake(p, 50*FRACUNIT, 7)
						if yu.lsaattack and not p.powers[pw_super]
						s.yulsaresist = true
						return
						end
					end
				end
			end
			if s.yulsaresist
				if not yu.lsaattack
					for d = 1,16
						local ghs = P_SpawnMobjFromMobj(s, 0, 0, s.height/2, MT_THOK)
						ghs.blendmode = AST_ADD
						ghs.sprite = SPR_YUSO
						ghs.color = SKINCOLOR_SUPERSKY1+(d/3)
						ghs.frame = J|FF_FULLBRIGHT|TR_TRANS10
						ghs.fuse = 12
						ghs.tics = 30
						ghs.destscale = $*99
						P_SetObjectMomZ(ghs, 6*FRACUNIT)
						P_InstaThrust(ghs, s.angle+((ANGLE_45/2)*d), 25*FRACUNIT)
					end
					s.yulsaresist = false --Let the shackles be released!
					S_StartSound(s, sfx_shattr)
					S_StartSound(so, sfx_shattr)			
					P_YuQuake(p, 21*FRACUNIT, 2)
				else
					local ghs = P_SpawnGhostMobj(s)
					ghs.destscale = $*99
					ghs.scalespeed = $*3
					ghs.fuse = 18
					ghs.tics = 40
					ghs.colorized = true
					ghs.color = SKINCOLOR_YULSA
					S_StartSound(s, sfx_iceb)
					S_StartSound(so, sfx_iceb)
					S_StartSound(so, sfx_s1b4)
				end
			end
		end
end)
addHook("MobjDeath", function(s, i, so)
		if so and so.player and so.skin == "adventuresonic" and so.player.yusonictable
local yu = so.player.yusonictable
local p = so.player
			if yu.lsaattack and not yu.lsadeadjump
			and so.target and s.valid and (so.target == s)
			YuLSAimpact(p, so, yu, s)	
			end
			if s.valid and (s.flags & MF_BOSS)
			yu.bosskiller = 1 --For later!
			end
		end
end)

addHook("MobjDeath", function(sp, i, s)
	if sp and sp.valid and s and s.player and s.player.homing and (s.skin == "adventuresonic")
		P_SetObjectMomZ(sp, 12*FRACUNIT, false)
		P_Thrust(sp, s.angle, 35*FRACUNIT)
			if not (sp.state == S_TNTBARREL_FLYING)	
			S_StartSound(sp, sfx_bowl)
			sp.state = S_TNTBARREL_FLYING
			end
		s.player.homing = 0
		s.momx = 0 s.momy = 0
		s.state = S_PLAY_SPRING
		s.player.pflags = $|PF_NOJUMPDAMAGE
		P_SetObjectMomZ(s, 11*FRACUNIT, false)
		P_Thrust(s, s.angle, -8*FRACUNIT)
		sp.health = 1
		sp.tntdelayexp = 4
		sp.flags = MF_NOBLOCKMAP
		return true
	end
end, MT_TNTBARREL)

--Wouldn't do this if SRB2 didn't intentionally force you to lock on despite these being hazards and blocking your airdash.
--But they do it anyway, so.....here we are.

addHook("MobjDeath", function(sp, i, s)
	if sp and sp.valid and s and s.player and (s.player.homing or (s.player.yusonictable and s.player.yusonictable.lsahoming) or s.player.powers[pw_super]) and (s.skin == "adventuresonic")
	
		P_SpawnMobj(sp.x, sp.y, sp.z+sp.height, MT_EXPLODE)
		local gag = P_SpawnMobj(sp.x, sp.y, sp.z+sp.height, MT_YUTHOK)
		gag.sprite = SPR_TVEG
		gag.frame = C|FF_FULLBRIGHT
		gag.tics = -1
		gag.flags = MF_BOUNCE|MF_GRENADEBOUNCE|MF_NOCLIPTHING
		gag.rollangle = P_RandomRange(1,359)*ANG1
		gag.fuse = 59
		gag.z = $+10*FRACUNIT
		
		P_SetObjectMomZ(gag, 14*FRACUNIT)	
		P_InstaThrust(gag, s.angle, 40*FRACUNIT)
		S_StartSound(gag, sfx_bowl)
		S_StartSound(gag, sfx_behurt)
		
		sp.health = 0
		sp.flags = $|MF_NOCLIP & ~MF_SOLID
		sp.state = (sp.info.deathstate)+1
		
		return true
	end
end, MT_EGGMAN_BOX)

--Test for moving platforms....

local function platformtest(t, s)
	if t.subsector != s.subsector
		return true
	elseif t.subsector
		for polyobj in t.subsector.polyList()
			if polyobj.valid
			return true
			end
		end
	end
return false	
end

--Ledge Grab object!

addHook("MobjThinker", function(s)
  if s.target and s.target.valid and s.target.skin == "adventuresonic" and s.target.health and s.target.player and s.target.player.yusonictable
local t = s.target
local p = t.player
local yu = p.yusonictable	

	--Ledgegrab function!

	if s.yuactive

		if P_IsObjectOnGround(s) and not (yu.lsacheck or yu.lightdashing)
			--Flipped version
			if (t.eflags & MFE_VERTICALFLIP)
			and ((t.ceilingz+48*t.scale) > s.ceilingz)
			and (t.z+24*t.scale > s.z)
			and (t.floorz < (s.ceilingz-20*t.scale))
			and (t.z < t.ceilingz+48*t.scale) 
		
			--Non-flipped version
			or ((t.floorz-48*t.scale) < s.floorz)
			and (t.z-24*t.scale < s.z)  
			and (t.ceilingz > (s.floorz+20*t.scale)) 
			and (t.z > t.floorz+48*t.scale) 
			
				if s.subsector and s.subsector.sector --Nuh-huh, not these!
					for fof in s.subsector.sector.ffloors()
						if (fof.flags & FF_EXISTS) and ((fof.flags & FF_BLOCKOTHERS) and not (fof.flags & FF_BLOCKPLAYER))
							if (t.floorz < fof.topheight) --Is our actual floor below the fof's topheight?
								if not (t.z+5*t.scale > fof.topheight)
									if not (t.eflags & MFE_VERTICALFLIP)
									and not (fof.topheight > t.z+t.height+(230*t.scale))
									return
									end
								end
							end
						end
					end
				end
				--Cancel aaaaall this stuff first...
				yu.homingtrick = 0
				yu.lsatrick = nil
				s.rollangle = 0
				yu.chosentrick = 0
				yu.lsatrickrollang = 0
				yu.lsatrickanim = 0
				yu.htrickroll = 0
				
				t.momx = 0 t.momy = 0 t.momz = 0
				t.state = S_PLAY_RIDE
				t.spriteyoffset = -6*FRACUNIT
				t.spriteyscale = 61355 --Squash and stretch since 1 frame has to do the whole animation for us.
				yu.farthok = 0
				S_StopSoundByID(s, sfx_thok)
				S_StopSoundByID(s, sfx_yuvoc1)
				
				--Use Melee sprite2 for ledgegrab.
				s.sprite2 = SPR2_MLEE
				s.frame = A

				yu.ledgegrabcooldown = 30
				yu.airstart = 2
				S_StartSound(t, sfx_s3k4a) --grab sound
				YuVoice(p, t, yu, sfx_yuvoc2)
				yu.airborne = 50
				yu.sa2jumpdone = false
				
				--Less pull-up power underwater, even if it should work the other way.
				if (t.eflags & MFE_UNDERWATER)
					P_SetObjectMomZ(t, 10*FRACUNIT, false)
				else
					P_SetObjectMomZ(t, 16*FRACUNIT, false)
				end
				
				--If grabbing onto a vertically moving platform, add that platform momentum to make sure we don't undershoot it!
				local pmomz = 0
				if s.pmomz and (s.pmomz*P_MobjFlip(t) > 1)
					pmomz = s.pmomz
					if pmomz < 0
					pmomz = $/-2
					end
					P_SetObjectMomZ(t, pmomz+FRACUNIT, true)
				elseif t.pmomz  and (t.pmomz*P_MobjFlip(t) > 1)
					pmomz = t.pmomz
					if pmomz < 0
					pmomz = $/-2
					end			
					P_SetObjectMomZ(t, pmomz+FRACUNIT, true)				
				end
				--Face the ledge!
				if yu.ledgeturnangle
					p.drawangle = yu.ledgeturnangle
					t.angle = yu.ledgeturnangle
					yu.ledgeupangle = t.angle
				else
					t.angle = R_PointToAngle2(t.x, t.y, s.x, s.y)
					yu.ledgeupangle = t.angle
				end
				
				if not p.powers[pw_nocontrol]
					p.powers[pw_nocontrol] = 2
				end
				p.pflags = $|PF_JUMPED & ~PF_THOKKED & ~PF_SPINNING & ~PF_STARTJUMP & ~PF_STARTDASH
				yu.startdash = 0
				YuResetBounce(p, t, yu)
			end
		end
		
	 --Okay, so this is the ledgeprotect version! Ledgestop? Ledge Fall? Antislip? I dunno.	
	 
	elseif s.yuactive2
		local doit = false
		
			--Flipped version is finnicky with ObjectOnGround checks.
			
			if (t.eflags & MFE_VERTICALFLIP)
				if ((s.ceilingz-60*s.scale) > t.ceilingz)
					if (s.floorz < t.ceilingz-48*s.scale)
						if (t.ceilingz-t.height > s.floorz) 
							doit = true
						end
					end
				end
			else
				--Non-flipped version
				if (s.floorz < t.z-(60*t.scale)) 
				and (t.ceilingz > s.floorz+s.height) --These sometimes give inconsistent info, so make doubly sure!!
				and not (t.floorz >= s.ceilingz-(23*t.scale))
				and platformtest(t, s)	
				and not P_PlayerTouchingSectorSpecial(p, 1, 3) --Prevent some weird bug where Sonic stops on Lava all the time.
					doit = true
				end
			end
			if doit		
				if not yu.startdash	
				YuVoice(p, s, yu, sfx_yuvoc3)
				t.state = S_PLAY_EDGE
				
				--Super Sonic exclusive Chaos Control effect!
					if yu.supersonic
					YuVoice(p, s, yu, sfx_yuvoc9, true)
					
						for d = 1,20,3
							local ghs = P_SpawnGhostMobj(t)
							ghs.colorized = true
							ghs.color = SKINCOLOR_SUPERSILVER4+d
							if d == 1
							ghs.frame = $|FF_PAPERSPRITE|FF_FULLBRIGHT & ~TR_TRANS10 & ~TR_TRANS20 & ~TR_TRANS30 & ~TR_TRANS40 & ~TR_TRANS50 & ~TR_TRANS60 & ~TR_TRANS70 & ~TR_TRANS80 & ~TR_TRANS90
							ghs.angle = p.drawangle+ANGLE_90
							else
								ghs.blendmode = AST_ADD
								ghs.rollangle = (ANG10+(ANG2*4))*d
								ghs.angle = (ANG10+(ANG2*4))*d
								ghs.frame = $|FF_PAPERSPRITE|FF_FULLBRIGHT
								ghs.z = $+(18*t.scale*P_MobjFlip(t))
							end
							ghs.flags = $|MF_NOCLIPHEIGHT|MF_NOCLIP
							ghs.scale = (t.scale*10)/7
							ghs.scalespeed = $*20/11
							ghs.tics = 16 ghs.fuse = 16
							ghs.destscale = 1
							if d == 1
							ghs.spriteyscale = 95356
							else
							ghs.spriteyscale = (FRACUNIT*11)/(d*5)
							end
							ghs.spritexscale = (FRACUNIT/(d*2))*100
						end
						
						local ghs = P_SpawnMobj(t.x, t.y, t.z, MT_YUTHOK)
						ghs.destscale = $*999
						ghs.scalespeed = $*13
						ghs.color = t.color
						ghs.tics = 12
						ghs.fuse = 12
						ghs.blendmode = AST_REVERSESUBTRACT
						ghs.flags2 = MF2_SHADOW
						S_StartSound(t, sfx_kc4d)
						yu.lsaattack = 1 --Blow up nearby enemies!
						P_RadiusAttack(t, t, 320*FRACUNIT)
						yu.lsaattack = 0
							
						for d = 1,16
							local ghs = P_SpawnMobjFromMobj(t,0,0,10*t.scale, MT_MINECARTSPARK)
							ghs.scale = t.scale/4
							ghs.fuse = 17
							P_InstaThrust(ghs, (ANGLE_45/2)*d, 22*t.scale)
							ghs.momz = (14*t.scale)*P_MobjFlip(t)
						end						
					end
					if not p.powers[pw_nocontrol]
					p.powers[pw_nocontrol] = 3
					end
				end
				
				--Stop player and play animation.
				
				yu.edgenope = 0
				t.momx = t.player.cmomx t.momy = t.player.cmomy
				yu.ledgeprotectcd = 11
				yu.ledgereactivate = true
				yu.ledgeprotectanim = A
				yu.ledgeprotectangle = nil
				
				--Adjust player angle and position...
				
				if yu.ledgegrab.sonicang
					if not yu.startdash		
					t.angle = yu.ledgegrab.sonicang
					yu.ledgeprotectangle = yu.ledgegrab.sonicang
					end
					if not (t.player.cmomx or t.player.cmomy) --Can't trust trymove on moving platforms.
					and not ((s.flags2 & MF2_TWOD) or twodlevel)
						for d = 1,22 --Move Sonic as close to the ledge as possible.
							P_TryMove(s, s.x+FixedMul(FRACUNIT, cos(s.angle)), s.y+FixedMul(FRACUNIT, sin (s.angle)), false)
						end
					end
					yu.ledgegrab.sonicang = nil
				end
			end
	end
	
  elseif s.valid	
  	s.state = S_NULL --No Sonic, then no Ledge Grab object.
  end
end, MT_YULEDGEGRAB)
/*
--Ledge grab cannnot collide with anything ever. It has MF_NOCLIPTHING, but I'm making 100% sure.
addHook("MobjCollide", function(h, v)
return false
end, MT_YULEDGEGRAB)
addHook("MobjMoveCollide", function(h, v)
return false
end, MT_YULEDGEGRAB) */

--This one has tracking features!

addHook("MobjThinker", function(s)
	if s.yustartdash
		A_CapeChase(s, 1, 0)
		return
	elseif s.iamatree --Not actually a tree, any scenery item will do.
	
		s.tics = $+1
		s.iamatree = $-1
		
		if s.treedebris and s.treedebris.valid 
			s.treedebris.rollangle = $+(ANG20*s.treedebrisdir)
		end
		if s.target and s.target.valid
			if s.iamatree == 50--Regrow the tree.
				s.flags2 = $ & ~MF2_DONTDRAW
				s.destscale = s.target.scale
				s.colorized = s.target.colorized
				s.color = s.target.color
				s.mirrored = s.target.mirrored
				s.angle = s.target.angle
			elseif (s.iamatree <= 1)
				s.target.flags = $ & ~MF_NOCLIPTHING -- The tree has returned, all thanks to the power of friendship.
				s.target.flags2 = $ & ~MF2_DONTDRAW
				s.state = S_NULL
			end
		end		
		return
		
	elseif s.iamyudebris
	
		s.iamyudebris = $-1
		if s.yudebris and s.yudebris.valid 
			s.yudebris.rollangle = $+(ANG20*s.yudebrisdir)
		end
		if not s.iamyudebris
			s.state = S_NULL
		end
		return
		
	elseif s.yulsatarget and s.yulsatarget.valid and s.yulsatarget.player and s.yulsanum != nil
	
	local t = s.yulsatarget
	
		if s.superaura
			local offset = 0
			if (s.eflags & MFE_VERTICALFLIP)
			offset = (48*t.scale/3) --48*t.scale is the playerheight. Otherwise it'll screw up when you spin.
			end
			if t.player.panim == PA_RIDE and t.skin == "adventuresonic"
			offset = $+((25*t.scale)*P_MobjFlip(t))
			end
			s.tracer = s.yulsatarget
			s.target = s.yulsatarget
			A_CapeChase(s,offset,0)
		
		elseif s.invincibleaura
		local invert = 1
		local momx = (t.x-t.player.yusonictable.lastx) --because our actual momentum values can't be trusted. >:(
		local momy = (t.y-t.player.yusonictable.lasty)  
		local momz = (t.z-t.player.yusonictable.lastz) 
		
			s.momx = momx s.momy = momy s.momz = momz
			
			if s.invertspark
			invert = -1
			end
			
			P_TeleportMove(s,
			t.x+((s.invthrustx*7)/s.tics)+momx,
			t.y+((s.invthrusty*7)/s.tics)+momy,
			t.z+s.ivheight+((s.invthrustz*7*invert)/s.tics)+momz)
		
			--Lightline copy!
			if s.yuprevx != nil --Make sure there IS a comparison to be made.
			local density = 11
			local dmult = 1
				for d = 1,density
					local lightline = P_SpawnMobj(
					FixedMul((FRACUNIT-(6535*d/dmult)) , s.x+(s.momx*-1))+FixedMul((6535*d/dmult) , s.yuprevx), --x position
					FixedMul((FRACUNIT-(6535*d/dmult)) , s.y+(s.momy*-1))+FixedMul((6535*d/dmult) , s.yuprevy), --y position
					FixedMul((FRACUNIT-(6535*d/dmult)) , s.z+(s.momz*-1))+FixedMul((6535*d/dmult) , s.yuprevz), --z position
					MT_YUTHOK)
					lightline.blendmode = AST_ADD
					lightline.scale = s.scale
					lightline.scalespeed = $/12
					lightline.destscale = lightline.scalespeed+1
					lightline.color = s.color 
					lightline.tics = 2
					lightline.frame = $|TR_TRANS50|FF_FULLBRIGHT & ~TR_TRANS50
				end
			end
			
			s.yuprevx = s.x --Where were we last?
			s.yuprevy = s.y
			s.yuprevz = s.z

			if s.z < s.floorz and invert == -1
			s.state = S_NULL
			return
			end
		
		else		
		
			s.angle = t.player.drawangle
			P_TeleportMove(s, t.x, t.y, t.z-((((t.scale*7)*s.yulsanum)-(s.scale-t.scale))*P_MobjFlip(t)))
			s.sprite = t.sprite
			s.sprite2 = t.sprite2
			s.rollangle = t.rollangle
			s.frame = (t.frame & ~FF_TRANSMASK)|(TR_TRANS40+(s.yulsanum*FRACUNIT))|FF_FULLBRIGHT
			
		end
	end
end, MT_YUTHOKDISP)


//The Lightdash Orbs are truly special.

addHook("MobjThinker", function(s)
	if s.valid and s.target and s.target.valid and s.target.skin == "adventuresonic" and s.target.player and s.target.player.yusonictable
	and not s.yuldbye
	
	local t = s.target
	local p = s.target.player
	local yu = s.target.player.yusonictable
	local lsaangle = (yu.lsaangle*ANG1)
		A_FaceTarget(s) --Always face Sonic for calculation purposes.
		local finang = yu.lsaangle2+s.yuldangle //+s.yulddirect
		
		
		if not s.yuldorbspawned --Set this once.
		s.yuldorbspawned = true
		s.scalespeed = $/16
		s.blendmode = AST_ADD
			if yu.supersonic
			s.colorized = true
				if yu.hypersonic
				s.color = P_RandomRange(SKINCOLOR_SUPERSILVER1, SKINCOLOR_SUPERPURPLE5)
				else
					if yu.lsacharged
					s.color = P_RandomRange(SKINCOLOR_SUPERSILVER1, SKINCOLOR_SUPERGOLD5)
					else
					s.color = SKINCOLOR_SUPERSKY4
					end
				end
			end
			if (s.yuldmomz == 1)
			s.destscale = $*2
			s.frame = $|TR_TRANS30 & ~TR_TRANS50
			s.tics = 44 --How long does it last?
			else
			s.frame = $|TR_TRANS20 & ~TR_TRANS50
			s.tics = 25
			end
		end
		
local ZFlip = t.z
		if (t.eflags & MFE_VERTICALFLIP)
		ZFlip = YuFlipZ(t, s)
		end
		if not yu.startdash and not yu.lsacharged
			s.yulddisengage = true
			P_TeleportMove(s, t.x+FixedMul(s.yulddist, cos(finang)), t.y+FixedMul(s.yulddist, sin(finang)), ZFlip+((t.scale*22)*P_MobjFlip(s)), MT_YULDORB)
		elseif s.yuldmomz == 0 --First charging lines.
			P_TeleportMove(s, t.x+FixedMul(s.yulddist, cos(finang+ANGLE_180)), t.y+FixedMul(s.yulddist, sin(finang+ANGLE_180)), YuFlipZ(t, s)+t.scale, MT_YULDORB)
		elseif s.yuldmomz == 1 --Charged state
			P_TeleportMove(s, t.x+FixedMul(s.yulddist, cos(finang)), t.y+FixedMul(s.yulddist, sin(finang)), ZFlip+(((t.scale*80)-(s.tics*t.scale*2))*P_MobjFlip(s)), MT_YULDORB)
		elseif s.yuldmomz == -1 --Second charging lines.
			P_TeleportMove(s, t.x+FixedMul(s.yulddist, cos(finang)), t.y+FixedMul(s.yulddist, sin(finang)), ZFlip+((s.tics*t.scale*4)*P_MobjFlip(s)), MT_YULDORB)
		end	
		
		--How far away from Sonic? Close that distance!
		if (s.yuldmomz == 0)	
			if (s.yulddist > 8*t.scale)
			s.yulddist = $-6*t.scale
			end
			s.yuldangle = $-ANG2*7
			
			if not yu.startdash
			s.yuldbye = true
			end	
		elseif (s.yuldmomz == -1)
			if (s.yulddist > 8*t.scale)
			s.yulddist = $-6*t.scale
			end
			s.yuldangle = $-(ANG2*11)
			if not yu.startdash
			s.yuldbye = true
			end
		else
			s.yuldangle = $-(ANG30+(ANG2*5)) --What angle in relation to Sonic? Rotate around him!
			if (s.tics < 16) and (s.yuldmomz == 1) --Fading effect
				if not (s.frame & TR_TRANS90)
				and (s.tics % 2 == 0)
				s.frame = $+FRACUNIT
				end 
			end
		end
				
		--Lightline! Spawn in relation to your last known x, since momx and momy don't really apply with teleportation.
		if s.yuprevx != nil and s and s.valid --Make sure there IS a comparison to be made.
		local density = 11
		local dmult = 1
		local color = YuAuraColor(t, nil, yu)
			if yu.startdash
			density = 22
			dmult = 2
			end
				for d = 1,density
					local lightline = P_SpawnMobj(
					FixedMul((FRACUNIT-(6535*d/dmult)) , s.x)+FixedMul((6535*d/dmult) , s.yuprevx), --x position
					FixedMul((FRACUNIT-(6535*d/dmult)) , s.y)+FixedMul((6535*d/dmult) , s.yuprevy), --y position
					FixedMul((FRACUNIT-(6535*d/dmult)) , s.z)+FixedMul((6535*d/dmult) , s.yuprevz), --z position
					MT_YUTHOK)
					lightline.blendmode = AST_ADD
					lightline.scale = s.scale/3
					lightline.scalespeed = $/12
					lightline.destscale = lightline.scalespeed+1
					lightline.color = color
					/*	if d > 5 This was experimental coloring...
						lightline.color = SKINCOLOR_SUPERSKY5+7-d
						else
						lightline.color = SKINCOLOR_SUPERSILVER5+1-d
						end */ 
					lightline.frame = $|TR_TRANS80|FF_FULLBRIGHT & ~TR_TRANS50
				end
			end
		s.yuprevx = s.x --Where were we last?
		s.yuprevy = s.y
		s.yuprevz = s.z
		
		if s.yulddisengage --Are we told to fade away gracefully?
			if (leveltime % 8 == 0)
				if not (s.frame & TR_TRANS90)
				s.frame = $+FRACUNIT
				elseif s.valid
				s.state = S_NULL
				return
				end
			end
		end
		
	elseif s.valid --No target. Bai, bish.
		s.state = S_NULL
	end
end, MT_YULDORB)

--Our Lightdash ring detector and LSA Enemy Detector. Stolen from SMSreborn, but still the best way to detect enemies as far as I know.

local function yuecheck(s, m)
	if s.target and s.target.player and s.target.player.yusonictable --We DO have a target and it IS a player?
local t = s.target
local p = s.target.player
local yu = s.target.player.yusonictable
		if m and m.valid --Okay, next, we're actually colliding with something, right? 	
		
			if yu.lsaattack --Lightspeed attack! If we decided to attack enemies, ignore the rings.
				--Our list of stuff we can attack.
				if  (m.type == MT_YUDUMMYTRACKER) or (m.type == MT_EGGROBO1) or (m.type == MT_SUSPICIOUSFACESTABBERSTATUE) or (m.type == MT_SPINCUSHION)
				
				elseif not (((m.flags & (MF_ENEMY|MF_BOSS|MF_MONITOR)) or (m.type == MT_EGGGUARD)  or (m.type == MT_METALSONIC_BATTLE) or (m.type == MT_EGGSHIELD) or (((m.type == MT_CYBRAKDEMON) or (m.type == MT_MINUS) or (m.type == MT_CYBRAKDEMON_ELECTRIC_BARRIER) or (m.type == MT_TURRET)))) and m.health and (m.flags & MF_SHOOTABLE 
				or (((m.type == MT_CYBRAKDEMON) or (m.type == MT_MINUS) or (m.type == MT_TURRET)  or (m.type == MT_METALSONIC_BATTLE) or (m.type == MT_METALSONIC_BATTLE) or (m.type == MT_CYBRAKDEMON_ELECTRIC_BARRIER) or (m.type == MT_EGGGUARD) or (m.type == MT_EGGSHIELD)))) and not (m.type == MT_DETON) and not m.fusrodah and not (m.flags2 & MF2_FRET))
				
				return
				end
				
				if (s.z > m.z+m.height)
				or (m.z > s.z+s.height)
				return false
				end
				if ((m.ceilingz < t.floorz) or (t.ceilingz < m.floorz))
				return false 
				end
				
				if (m.health or (m.type == MT_EGGROBO1))
				and (P_CheckSight(t, m))
					if not yu.lsaclosest or ( yu.lsaclosest and ((FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z)) < yu.lsaclosest) )
						if (m.type == MT_YUDUMMYTRACKER and m.target)
							t = m.target
							yu.lsaclosest = (FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z))
							t.target = m
							yu.lsatarget = m
						
						else
					
							yu.lsaclosest = (FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z))
							t.target = m
							yu.lsatarget = m
						end
					end
				end
					
				return false
				
		 	elseif not yu.lightdashing 
			
				if (m.type == MT_YUDUMMYTRACKER) or (m.type == MT_EGGROBO1) or (m.type == MT_SUSPICIOUSFACESTABBERSTATUE) or (m.type == MT_SPINCUSHION)
				
				elseif not (((m.flags & (MF_ENEMY|MF_BOSS|MF_MONITOR)) or (m.type == MT_EGGGUARD) or (m.type == MT_METALSONIC_BATTLE) or (m.type == MT_CYBRAKDEMON_ELECTRIC_BARRIER) or (m.type == MT_EGGSHIELD) or (((m.type == MT_CYBRAKDEMON) or (m.type == MT_MINUS) or (m.type == MT_TURRET)))) and m.health and (m.flags & MF_SHOOTABLE 
				or (((m.type == MT_CYBRAKDEMON) or (m.type == MT_MINUS) or (m.type == MT_METALSONIC_BATTLE) or (m.type == MT_CYBRAKDEMON_ELECTRIC_BARRIER) or (m.type == MT_METALSONIC_BATTLE) or (m.type == MT_EGGGUARD) or (m.type == MT_EGGSHIELD) or (m.type == MT_TURRET)))) and not (m.type == MT_DETON) and not m.fusrodah and not (m.flags2 & MF2_FRET))
				
				return
				end
				if (m.health or (m.type == MT_EGGROBO1)) and (P_CheckSight(t, m))
				
					if (s.z > m.z+m.height)
					or (m.z > s.z+s.height)
					return false
					end
					if ((m.ceilingz < t.floorz) or (t.ceilingz < m.floorz))
					return false 
					end			
					
				--By the way: Whenever I put an "if" check in front of a value with no further specifications, like "if yu.lsaclosest"...
				--It simply checks if it's not false, nil, or 0. If the value ends up being any 3 of those, it won't continue.
				--Use "if yu.lsaclosest != nil" if you merely want to make sure the value has been set up beforehand for avoiding Lua errors.
					
					if not yu.lsaclosest or ( yu.lsaclosest and ( (FixedHypot(m.x-s.x, m.y-s.y)) <= yu.lsaclosest) )
					
						if (m.type == MT_YUDUMMYTRACKER and m.target)
						yu.lsaclosest = (FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z))
						t.target = m.target
						yu.lsatarget = m.target
						else
						yu.lsaclosest = (FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z))
						t.target = m
						yu.lsatarget = m
						end
						s.timetodie = true
						return false
					end
				end
			
		 	end
		end
	end
	return false
end

addHook("MobjCollide", yuecheck, MT_YUECHECK)
addHook("MobjMoveCollide", yuecheck, MT_YUECHECK)

--But Sonic-sama, I have no hitbooox--God, do I need to do this for every character?

local function DummyHBadd(s)
	if not s.dummyhb5 and s.health
	s.dummyhb5 = P_SpawnMobj(s.x, s.y, s.z, MT_YUDUMMYTRACKER)
	s.dummyhb5.target = s
	s.dummyhb5.tics = -1
	s.dummyhb5.dummyhbx = true
	end
end
addHook("MobjThinker", DummyHBadd, MT_TURRET)
addHook("MobjThinker", DummyHBadd, MT_METALSONIC_BATTLE)
addHook("MobjThinker", DummyHBadd, MT_MINUS)

//DummyTracker. Makes trackable hitbox for enemies that have none.

addHook("MobjThinker", function(s)
	if s.target and s.target.valid
local t = s.target
			if t.health
			s.height = t.height
			s.radius = t.radius
			P_TeleportMove(s, t.x, t.y, t.z)
			elseif s.valid
			s.state = S_NULL
			end
			return
	elseif s.valid
		P_KillMobj(s, s)
		return
	end
end, MT_YUDUMMYTRACKER)

local function TrackerCollide(s, v)
	if v
		if (v.type == MT_YUECHECK)
		return
		else
		return false
		end
	end
return false
end
addHook("MobjCollide", TrackerCollide, MT_YUDUMMYTRACKER)
addHook("MobjMoveCollide", TrackerCollide, MT_YUDUMMYTRACKER)

local function TimeToDie(s)
	if s and s.valid and s.timetodie
	s.state = S_NULL
	return
	end
end
addHook("MobjThinker", TimeToDie, MT_YUECHECK)

local function SpikeBallDestruct(v, h)
	if h and h.player and h.skin == "adventuresonic" and h.player.powers[pw_super]
		if not (h.z > v.z+v.height)
		and not (v.z > h.z+h.height)
		
			S_StartSound(v, sfx_s3kb4)
			S_StartSound(v, sfx_kc40)
			v.flags = $|MF_NOCLIPTHING & ~MF_SOLID & ~MF_PAIN
			v.flags2 = $|MF2_DONTDRAW
			
			v.benope = P_SpawnMobj(v.x, v.y, v.z, MT_BOSSEXPLODE)
			
			if v.benope and v.benope.valid
			v.benope.scale = v.scale*2
			v.benope.destscale = $*99
			v.benope.flags2 = $|MF2_SHADOW
			end

			S_StartSound(v, sfx_bedeen)
			
			local s = h
				for d = 0,14
					local debris = P_SpawnMobj(s.x+(P_RandomRange(-50,50)*s.scale), s.y+(P_RandomRange(-50,50)*s.scale), s.z+(P_RandomRange(5,80)*s.scale), MT_BOSSJUNK)
					if debris and debris.valid
					debris.sprite = SPR_USPK
					debris.frame = A
					debris.rollangle = P_RandomRange(0,359)*ANG1
					debris.flags = $|MF_BOUNCE|MF_GRENADEBOUNCE|MF_NOCLIPTHING
					debris.scale = $*2/P_RandomRange(1,4)
					debris.color = P_RandomRange(SKINCOLOR_CARBON, SKINCOLOR_SILVER)
					debris.colorized = true
					P_InstaThrust(debris, s.angle+(P_RandomRange(-40,40)*ANG1), P_RandomRange(10,30)*s.scale)
					P_SetObjectMomZ(debris, P_RandomRange(-1,10)*s.scale, false)
					end
				end
				
				local boom = P_SpawnMobjFromMobj(s, 0, 0,0, MT_YUINSTASHIELD)
				boom.scalespeed = $*11
				boom.colorized = true
				boom.color = SKINCOLOR_RED
				boom.destscale = s.scale*99
				boom.blendmode = AST_ADD
			
		end
	end
end
addHook("MobjCollide", SpikeBallDestruct, MT_BIGMACE)
addHook("MobjMoveCollide", SpikeBallDestruct, MT_BIGMACE)
addHook("MobjCollide", SpikeBallDestruct, MT_SMALLMACE)
addHook("MobjMoveCollide", SpikeBallDestruct, MT_SMALLMACE)
addHook("MobjCollide", SpikeBallDestruct, MT_EGGMOBILE4_MACE)
addHook("MobjMoveCollide", SpikeBallDestruct, MT_EGGMOBILE4_MACE)
addHook("MobjCollide", SpikeBallDestruct, MT_SPIKEBALL)
addHook("MobjMoveCollide", SpikeBallDestruct, MT_SPIKEBALL)
local function MineDestruct(v, h) --Don't get stopped by mines.
	if h and h.player and h.skin == "adventuresonic" and h.player.powers[pw_super]
		if not (h.z > v.z+v.height)
		and not (v.z > h.z+h.height)
			if v.health and v.valid
			P_SpawnGhostMobj(v)
			S_StartSound(v, sfx_kc41)
			P_InstaThrust(v, R_PointToAngle2(h.x, h.y, v.x, v.y), h.player.speed+(35*h.scale) )
			P_KillMobj(v, h, h)
			end			
		end
		return false
	end
end
addHook("MobjCollide", MineDestruct, MT_BIGMINE)
addHook("MobjMoveCollide", MineDestruct, MT_BIGMINE)


--SA2's superbounce glitch replicated in SRB2! It's seriously abuseable in SRB2's leveldesign, so we're keeping it exclusive to Super Sonic/Sneakers.

addHook("MobjMoveBlocked", function(s)
	if s.skin == "adventuresonic" and s.player and s.player.yusonictable
	and not s.player.powers[pw_carry]
local p = s.player
local yu = s.player.yusonictable
		if yu.bouncestate == 2 and yu.superbouncetime
		and (yu.direct > 2)
		and (s.momz*P_MobjFlip(s) > FRACUNIT) and ((p.speed > 15*s.scale) or (yu.savedspeed > 15*s.scale))
			local prespeed = p.speed
			if yu.savedspeed > p.speed
				if yu.savedspeed > 92*s.scale
				yu.savedspeed = 92*s.scale
				end
				if (yu.savedspeed > 50*FRACUNIT)
					yu.savedspeed = ($-36*FRACUNIT)+(yu.savedspeedtimer*12*FRACUNIT)
				end
			prespeed = yu.savedspeed
			end
			local bouncemult = ((yu.superbouncetime*4)+(prespeed/FRACUNIT))-46+yu.superbounceprep
				if bouncemult < 30
					bouncemult = 30
				elseif bouncemult > 150
					bouncemult = 150
				end
			yu.superbouncetime = 0
			s.momz = ($*bouncemult)/30
			if bouncemult > 30
				if bouncemult < 90
				bouncemult = $*5/4 --Make this a little easier at the lower levels.
				end
			yu.superbounce = bouncemult/6
				S_StartSound(s, sfx_yusfx3)
				if bouncemult > 44
				S_StartSound(s, sfx_yusfx3)
					if bouncemult > 55
					S_StartSound(s, sfx_yusfx3)
					S_StartSound(s, sfx_cdfm42)
					end
				end
			yu.savedspeed = 0
			end
		elseif ( (((p.pflags & PF_SPINNING) or yu.uncurlanim) and P_IsObjectOnGround(s) and not yu.nosarun4) or yu.superaura and yu.supersonic or (yu.farthok > 3))
		and (yu.direct > 2)
		and (p.speed > 12*s.scale) and not p.powers[pw_carry]
		and not (s.flags2 & MF2_TWOD or twodlevel)			
			local AngleTurn={0, ANG20, -ANG20, ANG30, ANG30+ANG10, -ANG30-ANG10, ANG30+ANG20, -ANG30-ANG20}
			for d = 1,8
				if P_TryMove(s, s.x+FixedMul(36*s.scale, cos(yu.turnang+AngleTurn[(d)]) ), s.y+FixedMul(36*s.scale, sin(yu.turnang+AngleTurn[(d)]) ))
					if not (yu.superaura and yu.supersonic)
					s.momx = $*79/80 s.momy = $*79/80
					end
					if (p.pflags & PF_SPINNING)
					yu.noubercontrol = 3
					end
					local momang = R_PointToDist2(s.x, s.y, s.x+s.momx, s.y+s.momy)
					P_InstaThrust(s, R_PointToAngle2(s.x-s.momx,s.y-s.momy, s.x+P_ReturnThrustX(s,yu.turnang, momang), s.y+P_ReturnThrustY(s, yu.turnang, momang)), momang)
					if yu.superaura and yu.supersonic
						if AngleTurn[(d)] < 0
						s.angle = $-(ANG1*6)
						elseif AngleTurn[(d)] > 0
						s.angle = $+(ANG1*6)
						end
					end
				return true
				end
			end
		elseif yu.lsadash and not yu.bonked
			yu.bonkeddash = $+3
			yu.bonked = 1
		end
	end
end, MT_PLAYER)

--Screw points. Tokens will give you 50 rings, regardless of SRB2's vanilla behaviour.

local function TokenCol(chaosany, s)
	if not All7Emeralds(emeralds)
	return
	end
	if chaosany and chaosany.valid and s and s.skin == "adventuresonic" and s.player
		if s.health
		and not (chaosany.z > s.z+(s.height*3/2))
		and not (s.z > chaosany.z+chaosany.height*2)
		
			S_StartSound(s, sfx_s3k34)
			P_GivePlayerRings(s.player, 50) 
			
	   		if chaosany
	   	   		if chaosany.tracer
	   	  	 	P_KillMobj(chaosany.tracer, s, s)
	   	   		end
	   		P_KillMobj(chaosany, s, s)
	   		end
			
	  		return false
		end
	end
end
addHook("MobjCollide", TokenCol, MT_TOKEN)
addHook("MobjMoveCollide", TokenCol, MT_TOKEN)

--We're definitely Sonic, so spawn hearts. And maybe have Sonic act a bit sour over it.

addHook("MobjThinker", function(s)
	if (s.state == S_ROSY_HUG) and s.target and s.target.skin == "adventuresonic" and (leveltime % 3 == 0)
		local ghs = P_SpawnMobj(s.x+(P_RandomRange(-31,31)*s.scale), s.y+(P_RandomRange(-31,31)*s.scale), s.z+(P_RandomRange(34,99)*s.scale), MT_YUTHOK)
		ghs.eflags = s.eflags
		ghs.sprite = SPR_LHRT
		ghs.frame = A|FF_FULLBRIGHT
		ghs.scale = s.scale/P_RandomRange(4,6)
		ghs.scalespeed = $/P_RandomRange(2,4)
		ghs.destscale = s.scale*4
		ghs.fuse = 18 ghs.tics = 18
		P_SetObjectMomZ(ghs, P_RandomRange(3,12)*s.scale)
			if P_RandomRange(0,1)
			ghs.color = SKINCOLOR_RED
			ghs.colorized = true
			end
			if (leveltime % 18 == 0) --"Are you serious? I don't got time to play, Amy!"
				local ghs2 = P_SpawnMobj(s.target.x+(P_RandomRange(-22,22)*s.target.scale), s.target.y+(P_RandomRange(-22,22)*s.target.scale), s.target.z+(P_RandomRange(55,80)*s.target.scale), MT_YUTHOK)
				ghs2.eflags = s.target.eflags
				ghs2.sprite = SPR_YUSO
				ghs2.frame = X|(P_RandomRange(1,5)*FRACUNIT)
				ghs2.scale = s.target.scale/P_RandomRange(3,4)
				ghs2.scalespeed = $/P_RandomRange(4,7)
				ghs2.destscale = s.scale*4
				ghs2.fuse = 41 ghs2.tics = 41
				P_SetObjectMomZ(ghs2, 4*s.target.scale)
				if P_RandomRange(0,1)
					ghs2.color = SKINCOLOR_GREY
				else
					ghs2.color = P_RandomRange(SKINCOLOR_CERULEAN, SKINCOLOR_DUSK)
				end
			end
			if not (s.target.player and s.target.player.powers[pw_super])
			and (s.target.player.panim == PA_IDLE)
				if s.target.state != S_PLAY_STND		
				s.target.state = S_PLAY_STND
				end
				s.target.tics = 50
				s.target.anim_duration = 50
				s.target.sprite2 = SPR2_HUG_
			end
	end
end, MT_ROSY)

//Ignore SA-Sonic's LSA if you're not in a vulnerable state!

addHook("ShouldDamage", function(s, i, so)
	if (so and so.skin == "adventuresonic")
	and (s.valid and s.health > 3)
		if not (s.state >= S_EGGMOBILE4_RAISE1)
			if so and so.player and so.player.yusonictable and so.player.yusonictable.lsahoming
			S_StartSound(so, sfx_pstop)
			S_StartSound(so, sfx_spsmsh)
			S_StartSound(s, sfx_bewar4)
				P_YuQuake(so.player, 50*FRACUNIT, 3)
				if so.player.powers[pw_flashing] < 70
				so.player.powers[pw_flashing] = 70
				end
			end
		return false
		end
	end
end, MT_EGGMOBILE4)

--Adventurey 1-up box opening!

addHook("MobjThinker", function(s)
	if s.target and s.target.valid
		if (s.target.skin == "adventuresonic")
		local t = s.target
		s.flags2 = $|MF2_DONTDRAW
		s.growlifeicon = s.growlifeicon and $+7000 or 32355
		s.spinlifeangle = s.spinlifeangle and $+ANG30 or ANG30
			if not (s.reallife and s.reallife.valid)
			local t = s.target
			s.reallife = P_SpawnMobj(s.x, s.y, s.z, MT_YUTHOK)
			s.reallife.tics = 40
			s.reallife.skin = t.skin
			s.reallife.color = t.color
			s.reallife.flags = $|MF_NOCLIPHEIGHT|MF_NOCLIP
			s.reallife.sprite = SPR_YUSO
			s.reallife.frame = 30|FF_FULLBRIGHT|FF_PAPERSPRITE
			s.reallife.eflags = s.eflags
			s.reallife.spriteyscale = s.growlifeicon
			s.reallife.spritexscale = s.growlifeicon
			s.reallife.angle = s.spinlifeangle
			s.reallife.shadowscale = FRACUNIT/2
			else
				local rl = s.reallife
				s.scale = 0
				rl.spriteyscale = $+5000
				rl.spritexscale = $+5000
				rl.angle = $+(ANG30)
				P_SetObjectMomZ(rl, 150000)
				if (rl.tics < 18)
				s.reallife.frame = 30|FF_FULLBRIGHT|FF_PAPERSPRITE|(((18-rl.tics)/2)*FRACUNIT)
				end
			
				for d = 1,3
				local ghs = P_SpawnMobj(rl.x+(P_RandomRange(60,-60)*FRACUNIT),rl.y+(P_RandomRange(60,-60)*FRACUNIT),rl.z+(P_RandomRange(60,-40)*FRACUNIT),MT_PARTICLE)
				ghs.fuse = 12
				ghs.scale = s.scale/P_RandomRange(1,4)
				ghs.frame = $|FF_FULLBRIGHT
				ghs.color = P_RandomRange(SKINCOLOR_SUPERORANGE1, SKINCOLOR_SUPERRUST2)
				ghs.colorized = true
				ghs.blendmode = AST_ADD
				ghs.destscale = ghs.scalespeed+1
				end
			end
		end
	end	
end, MT_1UP_ICON)

--Cancel Super afterimages.

addHook("MobjThinker", function(s)
	if (s.fuse == 2) and (s.sprite2 & FF_SPR2SUPER) and s.skin == "adventuresonic"
		s.state = S_NULL
		return
	end
end, MT_GHOST)

//Make sure Sonic can only boost himself in NiGHTs.

local function SonicFboost(s, v)
	if s and (s.nsonicb and v and v != s.nsonicb)
	return false
	end
end

//I can't just watch this happen. Seeing Super Sonic die to a fucking minecart crash is so WRONG. 

addHook("MobjDeath", function(s, i, so)
		if (s.type == MT_MINECART)
		and s.target and s.target.skin == "adventuresonic"  and s.target.player and s.target.player.powers[pw_super] and s.target.health 
		and (gametype == GT_COOP) --'cause SA-Sonic cannot break minecart doors outside of COOP.
			s.target.player.powers[pw_carry] = 0
			s.target.momx = s.momx
			s.target.momy = s.momy
			P_SetObjectMomZ(s.target, 6*FRACUNIT, true)
			s.target = nil
		end
end, MT_MINECART)

--The fabled wireframe emerald finally serves a purpose.

addHook("MobjThinker", function(s)
	if s.target and s.target.valid and s.target.player // and s.target.player.exiting and s.target.player.yusonictable
		if (s.sprite == SPR_CEMG) and (s.frame == H or s.frame >= H|FF_FULLBRIGHT)
			for z in players.iterate
				if z.yusonictable
				z.yusonictable.spawnmemories = true
				end
			end
		end
	end
end, MT_GOTEMERALD)

//Touch these and...?!

local function YumeHitBox(s, so)
	if so and so.player and so.player.yusonictable and not so.player.yusonictable.hyperpower and so.skin == "adventuresonic" and so.health
		S_StartSound(so, sfx_s3k7d)
		S_StartSound(so, sfx_s3kcel)
		so.player.yusonictable.hypermemories = 220
		so.player.yusonictable.superflash = 9*FRACUNIT
		so.player.yusonictable.hyperpower = true
		return false
	end
end
addHook("MobjCollide", YumeHitBox,  MT_EMERALD8)
addHook("MobjCollide", YumeHitBox,  MT_MASTEREMERALD)

--Fans and Steam jets make you spin!
local function SpinHitBox(s, so)
	if s.valid and so and so.player and so.player.yusonictable and so.skin == "adventuresonic" and so.health
	local yu = so.player.yusonictable
		if ((so.player.pflags & PF_JUMPED) or (so.player.pflags & PF_THOKKED))
		or yu.supersonic
		return
		end	
		
		if (s.type == MT_FAN)
			if (so.state == S_PLAY_FALL)
			and (so.momz*P_MobjFlip(so) > so.scale)
			or yu.fanspin and so.state == S_PLAY_YUHELICOPTER
			so.state = S_PLAY_YUHELICOPTER
			yu.fanspin = 8
			end
		elseif (s.type == MT_STEAM) and (s.state <= S_STEAM2)
			if (so.state == S_PLAY_FALL)
			and (so.momz*P_MobjFlip(so) > so.scale)
				if (s.z <= so.z+66*FRACUNIT)		
					if (so.state != S_PLAY_YUHELICOPTER)
						so.state = S_PLAY_YUHELICOPTER
					end
				end
			end
		end
	end
end
addHook("MobjMoveCollide", SpinHitBox,  MT_STEAM)
addHook("MobjCollide", SpinHitBox,  MT_STEAM)
addHook("MobjMoveCollide", SpinHitBox,  MT_FAN)
addHook("MobjCollide", SpinHitBox,  MT_FAN)

local function BalloonCollide(s, so)
	if s and s.health and (s.state != s.info.deathstate)
		if so and so.player
			if so.skin == "adventuresonic" and not so.player.powers[pw_super] and so.player.yusonictable
			and (so.z <= s.z + s.height)
			and (s.z <= so.z + so.height)
			and so.health
			
		local yu = so.player.yusonictable
		local p = so.player
		
			so.rollangle = 0
			so.state = S_PLAY_ROLL
			yu.noairdrag2 = 6
			p.pflags = $|PF_JUMPED & ~PF_THOKKED & ~PF_NOJUMPDAMAGE
			yu.homingtrick = 40
			yu.chosentrick = 200
			yu.lasttrick = 2
			yu.sa2jumpdone = true	
			yu.airborne = 30
			end
		end
	end
end
addHook("MobjCollide", BalloonCollide, MT_BALLOON)
addHook("MobjMoveCollide", BalloonCollide, MT_BALLOON)

--End of level goal time! Do a special animation!

addHook("MobjThinker", function(s)
	if not s.valid
	or (gametype != GT_COOP) or s.forgetabouttheangle
	return
	end
	if s.yuendcam and s.yuendcam.valid	--Compatibility
	s.yuendcam.tics = 99999
	end
	if s.prespinangle == nil --Record initial angle for later!
		s.prespinangle = s.angle
	end
	if (s.state == S_SIGN) --Performance.
	return
	end
	for z in players.iterate
	 if z.realmo and z.realmo.skin == "adventuresonic" and z.exiting
local t = z.realmo
local yu = z.yusonictable
local p = z

		if not yu.endlvl --Start sequence!
			yu.bosskiller = 0
			yu.endlvl = 1
			yu.superflash = 9*FRACUNIT
			S_FadeOutStopMusic(2000, z)
			
			if not (s.yuendcam and s.yuendcam.valid) --If we already have one, continue!
			s.yuendcam = P_SpawnMobj(s.x, s.y, s.z+(s.height*P_MobjFlip(s)), MT_GARGOYLE) --Spawn our camera.			
			local ghs = s.yuendcam
				ghs.flags2 = $|MF2_DONTDRAW
				ghs.tics = 99999
				ghs.flags = $|MF_NOGRAVITY|MF_SOLID|MF_NOCLIPTHING & ~MF_PUSHABLE & ~MF_NOCLIP & ~MF_NOCLIPHEIGHT
				if (s.eflags & MFE_VERTICALFLIP)
					ghs.eflags = $|MFE_VERTICALFLIP
				end
				if (P_TryMove(ghs, s.x+FixedMul(-250*FRACUNIT,cos(s.prespinangle)), s.y+FixedMul(-250*FRACUNIT,sin(s.prespinangle)), true) )
					ghs.angle = R_PointToAngle2(ghs.x, ghs.y, s.x, s.y) --Ideal location!
					s.tpsetup = 1
				end
				if not s.tpsetup --Alternate cam locations!
					for d = -4,4
						if not s.tpsetup and (P_TryMove(ghs, s.x+FixedMul(-250*FRACUNIT,cos(s.prespinangle+(ANG10*d))), s.y+FixedMul(-250*FRACUNIT,sin(s.prespinangle+(ANG10*d))), true) )
						ghs.angle = R_PointToAngle2(ghs.x, ghs.y, s.x, s.y)
						s.tpsetup = 1
						end
					end
					if not s.tpsetup --Alternate closer cam locations!
						for d = -8,8
							if not s.tpsetup and (P_TryMove(ghs, s.x+FixedMul(-215*FRACUNIT,cos(s.prespinangle+((ANG10/2)*d))), s.y+FixedMul(-215*FRACUNIT,sin(s.prespinangle+((ANG10/2)*d))), true) )
							ghs.angle = R_PointToAngle2(ghs.x, ghs.y, s.x, s.y)
							s.tpsetup = 1
							end
						end
						if not s.tpsetup --Alternate closer-er cam locations!
							for d = -4,4
								if not s.tpsetup and (P_TryMove(ghs, s.x+FixedMul(-180*FRACUNIT,cos(s.prespinangle+(ANG10*d))), s.y+FixedMul(-180*FRACUNIT,sin(s.prespinangle+(ANG10*d))), true) )
								ghs.angle = R_PointToAngle2(ghs.x, ghs.y, s.x, s.y)
								s.tpsetup = 1
								end
							end
							if not s.tpsetup --Aaaagh, damnit, ONE last try!
								for d = -4,4
									if not s.tpsetup and (P_TryMove(ghs, s.x+FixedMul(-155*FRACUNIT,cos(s.prespinangle+(ANG10*d))), s.y+FixedMul(-155*FRACUNIT,sin(s.prespinangle+(ANG10*d))), true) )
									ghs.angle = R_PointToAngle2(ghs.x, ghs.y, s.x, s.y)
									s.tpsetup = 1
									end
								end
							end							
						end	
					end
				end	
				--Really couldn't move the camera anywhere? Forget about this whole thing, then.
				if not s.tpsetup 
				yu.endlvl = 0
				yu.superflash = 0
				s.forgetabouttheangle = true
					if ghs.valid
					P_RemoveMobj(ghs)
					end
				end			
			end
		end
		
		if yu.endlvl --Our players have the endlvl status. Do the thing.
			if (yu.endlvl > 38)		
				if (yu.endlvl == 39)
					p.powers[pw_carry] = 0
					ResetLSA(p, t, yu)
					yu.tauntcd = 120
					YuStopLSAdash(p, t, yu)	
					p.pflags = $ & ~PF_STARTDASH & ~PF_SPINNING & ~PF_JUMPED
					P_TeleportMove(t, s.x+FixedMul(-5*FRACUNIT, cos(s.prespinangle)), s.y+FixedMul(-5*FRACUNIT,sin(s.prespinangle)), s.z)
					if ((s.eflags & MFE_VERTICALFLIP) or (s.flags2 & MF2_OBJECTFLIP))
						t.eflags = $|MFE_VERTICALFLIP
						t.flags2 = $|MF2_OBJECTFLIP
						t.z = t.ceilingz
					else
						t.eflags = $ & ~MFE_VERTICALFLIP
						t.flags2 = $ & ~MF2_OBJECTFLIP
						t.z = t.floorz
					end
					yu.spin = 10 yu.jump = 10 yu.custom3 = 10 yu.custom2 = 10
					t.momz = 0 t.momx = 0 t.momy = 0
					
					--Try to move to a prettier location.
					if (s.tpsetup and s.tpsetup == 1) --This part is new. If it turns out bad in netgames, removing it should solve things.
						for d = 1,-1,-2
							if (s.tpsetup != 2)
							and P_TryMove(t, s.x+FixedMul(-90*FRACUNIT, cos(s.prespinangle+(ANG1*55*d))), s.y+FixedMul(-90*FRACUNIT,sin(s.prespinangle+(ANG1*55*d))) , true)
							s.tpsetup = 2
							end
						end
					end
					if not (s.tpsetup and s.tpsetup == 2)
						for d = 1,-1,-2
							if (s.tpsetup != 2)
							and P_TryMove(t, s.x+FixedMul(-35*FRACUNIT, cos(s.prespinangle+(ANG1*55*d))), s.y+FixedMul(-35*FRACUNIT,sin(s.prespinangle+(ANG1*55*d))) , true)
							s.tpsetup = 2
							end
						end
					end
					
					p.pflags = $ & ~PF_JUMPED & ~PF_SPINNING & ~PF_THOKKED & ~PF_STARTDASH & ~PF_STARTJUMP
					t.eflags = $ & ~MFE_JUSTHITFLOOR
					if (t.eflags & MFE_UNDERWATER)
						t.state = S_PLAY_FLY --Teleport in plain view then taunt!
						yu.tauntstate = 1
						t.frame = 9
					else
						if yu.yuvoice
							if (leveltime % 2 == 0)
							S_StartSound(nil, sfx_yuvocr, p)
							else
							S_StartSound(nil, sfx_yuvocn, p)
							end
						end
						t.state = S_PLAY_FLY --Teleport in plain view then taunt!
						yu.tauntstate = 1			
					end
					
					if multiplayer and (p != players[0]) --Don't all stand in 1 place, boys!
						local dist = 90*FRACUNIT
						for d = 1,31
							if d > 8
								dist = 160*FRACUNIT
							elseif d > 16
								dist = 220*FRACUNIT
							elseif d > 24
								dist = 280*FRACUNIT
							end
							if players[d] and players[d].mo and players[d].mo.valid and (players[d].mo == t) --It's what they call "you"! :D
								P_TryMove(t, t.x+FixedMul(dist,cos(s.prespinangle+(ANGLE_45*d))), 
								t.y+FixedMul(dist,sin(s.prespinangle+(ANGLE_45*d))),
								true) 
							end
						end
					end
				end			
				if s.yuendcam and s.yuendcam.valid
				local ghs = s.yuendcam
				//	if ghs.aimatsonic 
				//	ghs.angle = R_PointToAngle2(ghs.x, ghs.y, t.x, t.y)
				//	end
					p.awayviewtics = 3
					p.awayviewmobj = ghs --Use this invisible untouchable Gargoyle as camera. It'll have to do!
					if (t.state != S_PLAY_FLY) and (t.state != S_PLAY_SWIM)
					t.state = S_PLAY_FLY 
					yu.tauntstate = 1
					end
				end
			end
		end
	 end
	end
end, MT_SIGN)

--Don't keep getting Mystic Super if you're already Mystic Super.

addHook("TouchSpecial", function(pwsh, s)
	if s and s.player and s.player.yusonictable and s.player.yusonictable.mysticsuper
		return true
	end
end, MT_POWERSHARE)
local function PowerShare(pw, s)
	if s and s.player and s.player.yusonictable and s.player.yusonictable.mysticsuper
		return false
	end
end
addHook("MobjMoveCollide", PowerShare, MT_POWERSHARE)
addHook("MobjCollide", PowerShare, MT_POWERSHARE)

--Bots are gonna be alot more important in 2.2.10, so let's have our chad Adventure Sonic play along!

addHook("BotTiccmd", function(p, cmd)
if not (p and p.valid and gamestate == GS_LEVEL)
return
end
if not (p.realmo and p.realmo.skin == "adventuresonic" and p.bot == 1)
return
end

local tp = nil --Set up our leader. They become tp.mo
	if p.botleader and p.botleader.realmo and p.botleader.realmo.valid
		tp = p.botleader
	elseif (players[0] and players[0].valid and players[0].realmo and players[0].realmo.valid)
		tp = players[0]
	end
	
	if tp == nil
	or not p.yusonictable
	or not tp.yusonictable
		return
	end
	
local s = p.mo
local yu = p.yusonictable
local yu2 = tp.yusonictable

		--Encourage flying players!
		if (p.powers[pw_carry] == CR_PLAYER) and (tp.mo.state == S_PLAY_FLY_TIRED)
			if not p.yubotwaitforit
				p.yubotwaitforit = 26
			else
			p.yubotwaitforit = $-1
				if p.yubotwaitforit == 1
				cmd.buttons = $|BT_SPIN
				p.yubotwaitforit = 30
				end
			end
			return true
		end
	
		if p.powers[pw_shield] and not p.powers[pw_super]
			if (tp.powers[pw_shield] == SH_PINK or tp.powers[pw_shield] == SH_PITY or tp.powers[pw_shield] == 0) 
			and not (tp.powers[pw_shield] == SH_PINK and p.powers[pw_shield] == SH_PINK)
			and not (tp.powers[pw_shield] == SH_YUSUPERSHIELD)
			and not tp.hypermysticsonic
			tp.powers[pw_shield] = p.powers[pw_shield]
			P_SpawnShieldOrb(tp)
			S_StartSound(tp.mo, sfx_s3k65)
			S_StartSound(tp.mo, sfx_jshard)
			CONS_Printf(tp, "\x83"+"Sonic gave you his shield!")
			p.powers[pw_shield] = 0
			end
		end
		if not tp.powers[pw_invulnerability] and p.powers[pw_invulnerability] and (p.powers[pw_invulnerability] > 95)
			tp.powers[pw_invulnerability] = p.powers[pw_invulnerability]
			S_StartSound(tp.mo, sfx_jshard)
			S_StartSound(tp.mo, sfx_s3k65)
			CONS_Printf(tp, "\x83"+"Sonic is sharing his invulnerability powers!")
		end
		if not tp.powers[pw_sneakers] and p.powers[pw_sneakers] and (p.powers[pw_sneakers] > 95)
			tp.powers[pw_sneakers] = p.powers[pw_sneakers]
			S_StartSound(tp.mo, sfx_jshard)
			S_StartSound(tp.mo, sfx_s3k65)
			CONS_Printf(tp, "\x83"+"Sonic is sharing his sneaker powerup!")
		end
		
		--Adjustments that needed to be made.
		
		if yu.lsahoming
			cmd.sidemove = 0
			cmd.forwardmove = 0
			return true		
		
		--Super Stuff!
		elseif (tp.powers[pw_super] and (tp.powers[pw_super] < 4 or (tp.cmd.buttons & BT_CUSTOM3)))
			p.rings = 150
			cmd.buttons = $|BT_CUSTOM3
			return true
		elseif p.powers[pw_super]
			if (tp.cmd.buttons & BT_CUSTOM3) and not yu.superflight
				cmd.buttons = $|BT_CUSTOM3
			elseif yu.superflight
				if (tp.powers[pw_carry] == CR_PLAYER)
					if (yu2.direct > 2)
						if tp.cmd.forwardmove > 0
						cmd.forwardmove = 50
						end
						cmd.sidemove = 0
						s.angle = tp.mo.angle
					else
						cmd.buttons = $|BT_JUMP & ~BT_SPIN
					end
					if (tp.cmd.buttons & BT_SPIN) or (tp.cmd.buttons & BT_JUMP)
						cmd.buttons = $|BT_SPIN & ~BT_JUMP	
					end
					return true
				elseif (s.z > tp.mo.z+350*FRACUNIT)
					cmd.buttons = $|BT_SPIN & ~BT_JUMP
					return true
				elseif (s.z-s.momz <= s.floorz)
					s.momz = 0
					cmd.buttons = $|BT_CUSTOM3|BT_TOSSFLAG & ~BT_SPIN & ~BT_JUMP
					return true
				end
			end
		else
			yu.nosarun4 = 2 --Bots assume regular SRB2 physics
			yu.noairdrag = 2
		end
		
		if yu.lsadash and not yu.lsahoming
			s.angle = tp.mo.angle
			yu.forceyuangle = tp.mo.angle
		end
		--Now to accomodate our moveset!
		
		if p.botyuspincd
			if not (tp.cmd.buttons & BT_SPIN)
			p.botyuspincd = $-1
			end
		end

		if yu.lsacharged and yu2.lsacharged or ((p.pflags & PF_STARTDASH) and yu2.lsacharged)
		or ((tp.cmd.buttons & BT_SPIN) and (yu.lsacharged or (yu.lsacharge > 74)))
		
			yu.jump = 10
			yu.fallbuffer = 0
			yu.fallbuffer2 = 0
			yu.spin = 10 --No instant LSA release, you maniac.
			p.pflags = $|PF_USEDOWN
			if (yu.lsacharge > 74) and (p.pflags & PF_STARTDASH) --Hurry it up, slowpoke.
				yu.lsacharge = 85
				p.pflags = $|PF_JUMPDOWN|PF_USEDOWN
				cmd.buttons = $|BT_SPIN & ~BT_JUMP
				return true
			end
			if (tp.cmd.buttons & BT_SPIN)
			cmd.buttons = $|BT_SPIN 
			end		
		elseif ((p.pflags & PF_SPINNING) and not (p.pflags & PF_STARTDASH)) and ((tp.pflags & PF_SPINNING) and not (tp.pflags & PF_STARTDASH))
		and not (p.pflags & PF_JUMPED)
			yu.spin = 10 --No constant uncurling
			p.pflags = $|PF_USEDOWN|PF_JUMPDOWN
		end
		
		if ((yu.startdash) or (p.pflags & PF_STARTDASH))
		and (p.speed < 30*s.scale)
		and not ((tp.pflags & PF_STARTDASH) or yu2.startdash)
		and not p.botyuspincd
			p.botyuspincd = 5
			cmd.buttons = $ & ~BT_SPIN & ~BT_JUMP --Quit constantly uncurling
			return true
		end
		if (p.panim == PA_IDLE)
			p.botyuwait = p.botyuwait and p.botyuwait+1 or 1
			if tp.mo.state == S_PLAY_FLY and tp.mo.skin == "adventuresonic"
			p.botyuwait = $+40
			yu.tauntno = P_RandomRange(0,5)
			end
			if p.botyuwait > 351
				if yu.supersonic
				cmd.buttons = $|BT_TOSSFLAG|BT_CUSTOM3 & ~BT_CUSTOM2
				else
				cmd.buttons = $|BT_TOSSFLAG|BT_CUSTOM2
				end
				return true
			end
		else
			p.botyuwait = 0
		end
		
		if (p.pflags & PF_JUMPED) or yu.bouncestate
			if (tp.pflags & PF_THOKKED) or yu2.hominganimstart or yu2.farthok
				if (tp.cmd.buttons & BT_SPIN) or yu2.farthok
				yu.spin = 0 yu.jump = 0
				p.pflags = $ & ~PF_JUMPDOWN & ~PF_USEDOWN
				cmd.buttons = $|BT_JUMP|BT_SPIN
				return true
				end
				yu.jump = 0
				p.pflags = $ & ~PF_JUMPDOWN
				cmd.buttons = $|BT_JUMP & ~BT_SPIN
				return true
			end
			if yu2.bouncestate == 1
			or ((tp.cmd.buttons & BT_SPIN) and not (tp.pflags & PF_USEDOWN) and not P_IsObjectOnGround(tp.mo))
			or (P_IsObjectOnGround(tp.mo) and (tp.mo.z > s.z+120*FRACUNIT) and not (s.eflags & MFE_VERTICALFLIP) and (s.momz < 0))
				if not (p.botyuspincd and yu2.bouncestate == 1)
					yu.spin = 0
					p.botyuspincd = 5
					cmd.buttons = $|BT_SPIN
					return true
				else
					yu.spin = 10
					cmd.buttons = $ & ~BT_SPIN	
					return true
				end
			end
		end
		
		if s.state == S_PLAY_FLY and (yu.tauntstate < 5)
			if (leveltime % 2 == 0)
			cmd.buttons = $|BT_SPIN|BT_JUMP
			elseif (leveltime % 3 == 0)
			cmd.buttons = $|BT_SPIN & ~BT_JUMP
			elseif (leveltime % 13 == 0)
			cmd.buttons = $ & ~BT_JUMP & ~BT_JUMP			
			else
			cmd.buttons = $|BT_JUMP & ~BT_JUMP
			end
		end
		if (tp.cmd.buttons & BT_SPIN) and tp.mo.skin != "adventuresonic" and P_IsObjectOnGround(s) and not (tp.cmd.buttons & BT_JUMP)
			if (yu.spin > 5) and not (p.pflags & PF_SPINNING)
			yu.spin = 0
			end
			p.pflags = $|PF_JUMPDOWN|PF_USEDOWN
			cmd.buttons = $|BT_SPIN & ~BT_JUMP			
		end
end)