local function P_ChangeOrigin(mo, x,y,z, inter) --A thing for teleporting stuff
	if (P_MoveOrigin(mo,x,y,z) or P_SetOrigin(mo,x,y,z))
		if inter
			P_MoveOrigin(mo,x,y,z)
		else
			P_SetOrigin(mo,x,y,z)
		end
	elseif P_TeleportMove(mo,x,y,z)
		P_TeleportMove(mo,x,y,z)
	end
end

COM_AddCommand("p_grab", function(player, arg) //A very shitty command, but it works!
	if player and player.valid
		if not arg
			CONS_Printf(player, "partnergrab - If on, by running into players, you grab em`!")
		else
			local stren = tostring(arg)
			if stren == "1"
			or string.lower(stren) == "yes"
			or string.lower(stren) == "on"
				player.mo.cangrab = true
			elseif stren == "0"
			or string.lower(stren) == "no"
			or string.lower(stren) == "off"
				player.mo.cangrab = false
			else
				CONS_Printf(player, "Invalid argument!")
			end
		end
	else
		CONS_Printf(player, "You need to be in-game!")
	end
end)

local function CheckPlayer(mobj) //mobj_t mobj - checked player
	if mobj.player
	and mobj.player.valid
	and mobj.player.playerstate == PST_LIVE
	and mobj.player.powers[pw_carry] != CR_NIGHTSMODE
	and not mobj.player.spectator
		return true
	end
end

local function IsInZ(mobj, mo)
	if mo.z > (mobj.z+mobj.height)
		return
	end
	if (mobj.z-mobj.height) > (mo.z+mo.height)
		return
	end
	return true
end

local function OopsGrab(mobj, mo, donotaffect)
	if mobj and mobj.valid
	and CheckPlayer(mobj)
		if (not donotaffect) 
		or (donotaffect and donotaffect != mobj)
			mobj.grabbed = nil
			mobj.troller = 0
			mobj.totallydunked = false
			mobj.player.powers[pw_carry] = 0
			mobj.flags = $ & ~MF_NOCLIPHEIGHT
			
			local pspeed = {}
			pspeed.x = mobj.momx
			pspeed.y = mobj.momy
			pspeed.z = mobj.momz
			
			P_DoPlayerPain(mobj.player)
			mobj.momx = pspeed.x
			mobj.momy = pspeed.y
			mobj.momz = pspeed.z
			
			mobj.cantbegrabbed = 5
		end
	end
	if mo and mo.valid
	and CheckPlayer(mo)
		if (not donotaffect) 
		or (donotaffect and donotaffect != mo)
			S_StartSound(mo, sfx_s3k52)
			mo.grabbed = nil
			mo.troller = 0
			mo.totallydunked = false
			mo.flags =  $ & ~MF_NOCLIPHEIGHT
			mo.player.powers[pw_carry] = 0
			
			local pspeed = {}
			pspeed.x = mo.momx
			pspeed.y = mo.momy
			pspeed.z = mo.momz
			
			P_DoPlayerPain(mo.player)
			mo.momx = pspeed.x
			mo.momy = pspeed.y
			mo.momz = pspeed.z
			
			mo.cantbegrabbed = 5
		end
	end
end

local function StopGrab(mo)
	if mo and mo.valid
	and CheckPlayer(mo)
		if mo.grabbed and mo.grabbed.valid
		and CheckPlayer(mo.grabbed)
			P_ChangeOrigin(mo.grabbed, mo.x, mo.y, mo.z,true)
			S_StartSound(mo, sfx_s3k52)
			P_DoPlayerPain(mo.grabbed.player, mo, mo)
			mo.grabbed.momx = mo.momx
			mo.grabbed.momy = mo.momy
			local mom = R_PointToAngle2(0, 0, mo.momx, mo.momy)
			if R_PointToDist2(0, 0, mo.momx, mo.momy)
				mom = R_PointToAngle2(0, 0, mo.momx, mo.momy)
			else
				if CheckPlayer(mo)
					mom = mo.player.drawangle
				else
					mom = mo.angle
				end
			end
			P_Thrust(mo.grabbed, mom, -5*FRACUNIT)
			P_SetObjectMomZ(mo.grabbed, mo.momz*P_MobjFlip(mo)+5*FRACUNIT, false)
			mo.grabbed.totallydunked = false
			mo.grabbed.player.pflags = $ & ~PF_FULLSTASIS
			mo.grabbed.player.powers[pw_carry] = 0
			mo.grabbed.flags = $ & ~MF_NOCLIPHEIGHT
			mo.grabbed.cantbegrabbed = 5
		end
		mo.cantbegrabbed = 5
		mo.grabbed = nil
	end
end

local function GrabPlayer(mobj, mo) //mobj_t mobj - grabber, mobj_t mo - grabbed
	mobj.grabbed = mo
	mobj.troller = TICRATE*3
	mo.grabbedby = mobj
	mo.totallydunked = true
	if CheckPlayer(mo)
		mo.player.powers[pw_carry] = CR_GENERIC
	end
	if P_MobjFlip(mo) != P_MobjFlip(mo)
		if P_MobjFlip(mobj) == -1
			mo.flags2 = $|MF2_OBJECTFLIP
			mo.eflags = $|MFE_VERTICALFLIP
		else
			mo.flags2 = $ & ~MF2_OBJECTFLIP
			mo.eflags = $ & ~MFE_VERTICALFLIP
		end
	end
	mobj.justgrabbed = 5
	S_StartSound(mo, sfx_s3k4a)
end

local function ThrowPlayer(mobj, mo, youaregoingtobrasil) //This is a mess
	P_ChangeOrigin(mo, mobj.x, mobj.y, mobj.z,true)
	
	local jumpfactors = 39*(FRACUNIT/4)
	local accelspeed = 36*FRACUNIT
	if CheckPlayer(mobj)
		if mobj.player.accelspeed
			accelspeed = mobj.player.accelspeed
		else
			accelspeed = mobj.player.speed
		end
	else
		accelspeed = 36*FRACUNIT
	end
	if CheckPlayer(mo)
		jumpfactors = FixedMul(39*(FRACUNIT/4), mo.player.jumpfactor)
	else
		jumpfactors = 39*(FRACUNIT/4)
	end
	P_InstaThrust(mo, mobj.angle, accelspeed)
	P_SetObjectMomZ(mo, jumpfactors+accelspeed/5, false)
	
	P_SetObjectMomZ(mobj, 5*FRACUNIT, false)
	
	if CheckPlayer(mo)
		if youaregoingtobrasil
		and (P_AproxDistance(P_AproxDistance(mo.momx,mo.momy),mo.momz) >= mo.player.normalspeed+10*FRACUNIT)
			P_DoPlayerPain(mo.player)
			P_InstaThrust(mo, mobj.angle, accelspeed)
			P_SetObjectMomZ(mo, jumpfactors+accelspeed/5, false)
		end
		mo.player.pflags = $|PF_JUMPED
		mo.player.pflags = $ & ~PF_THOKKED
		mo.player.drawangle = mobj.angle
		if (not youaregoingtobrasil)
		or (youaregoingtobrasil
		and P_AproxDistance(P_AproxDistance(mo.momx,mo.momy),mo.momz) < mo.player.normalspeed+10*FRACUNIT)
			mo.player.pflags = $ & ~PF_FULLSTASIS
			mo.state = S_PLAY_JUMP
		end
	end
	mo.totallydunked = false
	mo.player.powers[pw_carry] = 0
	mobj.cantbegrabbed = 15
	mo.flags = $ & ~MF_NOCLIPHEIGHT
	mo.cantbegrabbed = 15
	mobj.grabbed = nil
	mobj.troller = 0
	S_StartSound(mo, sfx_s3k51)
	S_StartSound(mobj, sfx_s3k4d)
end

addHook("MobjMoveCollide", function(mobj, mo)
	if mobj and mobj.valid
	and CheckPlayer(mobj)
	and mobj.skin == "knoockles"
	and mobj.player.speed >= mobj.player.runspeed
	and not mobj.player.powers[pw_carry]
	and not mobj.grabbed
	and not mobj.oofdunk
	and mobj.cangrab
		if CheckPlayer(mo)
		and not mo.player.powers[pw_carry]
		and IsInZ(mobj, mo)
		and not mo.cantbegrabbed
		and not mobj.oofdunk
		and not mobj.grabbed
		and P_MobjFlip(mobj) == P_MobjFlip(mo)
			local p1 = mobj.player //Mobj
			local p2 = mo.player //Mo
			
			if p1.speed > p2.speed //P1 dominant
				GrabPlayer(mobj, mo)
				
			elseif ((p1.speed < p2.speed) //P2 dominant
			and p2.speed >= p2.runspeed) 
				GrabPlayer(mo, mobj)
				
			else //Draw
				OopsGrab(mobj, mo)
				mobj.cantbegrabbed = TICRATE
				mo.cantbegrabbed = TICRATE
			end
		end
	end
end, MT_PLAYER)

addHook("MobjThinker", function(mo)
	if mo and mo.valid
	and CheckPlayer(mo)
		local player = mo.player
		if mo.cangrab == nil
			mo.cangrab = true
			if (skins["pointy"] and mo.skin == "pointy")
			or (skins["flarethetrex"] and mo.skin == "flarethetrex")
				mo.cangrab = false
			end
		end
		mo.stappls = $ or 0
		if mo.stappls
			mo.stappls = $ - 1
		end
		mo.troller = $ or 0
		mo.cantbegrabbed = $ or 0
		mo.justgrabbed = $ or 0
		mo.oofdunk = $ or 0
		if mo.oofdunk
			mo.oofdunk = $ - 1
			if ((player.cmd.buttons & BT_SPIN)
			or (player.cmd.buttons & BT_JUMP))
			and mo.grabbedby and mo.grabbedby.valid
				mo.grabbedby.stappls = 5
			end
		end
		if mo.justgrabbed
			mo.justgrabbed = $ - 1
		end
		if mo.cantbegrabbed
			mo.cantbegrabbed = $ - 1
		end
		if player.speed >= player.runspeed
			mo.troller = TICRATE*3
			mo.troling = false
		else
			if mo.troller
				mo.troller = $ - 1
				mo.troling = true
			end
		end
		player.getready = $ or 0
		if player.getready
			player.getready = $ - 1
		end
		if mo.grabbed and mo.grabbed.valid
		and CheckPlayer(mo.grabbed)
			mo.grabbed.player.pflags = $|PF_FULLSTASIS
			mo.grabbed.oofdunk = 5
			local moheg = 5*FRACUNIT
			if P_MobjFlip(mo)
				moheg = 5*FRACUNIT
			else
				moheg = mo.height - 5*FRACUNIT
			end
			mo.grabbed.flags = $|MF_NOCLIPHEIGHT
			mo.speen = $ or 0
			mo.addang = $ or 0
			
			mo.grabbed.momx = mo.momx
			mo.grabbed.momy = mo.momy
			mo.grabbed.momz = mo.momz
			if player.pflags & PF_JUMPED
				mo.grabbed.player.drawangle = player.drawangle
			else
				mo.grabbed.player.drawangle = R_PointToAngle2(mo.x, mo.y, mo.grabbed.x, mo.grabbed.y)
			end
			if P_GetPlayerControlDirection(player) <= 1
			or (player.pflags & PF_THOKKED)
				player.accelspeed = player.speed
			end
			if P_IsObjectOnGround(mo)
			and player.mo.state == S_PLAY_SKID
			and P_GetPlayerControlDirection(player) == 2
				player.getready = 5
			end
			
			local mom = R_PointToAngle2(0, 0, mo.momx, mo.momy)
			if player.speed
				mom = R_PointToAngle2(0, 0, mo.momx, mo.momy)
			else
				mom = player.drawangle
			end
			
			if player.pflags & PF_JUMPED
				if mo.grabbed.state != S_PLAY_ROLL
					mo.grabbed.state = S_PLAY_ROLL
				end
				mo.grabbed.player.pflags = $|PF_JUMPED & ~PF_NOJUMPDAMAGE
				local rads = -mo.radius - mo.grabbed.radius
				local targheg = mo.z+mo.height
				if P_MobjFlip(mo) == 1
					targheg = mo.z+mo.height
				else
					targheg = mo.z
				end
				local jumpfactor = FixedMul(39*(FRACUNIT/4), player.jumpfactor)
				local angfact = FixedAngle(FixedMul(jumpfactor, FRACUNIT/2*3))
				local momzz = mo.momz*P_MobjFlip(mo)
				
				local t = {}
				local rads = FixedMul(mo.radius*2, cos(((momzz/FRACUNIT)*angfact)+ANGLE_90))
				t.x = FixedMul(rads, cos(player.drawangle))
				t.y = FixedMul(rads, sin(player.drawangle))
				t.z = FixedMul(mo.height*P_MobjFlip(mo), cos(((momzz/FRACUNIT)*angfact)))
				if mo.grabbed and mo.grabbed.valid
					mo.grabbed.player.drawangle = player.drawangle
					P_ChangeOrigin(mo.grabbed, mo.grabbed.x, mo.grabbed.y, mo.z+moheg+t.z,true)
					P_TryMove(mo.grabbed, mo.x+t.x, mo.y+t.y, true)
				end
				local thok = P_SpawnMobjFromMobj(mo.grabbed, 0, 0, 0, mo.grabbed.player.spinitem)
				thok.color = mo.grabbed.color
				thok.target = mo.grabbed
				thok.fuse = 4
				
				if player == consoleplayer
					local chr = P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_OVERLAY)
					chr.target = mo
					chr.color = mo.color
					chr.state = S_THOK
					chr.tics = 2
					chr.fuse = 2
					chr.spriteyoffset = -7*FRACUNIT
					chr.spritexscale = FRACUNIT + ((momzz/FRACUNIT)*FRACUNIT/7)
					if momzz > 0
						chr.blendmode = AST_TRANSLUCENT
					else
						chr.blendmode = AST_MODULATE
					end
				end
			else
				if mo.grabbed.state != S_PLAY_PAIN
					mo.grabbed.state = S_PLAY_PAIN
				end
				if mo.grabbed and mo.grabbed.valid
					mo.grabbed.player.drawangle = R_PointToAngle2(mo.x, mo.y, mo.grabbed.x, mo.grabbed.y)
					local addx = P_ReturnThrustX(mo, player.drawangle, -mo.radius - mo.grabbed.radius)
					local addy = P_ReturnThrustY(mo, player.drawangle, -mo.radius - mo.grabbed.radius)
					P_ChangeOrigin(mo.grabbed, mo.grabbed.x, mo.grabbed.y, mo.z+moheg,true)
					P_TryMove(mo.grabbed, mo.x+addx, mo.y+addy, true)
				end
			end
			
			if player.speed >= player.runspeed
			and not (player.pflags & PF_JUMPED)
				if mo.grabbed and mo.grabbed.valid
					local ghost = P_SpawnGhostMobj(mo.grabbed)
					if ghost and ghost.valid
						ghost.colorized = true
						ghost.fuse = 3
						if ghost.tracer and ghost.tracer.valid
							ghost.tracer.colorized = ghost.colorized
							ghost.tracer.fuse = ghost.fuse
						end
					end
				end
			end
			
			if (mo.momz*P_MobjFlip(mo) <= 0
			and player.pflags & PF_JUMPED
			and (player.cmd.buttons & BT_JUMP
			and P_GetPlayerControlDirection(player) == 2)
			and not mo.justgrabbed)
			
			or (player.getready
			and player.cmd.buttons & BT_SPIN)
			
			or (mo.stappls)
				local losectrl = true
			
				if (player.getready
				and player.cmd.buttons & BT_SPIN)
				or (mo.stappls)
					mo.state = S_PLAY_FALL
					P_Thrust(mo, mo.angle, -15*FRACUNIT)
					
					mo.player.pflags = $ & ~(PF_SPINNING|PF_STARTDASH)
				end
				losectrl = true
				if (mo.stappls)
					losectrl = false
				end
				mo.cantbegrabbed = 15
				ThrowPlayer(mo, mo.grabbed, losectrl)
			end
		end
		if mo.grabbed and mo.grabbed.valid
			if mo.grabbed.grabbed and mo.grabbed.grabbed == mo
				OopsGrab(mo, mo.grabbed, mo)
			end
			mo.jumpedhide = false
		end
		if not mo.troller
		and mo.troling
			mo.troling = false
			mo.jumpedhide = false
			if mo.grabbed and mo.grabbed.valid
			and CheckPlayer(mo.grabbed)
				StopGrab(mo)
			end
		end
	end
end, MT_PLAYER)

addHook("MobjDamage", function(mo, mobj, src, dmg, dmgtype)
	if mo and mo.valid
	and CheckPlayer(mo)
		if mo.grabbed and mo.grabbed.valid
			StopGrab(mo)
		end
		if mo.oofdunk
			if not (dmgtype & DMG_DEATHMASK)
				return true
			end
		end
	end
end)

addHook("PlayerSpawn", function(player)
	if player.valid
	and not player.spectator
		local mo = player.mo
		mo.grabbed = nil
		mo.grabbedby = nil
		mo.troller = 0
		mo.troling = false
		mo.oofdunk = 0
		mo.stappls = 0
		mo.getready = 0
		mo.justgrabbed = 0
		mo.cantbegrabbed = 0
		mo.totallydunked = false
	end
end)