-- EveryoneHugs detection.
-- Just do your think normally if it's not added, but let it take control once it is.
local everyonehugs = false

addHook("ThinkFrame", do
	if AddHugger and not everyonehugs
		AddHugger("maimy")
		everyonehugs = true
	end
end)

addHook("NetVars", function(net)
	everyonehugs = net($)
end)

-- Heart object definition
freeslot("S_PLAY_MAIMYHUG", "S_COLORHEART", "SPR2_HUG_", "SPR_RHRT")

states[S_PLAY_MAIMYHUG] = {SPR_PLAY, SPR2_HUG_|FF_ANIMATE, 12, nil, 3, 4, S_PLAY_MAIMYHUG}

states[S_COLORHEART] = {
	sprite = SPR_RHRT,
	tics = -1
}

-- Love is in the air!
local function SpawnHearts(player)
	local heart = P_SpawnMobjFromMobj(player.mo, 0, 0, player.mo.height, MT_CDLHRT)
	heart.destscale = (5*player.mo.scale)>>4
	heart.fuse = (5*TICRATE)>>1
	heart.momz = player.mo.scale*P_MobjFlip(player.mo)
	heart.target = player.mo
	heart.extravalue1 = player.mo.x
	heart.extravalue2 = player.mo.y
end

-- Let's find the closest person to hug!
local function FindHugCandidates(rosy)
	local huggable
	
	for p in players.iterate
		-- Don't hug yourself
		if p == rosy continue end
		
		-- Hug only living players
		if not p.mo continue end
		if p.playerstate != PST_LIVE continue end
		
		-- No hugging in NiGHTS
		if p.powers[pw_carry] == CR_NIGHTSMODE continue end
		
		-- It's another Rosy and she's not hugging you
		if p.hugging and rosy.hugged != p continue end
		
		-- No hugging the enemy
		if p.ctfteam != rosy.ctfteam continue end
		
		-- Can't hug if not on the floor or doing anything
		if not(P_IsObjectOnGround(p.mo) and (p.panim == PA_IDLE or p.panim == PA_WALK or p.panim == PA_RUN))
			continue
		end
		
		-- Too high or too low to hug
		if abs(rosy.mo.z - p.mo.z) > 4*FRACUNIT continue end
		
		-- Too far away
		local dist = R_PointToDist2(rosy.mo.x, rosy.mo.y, p.mo.x, p.mo.y)
		if dist > rosy.mo.radius + p.mo.radius continue end
		
		-- Update the most likely candidate
		if not huggable or (huggable and dist < huggable.dist)
			huggable = p
			huggable.dist = dist
		end
	end
	
	return huggable
end

local function ReleaseHug(p)
	if p.hugging -- We're hugging
		-- Do this to me
		p.pflags = $ & ~PF_STASIS
		p.mo.state = S_PLAY_STND
		p.hugdelay = TICRATE
		
		-- Do this to them
		if p.hugging.valid
			p.hugging.pflags = $ & ~PF_STASIS
			p.hugging.hugged = nil
		end
		
		p.hugging = nil
	elseif p.hugged -- We're being hugged
		-- Do this for me
		p.pflags = $ & ~PF_STASIS
		
		-- Do this to them
		if p.hugged.valid
			p.hugged.pflags = $ & ~PF_STASIS
			--p.hugged.mo.angle = R_PointToAngle2(p.mo.x, p.mo.y, p.hugged.mo.x, p.hugged.mo.y)
			p.hugged.drawangle = p.hugged.mo.angle
			p.hugged.mo.state = S_PLAY_PAIN
			
			if p.hugged.hugtime > 1*TICRATE
				-- That's enough hugging
				p.hugged.mo.sprite2 = SPR2_TRNS
				p.hugged.mo.frame = G
				p.hugged.thrown = true
			else
				-- Ouch!
				p.hugged.mo.sprite2 = SPR2_PAIN
				S_StartSound(p.hugged.mo, sfx_cdpcm7)
			end
			
			-- Fling Amy
			A_DoNPCPain(p.hugged.mo)
			
			p.hugged.hugging = nil
		end
		
		p.hugged = nil
		p.hugdelay = TICRATE
	end
end

-- Release!
local function UnhugEveryone()
	for p in players.iterate
		if p.hug
			ReleaseHug(p)
		end
	end
end

addHook("PlayerThink", function(p)
	if everyonehugs return end
	if not p.mo return end
	
	if p.mo.skin != "maimy"
		if p.hug ReleaseHug(p) end
		
		return
	end
	
	local rosy = p
	
	-- Hug delay ticker
	rosy.hugdelay = $ and $ - 1 or 0
	
	-- Curl when you're falling from being thrown
	if rosy.thrown and rosy.mo.momz < 0
		rosy.mo.sprite2 = SPR2_ROLL
		rosy.mo.frame = D
		rosy.thrown = false
	end
	
	-- Hug thinker ~
	if rosy.hugging
		if not rosy.hugging.valid
			ReleaseHug(rosy)
			return
		end
		
		rosy.hugging.pflags = $ | PF_STASIS	
		rosy.pflags = $ | PF_STASIS
		rosy.target = rosy.hugging
		rosy.drawangle = rosy.hugangle
		if rosy.mo.state ~= S_PLAY_MAIMYHUG
			rosy.mo.state = S_PLAY_MAIMYHUG
		end
		rosy.hugtime = $ + 1
		
		if  rosy.hugging.mo.skin == "maimy" --Hug back! This is a war of affection!
		and (rosy.hugging.cmd.buttons & BT_CUSTOM2) 
			if not (rosy.hugging.mo.state == S_PLAY_MAIMYHUG)	
					rosy.hugged = rosy.hugging
					rosy.hugging.hugging = rosy
					rosy.hugging.hugtime = 0
					rosy.hugging.hugangle = R_PointToAngle2(rosy.hugging.mo.x, rosy.hugging.mo.y, rosy.mo.x, rosy.mo.y)
					rosy.hugging.hugdelay = TICRATE
					S_StartSound(rosy.hugging.mo, sfx_cdpcm6)
			end
		end		
		
		if rosy.cmd.buttons & BT_CUSTOM2 and not rosy.hugdelay
			ReleaseHug(rosy)
		elseif rosy.cmd.buttons & BT_CUSTOM3
			if not (leveltime & 7)
				SpawnHearts(rosy)
			end
		end
		
		return
	end
	
	local cand = FindHugCandidates(rosy)
	
	-- We don't have candidates? Get rid of the heart
	if not cand
		if rosy.heart and rosy.heart.valid
			P_RemoveMobj(rosy.heart)
		end
		
		rosy.heart = nil
		return
	end
	
	-- Found someone? Place the heart indicator
	if not rosy.heart
		rosy.heart = P_SpawnMobjFromMobj(cand.mo, 0, 0, cand.mo.height + 24*cand.mo.scale, MT_LOCKON)
		rosy.heart.state = S_COLORHEART
		rosy.heart.color = rosy.mo.color
		rosy.heart.scale = 2*cand.mo.scale/3
		rosy.heart.fuse = -1
		rosy.heart.tracer = rosy.mo
		rosy.heart.target = cand.mo
	end
	
	-- Hug!
	if rosy.cmd.buttons & BT_CUSTOM2 and not rosy.hugdelay
	and not (rosy.pflags & PF_STASIS)
	and not (rosy.pflags & PF_FULLSTASIS)
		rosy.mo.momx = 0
		rosy.mo.momy = 0
		
		cand.hugged = rosy
		rosy.hugging = cand
		
		rosy.hugtime = 0
		rosy.hugangle = R_PointToAngle2(rosy.mo.x, rosy.mo.y, cand.mo.x, cand.mo.y)
		rosy.hugdelay = TICRATE
		
		-- Ufufu~
		S_StartSound(rosy.mo, sfx_cdpcm6)
	end
end)

addHook("MobjThinker", function(mo)
	if everyonehugs return end
	
	if mo.tracer
		if mo.tracer.skin != "maimy" or mo.tracer.player.hugging
			mo.tracer.heart = nil
			P_RemoveMobj(mo)
			return
		else
			mo.color = mo.tracer.color
			mo.scale = 2*mo.target.scale/3
			
			local bob = 5 * sin(FixedAngle(leveltime*FRACUNIT*3))
			
			P_TeleportMove(mo, mo.target.x, mo.target.y, mo.target.z + mo.target.height + 15 * mo.scale + FixedMul(bob, mo.scale))
			return true
		end
	elseif mo.state == S_COLORHEART
		P_RemoveMobj(mo)
	end
end, MT_LOCKON)

addHook("JumpSpecial", ReleaseHug)