local function SpawnJellyGoop(p, x, y, z) 
	local o = P_SpawnMobj(x, y, z, MT_JELLYGOOP)
	o.color = p.mo.color
	o.scale = p.mo.scale
	return o
end
local jellyStronk = false
local jellyPlayerBounce = true
local jellySpikeNoclip = false
COM_AddCommand("jt_stronk", function(p)
	jellyStronk = not jellyStronk
	if jellyStronk then
		print("Jelly Tails can now push any object")
	else
		print("Jelly Tails can no longer push any object")
	end
end, COM_ADMIN)
COM_AddCommand("jt_playerbounce", function(p)
	jellyPlayerBounce = not jellyPlayerBounce
	if jellyPlayerBounce then
		print("Jelly Tails can now bounce off of players")
	else
		print("Jelly Tails can no longer bounce off of players")
	end
end, COM_ADMIN)
COM_AddCommand("jt_spikeinv", function(p)
	jellySpikeNoclip = not jellySpikeNoclip
	if jellySpikeNoclip then
		print("Jelly Tails can now go through spikes")
	else
		print("Jelly Tails can no longer go through spikes")
	end
end, COM_ADMIN)
addHook("NetVars", function(net)
	jellyStronk = net(jellyStronk)
	jellyPlayerBounce = net(jellyPlayerBounce)
	jellySpikeNoclip = net(jellySpikeNoclip)
end)
local function JellyGoopSplash(p, count, speed, zspeed)
	local step = FixedDiv(FRACUNIT*360, count*FRACUNIT)
	local i = 0
	while i < count do
		local o = SpawnJellyGoop(p, p.mo.x, p.mo.y, p.mo.z)
		P_InstaThrust(o, FixedAngle(i * step), speed)
		o.momz = zspeed
		i = i + 1
	end
end

addHook("ThinkFrame", function()
	for p in players.iterate() do
		if not p.mo then return end
		if p.prevskin == "jellytails" and p.mo.skin ~= "jellytails" then
			p.mo.spritexscale = FRACUNIT
			p.mo.spriteyscale = FRACUNIT
		end
		p.prevskin = p.mo.skin
		if p.mo.skin ~= "jellytails" then
			continue
		end
		local gravmul = 1
		if p.mo.eflags & MFE_VERTICALFLIP then
			gravmul = -1
		end
		if p.deadtimer == 1 then
			JellyGoopSplash(p, 32, FRACUNIT*4, FRACUNIT*6*gravmul)
			p.mo.momx = 0
			p.mo.momy = 0
			p.mo.momz = gravmul * FRACUNIT * 15
			P_TeleportMove(p.mo, p.mo.x, p.mo.y, p.mo.z + (FRACUNIT * gravmul))
			p.mo.flags = p.mo.flags & ~MF_NOCLIPHEIGHT
			-- p.mo.flags2 = p.mo.flags2 | MF2_DONTDRAW
			local o = SpawnJellyGoop(p, p.mo.x, p.mo.y, p.mo.z)
			o.spritexscale = FRACUNIT*5
			o.spriteyscale = FRACUNIT*5
			o.momz = FRACUNIT*gravmul*5
		end
		if p.crushedtimer then
			p.crushedtimer = $ - 1
			p.normalspeed = 0
			p.jumpfactor = 0
			p.maxdash = 0
			p.mindash = 0
			p.scalex = FRACUNIT*3
			p.scaley = FRACUNIT/3
			if p.crushedtimer <= 0 then
				p.normalspeed = skins[p.mo.skin].normalspeed
				p.jumpfactor = skins[p.mo.skin].jumpfactor
				p.maxdash = skins[p.mo.skin].maxdash
				p.mindash = skins[p.mo.skin].mindash
			end
		end
		if p.deadtimer > 0 then
			if P_IsObjectOnGround(p.mo) then
				local x = p.mo.spritexscale
				local y = p.mo.spriteyscale
				local targetx = FRACUNIT*5
				local targety = 0
				p.mo.spritexscale = x + (max(min(targetx - x, (FRACUNIT + (abs((targetx - x) * 2))/TICRATE)), -(FRACUNIT + (abs((targetx - x) * 2))/TICRATE)) / 2)
				p.mo.spriteyscale = y + (max(min(targety - y, (FRACUNIT + (abs((targety - y) * 2))/TICRATE)), -(FRACUNIT + (abs((targety - y) * 2))/TICRATE)) / 2)
			end
		end
		if p.mo.state == S_PLAY_PAIN then
			p.paintime = (p.paintime or 0) + 1
			if p.paintime == 1 then
				JellyGoopSplash(p, 8, FRACUNIT*5, FRACUNIT*5 * gravmul)
			end
		else
			p.paintime = 0
		end
		if p.mo.health <= 0 then continue end
		local factor = FRACUNIT + (FixedDiv(abs(p.mo.momz), p.mo.scale) / 50)
		if not p.mobjBounceTimer then p.mobjBounceTimer = 0 end
		if not p.scaley then p.scaley = FRACUNIT end
		if not p.scalex then p.scalex = FRACUNIT end
		local x = p.scalex
		local y = p.scaley
		if not p.scalemomx then p.scalemomx = 0 end
		if not p.scalemomy then p.scalemomy = 0 end
		
		p.scalemomx = FixedMul(p.scalemomx, FRACUNIT/32*30)
		p.scalemomy = FixedMul(p.scalemomy, FRACUNIT/32*30)
		
		p.scalemomx = $ + max(min(FRACUNIT - x, (FRACUNIT/20) + (abs(x)/200)), -((FRACUNIT/20) + (abs(x)/200)))/TICRATE*3
		p.scalemomy = $ + max(min(FRACUNIT - y, (FRACUNIT/20) + (abs(y)/200)), -((FRACUNIT/20) + (abs(y)/200)))/TICRATE*3
		
		p.scalex = $ + p.scalemomx
		p.scaley = $ + p.scalemomy
		
		p.scalex = $ + max(min(FRACUNIT - x, FRACUNIT/200), -FRACUNIT/200)
		p.scaley = $ + max(min(FRACUNIT - y, FRACUNIT/200), -FRACUNIT/200)
		
		p.mo.spritexscale = FixedMul(FixedDiv(FRACUNIT, factor), p.scalex)
		p.mo.spriteyscale = FixedMul(factor, p.scaley)
		
		if p.followmobj then
			local mo = p.followmobj
			mo.spritexscale = p.mo.spritexscale
			mo.spriteyscale = p.mo.spriteyscale
		end
		if p.powers[pw_carry] then return end
		if p.mo.sprite2 == SPR2_RUN and P_IsObjectOnGround(p.mo) then
			if leveltime % 5 == 0 then
				JellyGoopSplash(p, 2 + (leveltime%2), FRACUNIT*2, FRACUNIT*5* gravmul)
			end
		end
		p.mo.frame = p.mo.frame | TR_TRANS30
		if p.dashmode > 3*TICRATE then
			if leveltime % 2 == 0 then
				local g = P_SpawnGhostMobj(p.mo)
				g.blendmode = AST_ADD
			end
		end
		if p.followmobj then
			p.followmobj.frame = p.followmobj.frame | TR_TRANS30
		end
		if p.mo.sprite2 == SPR2_ROLL then
			local g = P_SpawnGhostMobj(p.mo)
			g.blendmode = AST_ADD
		end
		if not P_IsObjectOnGround(p.mo) and p.pflags & PF_JUMPED then
			if p.spintapping and not p.stomp then
				p.stomp = true
				p.mo.momz = FixedMul(-40 * FRACUNIT, p.mo.scale) * gravmul
				p.mo.momx = $ / 2
				p.mo.momy = $ / 2
				p.spintapping = false
			end
		end
		if P_IsObjectOnGround(p.mo) then
			p.wasDashmode = p.dashmode > 3*TICRATE
			p.airtime = 0
			p.stomp = false
			if p.fallmom then
				local factor = FRACUNIT + (abs(FixedDiv(p.fallmom, p.mo.scale))/25)
				if factor > (FRACUNIT) + (FRACUNIT/8*7) then
					p.mo.momz = -(p.fallmom/8) * 5
					p.mo.state = S_PLAY_JUMP
					p.pflags = p.pflags | PF_JUMPED
					S_StartSound(p.mo, sfx_s3k44)
				end
				if factor > FRACUNIT + (FRACUNIT/16) then
					S_StartSoundAtVolume(p.mo, sfx_ghit, max(min(FixedMul(factor-FRACUNIT, FRACUNIT*255)/FRACUNIT, 255), 0))
				end
				if factor > FRACUNIT/16*25 then
					JellyGoopSplash(p, 8, abs(p.fallmom) / 6, -p.fallmom/2)
				end
				local o = SpawnJellyGoop(p, p.mo.x, p.mo.y, p.mo.z)
				o.momz = -p.fallmom/4
				o.momx = p.mo.momx/4
				o.momy = p.mo.momy/4
				p.scalex = FixedMul(FRACUNIT, factor)
				p.scaley = FixedDiv(FRACUNIT, factor)
				if (abs(p.fallmom) > 70*FRACUNIT) then
					JellyGoopSplash(p, 16, abs(p.fallmom) / 6, -p.fallmom/2)
					p.scalex = FRACUNIT*4
					p.scaley = FRACUNIT/4
					P_DamageMobj(p.mo, nil, nil, 1, DMG_INSTAKILL)
				end
				p.fallmom = 0
			end
			/*
			-- Broken attempt to make a spamdash
			if p.mo.state ~= S_PLAY_SPINDASH and p.cmd.buttons & BT_SPIN then
				p.spindashing = true
				p.mo.state = S_PLAY_SPINDASH
				p.dashspeed = max(p.speed, p.mindash)
				p.pflags = p.pflags | PF_STARTDASH
			end
			if p.mo.state == S_PLAY_SPINDASH then
				p.mo.state = S_PLAY_SPINDASH
				p.mo.momx = FixedMul(p.mo.momx, FRACUNIT - (FRACUNIT/4))
				p.mo.momy = FixedMul(p.mo.momy, FRACUNIT - (FRACUNIT/4))
				if p.dashspeed < p.maxdash then
					p.dashspeed = p.dashspeed + (FRACUNIT*2)
				end
			end
			if p.mo.state == S_PLAY_SPINDASH and not (p.cmd.buttons & BT_SPIN) then
				P_InstaThrust(p.mo, p.mo.angle, p.dashspeed)
				p.dashspeed = 0
				p.mo.state = S_PLAY_ROLL
				p.spindashing = false
				p.pflags = p.pflags & ~PF_STARTDASH
				p.pflags = p.pflags | PF_SPINNING
			end
			*/
		else
			p.airtime = $ + 1
		end
		if not P_IsObjectOnGround(p.mo) then
			p.fallmom = p.mo.momz
			if p.wasDashmode then
				p.dashmode = (3*TICRATE) + (TICRATE/2)
			end
		end
		if p.powers[pw_justsprung] then
			p.scaley = FRACUNIT + (FRACUNIT/2)
			p.scalex = FRACUNIT - (FRACUNIT/2)
		end
		if p.pflags & PF_STARTJUMP and p.airtime < 2 then
			p.scalex = FRACUNIT + (FRACUNIT/2)
			p.scaley = FRACUNIT - (FRACUNIT/2)
		end
		if p.skidtime > 0 then
			if p.skidtime % 5 == 0 then
				p.scalex = FRACUNIT + (FRACUNIT/4)
				p.scaley = FRACUNIT - (FRACUNIT/4)
				JellyGoopSplash(p, 1 + (leveltime%2), FRACUNIT*3, FRACUNIT*4*gravmul)
			end
		end
		if p.powers[pw_tailsfly] then
			if p.powers[pw_super] then
				p.powers[pw_tailsfly] = 8*TICRATE
			end
			if p.powers[pw_tailsfly] < 3 then
				p.scalex = FRACUNIT + (FRACUNIT/2)
				p.scaley = FRACUNIT - (FRACUNIT/2)
			end
			if p.spintapping and p.mo.state == S_PLAY_FLY then
				p.powers[pw_tailsfly] = 0
				p.mo.state = S_PLAY_JUMP
				p.pflags = p.pflags | PF_JUMPED
				p.mo.momz = FixedMul(10 * FRACUNIT * gravmul, p.mo.scale)
				p.scalex = FRACUNIT + (FRACUNIT/2)
				p.scaley = FRACUNIT - (FRACUNIT/2)
				p.spintapping = false
				S_StartSound(p.mo, sfx_spin)
			end
			if p.cmd.buttons & BT_JUMP then
				if (gravmul == 1 and p.mo.momz < FixedMul(FRACUNIT*24, p.mo.scale)) or (gravmul == -1 and p.mo.momz > FixedMul(-FRACUNIT*24, p.mo.scale)) then p.mo.momz = p.mo.momz + FixedMul(FRACUNIT / 2 * gravmul, p.mo.scale) end
			else
				if (gravmul == 1 and p.mo.momz > FixedMul(-FRACUNIT*24, p.mo.scale)) or (gravmul == -1 and p.mo.momz < FixedMul(FRACUNIT*24, p.mo.scale)) then p.mo.momz = p.mo.momz - FixedMul(gravmul * FRACUNIT / 2, p.mo.scale) end
			end
		else
			if not P_IsObjectOnGround(p.mo) and not P_PlayerInPain(p) then
				if p.mo.state ~= S_PLAY_JUMP and p.spintapping then
					p.mo.state = S_PLAY_JUMP
					p.pflags = p.pflags | PF_JUMPED
					p.spintapping = false
					S_StartSound(p.mo, sfx_spin)
				end
			end
		end
		if P_IsObjectInGoop(p.mo) then
			p.dashmode = p.dashmode + 1
			if not (p.cmd.buttons & BT_JUMP) and not p.powers[pw_tailsfly] then p.mo.momz = $ - (gravmul*FRACUNIT/8) end
			if p.powers[pw_tailsfly] < 4*TICRATE and p.powers[pw_tailsfly] then p.powers[pw_tailsfly] = 4*TICRATE end
			if p.powers[pw_underwater] then
				p.powers[pw_underwater] = 30*TICRATE
			end
		end
		if p.mobjBounceTimer > 0 then
			p.mobjBounceTimer = $ - 1
		end
		if not (p.cmd.buttons & BT_USE) then
			p.spintapready = true
			p.spintapping = false
		elseif p.spintapready then
			p.spintapping = true
			p.spintapready = false
		else
			p.spintapping = false
		end
	end
end)
local function doJellyBounce(mo, other)
	local p = mo.player
	if mo.skin ~= "jellytails" then return end
		if p.mobjBounceTimer > 0 then return end
		local mul = 1
		if p.mo.eflags & MFE_VERTICALFLIP then
			mul = -1
		end
		S_StartSound(p.mo, sfx_s3k7b)
		p.scalex = FRACUNIT + (FRACUNIT/3)
		p.scaley = FRACUNIT - (FRACUNIT/3)
		other.momx = $ + (mo.momx/2)
		other.momy = $ + (mo.momy/2)
		other.momz = $ + (mo.momz/2) + (5*FRACUNIT*mul)
		
		p.bouncemomx = -other.momx
		p.bouncemomy = -other.momy
		p.bouncemomz = -other.momz + (10*FRACUNIT*mul)
		p.mobjBounceTimer = 2
		JellyGoopSplash(p, 4, FRACUNIT*5, FRACUNIT*7*mul)
end
addHook("MobjMoveCollide", function(mo, other)
	local p = mo.player
	if mo.skin ~= "jellytails" then return end
	if (mo.z > (other.z + other.height) or (mo.z < other.z)) then return end
	if other.type == MT_PLAYER and not jellyPlayerBounce then return end
	if (other.type == MT_SPIKE or other.type == MT_WALLSPIKE) and jellySpikeNoclip then
		p.scalex = FRACUNIT - (FRACUNIT/8)
		p.scaley = FRACUNIT + (FRACUNIT/8)
		return false
	end
	if (abs(mo.momx) + abs(mo.momy) + abs(mo.momz)) < FRACUNIT * 5 then return end
	local gravmul = 1
	if p.mo.eflags & MFE_VERTICALFLIP then
		gravmul = -1
	end
	if not (other.flags & MF_ENEMY) and not (other.flags & MF_MONITOR) and not (other.type == MT_PLAYER) and not jellyStronk then 
		if other.flags & MF_SOLID then
			p.bouncemomx = -mo.momx/2
			p.bouncemomy = -mo.momy/2
			p.scalex = FRACUNIT / 2
			p.scaley = FRACUNIT - (FRACUNIT/4)
			S_StartSound(p.mo, sfx_s3k7b)
			JellyGoopSplash(p, 4, FRACUNIT*5, FRACUNIT*7*gravmul)
		end
		return 
	end
	if other.flags & MF_ENEMY and P_PlayerCanDamage(p, other) then
		return
	end
	if p.powers[pw_flashing] then return end
	if other.type == MT_PLAYER then
		other.player.mobjBounceTimer = 5
	end
	S_StartSoundAtVolume(p.mo, sfx_ghit, 127)
	doJellyBounce(mo, other)
end, MT_PLAYER)
addHook("PostThinkFrame", function()
	for p in players.iterate() do
		if not p.mo then continue end
		if p.mo.skin ~= "jellytails" then
			continue
		end
		p.mo.momx = (p.bouncemomx or p.mo.momx)
		p.bouncemomx = 0
		
		p.mo.momy = (p.bouncemomy or p.mo.momy)
		p.bouncemomy = 0
		
		p.mo.momz = (p.bouncemomz or p.mo.momz)
		p.bouncemomz = 0
		p.mo.frame = p.mo.frame | TR_TRANS30
		if p.followmobj then
			p.followmobj.frame = p.followmobj.frame | TR_TRANS30
		end
	end
end)
addHook("MobjMoveBlocked", function(mo)
	local p = mo.player
	if mo.skin ~= "jellytails" then return end
	local factor = FRACUNIT + (FixedDiv((abs(mo.momx) + abs(mo.momy)), p.mo.scale)/100)
	if p.pflags & PF_SPINNING then
		factor = FixedMul(factor, FRACUNIT + (FRACUNIT/4))
	end
	local gravmul = 1
	if p.mo.eflags & MFE_VERTICALFLIP then
		gravmul = -1
	end
	factor = min(factor, FRACUNIT*4*3)
	if factor > FRACUNIT + (FRACUNIT/4) then
		mo.momx = FixedMul(FixedMul(-mo.momx, factor - FRACUNIT), FRACUNIT/16*17)
		mo.momy = FixedMul(FixedMul(-mo.momy, factor - FRACUNIT), FRACUNIT/16*17)
		if not P_IsObjectOnGround(mo) and not p.powers[pw_tailsfly] then
			mo.state = S_PLAY_JUMP
			p.pflags = p.pflags | PF_JUMPED
		end
		JellyGoopSplash(p, 4, FRACUNIT*5, FRACUNIT*7*gravmul)
		S_StartSound(p.mo, sfx_s3k7b)
	end
	if factor > (FRACUNIT/16*17) then
		S_StartSoundAtVolume(p.mo, sfx_ghit, max(min(FixedMul(factor-FRACUNIT, FRACUNIT*255)/FRACUNIT, 255), 0))
	end
	p.scalex = FixedMul(FRACUNIT, factor)
	p.scaley = FixedDiv(FRACUNIT, factor)
end, MT_PLAYER)