/*
Animation 
version 1.0.1
by Sonic Team Junior
modified by Kaldrum
Pretty much direct source code ports to fix Tails' and Metal Sonic's overlays when in the wallrun state.
*/

if HPUAnim
	return
end

rawset(_G, "HPUAnim", true)

//source code but wallrun allows tails' tails to display. not needed if custom sprites are made
local function P_DoTailsOverlay(player, tails)
	local mo = player.mo
	local onground = P_IsObjectOnGround(mo)
	local horizangle = player.drawangle
	if (mo.flags2 & MF2_TWOD or twodlevel) and (mo.state == S_PLAY_WALLRUN) and (player.wallangle ~= nil) and player.forwardinput < 0
		horizangle = $ + ANGLE_180
	end
	local zoffs = 0
	local backwards = -1 * FRACUNIT
	local doswim = (player.panim == PA_ABILITY) and (player.mo.eflags & MFE_UNDERWATER)
	local doroll = (player.panim == PA_ROLL) or (player.panim == PA_JUMP and not (player.charflags & SF_NOJUMPSPIN)) or doswim or mo.state == S_PLAY_WALLROLL
	local rollangle = 0
	local panimchange = nil
	local ticnum = 0
	local chosenstate = 0
	
	if not tails.skin
		tails.skin = mo.skin
		tails.state = S_TAILSOVERLAY_STAND
		tails.movecount = -1
	end
	
	panimchange = (tails.movecount ~= player.panim)
	
	if doroll
		local testval = 0
		local zdist = 0
		if player.speed < FRACUNIT
			testval = FRACUNIT
		else
			testval = FixedMul(player.speed, cos(horizangle - R_PointToAngle2(0,0, player.rmomx, player.rmomy)))
			if testval < FRACUNIT
				testval = FRACUNIT
			end
		end
		if doswim
			zdist = mo.momz<<1
		elseif onground and not mo.reactiontime
			zdist = mo.z - tails.threshold
		else
			zdist = mo.momz
		end
		
		rollangle = R_PointToAngle2(0,0, testval, -P_MobjFlip(mo) * zdist)
		
		if not doswim
			zoffs = 3*FRACUNIT + 12 * sin(rollangle)
			backwards = -12 * cos(rollangle)
		end
	elseif player.panimn == PA_RUN or mo.state == S_PLAY_WALLRUN
		backwards = -5*FRACUNIT
	elseif player.panim == PA_SPRING or player.panim == PA_JUMP
		zoffs = $ + 4*FRACUNIT
		backwards = $ / 2
	elseif player.panim == PA_PAIN
		backwards = $ /16
	elseif mo.state == S_PLAY_GASP
		backwards = $ / 16
		zpffs = $ + 12*FRACUNIT
	elseif mo.state == S_PLAY_EDGE
		backwards = $ / 16
		zoffs = 3*FRACUNIT
	elseif player.panim == PA_ABILITY2
		zoffs = -7*FRACUNIT
		backwards = -9*FRACUNIT
	elseif player.panim == PA_ABILITY
		backwards = -5*FRACUNIT
	end
	
	if doroll
		local add = nil
		if rollangle > ANGLE_180
			add = 2
		else
			add = 0
		end
		
		if add
			rollangle = InvAngle(rollangle)
		end
		
		rollangle = $ + ANG15
		
		if rollangle > ANG60
			chosenstate = S_TAILSOVERLAY_PLUS60DEGREES + add
		elseif rollangle > ANG30
			chosenstate = S_TAILSOVERLAY_PLUS30DEGREES + add
		else
			chosenstate = S_TAILSOVERLAY_0DEGREES
		end
		
	elseif player.panim == PA_SPRING or player.panim == PA_JUMP
		chosenstate = S_TAILSOVERLAY_MINUS60DEGREES
	elseif player.panim == PA_FALL or player.panim == PA_RIDE
		chosenstate = S_TAILSOVERLAY_PLUS60DEGREES
	elseif player.panim == PA_PAIN
		chosenstate = S_TAILSOVERLAY_PAIN
	elseif mo.state == S_PLAY_GASP
		chosenstate = S_TAILSOVERLAY_GASP
	elseif mo.state == S_PLAY_EDGE
		chosenstate = S_TAILSOVERLAY_EDGE
	elseif player.panim == PA_DASH
		chosenstate = S_TAILSOVERLAY_DASH
	elseif player.panim == PA_RUN
		chosenstate = S_TAILSOVERLAY_RUN
	elseif mo.state == S_PLAY_WALLRUN
		chosenstate = S_TAILSOVERLAY_WALLRUN
	elseif player.panim == PA_WALK
		if not onground or mo.state == S_PLAY_SKID
			chosenstate = S_TAILSOVERLAY_PLUS30DEGREES
		elseif player.speed >= FixedMul(player.runspeed/2, mo.scale)
			chosenstate = S_TAILSOVERLAY_0DEGREES
		else
			chosenstate = S_TAILSOVERLAY_MINUS30DEGREES
		end
	elseif mo.sprite2 == SPR2_FLY
		chosenstate = S_TAILSOVERLAY_FLY
	elseif mo.sprite2 == SPR2_SWIM
		chosenstate = S_TAILSOVERLAY_FLY
	elseif mo.sprite2 == SPR2_TIRE
		chosenstate = S_TAILSOVERLAY_TIRE
	elseif player.panim == PA_ABILITY2
		chosenstate = S_TAILSOVERLAY_PLUS30DEGREES
	elseif player.panim == PA_IDLE
		chosenstate = S_TAILSOVERLAY_STAND
	else
		chosenstate = S_INVISIBLE
	end
	
	if panimchange
		tails.sprite2 = -1
		tails.state = chosenstate
	else
		if tails.state ~= chosenstate
			if states[chosenstate].sprite == SPR_PLAY
				tails.frame = states[chosenstate].frame & FF_FRAMEMASK
			end
			
			tails.state = chosenstate
		end
	end
	
	if player.fly1 ~= 0 and player.powers[pw_tailsfly] ~= 0 and not onground
		tails.state = chosenstate
	end
	
	if player.panim == PA_SPRING or player.panim == PA_FALL or mo.state == S_PLAY_RIDE
		if FixedDiv(abs(mo.momz), mo.scale) < 20<<16
			ticnum = 2
		else
			ticnum = 1
		end
	elseif player.panim == PA_PAIN
		ticnum = 2
	elseif mo.state == S_PLAY_GASP
		tails.tics = -1
	elseif mo.sprite2 == SPR2_TIRE
		if doswim
			ticnum = 2
		else
			ticnum = 4
		end
	elseif player.panim ~= PA_IDLE
		ticnum = mo.tics
	end
	
	if ticnum and (tails.tics > ticnum)
		tails.tics = ticnum
	end
	
	tails.color = mo.color
	tails.threshold = mo.z
	tails.movecount = player.panim
	tails.angle = horizangle
	P_SetScale(tails, mo.scale)
	tails.destscale = mo.destscale
	tails.radius = mo.radius
	tails.height = mo.height
	zoffs = FixedMul(zoffs, tails.scale)
	
	if mo.eflags & MFE_VERTICALFLIP
		tails.eflags = $ | MFE_VERTICALFLIP
		tails.flags2 = $ | MF2_OBJECTFLIP
		zoffs = player.mo.height - tails.height - zoffs
	else
		tails.eflags = $ & ~ MFE_VERTICALFLIP
		tails.flags2 = $ & ~ MF2_OBJECTFLIP
	end
	
	local x = mo.x + P_ReturnThrustX(tails, tails.angle, FixedMul(backwards, tails.scale))
	local y = mo.y + P_ReturnThrustY(tails, tails.angle, FixedMul(backwards, tails.scale))
	local z = mo.z + zoffs
	P_TeleportMove(tails, x, y, z)
end
			
//source code but wallrun also allows a jetfume to appear. slightly offset when rollangle makes the player face upside down
local function P_DoMetalJetFume(player, fume) 
	local FUME_SKINCOLORS =
	{
		SKINCOLOR_ICY,
		SKINCOLOR_SKY,
		SKINCOLOR_CYAN,
		SKINCOLOR_WAVE,
		SKINCOLOR_TEAL,
		SKINCOLOR_AQUA,
		SKINCOLOR_SEAFOAM,
		SKINCOLOR_MINT,
		SKINCOLOR_PERIDOT,
		SKINCOLOR_LIME,
		SKINCOLOR_YELLOW,
		SKINCOLOR_SANDY,
		SKINCOLOR_GOLD,
		SKINCOLOR_APRICOT,
		SKINCOLOR_SUNSET
	}
	local mo = player.mo
	local angle = player.drawangle
	local rollangle = mo.rollangle
	local dist = 0
	local heightoffset = 0
	if (mo.eflags & MFE_VERTICALFLIP)
		heightoffset = mo.height - (P_GetPlayerHeight(player)>>1)
	else
		heightoffset = (P_GetPlayerHeight(player)>>1)
	end
	
	local dashmode = min(player.dashmode, DASHMODE_MAX)
	local underwater = mo.eflags & MFE_UNDERWATER
	
	if (mo.state ~= S_PLAY_WALLRUN and player.panim ~= PA_WALK and player.panim ~= PA_RUN and player.panim ~= PA_DASH)
		if fume.state ~= fume.info.spawnstate
			fume.state = fume.info.spawnstate
		end
		return
	end
	
	if underwater
		fume.movedir = $ + FixedAngle(FixedDiv(2*player.speed, 3 * mo.scale))
		fume.movefactor = $ + player.speed
		
		if fume.movefactor > FixedDiv(2*player.normalspeed, 3 *mo.scale)
			local i = 0
			local radiusv = 4*FRACUNIT
			local radiusx = P_ReturnThrustX(mo, angle, -mo.radius)
			local radiusy = P_ReturnThrustY(mo, angle, -mo.radius)
			local factorx = P_ReturnThrustX(mo, angle + ANGLE_90, mo.scale)
			local factory = P_ReturnThrustY(mo, angle + ANGLE_90, mo.scale)
			local offseth = 0
			local offsetv = 0
			local x = 0
			local y = 0
			local z = 0
			for i = -1, 1, 2 
			do
				offseth = i*P_ReturnThrustX(fume, fume.movedir, radiusv)
				offsetv = i*P_ReturnThrustY(fume, fume.movedir, radiusv)
				x = mo.x + radiusx + FixedMul(offseth, factorx)
				y = mo.y + radiusy + FixedMul(offseth, factory)
				z = mo.z + heightoffset + offsetv
				P_SpawnMobj(x, y, z, MT_SMALLBUBBLE).scale = mo.scale>>1
			end
			fume.movefactor = 0
		end
		
		if player.panim == PA_WALK
			if fume.state ~= fume.info.spawnstate
				fume.threshold = 0
				fume.state = fume.info.spawnstate
			end
			return
		end
	end
	
	if(fume.state == fume.info.spawnstate)
		fume.state = fume.info.seestate
	end
	
	if (dashmode > DASHMODE_THRESHOLD) and (fume.state ~= fume.info.seestate)
		fume.destscale = mo.scale
		fume.flags2 = $ & ~ MF2_DONTDRAW
		fume.flags2 = $ | (mo.flags2 & MF2_DONTDRAW)
		
	else
		if dashmode == DASHMODE_THRESHOLD and dashmode > fume.movecount
			fume.state = fume.info.seestate
			P_SetScale(fume, mo.scale << 1)
		end
		
		fume.flags2 = (fume.flags2 & ~ MF2_DONTDRAW) | (mo.flags2 & MF2_DONTDRAW)
		fume.destscale = mo.scale + FixedDiv(player.speed, player.normalspeed)
		
		if underwater
			fume.destscale = $/6
		else
			fume.destscale = $/3
		end
		
		fume.color = FUME_SKINCOLORS[(dashmode * table.maxn(FUME_SKINCOLORS) / (DASHMODE_MAX + 1))+1]
		
		if underwater
			fume.frame = fume.frame & FF_FRAMEMASK | FF_ANIMATE | (P_RandomRange(0,9) * FF_TRANS10)
			fume.threshold = 1
		elseif fume.threshold
			fume.frame = (fume.frame & FF_FRAMEMASK) | fume.state.frame
			fume.threshold = 0
		end
	end
	fume.movecount = dashmode
	fume.flags2 = (fume.flags2 & ~ MF2_OBJECTFLIP) | (mo.flags2 & MF2_OBJECTFLIP)
	fume.eflags = (fume.eflags & ~ MFE_VERTICALFLIP) | (mo.eflags & MFE_VERTICALFLIP)
	
	dist = -mo.radius - FixedMul(fume.info.radius, fume.destscale - mo.scale/3)
	//gets offset from the player sprite when rolled
	fume.rollangle = rollangle
	local x = mo.x + P_ReturnThrustX(fume, angle, dist)
	local y = mo.y + P_ReturnThrustY(fume, angle, dist)
	local z = mo.z + heightoffset - (fume.height>>1)
	P_TeleportMove(fume, x, y, z)
	if player.normalspeed >=  player.normalspeed * 2
		P_SpawnGhostMobj(fume)
	end
end

addHook("FollowMobj", function(player, tails)
	if (not player.mo.skin == "tails") or (active == false)
		return
	end
	P_DoTailsOverlay(player, tails)
	return true
end, MT_TAILSOVERLAY)
				
addHook("FollowMobj", function(player, fume)
	if (not player.mo.skin == "metalsonic") or (active == false)
		return
	end
	P_DoMetalJetFume(player, fume)
	return true
end, MT_METALJETFUME)