--Written by luigi budd, using assets from the original Glass Sonic (@thepartwherehekillsyou / theduderguy on discord)

freeslot("sfx_glasb1")
sfxinfo[sfx_glasb1].caption = "Breaking"
freeslot("sfx_glasb2")
sfxinfo[sfx_glasb2].caption = sfxinfo[sfx_glasb1].caption
freeslot("sfx_glasb3")
sfxinfo[sfx_glasb3].caption = sfxinfo[sfx_glasb1].caption
freeslot("sfx_glasb4")
sfxinfo[sfx_glasb4].caption = sfxinfo[sfx_glasb1].caption

freeslot("sfx_glasc1")
sfxinfo[sfx_glasc1].caption = "Clink"
freeslot("sfx_glasc2")
sfxinfo[sfx_glasc2].caption = sfxinfo[sfx_glasc1].caption
freeslot("sfx_glasc3")
sfxinfo[sfx_glasc3].caption = sfxinfo[sfx_glasc1].caption
freeslot("sfx_glasc4")
sfxinfo[sfx_glasc4].caption = sfxinfo[sfx_glasc1].caption

rawset(_G, "GlassEveryone",{})
local GE = GlassEveryone

GE.sounds = {
	breaking = {
		[1] = sfx_glasb1,
		[2] = sfx_glasb2,
		[3] = sfx_glasb3,
		[4] = sfx_glasb4,
	},
	clink = {
		[1] = sfx_glasc1,
		[2] = sfx_glasc2,
		[3] = sfx_glasc3,
		[4] = sfx_glasc4,
	},
}

/* format of @options:
	checkwater: (boolean) check for water invulnerability
	flashing: (boolean) check for flashing
	invuln: (boolean) check for flashing/invuln/godmode
	carry: (boolean) check for carries/spring, both removed when checked
*/
local checkopt = function(opt, default)
	if opt == nil then return default end
	return opt
end

GE.playerCanDie = function(p, options)
	options = $ or {}
	local checkwater	= checkopt(options.checkwater, true)
	local flashing		= checkopt(options.flashing, true)
	local invuln		= checkopt(options.invuln, true)
	local carry			= checkopt(options.carry, true)
	
	if (p.powers[pw_flashing] and flashing)
	or ((p.powers[pw_invulnerability]
	or (p.pflags & PF_GODMODE)) and invuln)
		return false
	end
	
	local me = p.mo
	if not (me and me.valid) then return false; end
	
	--Water breaks your fall
	if (me.eflags & (MFE_UNDERWATER|MFE_TOUCHWATER|MFE_GOOWATER) ~= 0)
	and checkwater
		return false
	end
	
	if ((p.powers[pw_carry] or me.last_carry)
	or (me.last_sprung))
	and me.grace_time
	and carry
		me.last_carry = nil
		me.last_sprung = nil
		return false
	end
	
	return true
end

--expects player mobj
GE.explodePMobj = function(me)
	local p = me.player
	
	local lastmomz = abs(FixedDiv(me.last_momz or 0,me.scale)/FU)/2
	me.momx,me.momy,me.momz = 0,0,0
	me.flags = $|MF_NOGRAVITY &~(MF_NOCLIPHEIGHT|MF_SOLID)
	S_StopSound(me)
	
	local explode = P_SpawnMobjFromMobj(me,0,0,0,MT_THOK)
	explode.state = S_MINECART_DTH1
	explode.color = me.color
	explode.colorized = true
	explode.fuse = -1
	
	local height = FixedDiv(P_GetPlayerHeight(p), me.scale) / FU
	for i = 0,P_RandomRange(13,20) + lastmomz
		local gib = P_SpawnMobjFromMobj(me,
			0,0,
			P_RandomRange(2,height)*FU,
			MT_GLASSGIB
		)
		P_SetObjectMomZ(gib, P_RandomRange(1,10)*FU + abs(me.last_momz or 0)*3/4)
		P_Thrust(gib,
			me.angle + P_RandomRange(0,360)*ANG1,
			P_RandomRange(3,8)*gib.scale + (me.last_speed or 0)/5
		)
		gib.colorized = true
		gib.color = me.color
		if (P_RandomChance(FU/2))
			P_SetScale(gib,
				FixedMul(gib.scale, FU/3 + P_RandomFixed()),
				true
			)
		end
		if me.inf_v
			gib.momx = $ + me.inf_v.x
			gib.momy = $ + me.inf_v.y
		end
		
		if (i <= 1)
			S_StartSound(gib,
				GE.sounds.breaking[P_RandomRange(1, #GE.sounds.breaking)]
			)
			if i == 0
				S_StartSound(gib, sfx_s3kb9)
			end
		end
		
		P_SpawnMobjFromMobj(me,
			P_RandomRange(-16,16)*FU,
			P_RandomRange(-16,16)*FU,
			P_RandomRange(0,height)*FU,
			MT_SPINDUST
		)
	end
	
	return true
end

addHook("PostThinkFrame", function()
	for p in players.iterate do
		if (p.spectator) then continue end
		if not (p.mo and p.mo.valid) then continue end
		
		if (p.powers[pw_carry] == CR_ROLLOUT)
		or (p.powers[pw_carry] == CR_ZOOMTUBE)
			p.mo.last_carry = p.powers[pw_carry]
			p.mo.grace_time = TICRATE + abs(FixedDiv(p.mo.momz,p.mo.scale)/FU)/2
		end
		
		if (p.mo.eflags & MFE_SPRUNG)
		and (p.mo.momz <= 0)
			p.mo.last_sprung = true
			p.mo.grace_time = TICRATE + abs(FixedDiv(p.mo.momz,p.mo.scale)/FU)/2
		end
		
		if p.mo and p.mo.valid
			local me = p.mo
			me.alpha = min($, FU*7/10)
			me.renderflags = $|RF_SEMIBRIGHT
			
			local terminal = (p.pflags & PF_SPINNING) and -30*me.scale or -20*me.scale
			if me.health
				if (P_IsObjectOnGround(me) or (p.pflags & PF_BOUNCING and me.state == S_PLAY_BOUNCE_LANDING))
				--Dont check for gravflip to keep bug in original mod
				and (me.last_momz ~= nil and me.last_momz <= terminal)
				and GE.playerCanDie(p, {checkwater = true})
					P_KillMobj(me)
				end
			else
				me.eflags = $|MF2_DONTDRAW
			end
			
			me.last_momz = me.momz
			me.last_speed = FixedHypot(me.momx,me.momy)
			if me.alivetime ~= nil
				me.alivetime = $ + 1
			else
				me.alivetime = 0
			end
			if me.grace_time
				me.grace_time = $ - 1
			end
		end
		
	end
end)

local function toofast(me)
	local p = me.player
	if not (p and p.valid) then return end
	if not (me.health) then return end
	if not GE.playerCanDie(p) then return end
	if (p.speed < 28*me.scale) then return end
	
	P_KillMobj(me)
end

local function diebro(me, inf, s)
	local p = me.player
	if not (p and p.valid) then return end
	
	if GE.playerCanDie(p,{invuln = false,flashing = false}) then
		if inf and inf.valid
			me.inf_v = {
				x = inf.momx,
				y = inf.momy
			}
		end
		P_KillMobj(me,inf,s)
		return true
	end
end

addHook("MobjMoveBlocked", function(me,thing)
	local p = me.player
	if (me.standingslope and me.standingslope.valid)
		local goingup = false
		local posfunc = P_GetZAt --P_MobjFlip(me) == 1 and P_FloorzAtPos or P_CeilingzAtPos
		
		if posfunc(me.standingslope, me.x, me.y, me.z) > me.z
		or posfunc(me.standingslope, me.x + me.momx, me.y + me.momy, me.z + me.momz) > me.z
			goingup = true
		end
		
		if goingup
			return
		end
	end
	if (p.powers[pw_justlaunched]) then return end
	
	if (thing and thing.valid)
		if (thing.flags & MF_MONITOR)
		and P_PlayerCanDamage(p, thing)
			return
		end
	end
	
	toofast(me)
end, MT_PLAYER)
addHook("MobjDamage", diebro, MT_PLAYER)
addHook("MobjDeath", GE.explodePMobj, MT_PLAYER)

addHook("PlayerSpawn",function(p)
	if not (p.mo and p.mo.valid) then return end
	
	if not P_IsObjectOnGround(p.mo)
		p.mo.last_sprung = true
		p.mo.grace_time = 3*TICRATE
	end
end)