-- LUA_FLING2(1).lua (amended to skip fling pause while drifting)

local function SingleFreeSlot(...)
	for _,slot in ipairs({...})
		if not rawget(_G, slot) freeslot(slot) end --doth not exist? Then add.
	end
end
SingleFreeSlot("sfx_yuedth", "MT_YUEDTH", "SPR_GPLZ")

-- Optional: detect Tysson roll state symbol if present
local S_TYSSONROLL = rawget(_G, "S_PLAY_TYSSONROLL") or -1

local function IsRollState(st)
	return (st == S_PLAY_ROLL) or (S_TYSSONROLL ~= -1 and st == S_TYSSONROLL)
end

mobjinfo[MT_YUEDTH] = {
spawnstate = S_THOK,
deathsound = sfx_yusfxz,
flags = MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOBLOCKMAP|MF_SCENERY|MF_NOGRAVITY
}

local SAEXPLOSIONS = true

--Hmm...I can't think a better way than a global MobjDeath hook. Oh well, it should be okay!
addHook("MobjDeath", function(s, i, so)
	if SAEXPLOSIONS == false return end --
	if s.valid and (s.flags & MF_ENEMY) and not (s.flags & (MF_BOSS|MF_NOSECTOR|MF_MONITOR))
	
		local type = s.type

		if not ((type > MT_BUGGLE) and (type < MT_SPINBOBERT_FIRE2))
		and not ((type > MT_COIN) and (type <= MT_SHLEEP)) --Hey, these are NOT robots!
		and not (type == MT_FANG)
		
			if not s.spawnedyuexplosion --Makes the mod stackable and modifyable. You can add it to a MobjSpawn hook for example.
			and not s.player --...duh.
			
				s.spawnedyuexplosion = true
				s.flags2 = $|MF2_DONTDRAW
				s.spritexscale,s.spriteyscale = 1,1
								
				local sh = P_SpawnMobjFromMobj(s,0,0,0,MT_THOK) --This invisible object makes the sound for us.
				sh.tics,sh.fuse = 60,60 sh.flags2 = MF2_DONTDRAW sh.spriteyscale = 1
								
				S_StartSound(sh, sfx_yuedth)
			
				local ghs = P_SpawnMobjFromMobj(s, 0, 0, s.height/3, MT_YUEDTH)
				ghs.scale = max(s.scale, s.height/32) --A bigger explosion the bigger you are!
				ghs.target = s
				ghs.scalespeed = FRACUNIT/6
				ghs.destscale = 99<<16
			end
		end
	end
end)

addHook("MobjSpawn", function(s)
	s.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
	s.fuse,s.tics = 25,40 --We're merely using the S_THOK state, so we need to put some custom properties first.
	s.sprite = SPR_GPLZ
	s.frame = A|FF_TRANS30
	s.blendmode = AST_ADD
	s.dispoffset = 99
end, MT_YUEDTH)

addHook("MobjThinker", function(s) 
	if not s.valid or not s.fuse return end --
	if ((s.frame & FF_FRAMEMASK) < P)
		s.frame = $+1
		local t = s.target
		if t and t.valid --Stop an enemy's generic "pop" sounds.
			S_StopSoundByID(t, sfx_pop)
		end
	end
	if (s.fuse < 18)
		local TRANS = min(FF_TRANS90, 10<<16 - (s.fuse/2)<<16 ) --Fading away!
		s.frame = ($ & ~FF_TRANSMASK)|TRANS 
	end
end, MT_YUEDTH)

COM_AddCommand("SAExplosions", function() 
	SAEXPLOSIONS = (not SAEXPLOSIONS)
	print("SA-explosions are now set to\x82 "+SAEXPLOSIONS+"\x80"+"!")
end, COM_ADMIN)

addHook("NetVars", function(network) --Sync emblem count and SAemblems value.
	SAEXPLOSIONS = network($)
end)

addHook("MobjThinker", function(s)
	if s.valid
		if not s.fuse --This shouldn't happen...
			s.fuse = 25
			return
		else
			if ((s.frame & ~FF_FULLBRIGHT & ~FF_TRANSMASK) < P) --I really couldn't just use a single custom state?
			s.frame = ($+1)|FF_FULLBRIGHT
			end
			if (s.fuse < 18) --Fade out!
			s.frame = $|(TR_TRANS90-((s.fuse/2)*FRACUNIT))
			else
			s.frame = $|TR_TRANS30
			end
		end
		if s.valid and s.target and s.target.valid --Stop popping sounds.
			S_StopSoundByID(s.target, sfx_pop)
			return
		end
	end
end, MT_YUEDTH)

freeslot(
"MT_NEO_FLING"
)
mobjinfo[MT_NEO_FLING] = {
	doomednum = -1,
	spawnstate = S_PLAY_STND,
	flags = MF_NOCLIPHEIGHT,
	radius = 5*FRACUNIT,
	deathstate = S_XPLD1,
	height = 5*FRACUNIT
}

-- FIX: nil-safe inf/src handling + robust state checks
addHook("MobjDeath", function(trg, inf, src)
	-- must be a valid enemy
	if not (trg and trg.valid) then return end
	if (trg.flags & MF_ENEMY) == 0 then return end

	-- choose an attacker reference we can safely read from
	local attacker = nil
	if inf and inf.valid then
		attacker = inf
	elseif src and src.valid then
		attacker = src
	end

	-- identify player (if any)
	local p = (attacker and attacker.player) and attacker.player or nil
	local is_animesonic = (p and p.mo and p.mo.valid and p.mo.skin == "sonic")

	-- stomp FX also count as valid triggers
	local atk_type = attacker and attacker.type or 0
	local is_stomp_fx = (atk_type == MT_STOMPSPLOSION) or (atk_type == MT_STOMPDUST)

	-- if neither animesonic nor stomp FX, bail
	if not is_animesonic and not is_stomp_fx then return end

	-- gate by state/effects (only when we actually have a player/attacker)
	local ok = false
	if attacker and attacker.valid and (attacker.state == S_PLAY_DASH 
	or attacker.state == S_PLAY_JUMP or attacker.state == S_PLAY_ROLL or attacker.state 
	or attacker.state == S_PLAY_TYSSONROLL or attacker.state == S_PLAY_FALL or attacker.state == S_PLAY_TYBOUNCE or attacker.state == S_PLAY_HTOP) then
		ok = true
	end
	if p and (p.neoboosting or p.stomping or ((p.pflags & PF_THOKKED) ~= 0)) then
		ok = true
	end
	if is_stomp_fx then ok = true end
	if not ok then return end

	-- play hit SFX on whichever object we have, fallback to target
	S_StartSound(attacker or trg, sfx_s3k49)

	-- hide target and spawn its fling proxy
	trg.flags2 = $|MF2_DONTDRAW
	local fling = P_SpawnMobjFromMobj(trg, 0, 0, 0, MT_NEO_FLING)
	if not (fling and fling.valid and trg and trg.valid) then return end

	trg.tics = -1
	fling.sprite  = trg.sprite
	fling.sprite2 = trg.sprite2
	fling.frame   = trg.frame
	fling.angle   = trg.angle
	fling.color   = trg.color
	fling.info.deathstate  = S_CYBRAKDEMONVILEEXPLOSION1
	fling.info.deathsound  = sfx_s3kb4
	fling.nokilltimer      = 3
	S_StopSound(trg)

	-- compute a safe launch angle:
	-- 1) from attacker to target, if we have an attacker
	-- 2) otherwise, use target's recent movement vector
	local ax, ay
	if attacker and attacker.valid then
		ax, ay = attacker.x, attacker.y
	else
		ax, ay = trg.x + trg.momx, trg.y + trg.momy
	end
	fling.fling_pending_angle = R_PointToAngle2(ax, ay, trg.x, trg.y)
	fling.fling_pending_speed = 25*FRACUNIT
	fling.fling_pending_momz  = 5*FRACUNIT

	-- DRIFT-BYPASS: skip the pause if drifting or actively stomping
	local skip_pause = (p and (p.shadowdrifting or p.stomping == true))
	if not skip_pause then
		fling.fling_delay = 3
		fling.momx, fling.momy, fling.momz = 0, 0, 0
		fling.flags = $|MF_NOGRAVITY
	else
		fling.fling_delay = 0
	end

	-- Player pause/roll preservation (unchanged logic, nil-safe)
	if p and p.mo and p.mo.valid and not skip_pause then
		p.fling_delay = 3

		-- store momentum to restore after pause
		p.fling_store_momx = p.mo.momx
		p.fling_store_momy = p.mo.momy
		p.fling_store_momz = p.mo.momz

		-- if they were rolling/tyssonrolling/spinning, keep them rolling
		p.fling_force_roll = (IsRollState(p.mo.state) or ((p.pflags & PF_SPINNING) ~= 0)) and 1 or 0

		-- freeze during pause
		p.mo.momx, p.mo.momy, p.mo.momz = 0, 0, 0
		p.mo.flags = $|MF_NOGRAVITY
		p.powers[pw_nocontrol] = 3

		if p.fling_force_roll == 1 then
			p.pflags = $|PF_SPINNING
			p.mo.state = S_PLAY_ROLL
		end
	end
end)


addHook("MobjThinker", function(mo)
	if mo and mo.valid and mo.health
		if not (mo.secondphase)
			
			-- Enemy fling delay handler
			if mo.fling_delay and mo.fling_delay > 0
				mo.fling_delay = $ - 1
				-- keep frozen
				mo.momx, mo.momy, mo.momz = 0, 0, 0
				return
			elseif mo.fling_pending_angle
				-- time to launch in the exact stored direction/speed
				mo.flags = $ & ~MF_NOGRAVITY
				P_SetObjectMomZ(mo, mo.fling_pending_momz, false)
				P_InstaThrust(mo, mo.fling_pending_angle, mo.fling_pending_speed)

				-- clear once applied
				mo.fling_pending_angle = nil
				mo.fling_pending_speed = nil
				mo.fling_pending_momz  = nil
			end
			
			if mo.nokilltimer > 0
				mo.nokilltimer = $ - 1
			end
			--print(mo.info.deathstate)
			if P_IsObjectOnGround(mo) or (FixedHypot(mo.momx, mo.momy) == 0)
				--mo.rollangle = 0
				if mo.nokilltimer == 0
					mo.momx = 0
					mo.momy = 0
					mo.momz = 0
					mo.flags = $ | MF_NOGRAVITY
					local lmao = P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_NEO_FLING)
					lmao.sprite = mo.sprite
					lmao.sprite2 = mo.sprite2
					lmao.color = mo.color
					lmao.target = mo
					lmao.secondphase = true
					lmao.rollangle = mo.rollangle
					lmao.angle = mo.angle
					P_KillMobj(mo)
					mo.rollangle = 0
				end
			else
				mo.rollangle = $ + FixedAngle(20*FRACUNIT)
			end
		else
			if not (mo.target and mo.target.valid)
				P_RemoveMobj(mo)
			else
				mo.momx = 0
				mo.momz = 0
				mo.momy = 0
				if leveltime % 2 == 0
					mo.flags2 = $|MF2_DONTDRAW
				else
					mo.flags2 = $&~MF2_DONTDRAW
				end
			end
		end
	end
end, MT_NEO_FLING)

-- Player: spin-safe delayed launch finisher forcing S_PLAY_ROLL when applicable
addHook("PlayerThink", function(p)
	local mo = p.mo
	if not (mo and mo.valid) return end

	-- During pause: hold completely still and keep gravity off,
	-- while forcing S_PLAY_ROLL if they were spinning/rolling at impact.
	if p.fling_delay and p.fling_delay > 0
		p.fling_delay = $ - 1
		mo.momx, mo.momy, mo.momz = 0, 0, 0
		mo.flags = $|MF_NOGRAVITY

		-- keep nocontrol alive through the pause
		if p.powers[pw_nocontrol] < 1 p.powers[pw_nocontrol] = 1 end

		if p.fling_force_roll == 1
			p.pflags = $|PF_SPINNING
			mo.state = S_PLAY_ROLL
		end
		return
	end

	-- Pause over: restore gravity, momentum, and enforce S_PLAY_ROLL if flagged
	if p.fling_store_momx ~= nil
		mo.flags = $ & ~MF_NOGRAVITY

		-- restore stored momentum
		mo.momx = p.fling_store_momx
		mo.momy = p.fling_store_momy
		mo.momz = p.fling_store_momz

		if p.fling_force_roll == 1
			p.pflags = $|PF_SPINNING
			mo.state = S_PLAY_ROLL
		end

		-- Clear stored fields so this fires once
		p.fling_store_momx = nil
		p.fling_store_momy = nil
		p.fling_store_momz = nil
		p.fling_force_roll = nil
	end
end)
