--[[
	framedata constants for easy modding :3
	note: a second (TICRATE) equals to 35 frames in srb2
	~lumy
]]
--damage stuffs
local KOU_UNCHARGED_DAMAGE = 1
local KOU_CHARGED_DAMAGE = 2
local KOU_OVERCHARGE_DAMAGE = 3 --unreachable by strike

--wolf missile
local KOU_M_MINCHARGE = 9 --0.5s
local KOU_M_PEAKCHARGE = 35 --1s
local KOU_M_OVERCHARGE = 35+18+9 --1.75s
local KOU_M_MINLANDTIME = 7 --0.2s

--wolf strike
local KOU_S_MINCHARGE = 9 --0.25s
local KOU_S_PEAKCHARGE = 35 --1s
local KOU_S_OVERCHARGE = 35+18+9 --1.75s
local KOU_S_MINTHRUST = 41 * FRACUNIT
local KOU_S_PEAKTHRUST = 82 * FRACUNIT
--local KOU_S_OVERTHRUST = nil --not needed because scaling is calculated automatically
local KOU_S_LANDTIME = 18 --0.5s
local KOU_S_PARRYTIME = 10 --~0.286s

--battlemod lmao
local KOU_BM_EXTRAMINCHARGE = 9 --0.25s

freeslot(
"MT_KOU_CIRC",
"MT_KOU_FOFHITBOX",
"MT_KOU_MURDER",
"S_KOU_CIRC1",
"S_KOU_CIRC2",
"S_PLAY_KOU_WINDUP",
"S_PLAY_KOU_AIRWINDUP",
"S_PLAY_KOU_LUNGE",
"S_PLAY_KOU_DROP",
"S_PLAY_KOU_PUNCH1",
"S_PLAY_KOU_PUNCH2",
"S_PLAY_KOU_FLIP",
"S_PLAY_KOU_REVERSEROLL",
"S_PLAY_KOU_WALLROLL",
"S_PLAY_KOU_REVERSEROLL_STARTUP",
"S_PLAY_KOU_CHARGE",
"S_PLAY_KOU_MISSILE",
"S_PLAY_KOU_SLEEP",
"S_PLAY_KOU_SIXFEET",
"SPR_KLCR",
"SFX_KREXPL",
"sfx_kouslp",
"SFX_KOFAL1",
"SFX_KOFAL2",
"SFX_KOZOOM",
"SFX_SPNCH1",
"SFX_SPNCH2",
"SFX_WPNCH1",
"SFX_WPNCH2",
"SFX_KODRP1",
"SFX_KODRP2",
"SPR_KREX",
"S_KOU_REALISTICEXPLOSION",
--"MT_KOU_REALISTICEXPLOSION",
"S_KOU_NORMALEXPLOSION",
"MT_KOU_NORMALEXPLOSION",
"SPR_KONX",
"MT_KOU_BOOMBOX",
"S_KOU_BOOMBOX",
"SPR_BMBX",
"MT_KOU_BOOMPART",
"S_KOU_BOOMPART",
"SPR_BMBP",
"SPR_KOUD",
"S_KOU_MISSILEPARTICLE",
"MT_KOU_MISSILEPARTICLE",
"SPR_KOUE",
"S_KOU_SMISSILEPARTICLE",
"MT_KOU_SMISSILEPARTICLE"
)

sfxinfo[sfx_krexpl].caption = "Explosion"
sfxinfo[sfx_kouslp].caption = "Snoring"
sfxinfo[sfx_kofal1].caption = "Falling"
sfxinfo[sfx_kofal2].caption = sfxinfo[sfx_kofal1].caption
sfxinfo[sfx_kozoom].caption = "Zoom"
sfxinfo[sfx_kodrp1].caption = "Titan Drop"
sfxinfo[sfx_kodrp2].caption = sfxinfo[sfx_kodrp1].caption
--todo: verify sfxinfo for the new audio

mobjinfo[MT_KOU_MISSILEPARTICLE] = {
	doomednum = -1,
	spawnstate = S_KOU_MISSILEPARTICLE,
	height = 125,
	radius = 20,
	flags = MF_SCENERY|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT
}
mobjinfo[MT_KOU_SMISSILEPARTICLE] = {
	doomednum = -1,
	spawnstate = S_KOU_SMISSILEPARTICLE,
	height = 125,
	radius = 20,
	flags = MF_SCENERY|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT
}
mobjinfo[MT_KOU_BOOMBOX] = {
	doomednum = -1,
	spawnstate = S_KOU_BOOMBOX,
	height = 125,
	radius = 20,
	flags = MF_SCENERY
}
mobjinfo[MT_KOU_BOOMPART] = {
	doomednum = -1,
	spawnstate = S_KOU_BOOMPART,
	height = 20,
	radius = 20,
	flags = MF_SCENERY|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT
}
mobjinfo[MT_KOU_CIRC] = {
	doomednum = -1,
	spawnstate = S_KOU_CIRC1,
	height = 125,
	radius = 20,
	dispoffset = 5,
	flags = MF_SCENERY|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT
}
mobjinfo[MT_KOU_FOFHITBOX] = {
	doomednum = -1,
	spawnhealth = 1,
	spawnstate = S_INVISIBLE,
	radius = 100*FRACUNIT,
	height = 170*FRACUNIT,
	flags = MF_SOLID|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT
}
mobjinfo[MT_KOU_MURDER] = {
	doomednum = -1,
	spawnstate = S_INVISIBLE,
	height = 16*FRACUNIT,
	radius = 16*FRACUNIT,
	speed = 40*FRACUNIT,
	deathstate = S_CYBRAKDEMONVILEEXPLOSION1,
	deathsound = sfx_s3kb4
}
mobjinfo[MT_KOU_NORMALEXPLOSION] = {
	doomednum = -1,
	spawnstate = S_KOU_NORMALEXPLOSION,
	height = 125,
	radius = 20,
	dispoffset = 5,
	flags = MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT
}
states[S_KOU_MISSILEPARTICLE] = {
	sprite = SPR_KOUD,
	frame = FF_FULLBRIGHT|FF_ANIMATE,
	var1 = 2,
	var2 = 2
}
states[S_KOU_SMISSILEPARTICLE] = {
	sprite = SPR_KOUE,
	frame = FF_FULLBRIGHT|FF_ANIMATE,
	var1 = 4,
	var2 = 2
}
states[S_KOU_BOOMBOX] = {
	sprite = SPR_BMBX,
	frame = A
}
states[S_KOU_BOOMPART] = {
	sprite = SPR_BMBP,
	frame = A
}
states[S_KOU_NORMALEXPLOSION] = {
	sprite = SPR_KONX,
	frame = FF_FULLBRIGHT|FF_ANIMATE,
	var1 = 23,
	var2 = 1
}
states[S_KOU_REALISTICEXPLOSION] = {
	sprite = SPR_KREX,
	frame = FF_FULLBRIGHT|FF_ANIMATE,
	var1 = 16,
	var2 = 2,
	nextstate = S_NULL
}
states[S_KOU_CIRC1] = {
	sprite = SPR_KLCR,
	frame = A|FF_FULLBRIGHT,
	dispoffset = -15
}
states[S_KOU_CIRC2] = {
	sprite = SPR_KLCR,
	frame = B|FF_FULLBRIGHT,
	dispoffset = -15
}
states[S_PLAY_KOU_WINDUP] = {
	sprite = SPR_PLAY,
	frame = SPR2_TWIN|FF_ANIMATE,
	var2 = 3
}
states[S_PLAY_KOU_AIRWINDUP] = {
	sprite = SPR_PLAY,
	frame = SPR2_FLY_|FF_ANIMATE,
	var2 = 2
}
states[S_PLAY_KOU_LUNGE] = {
	sprite = SPR_PLAY,
	frame = SPR2_MLEE|FF_ANIMATE,
	var2 = 2
}
states[S_PLAY_KOU_DROP] = {
	sprite = SPR_PLAY,
	frame = SPR2_FIRE|FF_ANIMATE,
	var2 = 5,
	tics = 20,
	nextstate = S_PLAY_STND
}
states[S_PLAY_KOU_PUNCH1] = {
	sprite = SPR_PLAY,
	frame = SPR2_MLEL|A
}
states[S_PLAY_KOU_PUNCH2] = {
	sprite = SPR_PLAY,
	frame = SPR2_MLEL|B,
}
states[S_PLAY_KOU_REVERSEROLL] = {
	sprite = SPR_PLAY,
	frame = SPR2_ROLL
}
states[S_PLAY_KOU_WALLROLL] = {
	sprite = SPR_PLAY,
	frame = SPR2_ROLL|FF_ANIMATE,
	var2 = 4
}
states[S_PLAY_KOU_FLIP] = {
	sprite = SPR_PLAY,
	frame = SPR2_ROLL|FF_ANIMATE,
	var2 = 2,
	tics = 8,
	nextstate = S_PLAY_FALL
}
states[S_PLAY_KOU_REVERSEROLL_STARTUP] = {
	sprite = SPR_PLAY,
	frame = SPR2_MLEL|B,
	tics = 8,
	nextstate = S_PLAY_KOU_REVERSEROLL
}
states[S_PLAY_KOU_CHARGE] = {
	sprite = SPR_PLAY,
	frame = SPR2_ROLL|FF_ANIMATE,
	var2 = 2
}
states[S_PLAY_KOU_MISSILE] = {
	sprite = SPR_PLAY,
	frame = SPR2_GLID|FF_ANIMATE,
	var2 = 2
}
states[S_PLAY_KOU_SLEEP] = {SPR_PLAY, SPR2_CNT1, TICRATE, nil, 0, 0, S_PLAY_KOU_SLEEP}
states[S_PLAY_KOU_SIXFEET] = {SPR_PLAY, SPR2_SHIT, -1, nil, 0, 0, S_PLAY_KOU_SIXFEET}

freeslot("MT_KOUPARRY", "S_KOUPARPRY", "MT_KOU_PROJECTILE", "S_KOU_PROJECTILE", "S_KOU_PROJECTILEPOP", "SPR_KOUP", "S_KOU_DIEFALL")
mobjinfo[MT_KOUPARRY] = {
	doomednum = -1,
	spawnhealth = 1,
	spawnstate = S_KOUPARPRY,
	deathstate = S_NULL,
	radius = 45*FRACUNIT,
	height = 70*FRACUNIT,
	flags = MF_SOLID|MF_NOGRAVITY|MF_NOCLIPHEIGHT
}
states[S_KOUPARPRY] = {SPR_NULL, 0, -1, NULL, 0, 0, S_NULL}

mobjinfo[MT_KOU_PROJECTILE] = {
	doomednum = -1,
	spawnstate = S_KOU_PROJECTILE,
	deathstate = S_KOU_PROJECTILEPOP,
	height = 35*FRACUNIT,
	radius = 20*FRACUNIT,
	flags = MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY,
	damage = 2
}
states[S_KOU_PROJECTILE] = {
	sprite = SPR_KOUP,
	frame = FF_FULLBRIGHT|A
}
states[S_KOU_PROJECTILEPOP] = {SPR_KOUP, FF_FULLBRIGHT|FF_ANIMATE|A, 10, nil, 4, 2, S_NULL}
states[S_KOU_DIEFALL] = {SPR_PLAY, SPR2_DEAD, -1, nil, 0, 0, S_KOU_DIEFALL}

freeslot("MT_KOUCIRCLE","S_KOUCIRCLE")
mobjinfo[MT_KOUCIRCLE] = {
	doomednum = -1,
	spawnstate = S_KOUCIRCLE,
	flags = MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOBLOCKMAP,
	dispoffset = -4,
	radius = 5*FRACUNIT,
	height = 30*FRACUNIT
}
states[S_KOUCIRCLE] = {
	--sprite = SPR_STAB,
	sprite = SPR_KLCR,
	frame = FF_FULLBRIGHT,
	tics = 10,
	nextstate = S_NULL,
}

--Kou can't do abilities (PA_DEAD doesn't exist lumy)
local function KouCantMove(player)
	local mo = player and player.mo
	return P_PlayerInPain(player)
	or player.pflags & PF_SLIDING
	or player.powers[pw_carry]
	or player.spectator
	or player.playerstate
	or not mo.health
	or mo.state == S_PLAY_DEAD
	or player.powers[pw_super] == 1
	or player.iseggrobo
	or player.isjettysyn
	or player.guard
	or player.actionstate
	or player.powers[pw_nocontrol]
	or (player.isxmomentum and mo.state == S_PLAY_FACEPLANT)
end

local function KouDead(player)
	local mo = player.mo
	
	if mo.koudeath
		if (mo.koudeath != true and mo.koudeath.valid) then
			local inflictor = mo.koudeath
			local fallbackspeed = FixedMul(4*FRACUNIT, inflictor.scale)
			local ang = R_PointToAngle2(inflictor.x-inflictor.momx, inflictor.y - inflictor.momy, mo.x - mo.momx, mo.y - mo.momy)
			
			if (mo.player)
				mo.player.drawangle = ang + ANGLE_180
			end
			P_SetObjectMomZ(mo, FixedDiv(69*FRACUNIT,10*FRACUNIT), false)
			P_InstaThrust(mo, ang, fallbackspeed)
		else
			P_SetObjectMomZ(mo, FixedDiv(69*FRACUNIT,10*FRACUNIT), false)
		end
		
		mo.state = S_KOU_DIEFALL
		mo.flags = $ | MF_NOCLIPTHING &~ (MF_NOCLIP|MF_NOCLIPHEIGHT)
		mo.koudeath = nil
	end
	
	if (mo.state == S_KOU_DIEFALL)
	and (P_IsObjectOnGround(mo)) then
		mo.state = S_PLAY_KOU_SIXFEET
	end
end

local function KouDeathed(mo, inflictor, source, damagetype)
	if not (mo.skin == "kou") then return end
	mo.colorized = false
	if (damagetype == DMG_DEATHPIT) then return end
	mo.koudeath = inflictor or true
end
addHook("MobjDeath", KouDeathed, MT_PLAYER)

addHook("ThinkFrame", do
	for player in players.iterate 
		if not (player.mo and player.mo.valid and player.mo.skin == "kou") continue end
		if not (player.mo.health) then
			KouDead(player)
			continue
		end
	end
end)	

local function KouVars(p, nullout)
	if p.kou and p.kou.hittarget and p.kou.hittarget.valid
		p.kou.hittarget.flags = $&~MF_NOTHINK
	end
	
	if nullout then p.kou = nil return end
	p.kou = {
		--dtapcooldown = 0,
		--dtapped = 0,
		--dtapdash = false,
		--sprinting = false,
		--canstartdash = true,
		--c1down = false,
		--jumpdown = false,
		lungetime = 0,
		dontcancellunge = false,
		--OhMyGodPleaseJustFuckingWork = 0,
		hittarget = nil,
		lastx = 0,
		lasty = 0,
		lastz = 0,
		--tx = 0,
		--ty = 0,
		--tz = 0,
		--up = 0,
		--side = 0,
		--orbitangle = 0,
		beginmurdering = 0,
		charge = 0,
		strikecharge = false,
		spin = 0,
		forwardmove = 0,
		--walllungetimer = 0,
		sidemove = 0,
		touchedline = 0,
		touchedside = 0,
		sidexscroll = nil,
		sideyscroll = nil,
		walltouch = 0,
		waswalltouching = false,
		wallmoved = false,
		--sprintleeway = 0,
		dancetimer = 0, --for LUA_TAUNT
		tfdown = false, --for LUA_TAUNT
		currentlyDiving = false,
		diveWaiter = 0,
		funnyfall = false,
		passivefunnyfall = false,
		kousleep = 0,
		chargetimer = 0,
		wallnuked = 0,
		jumptap = false,
		spintap = false,
		parrytimer = 0,
		punchgrace = 0,
		--supercharged = false,
		forcetimer = 0,
		spikehatred = 0,
		punchlagactive = 0,
		enemybounceback = false
	}
end

local spawnnormalexplosion = function(mo, size) --so you're saying this shit is NORMAL? like, he can just MAKE those by PUNCHING? just like that? what the sigma? oh, sorry, dataminer... is this really long line here bothering you? too bad, now that you know it exists, you'll have to live with it, forever.
	local ex = P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_KOU_NORMALEXPLOSION)
	ex.blendmode = AST_ADD
	ex.scale = size or mo.scale
	if P_RandomRange(1, 100) <= 10 then
		ex.blendmode = 0
		ex.state = S_KOU_REALISTICEXPLOSION
		local audiosource = P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_THOK)
		audiosource.tics = 40 ; audiosource.state = S_INVISIBLE
		S_StartSound(audiosource, sfx_krexpl)
		ex.scale = $*2
		ex.tics = states[ex.state].var1 * states[ex.state].var2
	else
		ex.state = S_TNTBARREL_EXPL1
		--ex.flags = ($|MF_NOCLIPTHING) &~ (MF_SPECIAL|MF_PAIN|MF_FIRE)
		ex.FUCKINGSTOP = true
		S_StartSound(ex, sfx_s3k4e)
	end
	return ex
end

local yoshiExplodeFavEdit = function(inflictor, radius, grounded, spikesplease, BIGFUCKINGEXPLOSION) -- what
	searchBlockmap("objects", function(me, mo)
		if not (mo and mo.valid and mo.health)
			return
		end
		if not (mo.flags & MF_SHOOTABLE) and not (mo.type == MT_EGGGUARD or mo.type == MT_MINUS
		or mo.type == MT_SPIKE or mo.type == MT_WALLSPIKE)
			return
		end
		if grounded and not P_IsObjectOnGround(mo)
			return
		end
		local zradius = BIGFUCKINGEXPLOSION and radius/10 or radius/6
		if abs(inflictor.x - mo.x) > radius or abs(inflictor.y - mo.y) > radius or (abs(inflictor.z - mo.z) > zradius)
			return
		end
		if FixedHypot(FixedHypot(inflictor.x - mo.x, inflictor.y - mo.y), inflictor.z - mo.z) > radius
			return
		end
		if mo.type == MT_MINUS and not (mo.flags & (MF_SPECIAL|MF_SHOOTABLE))
			mo.flags = (mo.flags & ~MF_NOCLIPTHING)|MF_SPECIAL|MF_SHOOTABLE
		end
		local DMG = KOU_UNCHARGED_DAMAGE
		if BIGFUCKINGEXPLOSION then
			DMG = KOU_OVERCHARGE_DAMAGE
		elseif inflictor and inflictor.player and inflictor.player.kou then
			if inflictor.player.kou.charge >= KOU_M_PEAKCHARGE then
				DMG = KOU_CHARGED_DAMAGE
			elseif inflictor.player.kou.passivefunnyfall then
				DMG = KOU_UNCHARGED_DAMAGE
			end
		end
		if mo.type == MT_EGGGUARD and mo.tracer 
			P_KillMobj(mo.tracer, inflictor, inflictor, DMG_NUKE)
		elseif spikesplease
		and (mo.type == MT_SPIKE or mo.type == MT_WALLSPIKE)
			P_KillMobj(mo, inflictor, inflictor, DMG_NUKE)
		elseif P_DamageMobj(mo, inflictor, inflictor, DMG, DMG_NUKE)
		and BIGFUCKINGEXPLOSION
			spawnnormalexplosion(mo)
		end
	end, inflictor, inflictor.x - radius, inflictor.x + radius, inflictor.y - radius, inflictor.y + radius)
end

local domurder = function(targ, inflictor, DMG, speed) --KILL!!
	if not(targ and targ.valid and targ.health and not targ.FUCKINGSTOP) then
		return false --Invalid target
	end
	if (targ.flags & MF_PUSHABLE or targ.type == MT_ROLLOUTROCK
	or targ.type == MT_EGGMOBILE_FIRE or targ.type == MT_LASER) then
		return false
	end
	local ishazard = DMG < 0
	if ishazard or ((targ.flags & MF_ENEMY) and targ.health <= DMG)
		targ.flags2 = $|MF2_DONTDRAW
		targ.FUCKINGSTOP = true
		if targ.type == MT_CRUSHCLAW --dude the I hate these things
			local chain = targ.target
			local chainnext
			while (chain)
				chainnext = chain.target
				P_RemoveMobj(chain)
				chain = chainnext
			end
		end
		
		local m = P_SpawnMobjFromMobj(targ, 0, 0, 0, MT_KOU_MURDER)
		if m and m.valid --fuck this game
			if inflictor and inflictor.valid then
				speed = $ and max($, 40*inflictor.scale) or 40*inflictor.scale
			end
			m.speed = targ.speed and max(targ.speed or 0, speed) or speed
			m.scale = targ.scale
			m.sprite = targ.sprite
			m.sprite2 = targ.sprite2
			m.frame = targ.frame
			m.color = targ.color
			m.origin = targ
			m.parent = inflictor
			if DMG < -1 or targ.flags & MF_MISSILE then ishazard = false end
			m.angle = ishazard and R_PointToAngle2(m.x, m.y, inflictor.x, inflictor.y) + ANGLE_180 or targ.angle
			m.nohoming = ishazard
			--print(leveltime)
			return m
		end
		return false --Couldn't spawn murder object
	end
	return false --Target's HP wasn't low enough for a murder
end

local IHATESPIKES = function(inflictor, radius, titan) -- what
	local killcount = 0
	if inflictor.player and inflictor.player.kou
		inflictor.player.kou.spikehatred = 2
	end
	
	searchBlockmap("objects", function(me, mo)
		if not (mo and mo.valid and mo.health)
			return
		end
		if abs(inflictor.x - mo.x) > radius or abs(inflictor.y - mo.y) > radius or abs(inflictor.z - mo.z) > radius
			return 
		end
		if FixedHypot(FixedHypot(inflictor.x - mo.x, inflictor.y - mo.y), inflictor.z - mo.z) > radius
			return
		end
		local iscactus = false
		local titandropd = false
		if titan then
			if mo.type == MT_CACTITINYSEG or mo.type == MT_CACTISMALLSEG then
				iscactus = true
			else
				for i=1,11 do
					if mo.type == _G["MT_CACTI"..i] then iscactus = true ; break end
				end
			end
			if (iscactus or mo.type == MT_POINTYBALL or mo.type == MT_SPIKEBALL) then
				domurder(mo, inflictor, -1)
				if mo and mo.valid then P_KillMobj(mo, inflictor, inflictor, DMG_NUKE) end
				killcount = $+1
				titandropd = true
			end
		end
		if (not titandropd) and (mo.type == MT_SPIKE or mo.type == MT_WALLSPIKE)
			P_KillMobj(mo, inflictor, inflictor, DMG_NUKE)  
			killcount = $+1
		end
	end, inflictor, inflictor.x - radius, inflictor.x + radius, inflictor.y - radius, inflictor.y + radius)
	
	return killcount
end

local function inputangle(player) 
	if (twodlevel or player.mo.flags2 & MF2_TWOD)
		return player.mo.angle
	else
		return (player.cmd.angleturn<<16 + R_PointToAngle2(0, 0, player.cmd.forwardmove*FRACUNIT, -player.cmd.sidemove*FRACUNIT))
	end
end	
//converting this check into a function in case of future mods needing this
local function SightlineCheck(spotted, seer, peripheral)
	//first check if they're within out horizontal sightline
	local isinsight = false
	local pangle = R_PointToAngle2(seer.x, seer.y, spotted.x, spotted.y)
	pangle = AngleFixed($)
	local minsight = AngleFixed(seer.angle - peripheral / 2)
	local maxsight = AngleFixed(seer.angle + peripheral / 2)
	if minsight > maxsight
		isinsight = (pangle <= 360 * FRACUNIT and pangle >= minsight) or
				(pangle >= 0 and pangle <= maxsight)
	else
		isinsight = pangle >= minsight and pangle <= maxsight
	end
	//then check vertical by ensuring nothing's in between the 2 objects
	local ceilingz
	if seer.ceilingrover != nil and seer.ceilingrover.valid
		ceilingz = seer.ceilingrover.bottomheight
	end
	local floorz
	if seer.floorrover != nil and seer.floorrover.valid
		floorz = seer.floorrover.topheight
	end
	local seerz = Echoes.ObjectZCoord(seer)
	local spottedz = Echoes.ObjectZCoord(spotted)
	local iszclear = true
	if spottedz > seerz
		for i = seerz, spottedz, seer.scale do
			if ceilingz != nil and seerz >= ceilingz and seerz < spottedz
				iszclear = false
			end
		end
	elseif spottedz < seerz
		for i = seerz, spottedz, seer.scale do
			if floorz != nil and seerz <= floorz and seerz > spottedz
				iszclear = false
			end
		end
	end
	return isinsight and iszclear
end
//sorry to do this to you lumi but homing was too op - Emmette Clyde
local function CobaltEnemyLook(player) 
	local twod = (twodlevel or player.mo.flags2 & MF2_TWOD)
	local ringdist, span
	if not(twod)
		ringdist = RING_DIST*2
		span = FixedAngle(45*FRACUNIT)
	else
		ringdist = RING_DIST
		span = ANG20
	end

	local maxdist = 512 * player.mo.scale//FixedMul(ringdist, player.mo.scale)
	local closestdist = maxdist//0
	local closestmo = nil
	local nonenemiesdisregard = MF_SPRING
	searchBlockmap("objects",function(pmo,mo)
		if (mo.flags & MF_NOCLIPTHING) return end
		if (mo.health <= 0) return end -- dead

		if not(mo.player)
		and (!((mo.flags & (MF_ENEMY|MF_BOSS|MF_MONITOR)
		and (mo.flags & MF_SHOOTABLE)) or (mo.flags & MF_SPRING)) == !(mo.flags2 & MF2_INVERTAIMABLE)) -- allows if it has the flags desired XOR it has the invert aimable flag
			return -- not a valid target
		end
		--CTF monitor 
		if mo.type == MT_RING_REDBOX and not(G_GametypeHasTeams() and player.ctfteam == 1) then return end
		if mo.type == MT_RING_BLUEBOX and not(G_GametypeHasTeams() and player.ctfteam == 2) then return end

		if (mo == pmo) return end
		if (mo.flags2 & MF2_FRET) return end
		if (mo.flags & nonenemiesdisregard) return end
		if mo.player and 
			(
			mo.player.spectator
			or gametyperules&GTR_FRIENDLY
			or G_GametypeHasTeams() and mo.player.ctfteam == player.ctfteam
			)
		then return end --Disallow targeting teammates


		--Do angle/distance checks
		local zdist = (pmo.z + pmo.height/2) - (mo.z + mo.height/2)
		local dist = P_AproxDistance(pmo.x-mo.x, pmo.y-mo.y)
		--CBW: 	Made the angle checks their own locals, for readability purposes
		--		I also unsigned the angle checks, which appears to correct failed OutOfBounds checks above the player.
		local xyz_angle = abs(R_PointToAngle2(0, 0, dist, zdist))
		/*local xy_angle = abs(R_PointToAngle2(
				pmo.x + P_ReturnThrustX(pmo, pmo.angle, pmo.radius),
				pmo.y + P_ReturnThrustY(pmo, pmo.angle, pmo.radius),
				mo.x, mo.y
			) - pmo.angle)
			
			
		dist = P_AproxDistance(dist, zdist)
		if (dist > maxdist)
			return -- out of range
		end*/
		local h_dist = R_PointToDist2(pmo.x, pmo.y, mo.x, mo.y)
		local temp = R_PointToDist2(0, pmo.z, h_dist, mo.z)
		if temp >= closestdist
			return
		end
		
		if (xyz_angle > span)
			return -- Don't home outside of desired angle!
		end
		
		if not SightlineCheck(mo, pmo, FixedAngle(120 * FRACUNIT))
			return
		end
		
		if (twod
		and abs(pmo.y-mo.y) > pmo.radius)
			return -- not in your 2d plane
		end

		/*if ((closestmo and closestmo.valid) and (dist > closestdist))
			return
		end
		if (xy_angle > span)
			return -- behind back
		end*/

		if not (P_CheckSight(pmo, mo))
			return -- out of sight
		end

		closestmo = mo
		closestdist = temp
	end,player.mo,player.mo.x-maxdist,player.mo.x+maxdist,player.mo.y-maxdist,player.mo.y+maxdist)
	return closestmo
end


local function P_CheckAndCrumble(mo) -- let the object find a wall/floor to fuck up
    local rad = mo.radius + FRACUNIT
    for x = mo.x - rad, mo.x + rad, rad do
        for y = mo.y - rad, mo.y + rad, rad do
            local subsec = R_PointInSubsectorOrNil(x,y)
            if not subsec then continue end
            local sec = subsec.sector
            for rover in sec.ffloors() do
                if (rover.flags & (FF_EXISTS|FF_BUSTUP)) ~= (FF_EXISTS|FF_BUSTUP) then continue end
                if (mo.z > rover.topheight) or (mo.z + mo.height < rover.bottomheight) then continue end
                EV_CrumbleChain(sec, rover)
            end
        end
    end
end

addHook("MobjThinker", function(mo) -- the object in question:
	if mo and mo.valid
		P_CheckAndCrumble(mo)
	end
end, MT_KOU_FOFHITBOX)

addHook("MobjThinker", function(its)
	if its.valid
		P_SpawnGhostMobj(its)
	end
end, MT_KOU_MISSILEPARTICLE)

addHook("MobjThinker", function(starsm)
	if (starsm) and starsm.valid
	and starsm.target
		if starsm.fadetime == nil then starsm.fadetime = 35 end
		starsm.fadetime = $-1
		starsm.info.speed = (R_PointToDist2(starsm.x, starsm.y, starsm.target.x, starsm.target.y)/7)
		P_HomingAttack(starsm, starsm.target)
	
		if starsm.fadetime == 5 
			starsm.frame = $|TR_TRANS20
		elseif starsm.fadetime == 4 
			starsm.frame = $|TR_TRANS40
		elseif starsm.fadetime == 3 
			starsm.frame = $|TR_TRANS60
		elseif starsm.fadetime == 2 
			starsm.frame = $|TR_TRANS80
		elseif starsm.fadetime == 1 
			starsm.frame = $|TR_TRANS90
		end
	end
end, MT_JANA_ENERGYBALL)
--reflect projectiles
local function collZCheck(m,n) return m.z < n.z+n.height and m.z+m.height > n.z end
/*
addHook("MobjCollide", function(thing, tmthing)
	if thing and thing.valid and thing.player and thing.skin == "kou" and (thing.player.kou and thing.player.kou.parrytimer != 0) and thing.health and tmthing and tmthing.valid and collZCheck(thing, tmthing)
		if (tmthing.flags & MF_MISSILE) and tmthing.target != thing 
		and (tmthing.type != MT_LASER) and (tmthing.type != MT_SHOCKWAVE) -- the blacklist of what you CANT parry 
			--if inf.tracer
				--re.tracer = inf.tracer
			--end
			--re.angle = inf.angle+FixedAngle(180*FRACUNIT)
			P_InstaThrust(tmthing, R_PointToAngle2(tmthing.x, tmthing.y, tmthing.target.x, tmthing.target.y), FixedHypot(tmthing.momx, tmthing.momy))
			tmthing.angle = R_PointToAngle2(tmthing.x, tmthing.y, tmthing.target.x, tmthing.target.y)
			tmthing.target = thing
			tmthing.momz = -$
			
			--fx ripped right from a regular punch
			local kicker = P_SpawnMobjFromMobj(thing, 0,0,0, MT_KOU_MISSILEPARTICLE)
			kicker.destscale = $*2
			kicker.scalespeed = thing.scale/12
			kicker.color = thing.player.skincolor
			kicker.blendmode = AST_ADD
			kicker.fuse = 6
			P_InstaThrust(thing, thing.angle, -8*thing.scale)
			thing.player.powers[pw_flashing] = max($,8) --this is just because you keep getting hit so easily after the first punch
			S_StartSound(thing, sfx_kc3b)
			thing.player.kou.punchlagactive = 8 -- FREEZE
			if thing.player.kou.charge >= KOU_S_OVERCHARGE then
				spawnnormalexplosion(tmthing, tmthing.scale*2)
				thing.player.kou.punchlagactive = $*2
			end
			thing.player.glidetime = 0
			thing.state = S_PLAY_KOU_PUNCH2
			
			
			S_StartSound(thing, sfx_s3kae)
			local ns = FixedMul(thing.radius, thing.scale)
			local FINEANGLES = (ANG10+ANG1)*2
			for i = 0, 16
				local fa = i*FINEANGLES
				local mo = P_SpawnMobjFromMobj(thing, 0, 0, 0, MT_SUPERSPARK)					
				P_InstaThrust(mo, R_PointToAngle2(tmthing.x, tmthing.y, tmthing.target.x, tmthing.target.y) + ANGLE_90, FixedMul(cos(fa), ns)/2)
				mo.momz = FixedMul(sin(fa), ns)/2
				mo.color = thing.color
			end
			--inf.flags = $|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING
			--inf.flags2 = $|MF2_DONTDRAW
			return false
		end
	end
end, MT_PLAYER)
*/
--specifically due to metal sonic's fight lol. todo: uhh
--also don't attack during the lunge before we actually punch anything
addHook("PlayerCanDamage", function(p, mobj)
	if not (p.mo and p.mo.valid and p.kou) then return end
	if (p.kou.lungetime > 0 or p.kou.punchgrace > 0 or p.kou.beginmurdering > 0)
		if (mobj.type == MT_METALSONIC_BATTLE and collZCheck(p.mo, mobj)
		and p.mo.x + p.mo.radius > mobj.x - mobj.radius
		and p.mo.x - p.mo.radius < mobj.x + mobj.radius
		and p.mo.y + p.mo.radius > mobj.y - mobj.radius
		and p.mo.y - p.mo.radius < mobj.y + mobj.radius)
			p.kou.beginmurdering = 19
			A_ForceStop(p.mo)
			return true
		end
		
		local blob1, blob2 = pcall(function(mo) return mo.type == MT_MINDSCAPE_BLOB1 end, mobj)
		return blob1 and blob2
	elseif p.mo.state == S_PLAY_KOU_FLIP
		return true
	end
end)
--FUCK saloon doors
--also don't collide with spikes during strike and dont collide with badniks during overcharged dive please and thank you
addHook("MobjMoveCollide", function(target, item)
	if not (target and target.valid and target.player and target.skin == "kou") return end
	if not (item and item.valid and item.health and not (item.flags & MF_NOCLIP)) return end
	if (item.type == MT_SPIKE or item.type == MT_WALLSPIKE)
	and (target.player.kou.spikehatred > 0)
		if (collZCheck(target, item))
			P_KillMobj(item, target, target)
		end
		return false
	elseif ((item.type == MT_SALOONDOOR) or (item.type == MT_SALOONDOORCENTER))
	and (collZCheck(target, item) and target.state == S_PLAY_KOU_LUNGE)
		S_StartSound(target, sfx_mspogo)
		S_StartSound(target, sfx_wbreak)
		for i = 0,8
			local wood = P_SpawnMobjFromMobj(item, P_RandomRange(-25, 25)*FRACUNIT, P_RandomRange(-25, 25)*FRACUNIT, P_RandomRange(-15, 40)*FRACUNIT+15*FRACUNIT, MT_WOODDEBRIS)
			wood.momx = P_RandomRange(-5, 5)*item.scale
			wood.momy = P_RandomRange(-5, 5)*item.scale
			wood.momz = P_RandomRange(-3, 10)*item.scale
			local explosion = P_SpawnMobjFromMobj(item, 0,0,0, MT_BOSSEXPLODE)
			explosion.scale = $*2
		end
		
		if (item.type == MT_SALOONDOOR)
			item.flags = $|MF_NOCLIP
			P_KillMobj(item, target, target)
		elseif (item.type == MT_SALOONDOORCENTER)
			item.flags = $|MF_NOCLIP
		end
		return false
	end
end, MT_PLAYER)


/*addHook("MobjCollide", function(tmthing, thing) --parry thing
	if thing and thing.valid and thing.player and thing.skin == "kou" and (thing.player.kou and thing.player.kou.parrytimer != 0) and thing.health and tmthing and tmthing.valid and collZCheck(thing, tmthing)
		if (tmthing.flags & MF_MISSILE) and tmthing.target != thing
			--if inf.tracer
				--re.tracer = inf.tracer
			--end
			--re.angle = inf.angle+FixedAngle(180*FRACUNIT)
			P_InstaThrust(tmthing, R_PointToAngle2(tmthing.x, tmthing.y, tmthing.target.x, tmthing.target.y), FixedHypot(tmthing.momx, tmthing.momy))
			tmthing.angle = R_PointToAngle2(tmthing.x, tmthing.y, tmthing.target.x, tmthing.target.y)
			tmthing.target = thing
			tmthing.momz = -$
			--inf.flags = $|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING
			--inf.flags2 = $|MF2_DONTDRAW
			return false
		end
	end
end)*/

addHook("PreThinkFrame", function(aeeaaeaeea)
	for p in players.iterate
		if p.mo and p.mo.valid and p.mo.skin == "kou"
			if not p.kou
				KouVars(p)
			end
		elseif p.kou
			KouVars(p, true)
		end
	end
end)

addHook("PlayerSpawn", function(p)
	if p.mo and p.mo.valid and p.mo.health and p.mo.skin == "kou"
		if p.mo.state == S_PLAY_STND
			p.mo.frame = $ | FF_ANIMATE
		end
		KouVars(p)
	else
		KouVars(p, true)
	end
end)
addHook("SpinSpecial", function(p)
	if p.mo and p.mo.valid and p.mo.health and p.mo.skin == "kou"
	and not (p.pflags & PF_SPINDOWN)
	and not (p.kou.walltouch)
	and not (p.mo.state == S_PLAY_KOU_WINDUP or p.mo.state == S_PLAY_KOU_AIRWINDUP)
	and p.mo.state != S_PLAY_GLIDE_LANDING
	and not (p.kou.lungetime > 0)
	and not (p.kou.punchlagactive)
	and not (KouCantMove(p))
	and not p.exiting
		if p.mo.state == S_PLAY_KOU_MISSILE or p.kou.diveWaiter or p.mo.state == S_PLAY_KOU_CHARGE
			--[[DIVE CANCEL]]
			S_StopSoundByID(p.mo, sfx_kc5a)
			if (p.mo.eflags & MFE_UNDERWATER)
				P_SetObjectMomZ(p.mo, 6*FRACUNIT*117/200, false)
				P_InstaThrust(p.mo, p.mo.angle, max(FixedHypot(p.mo.momx, p.mo.momy), 16*p.mo.scale))
			else
				P_SetObjectMomZ(p.mo, 6*FRACUNIT, false)
				P_InstaThrust(p.mo, p.mo.angle, max(FixedHypot(p.mo.momx, p.mo.momy), 32*p.mo.scale))
			end
			S_StartSound(p.mo, sfx_s3k81)
			
			--p.panim = PA_DASH
			p.pflags = $ | PF_JUMPED | PF_NOJUMPDAMAGE &~ PF_SPINNING
			p.mo.state = S_PLAY_KOU_FLIP
			p.panim = PA_ROLL
			if p.cmd.forwardmove == 0 and p.cmd.sidemove == 0
				p.cmd.forwardmove = 1 --fuck this game
			end	
			p.kou.currentlyDiving = false
			p.kou.diveWaiter = 0
			p.kou.funnyfall = false
			p.kou.charge = 0

			local circ = P_SpawnMobjFromMobj(p.mo, 0, 0, 1, MT_KOUCIRCLE)
			circ.frame = $ | FF_PAPERSPRITE
			circ.angle = p.drawangle + ANGLE_90
			circ.scale = p.mo.scale
			circ.destscale = p.mo.scale * 10
			circ.scalespeed = p.mo.scale
		elseif not (p.glidetime)
			--[[PUNCH]] --Ctrl+F for Wolf-Strike NOW
			p.pflags = $ &~ PF_SPINNING
			if p.pflags & PF_JUMPED then p.pflags = $ | PF_NOJUMPDAMAGE end
			p.kou.forcetimer = 0
			p.kou.parrytimer = KOU_S_PARRYTIME
			S_StartSound(p.mo, sfx_s227) --old: sfx_dive1
			local spark = P_SpawnMobjFromMobj(p.mo, 0,0,p.height/2, MT_SUPERSPARK)
			p.kou.charge = 1
			p.kou.strikecharge = true
			if (p.powers[pw_shield] & SH_NOSTACK) == SH_THUNDERCOIN
			or (p.powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT then
				p.kou.charge = KOU_S_MINCHARGE*3/4
				P_NukeEnemies(p.mo, p.mo, 0) --Purely for visual effect
				S_StartSound(p.mo, sfx_s3k45)
			end
			if P_IsObjectOnGround(p.mo) then
				p.mo.state = S_PLAY_KOU_WINDUP
			else
				p.mo.state = S_PLAY_KOU_AIRWINDUP
				p.panim = PA_DASH
				p.glidetime = 1
				
				local zthrust = (p.powers[pw_shield] & SH_NOSTACK) == SH_WHIRLWIND and 12 or 6
				if (p.mo.eflags & MFE_UNDERWATER) then zthrust = $*117/200 end
				if (p.powers[pw_shield] & SH_NOSTACK) == SH_WHIRLWIND then S_StartSound(p.mo, sfx_wdjump) end
				P_SetObjectMomZ(p.mo, FixedMul(zthrust*FRACUNIT,p.jumpfactor))
			end
			p.kou.wallnuked = 0
		end
		/*local cir1 = P_SpawnMobjFromMobj(p.mo, 0, 0, 0, MT_KOU_CIRC)
		cir1.scale = p.mo.scale*2
		cir1.destscale = 0
		cir1.scalespeed = p.mo.scale/8
		cir1.fuse = 25
		cir1.trg = p.mo
		cir1.angle = p.mo.angle+FixedAngle(90*FRACUNIT)
		cir1.blendmode = AST_ADD
		local cir2 = P_SpawnMobjFromMobj(p.mo, 0, 0, 0, MT_KOU_CIRC)
		cir2.scale = p.mo.scale*2
		cir2.destscale = 0
		cir2.state = S_KOU_CIRC2
		cir2.scalespeed = p.mo.scale/8
		cir2.trg = p.mo
		cir2.angle = p.mo.angle+FixedAngle(90*FRACUNIT)
		cir2.blendmode = AST_ADD
		if P_RandomRange(1, 2) == 1
			cir2.rang = -1
		else
			cir2.rang = 1
		end
		cir2.fuse = 25*/
	end
end)
--Wolf Missile
addHook("AbilitySpecial", function(p)
	if p and p.mo and p.mo.valid and p.mo.health and p.mo.skin == "kou"
	and not (p.pflags & PF_THOKKED) and not (p.kou.walltouch) and not p.exiting
		--print("lol. lmao.")
		p.kou.lungetime = 0
		p.kou.charge = 0
		if (p.powers[pw_shield] & SH_NOSTACK) == SH_THUNDERCOIN
		or (p.powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT then
			p.kou.charge = KOU_M_MINCHARGE*3/4
			P_NukeEnemies(p.mo, p.mo, 0) --Purely for visual effect
			S_StartSound(p.mo, sfx_s3k45)
		end
		p.pflags = ($|PF_THOKKED|PF_JUMPED) &~ (PF_SPINNING|PF_NOJUMPDAMAGE)
		p.mo.state = S_PLAY_KOU_CHARGE
		p.panim = PA_ROLL
		local zthrust = (p.powers[pw_shield] & SH_NOSTACK) == SH_WHIRLWIND and 12 or 6
		if (p.mo.eflags & MFE_UNDERWATER) then zthrust = $*117/200 end
		if (p.powers[pw_shield] & SH_NOSTACK) == SH_WHIRLWIND then S_StartSound(p.mo, sfx_wdjump) end
		P_SetObjectMomZ(p.mo, FixedMul(zthrust*FRACUNIT,p.jumpfactor))
		p.kou.forcetimer = 0
		p.kou.wallnuked = 0
	end
end)
--Prevent jumping out of landing attacks
addHook("JumpSpecial", function(p)
	if p and p.mo and p.mo.valid and p.mo.health and p.mo.skin == "kou"
	and p.kou and (p.kou.currentlyDiving or p.kou.lungetime > 0)
	and P_IsObjectOnGround(p.mo)
		return true
	end
end)

local function istree(m)
	return ((m.type >= MT_GFZTREE and m.type <= MT_SPRINGTREE) or m.type == MT_PINETREE)
end

addHook("MobjMoveCollide", function(thing, tmthing)
	if thing and thing.valid and thing.health and thing.player and thing.player.kou and thing.skin == "kou" and tmthing and tmthing.valid and tmthing.health 
		local p = thing.player
		local punch = (p.kou.lungetime > 0 or (not tmthing.player
		and (p.kou.punchgrace > 0 or p.mo.state == S_PLAY_GLIDE_LANDING)))
		local tree = istree(tmthing)
		
		if ((tmthing.flags & MF_SHOOTABLE) or tree
		or tmthing.flags & MF_PUSHABLE or tmthing.type == MT_ROLLOUTROCK)
		and collZCheck(thing, tmthing) and punch and not (tmthing.flags2 & MF2_FRET)
			if not p.kou.hittarget
				if tmthing.flags & MF_PUSHABLE or tmthing.type == MT_ROLLOUTROCK then
					P_InstaThrust(tmthing, thing.angle, p.kou.charge*2*thing.scale)
				end
				p.kou.hittarget = tmthing
			end
			if tree then return false end
		end
		if tmthing.flags & MF_SPRING and collZCheck(thing, tmthing)
		and (punch or p.kou.currentlyDiving)
			if tmthing.info.mass == 0
				thing.player.kou.spin = 2
			end
		end
	end
end, MT_PLAYER)

addHook("ThinkFrame", function(a) --this game is annoying so heres a thinkframe
	for p in players.iterate
		if p and p.mo and p.mo.valid and p.mo.skin == "kou"
			--roll after lunging into a spring
			if (p.powers[pw_carry])
				p.kou.spin = 0
			end
			if p.kou.spin
				p.pflags = $|PF_SPINNING
				if p.mo.state != S_PLAY_ROLL
					p.mo.state = S_PLAY_ROLL
				end
				if P_IsObjectOnGround(p.mo) or (p.mo.eflags & MFE_JUSTHITFLOOR)
					p.kou.spin = $-1
				end
			end
		end
	end
end)
addHook("ShouldDamage", function(trg, inf, src, damage, damagetype)
	if trg == src and not (damagetype & DMG_CANHURTSELF) then return end
	if inf and inf.valid and inf.FUCKINGSTOP then return false end
	if inf and inf.valid and inf.type == MT_KOU_MURDER then return end
	
	if trg and trg.valid and trg.health and trg.skin == "kou" and trg.player and trg.player.kou then
		if inf and inf.valid and src and trg.player.kou.parrytimer then --kou parried a bozo
			KouVars(trg.player)
			if not(trg.state == S_PLAY_KOU_DROP) then
				trg.state = S_PLAY_KOU_DROP
			end
			--direct hit? counter attack.
			if inf.player or (inf.flags & MF_ENEMY) and not(inf.flags & MF_BOSS) then --must be a badnik
				domurder(inf, trg, KOU_CHARGED_DAMAGE)
				P_DamageMobj(inf, trg, trg, KOU_CHARGED_DAMAGE)
				if inf and inf.valid then P_InstaThrust(inf, trg.angle, 45*trg.scale) end
			elseif not(inf.flags & MF_BOSS) then --must be a hazard
				domurder(inf, trg, -1)
				if inf and inf.valid then P_KillMobj(inf, trg, trg, DMG_NUKE) end
			else --must be a boss
				P_DamageMobj(inf, trg, trg, KOU_CHARGED_DAMAGE)
				if not (trg.type == MT_FANG) -- dude you're on a train getting flung by him sucks
				and not (trg.type == MT_METALSONIC_BATTLE) -- this arena is way too small...
					trg.player.bosshitter = true
				end
			end
			--but here's the kicker
			local circ = P_SpawnMobjFromMobj(trg, 0, 0, 1, MT_KOUCIRCLE)
			circ.tics = 15
			circ.scale = trg.scale + trg.scale
			circ.destscale = trg.scale * 30
			circ.scalespeed = trg.scale * 3
			IHATESPIKES(trg, 320*trg.scale, "TITAN!!")
			P_Telekinesis(trg.player, 45*trg.scale, 640*trg.scale)
			local kicker = P_SpawnMobjFromMobj(trg, 0,0,0, MT_KOU_MISSILEPARTICLE)
			kicker.destscale = $*8
			kicker.scalespeed = trg.scale/2
			kicker.color = trg.player.skincolor
			kicker.blendmode = AST_ADD
			kicker.fuse = 6
			local sounds = {sfx_kodrp1, sfx_kodrp2}
			S_StartSound(trg, sounds[P_RandomRange(1, #sounds)])
			--todo: check if all quake functions are global/mapwide (they probably are) (if they are we use consoleplayer hax)
			P_StartQuake(12*trg.scale, 12)
			for p in players.iterate --nearby players see the flash effect too
				if not(p.mo and p.mo.valid)
					continue
				end
				local radius = 128*p.mo.scale
				if abs(p.mo.x - trg.x) > radius or abs(p.mo.y - trg.y) > radius or (abs(p.mo.z - trg.z) > radius)
					continue 
				end
				if FixedHypot(FixedHypot(p.mo.x - trg.x, p.mo.y - trg.y), p.mo.z - trg.z) > radius
					continue
				end
				P_FlashPal(p, PAL_INVERT, 4)
			end
			if not trg.player.bosshitter
			or trg.player.kou.enemybounceback
				trg.player.kou.punchlagactive = 18
			else
				P_InstaThrust(trg, trg.angle, -45*trg.scale)
			end
			trg.player.recX = trg.momx/2
			trg.player.recY = trg.momy/2
			trg.player.recZ = trg.momz/2
		end
		if trg.state == S_PLAY_KOU_DROP then
			return false
		end
		local aiming = false
		if inf and inf.valid then
			local angle_diff = R_PointToAngle2(trg.x, trg.y, inf.x, inf.y)
			aiming = abs(angle_diff) - trg.angle <= ANGLE_90
			--if not aiming then print("stupid idiot. u needed to hit "..abs(angle_diff)/ANG1.." but u r aiming at "..trg.angle/ANG1) end
		end
		if (inf and inf.health and not(inf.flags & MF_PAIN))-- and aiming)
		and ((trg.player.kou.beginmurdering > 0 or trg.player.kou.lungetime > 0 or trg.player.kou.parrytimer > 0)
		or (G_CoopGametype() and (trg.player.kou.strikecharge or trg.state == S_PLAY_GLIDE_LANDING) and aiming))
		then
			if G_CoopGametype() and (trg.player.kou.strikecharge or trg.state == S_PLAY_GLIDE_LANDING) then
				if trg.player.kou.strikecharge or trg.player.kou.punchgrace > 0 then
					trg.player.kou.lungetime = 2
				else
					local lmao = domurder(inf, trg, 1) or P_DamageMobj(inf, trg, trg, 1)
				end
			end
			return false --kou landed the punch, don't damage him!
		end
	end
end, MT_PLAYER)

local kouchased = {false, false}
addHook("PreThinkFrame", function(e)
	for p in players.iterate
		if p.mo and p.mo.valid and p.mo.health and p.mo.skin == "kou" and p.kou
			p.kou.forwardmove = p.cmd.forwardmove
			p.kou.sidemove = p.cmd.sidemove
			if p.kou.walltouch > 0
			or (p.mo.state == S_PLAY_KOU_RARETAUNT and P_IsObjectOnGround(p.mo))
			or p.mo.state == S_PLAY_KOU_GROUNDTAUNT or p.mo.state == S_PLAY_KOU_WALLROLL
				p.cmd.forwardmove = 0
				p.cmd.sidemove = 0
				p.pflags = $ &~ PF_SHIELDABILITY
			elseif p.mo.state == S_PLAY_KOU_WINDUP or p.mo.state == S_PLAY_KOU_AIRWINDUP
				if p.kou.charge >= KOU_S_PEAKCHARGE then
					p.cmd.forwardmove = $/10
					p.cmd.sidemove = $/10
					if P_IsObjectOnGround(p.mo) then
						p.mo.momx = 0
						p.mo.momy = 0
					end
				else
					p.cmd.forwardmove = $/3
					p.cmd.sidemove = $/3
				end
			end
		end
		
		--Temporarily undo chasecam when climbing as Kou
		if p != displayplayer and p != secondarydisplayplayer then continue end
		local i = (p == secondarydisplayplayer) and 1 or 0
		local cam = (p == secondarydisplayplayer) and camera2 or camera
		if not cam then continue end
		
		if p.kou and p.kou.walltouch > 0
			if not cam.chase
				cam.chase = true
				kouchased[i] = not $
			end
		elseif kouchased[i]
			cam.chase = false
			kouchased[i] = false
		end
	end
end)

local SkyTextureCheck = function(mobj, line) --doesn't even work waah :SOB:
	return (line.special == 41)
	or (line.frontsector and line.frontsector.ceilingpic == "F_SKY1"
	and mobj.z > line.frontsector.ceilingheight)
	or (line.backsector and line.backsector.ceilingpic == "F_SKY1"
	and mobj.z > line.backsector.ceilingheight)
end

addHook("MobjMoveBlocked", function(mobj) --Detect a wall when lunging
	local p = mobj.player
    if not (p and mobj.skin == "kou" /*and p.wallability*/ and not P_IsObjectOnGround(mobj)) return end
	if mobj.state != S_PLAY_KOU_LUNGE and p.kou.walltouch <= 0 return end
	if p.powers[pw_carry] == CR_NIGHTSMODE return end

	local line = lines[p.lastlinehit]
	local side = sides[p.lastsidehit]
	local invalidline = not (line and line.valid)
	if invalidline or SkyTextureCheck(mobj, line) or (line.flags & ML_NOCLIMB) then return end

	p.lastlinehit = -1
	p.lastsidehit = -1
    P_SlideMove(mobj)
    p.powers[pw_pushing] = 3
	p.pflags = $ & ~ PF_THOKKED
	
	if p.lastlinehit != p.kou.touchedline
		p.kou.touchedline = p.lastlinehit
		p.kou.touchedside = p.lastsidehit
		p.kou.sidexscroll = nil
		p.kou.sideyscroll = nil
		p.koufofs = nil
		
		local newline = lines[p.lastlinehit]
		local newside = sides[p.lastsidehit]
		p.kousector = {}
		if newline and newline.valid
			if not (newside and newside.valid)
				p.kou.touchedside = newline.sidenum[P_PointOnLineSide(p.mo.x, p.mo.y, newline)]
				newside = sides[p.kou.touchedside]
			end
			if newside and newside.valid
				if newline.frontside != newside and newline.frontside and newline.frontside.valid
					p.kousector[1] = newline.frontside.sector
				elseif newline.backside != newside and newline.backside and newline.backside.valid
					p.kousector[1] = newline.backside.sector
				end
			end
		end
	end
	p.kou.walltouch = 2
	
	if p.lastlinehit == -1
		p.lastlinehit = #line
		p.lastsidehit = #side
	end
    return true
end, MT_PLAYER)

local function collZCheck(m,n) return m.z < n.z+n.height*5 and m.z+m.height*5 > n.z end
local TIMETRANS = function(time, speed, prefix, suffix, debug, minimum, cap)
	prefix = prefix or "V_"
	suffix = suffix or "TRANS"
    speed = speed or 1

    local level = (time / speed / 10) * 10
    level = max(10, min(100, level))
    
	if minimum then level = max($, minimum / 10 * 10) end
	if cap then level = min($, cap / 10 * 10) end

    if level == 100 then
		if debug then print(level) end
    else
		if debug then print(level) end
        return _G[prefix .. (100 - level) .. suffix]
    end
end
local dust = function(player, playsound)
	if playsound then S_StartSound(player.mo,sfx_s3k7e,255/3,player) end
	local r = player.mo.radius/player.mo.scale
	local dust = P_SpawnMobjFromMobj(player.mo,
		P_RandomRange(-r,r)*FRACUNIT,
		P_RandomRange(-r,r)*FRACUNIT,
		0,
		MT_DUST
	)
	if dust and dust.valid then dust.scale = player.mo.scale end
end
local firering = function(mo, power)
	power = $ and $*mo.scale or 4*mo.scale
	local m = 20
	local success = false
	for n = 0, m do
		local fire = P_SPMAngle(mo, MT_SPINFIRE, 0, 0)
		if fire and fire.valid then
			success = true
			if power >= 14*mo.scale then fire.state = mobjinfo[MT_FLAME].spawnstate end
			fire.flags = $ & ~MF_NOGRAVITY
			P_InstaThrust(fire, (360/m)*n*ANG1, power)
			fire.momz = 4 * mo.scale * P_MobjFlip(mo)
			fire.fuse = CBW_Battle and TICRATE or 4 * TICRATE
			fire.target = mo
		end
	end
	if success then
		S_StartSound(mo, sfx_s22e)
		S_StartSoundAtVolume(mo, sfx_s3k82, 180)
	end
	return success
end

addHook("MobjThinker", function(mo)
	local transrights = TIMETRANS(mo.tics*10,1,"FF_TRANS","", false) or 0
	mo.frame = $ | transrights
end, MT_KOUCIRCLE)

addHook("PlayerThink", function(p)
	if p.mo and p.mo.valid and p.mo.health and p.mo.skin == "kou"
		local mobjflip = P_MobjFlip(p.mo)
		if p.kou.dofirering and firering(p.mo, p.kou.dofirering) then
			p.kou.dofirering = 0 --stupid workaround so it always works regardless of srb2's bs, whatever
		end
		--[[
		local nspd
		local sprintspeed = 20*p.mo.scale
		]]
		
		if not (p.cmd.buttons & BT_SPIN) or p.exiting
			p.koutap = true
			p.kou.spintap = false
		elseif p.koutap then
			p.kou.spintap = true
			p.koutap = false
		else
			p.kou.spintap = false
		end
		
		if not (p.cmd.buttons & BT_JUMP) or p.exiting
			p.koujtap = true
			p.kou.jumptap = false
		elseif p.koujtap then
			p.kou.jumptap = true
			p.koujtap = false
		else
			p.kou.jumptap = false
		end
		
		--decrease spike hatred counter
		if p.kou.spikehatred > 0 then p.kou.spikehatred = $ - 1 end
		
		if p.powers[pw_carry] == CR_NIGHTSMODE
			if p.mo.state == S_PLAY_NIGHTS_DRILL then Kou_SpawnSpeedLine(p) end
			return 
		end -- nuh uh we aint using these abilities in nights get outta here
		
		--wall shit
		if p.kou.walltouch > 0 and not KouCantMove(p)
		and not (p.mo.eflags & MFE_SPRUNG) and not p.exiting
			p.kou.waswalltouching = true
			p.kou.walltouch = $-1
			local line = lines[p.kou.touchedline]
			local side = sides[p.kou.touchedside]
			if line and line.valid and side and side.valid
				if (line.frontsector)
					if (line.frontsector.ceilingheight == line.frontsector.floorheight) and (p.mo.z > line.frontsector.ceilingheight)
						return
					end
				end
				if (line.backsector)
					if (line.backsector.ceilingheight == line.backsector.floorheight) and (p.mo.z > line.backsector.ceilingheight)
						return
					end
				end
				p.kou.currentlyDiving = false
				p.kou.diveWaiter = 0
				p.kou.funnyfall = false
				if p.glidetime
					p.pflags = $ | PF_JUMPED | PF_NOJUMPDAMAGE &~ PF_SPINNING
					S_StartSound(p.mo, sfx_mspogo)
					p.glidetime = 0
				end
				local fullcharged = p.kou.charge >= KOU_S_PEAKCHARGE
				local overcharged = p.kou.charge >= KOU_S_OVERCHARGE
				p.kou.charge = 0
				p.kou.chargetimer = 0
				p.kou.parrytimer = 0
				--mobj.player.jana.throwangle = R_PointToAngle2(p.mo.x,p.mo.y,P_ClosestPointOnLine(p.mo.x,p.mo.y,line))+FixedAngle(180*FRACUNIT)
				if ((p.kou.sidemove != 0) or (p.kou.forwardmove != 0)) and not(p.kou.walllungetimer or overcharged)
					if p.mo.state != S_PLAY_CLIMB
						p.mo.state = S_PLAY_CLIMB
					end
					if not ((p.mo.frame & FF_FRAMEMASK) & 1)
					and p.mo.tics == 1
						S_StartSoundAtVolume(p.mo, sfx_s3k6d, 128)
						
						local rock = P_SpawnMobjFromMobj(p.mo, P_RandomRange(-25, 25)*FRACUNIT, P_RandomRange(-25, 25)*FRACUNIT, P_RandomRange(-15, 40)*FRACUNIT+30*FRACUNIT, MT_LAVAFALLROCK)
						rock.flags = $&~MF_PAIN
						rock.state = S_ROCKCRUMBLEA+P_RandomRange(1, 4)
						if rock.state == S_ROCKCRUMBLED then
							if G_GametypeHasTeams() then
								rock.colorized = true ; rock.color = p.mo.color
							else
								rock.state = S_ROCKCRUMBLEB
							end
						end
						rock.scale = p.mo.scale/2+p.mo.scale/5
						rock.fuse = 45
						P_Thrust(rock, R_PointToAngle2(p.mo.x, p.mo.y, rock.x, rock.y), P_RandomRange(3, 10)*p.mo.scale/2)
						P_SetObjectMomZ(rock, P_RandomRange(-3, 5)*FRACUNIT, false)
					end
				elseif not(p.kou.walllungetimer or overcharged)
					if p.mo.state != S_PLAY_CLING
						p.mo.state = S_PLAY_CLING
					end
				elseif not(overcharged)
					if p.mo.state != S_PLAY_KOU_WALLROLL
						p.mo.state = S_PLAY_KOU_WALLROLL
						p.panim = PA_ROLL
					end
					--p.jana.diving = false
					--if mobj.player.jana.walltouch < 3
						--S_StartSound(p.mo, sfx_s3k4c) 
					--end
				end

				--mobj.player.jana.walltouch = 5
				local wangle = R_PointToAngle2(line.v1.x, line.v1.y, line.v2.x, line.v2.y)
				if line.frontside == side
					wangle = $-ANGLE_180
				end
				p.drawangle = wangle-ANGLE_90
				
				local diff = p.drawangle - p.cmd.angleturn<<16
				if abs(diff) > ANGLE_90 or diff == ANGLE_180
					p.mo.angle = wangle
					if diff > 0
						p.mo.angle = $-ANGLE_180
					end
				end
				
				if p.kou.wallnuked == 0 and fullcharged and not(P_IsObjectOnGround(p.mo) or (p.mo.eflags & MFE_JUSTHITFLOOR))
					local ns = FixedMul(p.mo.radius, p.mo.scale)
					local FINEANGLES = (ANG10+ANG1)*2
					for i = 0, 16
						local fa = i*FINEANGLES
						local mo = P_SpawnMobjFromMobj(p.mo, 0, 0, 0, MT_SUPERSPARK)					
						P_InstaThrust(mo, p.drawangle + ANGLE_90, FixedMul(cos(fa), ns))
						mo.momz = FixedMul(sin(fa), ns)
						
						local mo2 = P_SpawnMobjFromMobj(p.mo, 0, 0, 0, MT_SUPERSPARK)					
						P_InstaThrust(mo2, p.drawangle + ANGLE_90, (FixedMul(cos(fa), ns))/2)
						mo2.momz = (FixedMul(sin(fa), ns))/2
					end
					
					S_StartSound(p.mo, sfx_s3k4a)
					S_StartSound(p.mo, sfx_s3k5f)
					P_StartQuake(8*p.mo.scale, 8)
					local range = overcharged and 300*p.mo.scale or 245*p.mo.scale
					yoshiExplodeFavEdit(p.mo, range, false, true, overcharged)
					if overcharged then
						if (p.powers[pw_shield] & SH_NOSTACK == SH_ARMAGEDDON) then
							p.powers[pw_shield] = SH_NONE
							P_FlashPal(p, PAL_NUKE, 10)
							S_StartSound(p.mo, sfx_bkpoof)
							P_NukeEnemies(p.mo, p.mo, 1500*p.mo.scale)
						end
						spawnnormalexplosion(p.mo, p.mo.scale*2)
						p.mo.state = S_PLAY_SPRING
						p.kou.diveWaiter = 0
						p.kou.walltouch = 0
						--P_BounceMove(p.mo)
						--p.mo.momx = $*-1
						--p.mo.momy = $*-1
						if (p.mo.eflags & MFE_UNDERWATER)
							P_InstaThrust(p.mo, p.mo.angle, -20*p.mo.scale*117/200)
							P_SetObjectMomZ(p.mo, FRACUNIT * 12)
						else
							P_InstaThrust(p.mo, p.mo.angle, -20*p.mo.scale)
							P_SetObjectMomZ(p.mo, FRACUNIT * 20)
						end
					end
					p.kou.wallnuked = 1
				end
				
				local speed = 10*p.mo.scale
				if not(p.kou.walllungetimer or overcharged)
					p.mo.rollangle = 0
					A_ForceStop(p.mo, 1)
					if (p.kou.sidemove)
						P_Thrust(p.mo, wangle, -speed*p.kou.sidemove/50)
						p.kou.wallmoved = true
					end
					P_Thrust(p.mo, p.drawangle, speed)
					
					if (p.kou.forwardmove)
						P_SetObjectMomZ(p.mo, FixedDiv(speed,p.mo.scale)*p.kou.forwardmove/100, false)
						p.kou.wallmoved = true
					else
						--p.kou.up = 0
						if not p.kou.wallmoved
							p.mo.momz = max($*mobjflip, 0)
						else
							p.mo.momz = 0
						end
					end
				--[[
				else
					p.kou.walllungetimer = max($-1, 0)
					if p.kou.up == -1
						if p.kou.side == 1
							p.mo.rollangle = FixedAngle(-45*FRACUNIT)+FixedAngle(180*FRACUNIT)
						elseif p.kou.side == -1
							p.mo.rollangle = FixedAngle(45*FRACUNIT)+FixedAngle(180*FRACUNIT)
						else
							p.mo.rollangle = FixedAngle(180*FRACUNIT)
						end						
					elseif p.kou.up == 1
						if p.kou.side == 1
							p.mo.rollangle = FixedAngle(45*FRACUNIT)
						elseif p.kou.side == -1
							p.mo.rollangle = FixedAngle(-45*FRACUNIT)
						else
							p.mo.rollangle = 0 
						end
					else
						if p.kou.side == 1
							p.mo.rollangle = FixedAngle(90*FRACUNIT)
						elseif p.kou.side == -1
							p.mo.rollangle = FixedAngle(-90*FRACUNIT)
						else
							p.mo.rollangle = 0 
						end
					end
					P_Thrust(p.mo, R_PointToAngle2(p.mo.x,p.mo.y,P_ClosestPointOnLine(p.mo.x,p.mo.y,line)), 5*p.mo.scale)
					p.mo.momz = $-P_GetMobjGravity(p.mo)
					if p.kou.walllungetimer <= 8
						p.mo.momz = $-$/5
						p.mo.momx = $-$/5
						p.mo.momy = $-$/5
					end
				]]
				end
				
				--move with conveyors
				local sidexoffset = side.textureoffset
				local sideyoffset = side.rowoffset
				
				if p.kou.sidexscroll != nil
				and p.kou.sideyscroll != nil
				and (sidexoffset != p.kou.sidexscroll
				or sideyoffset != p.kou.sideyscroll)
					--horizontal movement
					P_TryMove(p.mo,
					p.mo.x+FixedMul(sidexoffset-p.kou.sidexscroll, cos(wangle)),
					p.mo.y+FixedMul(sidexoffset-p.kou.sidexscroll, sin(wangle)),
					true)
					
					--verical movement
					p.mo.z = $ + sideyoffset-p.kou.sideyscroll
				end
				p.kou.sidexscroll = sidexoffset
				p.kou.sideyscroll = sideyoffset
				
				--move with sectors
				local sector = p.kousector and p.kousector[1]
				if sector and sector.valid
					local floor = p.kousector[2]
					local ceiling = p.kousector[3]
					local newfloor = sector.floorheight
					local newceiling = sector.ceilingheight
					
					--move with floor and ceiling
					if floor != newfloor or ceiling != newceiling
						--only move with the part of the sector we're touching
						if floor != nil
						and p.mo.z <= P_GetZAt(sector.f_slope, p.mo.x, p.mo.y, newfloor)
							p.mo.z = $ + newfloor - floor
						elseif ceiling != nil
						and p.mo.z + p.mo.height >= P_GetZAt(sector.c_slope, p.mo.x, p.mo.y, newceiling)
							p.mo.z = $ + newceiling - ceiling
						end
						p.kousector[2] = newfloor
						p.kousector[3] = newceiling
					end
				end
				
				--move with fofs
				if p.koufofs
					for i, fof in ipairs(p.koufofs)
						local top = p.koufoftops[i]
						local bottom = p.koufofbottoms[i]
						if not (fof and fof.valid)
						or top == nil or bottom == nil
							continue
						end
						
						--move with top and bottom
						local newtop = fof.topheight
						local newbottom = fof.bottomheight
						if newtop != top or newbottom != bottom
							--only move if we're inside the hitbox
							if p.mo.z <= P_GetZAt(fof.t_slope, p.mo.x, p.mo.y, newtop)
							and p.mo.z + p.mo.height >= P_GetZAt(fof.b_slope, p.mo.x, p.mo.y, newbottom)
							and fof.flags & FOF_EXISTS and fof.flags & FOF_BLOCKPLAYER
								--move wtih the greater difference
								if abs(newtop - top) > abs(newbottom - bottom)
									p.mo.z = $ + newtop - top
								else
									p.mo.z = $ + newbottom - bottom
								end
							end
							
							--update top and bottom info
							p.koufoftops[i] = newtop
							p.koufofbottoms[i] = newbottom
						end
					end
					
				--get the fofs
				else
					p.koufofs = {}
					p.koufoftops = {}
					p.koufofbottoms = {}
					for i = 0, 1
						side = line.sidenum[i]
						side = ($ != nil) and sides[$]
						if not (side and side.valid) then continue end
						local sector = side.sector
						if not (sector and sector.valid) then continue end
						for fof in sector.ffloors()
							table.insert(p.koufofs, fof)
							table.insert(p.koufoftops, fof.topheight)
							table.insert(p.koufofbottoms, fof.bottomheight)
						end
					end
				end
			end
			if p.kou.jumptap or p.kou.spintap
				p.kou.walltouch = 0
				if (p.mo.eflags & MFE_UNDERWATER)
					P_SetObjectMomZ(p.mo, 6*FRACUNIT*117/200, false)
					P_InstaThrust(p.mo, p.drawangle, -4*p.mo.scale)
				else
					P_SetObjectMomZ(p.mo, 6*FRACUNIT, false)
					P_InstaThrust(p.mo, p.drawangle, -8*p.mo.scale)
				end
				if p.kou.jumptap
					p.mo.state = S_PLAY_JUMP
					S_StartSound(p.mo, sfx_jump)
					p.drawangle = $ + ANGLE_180
					p.mo.angle = p.drawangle
				else
					p.mo.state = S_PLAY_FALL
				end
			end
		elseif p.kou.walltouch > 0 or p.kou.waswalltouching
			p.kou.sidexscroll = nil
			p.kou.sideyscroll = nil
			p.kou.wallmoved = false
			p.kou.walltouch = 0
			p.kou.waswalltouching = false
			if p.mo.state == S_PLAY_CLING or p.mo.state == S_PLAY_CLIMB or p.mo.state == S_PLAY_KOU_WALLROLL
				p.mo.state = P_IsObjectOnGround(p.mo) and S_PLAY_STND or S_PLAY_FALL
				--p.kou.walllungetimer = 0 
				p.kou.wallnuked = 0
			end
			if P_IsObjectOnGround(p.mo) then p.pflags = $ &~ PF_JUMPED end
		end
		if (p.kou.up or p.kou.side) and p.mo.state != S_PLAY_KOU_WALLROLL
			p.mo.rollangle = 0
		end
		--gravity shennanigans
		if p.mo.momz*mobjflip < -p.mo.scale and not P_IsObjectOnGround(p.mo) and not (p.mo.eflags & MFE_SPRUNG) --and not (p.kou.lungetime)
			p.mo.momz = $+P_GetMobjGravity(p.mo)*(4/5)
		end
		
		p.mo.koucloseenemies = {}
		if p.mo.lockedon and p.mo.lockedon.valid
			--p.mo.angle = R_PointToAngle2(p.mo.x, p.mo.y, p.mo.lockedon.x, p.mo.lockedon.y)
		end
		--A
		if (p.mo.eflags & MFE_SPRUNG) then
			if p.kou.diveWaiter then
				p.kou.diveWaiter = 0
				p.pflags = $1 & ~PF_STASIS|PF_JUMPSTASIS
			end
			if p.kou.walltouch then
				p.kou.walltouch = 0
				p.mo.state = S_PLAY_SPRING
			end
		end
		--Boost springs
		if (p.mo.eflags & MFE_SPRUNG) and (p.kou.lungetime > 0 or p.kou.punchgrace > 0)
			p.mo.momz = $*5/4
			p.mo.momx = $*5/4
			p.mo.momy = $*5/4
			S_StartSound(p.mo, sfx_sprong)
			p.mo.state = S_PLAY_SPRING --why on earth is this necessary??
		end
		
		--a grace period for when you land god forbid you missed your punch by a millimeter
		if KouCantMove(p) or (p.kou.punchgrace == -1 and p.kou.lungetime <= 0)
		or p.kou.beginmurdering > 0
			p.kou.punchgrace = 0
		elseif p.kou.punchgrace == -1
		and (p.mo.eflags & MFE_JUSTHITFLOOR or P_IsObjectOnGround(p.mo))
			p.kou.punchgrace = 10
		elseif p.kou.punchgrace != 0 and p.kou.punchgrace > 0
			p.kou.punchgrace = $-1
		end
		
		--parry time!
		if p.kou.parrytimer != 0 
			p.kou.parrytimer = $-1 
			p.mo.colorized = true
		end
		if p.mo.colorized and p.kou.parrytimer == 0 then p.mo.colorized = false end
		if p.mo.colorized and p.kou.parrytimer != 0 and not(p.kou.strikecharge or (p.mo.state == S_PLAY_KOU_WINDUP or p.mo.state == S_PLAY_KOU_AIRWINDUP))
			p.mo.colorized = false
			p.kou.parrytimer = 0
		end
		--flash indicator that time's up
		if p.kou.parrytimer == 1 
		or ((p.mo.eflags & MFE_JUSTHITFLOOR) or KouCantMove(p)) and p.kou.parrytimer > 1
			local alttrail = P_SpawnGhostMobj(p.mo)
			alttrail.color = p.skincolor
			alttrail.momx = p.mo.momx
			alttrail.momy = p.mo.momy
			alttrail.momz = p.mo.momz
			alttrail.colorized = true
			alttrail.blendmode = AST_ADD
			alttrail.destscale = $*2 
			alttrail.scalespeed = p.mo.scale/15
			P_SpawnGhostMobj(alttrail)
		end
		
		if (p.mo.eflags & MFE_JUSTHITFLOOR) or KouCantMove(p)
			p.kou.parrytimer = 0 
			p.mo.colorized = false
		end
		
		--spin fx
		--[[
		if (p.kou.supercharged)
		and leveltime % 5 == 0 and not p.glidetime
			local spooks = P_SpawnGhostMobj(p.mo)
			spooks.colorized = true
			spooks.color = SKINCOLOR_FLAME
			if p.kou.supercharged 
				spooks.color = SKINCOLOR_YELLOW
				spooks.fuse = 15
				spooks.spriteyoffset = $ -2*p.mo.scale
			end
			spooks.blendmode = AST_ADD
			spooks.destscale = $*2 
			spooks.scalespeed = p.mo.scale/17
		end
		]]
		
		--Lunge
		if p.kou.lungetime > 0 and not KouCantMove(p)
		and (p.mo.state == S_PLAY_KOU_LUNGE or p.mo.state == S_PLAY_SKID)
			--p.mo.momz = $ - ((P_GetMobjGravity(p.mo)/8)+P_GetMobjGravity(p.mo)/20)
			p.kou.lungetime = $-1
			IHATESPIKES(p.mo, 64*p.mo.scale)
			if p.speed then
				p.drawangle = R_PointToAngle2(0,0, p.mo.momx, p.mo.momy)
			end
			if (P_IsObjectOnGround(p.mo) or p.mo.state != S_PLAY_KOU_LUNGE or (p.mo.eflags & MFE_JUSTHITFLOOR)) and not p.kou.dontcancellunge
				p.kou.lungetime = 0
				p.kou.diveWaiter = KOU_S_LANDTIME
				/*
				if (P_IsObjectOnGround(p.mo) or (p.mo.eflags & MFE_JUSTHITFLOOR)) and (p.cmd.buttons & BT_SPIN) and not (p.kou.hittarget and p.kou.hittarget.valid) 
					p.mo.state = S_PLAY_ROLL
					p.pflags = $|PF_SPINNING
					S_StartSound(p.mo, sfx_spin)
				end
				*/
			end
			p.kou.funnyfall = false
			p.kou.dontcancellunge = false
			
			if p.kou.parrytimer or p.kou.charge >= KOU_S_OVERCHARGE
				local spooks = P_SpawnGhostMobj(p.mo)
				spooks.colorized = true
				spooks.color = p.kou.parrytimer and p.skincolor or SKINCOLOR_SUPERRED4
				spooks.blendmode = AST_ADD
			elseif leveltime % 2 == 0
				local spooks = P_SpawnGhostMobj(p.mo)
				spooks.colorized = true
				spooks.color = p.skincolor
				spooks.destscale = 0 
				spooks.scalespeed = p.mo.scale/10
			end

			--[[
			if p.kou.charge >= KOU_S_OVERCHARGE and leveltime % 10 < 5 then
				local spooks = P_SpawnGhostMobj(p.mo)
				spooks.colorized = true
				spooks.color = SKINCOLOR_SUPERRED4
				spooks.blendmode = AST_COPY
				spooks.tics = 2
				P_MoveOrigin(spooks, p.mo.x, p.mo.y, p.mo.z)
			end
			]]
			--P_TryCameraMove(camera, camera.x+P_ReturnThrustX(p.mo, camera.angle, -20*FRACUNIT), camera.y+P_ReturnThrustY(p.mo, camera.angle, -20*FRACUNIT), camera.z)
			/*local cir1 = P_SpawnMobjFromMobj(p.mo, 0, 0, 0, MT_KOU_CIRC)
			cir1.scale = p.mo.scale*1
			cir1.destscale = 0
			cir1.scalespeed = p.mo.scale/8
			cir1.fuse = 25
			cir1.trg = p.mo
			cir1.angle = p.mo.angle+FixedAngle(90*FRACUNIT)
			cir1.blendmode = AST_ADD
			local cir2 = P_SpawnMobjFromMobj(p.mo, 0, 0, 0, MT_KOU_CIRC)
			cir2.scale = p.mo.scale*1
			cir2.destscale = 0
			cir2.state = S_KOU_CIRC2
			cir2.scalespeed = p.mo.scale/8
			cir2.trg = p.mo
			cir2.angle = p.mo.angle+FixedAngle(90*FRACUNIT)
			cir2.blendmode = AST_ADD
			cir2.rollangle = p.kou.OhMyGodPleaseJustFuckingWork+FixedAngle(25*FRACUNIT)
			p.kou.OhMyGodPleaseJustFuckingWork = cir2.rollangle
			if P_RandomRange(1, 2) == 1
				cir2.rang = -1
			else
				cir2.rang = -1
			end
			cir2.fuse = 10
			if (p.kou.lungetime == 1)	
				--P_SetObjectMomZ(p.mo, -2*FRACUNIT, true)
			end*/
		elseif p.kou.lungetime or p.mo.state == S_PLAY_KOU_LUNGE
			p.kou.lungetime = 0
			if p.mo.state == S_PLAY_KOU_LUNGE
				p.mo.state = S_PLAY_FALL
			end
		end

		if p.mo.state == S_PLAY_KOU_REVERSEROLL
			p.panim = PA_ROLL
			if leveltime % 2 == 0
				local frame = (p.mo.frame & FF_FRAMEMASK)
				if frame-1 == -1
					frame = 3
				else
					frame = max($-1, 0)
				end
				p.mo.frame = ($ &~ FF_FRAMEMASK) | frame
			end
			if p.mo.momz*mobjflip <= 0
				p.mo.state = S_PLAY_FALL
			end
		end

		--uh
		if p.mo.state == S_PLAY_KOU_DROP and p.mo.tics == 1 and not(P_IsObjectOnGround(p.mo)) then
			p.mo.state = S_PLAY_FALL
		end

		--Charge up for Wolf-Strike
		if (p.mo.state == S_PLAY_KOU_WINDUP or p.mo.state == S_PLAY_KOU_AIRWINDUP)
		and not KouCantMove(p) then
			p.drawangle = p.mo.angle
			--states
			if P_IsObjectOnGround(p.mo) then
				p.pflags = $|PF_JUMPSTASIS
				if p.cmd.buttons & BT_JUMP and not(p.pflags & PF_JUMPDOWN) then
					p.pflags = $&~PF_JUMPSTASIS
					P_DoJump(p, true)
					p.pflags = $|PF_JUMPDOWN
					
					p.mo.state = S_PLAY_KOU_AIRWINDUP
					p.panim = PA_DASH
				elseif p.mo.state == S_PLAY_KOU_AIRWINDUP
					p.mo.state = S_PLAY_KOU_WINDUP
				end
				if p.speed > p.mo.scale*20 and (leveltime % 3 == 0) then
					dust(p, true)
				end
			elseif not p.glidetime
				p.glidetime = 1
			end
			--slowdown and float
			if FixedHypot(p.mo.momx, p.mo.momy) >= 3*p.mo.scale
				P_Thrust(p.mo, R_PointToAngle2(p.mo.x+p.mo.momx, p.mo.y+p.mo.momy, p.mo.x, p.mo.y), p.mo.scale-p.mo.scale/3)
			end
			if (p.powers[pw_shield] & SH_FORCE) and p.kou.forcetimer < TICRATE*4 then
				p.mo.momz = max(-1, $)
				p.kou.forcetimer = $+1
			end
			//attempt to add a reticle
			local victim = CobaltEnemyLook(p)
			if victim != nil and victim.valid
				P_SpawnLockOn(p, victim, S_LOCKON3)
			end
			--hold effects and release
			local extramincharge = 0
			if CBW_Battle then extramincharge = KOU_BM_EXTRAMINCHARGE end
			if (p.cmd.buttons & BT_SPIN) or (p.kou.charge < KOU_S_MINCHARGE+extramincharge)
				local maxcharge = (All7Emeralds(emeralds) or (p.powers[pw_shield] & SH_NOSTACK) == SH_ARMAGEDDON) and KOU_S_OVERCHARGE or KOU_S_PEAKCHARGE
				p.kou.charge = min($+1, maxcharge)
				--fx
				if (p.kou.charge >= KOU_S_OVERCHARGE)
					local fireball = P_SpawnMobjFromMobj(p.mo, P_RandomRange(-45, 45)*FRACUNIT,P_RandomRange(-45, 45)*FRACUNIT,P_RandomRange(-54, 54)*FRACUNIT, MT_JANA_ENERGYBALL)
					fireball.sprite = leveltime % 2 == 0 and SPR_SFLM or SPR_SMOK
					fireball.fuse = TICRATE
					fireball.frame = $|FF_TRANS50
					fireball.destscale = p.mo.scale * 2
					fireball.scalespeed = p.mo.scale/22
					fireball.momx = P_RandomRange(-5, 5)*p.mo.scale
					fireball.momy = P_RandomRange(-5, 5)*p.mo.scale
					fireball.momz = P_RandomRange(-5, 5)*p.mo.scale
					local spooks = P_SpawnGhostMobj(p.mo)
					spooks.colorized = true
					spooks.color = SKINCOLOR_SUPERRED4
					spooks.destscale = 0 
					spooks.scalespeed = p.mo.scale/10
				else
					local energyball = P_SpawnMobjFromMobj(p.mo, P_RandomRange(-45, 45)*FRACUNIT,P_RandomRange(-45, 45)*FRACUNIT,P_RandomRange(-54, 54)*FRACUNIT, MT_JANA_ENERGYBALL)
					energyball.fuse = TICRATE
					local transrights = TIMETRANS(99-(p.kou.chargetimer or 0),1,"FF_TRANS","", false) or 0
					energyball.frame = $|transrights
					energyball.destscale = 0
					energyball.scalespeed = p.mo.scale/22
					energyball.colorized = true
					energyball.color = SKINCOLOR_YELLOW
					energyball.spriteyoffset = $ + 12*p.mo.scale*mobjflip
					energyball.target = p.mo -- this is for homing shit
				end
				if (p.kou.charge == KOU_S_PEAKCHARGE-1)
					S_StartSound(p.mo, sfx_cdfm66) --old: sfx_dive2
					local circ = P_SpawnMobjFromMobj(p.mo, 0,0,0, MT_KOUCIRCLE)
					circ.blendmode = AST_ADD
					circ.frame = $ | FF_PAPERSPRITE
					circ.angle = p.drawangle + ANGLE_90
					circ.scale = p.mo.scale + p.mo.scale
					circ.destscale = 0
					circ.scalespeed = p.mo.scale/circ.tics
				elseif (p.kou.charge == KOU_S_OVERCHARGE-1)
					S_StartSound(p.mo, sfx_kc4d) --old: silence
				end
			else --strike release
				local found = CobaltEnemyLook(p)
				local homingmomx = 0
				local homingmomy = 0
				local homingmomz = 0
				if found and found.valid and found.health
					local thok = P_SpawnMobjFromMobj(found, 0,0,
					found.height/(found.scale*2), MT_THOK)
					thok.flags = $ &~ MF_NOCLIPTHING
					thok.flags2 = $ | MF2_INVERTAIMABLE
					
					P_HomingAttack(p.mo, thok)
					homingmomx = p.mo.momx
					homingmomy = p.mo.momy
					homingmomz = p.mo.momz
					
					P_RemoveMobj(thok)
				end
				p.pflags = $ &~ PF_JUMPSTASIS
				local chargepercentage = p.kou.charge and (p.kou.charge*FRACUNIT/KOU_S_PEAKCHARGE) or 1
				local thrust = 0
				if not (gametyperules & GTR_RACE) or leveltime >= 4*TICRATE
					thrust = ease.linear(chargepercentage, KOU_S_MINTHRUST, KOU_S_PEAKTHRUST)
					if (p.mo.eflags & MFE_UNDERWATER)
						thrust = $/2
					end
				end
				P_InstaThrust(p.mo, p.mo.angle, max(FixedHypot(p.mo.momx, p.mo.momy), FixedMul(thrust, p.mo.scale)))
				S_StartSound(p.mo, sfx_s3k81) --old: sfx_dive3
				if (p.mo.eflags & MFE_UNDERWATER)
					P_SetObjectMomZ(p.mo, 4*FRACUNIT*117/200, false)
				else
					P_SetObjectMomZ(p.mo, 4*FRACUNIT, false)
				end
				p.kou.lungetime = 420 --this is meant to be infinite, or at least until you land ((IMPORTANT: WHEN LANDING, KEEP THIS PUNCHING STATE FOR A FEW MORE TICS, SO NEAR MISSES STILL COUNT AS A HIT))
				p.mo.state = S_PLAY_KOU_LUNGE
				p.panim = PA_DASH
				p.pflags = $ | PF_JUMPED | PF_NOJUMPDAMAGE &~ PF_SPINNING
				p.kou.dontcancellunge = true
				p.kou.punchgrace = -1
				p.glidetime = 1
				if p.cmd.forwardmove == 0 and p.cmd.sidemove == 0
					p.cmd.forwardmove = 1 --fuck this game
				end
				if (p.powers[pw_shield] & SH_NOSTACK) == SH_FLAMEAURA
				then
					p.pflags = $|PF_SHIELDABILITY
					P_Thrust(p.mo, p.mo.angle, 10*p.mo.scale)
					S_StartSound(p.mo,sfx_s3k43)
				end
				if p.kou.charge >= KOU_S_PEAKCHARGE then
					local circ = P_SpawnMobjFromMobj(p.mo, 0, 0, 0, MT_KOUCIRCLE)
					circ.blendmode = AST_ADD
					circ.frame = $ | FF_PAPERSPRITE
					circ.angle = p.drawangle + ANGLE_90
					circ.scale = p.mo.scale
					circ.destscale = p.mo.scale * 3
					circ.scalespeed = p.mo.scale/circ.tics
					if p.kou.charge >= KOU_S_OVERCHARGE then
						S_StartSound(p.mo, sfx_rail2)
						circ.momx = -p.mo.momx/2
						circ.momy = -p.mo.momy/2
						circ.momz = -p.mo.momz/2
					else
						if p.kou.charge >= KOU_S_PEAKCHARGE then
							S_StartSound(p.mo, sfx_rail1) --old: silence
						end
						circ.momx = p.mo.momx/2
						circ.momy = p.mo.momy/2
						circ.momz = p.mo.momz/2
					end
				end
				if homingmomz then
					--p.mo.momx = ($ + homingmomx)/2
					--p.mo.momy = ($ + homingmomy)/2
					p.mo.momz = homingmomz * (175+p.kou.charge) / 100
				end
				p.kou.strikecharge = false
			end
		elseif p.kou.strikecharge
			p.kou.strikecharge = false
			if p.mo.state == S_PLAY_KOU_WINDUP
				p.mo.state = S_PLAY_STND
			elseif p.mo.state == S_PLAY_KOU_AIRWINDUP
				p.mo.state = S_PLAY_FALL
			end
		end
		
		--Charge up for Wolf-Missile
		if p.mo.state == S_PLAY_KOU_CHARGE
			p.kou.funnyfall = false
			--p.instafallcharge = false
			if (p.kou.charge == 1)
				S_StartSound(p.mo, sfx_kc5a)
			end
			local extramincharge = 0
			if CBW_Battle then extramincharge = KOU_BM_EXTRAMINCHARGE end
			if KouCantMove(p)
				p.mo.state = S_PLAY_ROLL
			elseif (p.cmd.buttons & BT_JUMP) or (p.kou.charge < KOU_M_MINCHARGE+extramincharge) --minimum amount of charge
				--p.powers[pw_nocontrol] = 32768
				p.mo.movefactor = 0
				local limiter = (p.kou.forcetimer < 4*TICRATE and p.powers[pw_shield] & SH_FORCE) and 1*mobjflip or p.mo.scale*mobjflip
				p.mo.momz = mobjflip > 0 and max(-1*limiter, $) or min(-1*limiter, $)
				p.kou.chargetimer = $+1
				if p.kou.charge >= KOU_M_OVERCHARGE then
					local spooks = P_SpawnGhostMobj(p.mo)
					spooks.colorized = true
					spooks.color = SKINCOLOR_SUPERRED4
					spooks.blendmode = AST_ADD
					spooks.destscale = 0
					spooks.scalespeed = p.mo.scale/10
					local fireball = P_SpawnMobjFromMobj(p.mo, P_RandomRange(-45, 45)*FRACUNIT,P_RandomRange(-45, 45)*FRACUNIT,P_RandomRange(-54, 54)*FRACUNIT, MT_JANA_ENERGYBALL)
					fireball.sprite = leveltime % 2 == 0 and SPR_SFLM or SPR_SMOK
					fireball.fuse = TICRATE
					fireball.frame = $|FF_TRANS50
					fireball.destscale = p.mo.scale * 2
					fireball.scalespeed = p.mo.scale/22
					fireball.momx = P_RandomRange(-5, 5)*p.mo.scale
					fireball.momy = P_RandomRange(-5, 5)*p.mo.scale
					fireball.momz = P_RandomRange(-5, 5)*p.mo.scale
				elseif (p.kou.charge == KOU_M_PEAKCHARGE-1)
					S_StartSound(p.mo, sfx_cdfm66) --old: silence
					for i=1,3 do
						local g = P_SpawnGhostMobj(p.mo)
						g.destscale = 6*p.mo.scale
						g.colorized = true
						g.blendmode = AST_ADD
					end
				elseif (p.kou.charge == KOU_M_OVERCHARGE-1)
					S_StartSound(p.mo, sfx_kc4d) --old: silence
				end
				local maxcharge = (All7Emeralds(emeralds) or (p.powers[pw_shield] & SH_NOSTACK) == SH_ARMAGEDDON) and KOU_M_OVERCHARGE or KOU_M_PEAKCHARGE
				p.kou.charge = min($+1, maxcharge)		
				if FixedHypot(p.mo.momx, p.mo.momy) >= 3*p.mo.scale
					P_Thrust(p.mo, R_PointToAngle2(p.mo.x+p.mo.momx, p.mo.y+p.mo.momy, p.mo.x, p.mo.y), p.mo.scale-p.mo.scale/3)
				end
				--fx
				local energyball = P_SpawnMobjFromMobj(p.mo, P_RandomRange(-45, 45)*FRACUNIT,P_RandomRange(-45, 45)*FRACUNIT,P_RandomRange(-54, 54)*FRACUNIT, MT_JANA_ENERGYBALL)
				energyball.fuse = TICRATE
				local transrights = TIMETRANS(99-(p.kou.chargetimer or 0),1,"FF_TRANS","", false) or 0
				energyball.frame = $|transrights
				energyball.destscale = 0
				energyball.scalespeed = p.mo.scale/22
				energyball.colorized = true
				energyball.color = SKINCOLOR_YELLOW
				energyball.spriteyoffset = $ + 12*p.mo.scale*mobjflip
				energyball.target = p.mo -- this is for homing shit
				-- you dun fucked
				if (maxcharge < KOU_M_OVERCHARGE) and p.kou.chargetimer == KOU_M_MINCHARGE + KOU_M_PEAKCHARGE + (TICRATE-5)
					p.mo.state = S_PLAY_PAIN
					S_StartSound(p.mo, sfx_kc59) 
					p.kou.charge = 0
					p.kou.chargetimer = 0
					--[[
					if All7Emeralds(emeralds)
						p.pflags = $ & ~ PF_JUMPED
						p.kou.supercharged = true
						p.mo.state = S_PLAY_FALL
						S_StartSound(p.mo, sfx_peww)
					end
					]]
				end
			else --start/release dive
				p.mo.state = S_PLAY_KOU_MISSILE
				p.powers[pw_justsprung] = 2
				p.drawangle = p.mo.angle
				local basehthrust = 40*p.mo.scale
				local maxhthrust = 40*p.mo.scale
				local maxvthrust = -30*FRACUNIT
				local vthrust = -1*FRACUNIT
				local chargespeed = p.kou.charge/2
				if p.kou.charge >= KOU_M_OVERCHARGE then
					--maxhthrust = $*3/2
					maxvthrust = $*3
					vthrust = $*2
					chargespeed = $*2
					S_StartSound(p.mo, sfx_rail2)
				elseif p.kou.charge >= KOU_M_PEAKCHARGE then
					S_StartSound(p.mo, sfx_rail1) --old: silence
				end
				if (p.mo.eflags & MFE_UNDERWATER)
					basehthrust = $/2
					vthrust = $*117/200
				end
				P_InstaThrust(p.mo, p.mo.angle, max(maxhthrust, basehthrust+chargespeed*p.mo.scale/2))
				P_SetObjectMomZ(p.mo, max(maxvthrust, (vthrust*chargespeed)*3), false)
				S_StopSoundByID(p.mo, sfx_kc5a)
				S_StartSound(p.mo, sfx_cdfm14)
				S_StartSound(p.mo, sfx_kozoom)
				if (p.powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL
				or (p.powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP
				then
					p.pflags = $|PF_SHIELDABILITY
				end
				p.kou.currentlyDiving = true
				p.kou.chargetimer = 0
			end
		end
		
		--hey check out this extra supercharge stuff
		--[[
		if p.funnyeffect == nil or p.mo.momz*mobjflip <= 0 p.funnyeffect = false end
		if p.kou.supercharged and All7Emeralds(emeralds)
			if not (p.janasuperaura and p.janasuperaura.valid) 
				local aura = P_SpawnMobjFromMobj(p.mo, 0, 0, 0, MT_JANA_BADASSAURA)
				aura.scale = p.mo.scale+p.mo.scale/2
				aura.target = p.mo
				p.janasuperaura = aura
			end
			
			if (p.pflags & PF_JUMPED) -- big fucking yump
				p.mo.momz = $*2
				p.kou.supercharged = false
				p.kou.charge = KOU_M_PEAKCHARGE
				p.mo.state = S_PLAY_ROLL
				S_StartSound(p.mo, sfx_s3k81)
				p.funnyeffect = true
				for i = 1,12
					local fa = (i*ANG30)
					local mo = P_SpawnMobjFromMobj(p.mo, 0,0,0, MT_SUPERSPARK)
					mo.scale = p.mo.scale*2
					if mo and mo.valid
						mo.momy = FixedMul(cos(fa), 14*p.mo.scale)
						mo.momx = FixedMul(sin(fa), 14*p.mo.scale)
						mo.momz = -2*p.mo.scale*p.mo.scale/3
					end
					mo.destscale = 0
					mo.scalespeed = p.mo.scale/14
					i = $+1
				end
			end
		end
		if p.funnyeffect
			for i = 0,3
				local sparkstrail = P_SpawnMobjFromMobj(p.mo, (P_RandomRange(-32,32)*FRACUNIT),(P_RandomRange(-32,32)*FRACUNIT),(P_RandomRange(-32,32)*FRACUNIT), MT_BOXSPARKLE)
				sparkstrail.momx = P_RandomRange(-1, 1)*p.mo.scale
				sparkstrail.momy = P_RandomRange(-1, 1)*p.mo.scale
				sparkstrail.scale = $*(3/2)
				sparkstrail.destscale = 0
				sparkstrail.scalespeed = p.mo.scale/10
				sparkstrail.fuse = TICRATE/2
				sparkstrail.colorized = true
				sparkstrail.color = p.skincolor
			end
			local spooks = P_SpawnGhostMobj(p.mo)
			spooks.colorized = true
			spooks.color = p.skincolor
			spooks.blendmode = AST_ADD
			spooks.destscale = 0 
			spooks.scalespeed = p.mo.scale/10
		end
		]]
		
		-- bro is a fuckin cartoon lmao
		if (p.mo.momz/mobjflip <= -20*p.mo.scale) and not p.kou.funnyfall and not KouCantMove(p)
		and not (P_IsObjectOnGround(p.mo) or (p.mo.eflags & MFE_JUSTHITFLOOR))
		and p.mo.state != S_PLAY_KOU_LUNGE
			if P_RandomRange(1, 2) == 1  
				S_StartSound(p.mo, sfx_kofal1)
			else S_StartSound(p.mo, sfx_kofal2) end
			p.kou.funnyfall = true
			p.kou.passivefunnyfall = true
			
			if not p.kou.currentlyDiving
				p.kou.currentlyDiving = true
				if p.panim >= PA_IDLE and p.panim <= PA_RUN
					p.mo.state = S_PLAY_FALL
				end
			end
		end
		
		if (P_IsObjectOnGround(p.mo) or (p.mo.eflags & MFE_JUSTHITFLOOR))
			p.kou.chargetimer = 0
			p.kou.wallnuked = 0
		end
		
		if p.kou.currentlyDiving == true
			--Can't move!
			if KouCantMove(p)
				p.kou.currentlyDiving = false
				p.kou.funnyfall = false
				
			--springs
			elseif p.mo.eflags & MFE_SPRUNG
				p.mo.momz = $*5/4
				p.mo.momx = $*5/4
				p.mo.momy = $*5/4
				p.mo.state = S_PLAY_SPRING
				S_StartSound(p.mo, sfx_sprong)
				p.kou.currentlyDiving = false
				p.kou.funnyfall = false
				
			--Landing from the dive (or a passive funnyfall)
			elseif (P_IsObjectOnGround(p.mo) or (p.mo.eflags & MFE_JUSTHITFLOOR)) then
				if p.kou.charge >= KOU_M_PEAKCHARGE or not((p.powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP and (p.pflags & PF_SHIELDABILITY) or (p.pflags & PF_THOKKED)) then			
					local overcharged = (p.kou.charge >= KOU_M_OVERCHARGE) and 2 or 1
					if p.kou.charge < KOU_M_PEAKCHARGE then
						P_StartQuake(10*p.mo.scale, 10)
						yoshiExplodeFavEdit(p.mo, (6*p.kou.charge)*p.mo.scale, false, true, false)
						local hbox = P_SpawnMobjFromMobj(p.mo, 0, 0, 0, MT_KOU_FOFHITBOX)
						hbox.fuse = 5
					else
						P_StartQuake(20*p.mo.scale, 12)
						S_StartSound(p.mo, sfx_bkpoof)
						local chargepower = p.kou.charge/2
						local explosionradius = (30*chargepower)*p.mo.scale
						yoshiExplodeFavEdit(p.mo, explosionradius, false, true, (overcharged > 1))
						local hbox = P_SpawnMobjFromMobj(p.mo, 0, 0, 0, MT_KOU_FOFHITBOX)
						hbox.fuse = 5
						hbox.radius = p.kou.charge >= KOU_M_OVERCHARGE and $*3 or $*2
						for i = 0,14 do
							local rock = P_SpawnMobjFromMobj(p.mo, P_RandomRange(-25, 25)*FRACUNIT, P_RandomRange(-25, 25)*FRACUNIT, P_RandomRange(-15, 40)*FRACUNIT+30*FRACUNIT, MT_LAVAFALLROCK)
							rock.flags = $&~MF_PAIN
							rock.state = S_ROCKCRUMBLEA+P_RandomRange(1, 4)
							if p.kou.charge >= KOU_M_OVERCHARGE and (i % 2 == 0) then rock.state = S_ROCKCRUMBLEA end
							if rock.state == S_ROCKCRUMBLED then
								if G_GametypeHasTeams() then
									rock.colorized = true ; rock.color = p.mo.color
								else
									rock.state = S_ROCKCRUMBLEB
								end
							end
							rock.scale = p.mo.scale/2+p.mo.scale/5
							rock.fuse = 45
							P_Thrust(rock, R_PointToAngle2(p.mo.x, p.mo.y, rock.x, rock.y), P_RandomRange(3, 10)*p.mo.scale/2)
							P_SetObjectMomZ(rock, P_RandomRange(4, 20)*FRACUNIT, false)
						end
						local circ = P_SpawnMobjFromMobj(p.mo, 0, 0, 1, MT_KOUCIRCLE)
						circ.tics = 20
						circ.scale = p.mo.scale + p.mo.scale
						circ.renderflags = $ | RF_FLOORSPRITE
						circ.destscale = p.mo.scale * chargepower * (overcharged>1 and 5 or 1)
						circ.scalespeed = p.mo.scale * 2 * (overcharged>1 and 5 or 1)
						circ.height = 1
						if P_MobjFlip(p.mo) < 0
							circ.z = p.mo.z + p.mo.height - 1
						end
					end		
					p.kou.currentlyDiving = false
					S_StartSound(p.mo, sfx_s3k5f)
					
					local circ2 = P_SpawnMobjFromMobj(p.mo, 0, 0, 0, MT_KOUCIRCLE)
					circ2.color = p.skincolor
					circ2.scale = p.mo.scale + p.mo.scale
					circ2.renderflags = $ | RF_FLOORSPRITE
					circ2.destscale = p.mo.scale * 20
					circ2.scalespeed = p.mo.scale/circ2.tics
					circ2.height = 1
					if P_MobjFlip(p.mo) < 0
						circ2.z = p.mo.z + p.mo.height
					end
					if p.kou.charge >= KOU_M_PEAKCHARGE then
						circ2.frame = $|B
						circ2.tics = $+5
					end

					for i = 0,7
						local dust = P_SpawnMobjFromMobj(p.mo, P_RandomRange(-34, 34)*FRACUNIT,P_RandomRange(-34, 34)*FRACUNIT,0, MT_TNTDUST)
						dust.scale = (p.kou.charge >= KOU_M_PEAKCHARGE) and p.mo.scale*2 or p.mo.scale
						dust.destscale = 0
						dust.scalespeed = p.mo.scale/17
						P_Thrust(dust, R_PointToAngle2(p.mo.x, p.mo.y, dust.x, dust.y), p.mo.scale*2)
					end

					--[[
					if p.instafallcharge
						p.kou.charge = KOU_M_PEAKCHARGE
						p.instafallcharge = false
					end
					]]

					if (p.powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL
					or (p.powers[pw_shield] & SH_NOSTACK) == SH_FLAMEAURA then
						p.kou.dofirering = p.kou.charge*2/5
					end

					if ((p.powers[pw_shield & SH_NOSTACK]) == SH_BUBBLEWRAP or overcharged > 1) and not(p.kou.passivefunnyfall) then
						p.mo.state = overcharged > 1 and S_PLAY_ROLL or S_PLAY_SPRING
						local multiplier = (p.powers[pw_shield & SH_NOSTACK]) == SH_BUBBLEWRAP and 10 or 3
						if p.mo.state == S_PLAY_ROLL then
							p.pflags =($|PF_SPINNING|PF_JUMPED) &~ PF_THOKKED
							if (p.powers[pw_shield & SH_NOSTACK]) == SH_BUBBLEWRAP then
								p.powers[pw_shield] = SH_NONE
							end
						end
						local chargespeed = p.kou.charge/2
						if (p.mo.eflags & MFE_UNDERWATER)
							chargespeed = $*117/200
						end
						P_SetObjectMomZ(p.mo, chargespeed * FRACUNIT * (overcharged+multiplier)/10)
						p.kou.currentlyDiving = false
					else
						p.kou.diveWaiter = TICRATE/2
						p.pflags = $|PF_JUMPSTASIS
					end

					if overcharged > 1 and (p.powers[pw_shield] & SH_NOSTACK == SH_ARMAGEDDON) then
						p.powers[pw_shield] = SH_NONE
						P_FlashPal(p, PAL_NUKE, 10)
						S_StartSound(p.mo, sfx_bkpoof)
						P_NukeEnemies(p.mo, p.mo, 1500*p.mo.scale)
					end
				else
					--P_DoBubbleBounce(p)
					p.mo.state = S_PLAY_SPRING
					local chargespeed = p.kou.charge
					if (p.mo.eflags & MFE_UNDERWATER)
						chargespeed = $*117/200
					end
					P_SetObjectMomZ(p.mo, chargespeed * FRACUNIT* 3/4)
					p.pflags = $ &~ PF_THOKKED
				end
				p.kou.passivefunnyfall = false
				p.kou.funnyfall = false
				p.kou.charge = 0
				
			--Knocked out of missile
			elseif ((p.mo.state != S_PLAY_KOU_MISSILE) and not p.kou.funnyfall)
				p.kou.currentlyDiving = false
				p.kou.funnyfall = false
				
			--Falling
			else
				p.powers[pw_strong] = STR_BOUNCE
				if ((mobjflip > 0 and p.mo.momz > 0)
				or (mobjflip < 0 and p.mo.momz < 0))
				and not P_IsObjectInGoop(p.mo) then
					p.mo.momz = $*19/20
				end
				
				local spikehatredintensity = p.kou.charge >= KOU_M_OVERCHARGE and 190*p.mo.scale or 95*p.mo.scale
				IHATESPIKES(p.mo, spikehatredintensity)
				
				if p.kou.charge >= KOU_M_OVERCHARGE or leveltime % 3 == 0
					local spooks = P_SpawnGhostMobj(p.mo)
					spooks.colorized = true
					spooks.color = p.kou.charge >= KOU_M_OVERCHARGE and SKINCOLOR_SUPERRED4 or SKINCOLOR_VAPOR
					spooks.blendmode = AST_ADD
					spooks.destscale = 0 
					spooks.scalespeed = p.mo.scale/10
				end
				
				Kou_SpawnSpeedLine(p) -- Lach: spawn speedline
			end
		end	

		if KouCantMove(p)
		or (p.panim >= PA_IDLE and p.panim <= PA_RUN)
		or not p.kou.funnyfall
			if S_SoundPlaying(p.mo, sfx_kofal1) then S_StopSoundByID(p.mo, sfx_kofal1) end
			if S_SoundPlaying(p.mo, sfx_kofal2) then S_StopSoundByID(p.mo, sfx_kofal2) end
		end
		
		--land state shenanigans
		if p.kou.diveWaiter != 0
			if KouCantMove(p) or p.kou.beginmurdering > 0
				p.kou.diveWaiter = 0
			else
				p.kou.diveWaiter = $-1
				p.pflags = $|PF_STASIS
				if p.kou.diveWaiter > KOU_M_MINLANDTIME then
					p.pflags = $|PF_JUMPSTASIS
					if p.speed > p.mo.scale*20 and (leveltime % 3 == 0) then
						dust(p, true)
					end
				end
				if p.mo.state == S_PLAY_SKID then
					p.mo.state = S_PLAY_RUN
					S_StopSoundByID(p.mo, sfx_skid)
				end
				if not (p.pflags & PF_JUMPED or p.mo.eflags & MFE_SPRUNG) then p.mo.state = S_PLAY_GLIDE_LANDING end
				if not (P_IsObjectOnGround(p.mo) or p.mo.eflags & MFE_JUSTHITFLOOR) and p.kou.diveWaiter > 1
					p.kou.diveWaiter = 1
				elseif p.kou.diveWaiter == 0 and (p.pflags & PF_STASIS)
					p.pflags = $1 & ~PF_STASIS|PF_JUMPSTASIS
					if (p.pflags & PF_JUMPED) or not P_IsObjectOnGround(p.mo) then
						p.mo.state = S_PLAY_JUMP
					elseif P_IsObjectOnGround(p.mo) then
						p.mo.state = S_PLAY_STND
					end
				end
			end
		end
		
		--[[
		if p.jumpbooster == nil then p.jumpbooster = 0 end
		if p.jumpboosttime == nil then p.jumpboosttime = 0 end
		if p.jumpboosttime != 0 and not KouCantMove(p)
			p.jumpboosttime = $-1
			if p.jumpboosttime > 4
				P_SetObjectMomZ(p.mo, p.jumpbooster, true)
			else
				P_SetObjectMomZ(p.mo, p.jumpbooster/2, false)
			end
			-- pretty sparkles........
			p.funnyeffect = true
		end
		]]
		
		if p.mo.state == S_PLAY_KOU_MISSILE
			p.kou.passivefunnyfall = false
			p.koumissiled = true
			p.acceleration = 0
			
			p.powers[pw_justsprung] = 2
			P_Thrust(p.mo, p.drawangle, p.mo.scale)
			p.mo.momz = $ + P_GetMobjGravity(p.mo)
		elseif p.koumissiled
			p.koumissiled = false
			p.acceleration = skins[p.mo.skin].acceleration
		end

		if p.kou.passivefunnyfall then
			if not(p.kou.funnyfall) then
				p.kou.passivefunnyfall = false
			elseif not(p.kou.strikecharge or p.kou.lungetime or p.mo.state == S_PLAY_KOU_CHARGE) then
				p.kou.charge = abs(p.mo.momz*2/3)/p.mo.scale
			end
		end
		
		--/Kou was originally going to have a sprinting mechanic. This is simply the remnants of that.
		/*if p.isxmomentum or p.mrce
			nspd = p.fakenormalspeed
		else
			nspd = p.normalspeed
		end
		--doubletap detection
		if p.kou.dtapcooldown < 4 and p.kou.dtapcooldown > 0 and (p.cmd.sidemove != 0 or p.cmd.forwardmove != 0) and not (p.koutapdash and p.koutapdash == 2)
			p.kou.dtapdash = true
		end
		if p.cmd.forwardmove == 0 and p.cmd.sidemove == 0
			if p.kou.dtapcooldown > 0
				p.kou.dtapcooldown = $ - 1
			end
			p.kou.dtapped = 0
		else
			if p.kou.dtapped <= 4
				p.kou.dtapcooldown = 4
			else
				p.kou.dtapcooldown = 0
			end
			p.kou.dtapped = $ + 1
		end
		
		--doubletap or press c1 to begin sprinting
		if (p.kou.dtapdash or (p.cmd.buttons & BT_CUSTOM1) and not (p.kou.c1down)) and p.kou.canstartdash == true
			p.kou.sprinting = true
			p.kou.-- = 25
			p.kou.dtapdash = false
			p.kou.canstartdash = false
			P_SpawnGhostMobj(p.mo)
			P_Thrust(p.mo, inputangle(p), 13*p.mo.scale)
		elseif not (p.kou.sprinting) and not (p.kou.--)
			p.kou.canstartdash = true
		end
		
		--actual sprint stat modification
		if p.kou.sprinting == true
			p.kou.-- = max($-1, 0)
			if p.kou.-- == 0 and p.speed < sprintspeed
				p.kou.sprinting = false
			end
			nspd = skins[p.mo.skin].normalspeed+skins[p.mo.skin].normalspeed/2
			p.thrustfactor = 6
			p.jumpfactor = FRACUNIT+FRACUNIT/20
		else
			nspd = skins[p.mo.skin].normalspeed
			p.thrustfactor = skins[p.mo.skin].thrustfactor
			p.jumpfactor = skins[p.mo.skin].jumpfactor
		end
		--im lazy lol
		if p.isxmomentum or p.mrce
			p.fakenormalspeed = nspd
		else
			p.normalspeed = nspd
		end
		p.kou.c1down = (p.cmd.buttons & BT_CUSTOM1)
		p.kou.jumpdown = (p.cmd.buttons & BT_JUMP)*/
		p.kou.lastx = p.mo.x
		p.kou.lasty = p.mo.y
		p.kou.lastz = p.mo.z
		
		--GO THE FUCK TO SLEEP
		if p.mo.state == S_PLAY_WAIT and p.kou.kousleep != -1 --countdown
			p.kou.kousleep = $ + 1
		end
		if p.kou.kousleep == 30*TICRATE
			p.kou.kousleep = -1
			p.mo.state = S_PLAY_KOU_SLEEP
		end
		
		if p.mo.state == S_PLAY_KOU_SLEEP
			if p.didntsnore == nil then p.didntsnore = 0 end
			if (p.mo.frame & FF_FRAMEMASK) != A then p.didntsnore = 0 end
			if (p.mo.frame & FF_FRAMEMASK) == A and p.didntsnore == 0
				p.didntsnore = 1
				if not S_SoundPlaying(p.mo, sfx_kouslp) then S_StartSound(p.mo, sfx_kouslp) end
			end
		end
		if (p.mo.state != S_PLAY_KOU_SLEEP or p.speed != 0) and p.kou.kousleep == -1
			p.kou.kousleep = 0 
			if p.speed > 0 then p.mo.state = S_PLAY_WALK end
		end
	end
	
	if p.mo and p.mo.valid and p.mo.health and p.mo.skin != "kou" --imagine not sleeping
	and p.mo.state == S_PLAY_KOU_SLEEP
		p.kou.kousleep = 0
		p.mo.state = S_PLAY_STND
	end
end)
addHook("ThinkFrame", do
	for p in players.iterate
		if p.mo and p.mo.valid and p.mo.health and p.mo.skin == "kou"
			--bitchslap someone if you hit them with lunge
			local targ = p.kou.hittarget
			if targ and targ.valid and targ.health
				if not (targ.punchsetup)
					targ.punchsetup = true
					if not (targ.player or (targ.flags & MF_BOSS)) then
						targ.flags = $|MF_NOTHINK
					end
					p.kou.beginmurdering = 19
					--p.kou.orbitangle = R_PointToAngle2(p.kou.lastx, p.kou.lasty, targ.x, targ.y)
					--p.kou.tx = targ.x
					--p.kou.ty = targ.y
					--p.kou.tz = targ.z
				end
			end
			
			--hey all, Fav here, today we're gonna record myself being a bumbling dumbass
			if p.kou.punchlagactive != 0 
				p.kou.punchlagactive = $-1 
				if p.kou.punchlagactive == 3 and not (p.kou.currentlyDiving or p.mo.state == S_PLAY_KOU_LUNGE) --its dumb but it works somehow!!
					p.recX = $/2
					p.recY = $/2
					p.recZ = $/2
				end
				if p.kou.punchlagactive > 1
					p.mo.momx = 0
					p.mo.momy = 0
					p.mo.momz = 0
				elseif not p.kou.enemybounceback 
					p.mo.momx = p.recX
					p.mo.momy = p.recY
					p.mo.momz = p.recZ
					if not(p.mo.state == S_PLAY_KOU_DROP or p.kou.currentlyDiving) then
						if (p.mo.eflags & MFE_UNDERWATER)
							P_SetObjectMomZ(p.mo, 8*FRACUNIT*117/200, false)
						else
							P_SetObjectMomZ(p.mo, 8*FRACUNIT, false)
						end
					end
				elseif (p.mo.eflags & MFE_UNDERWATER)
					P_InstaThrust(p.mo, p.mo.angle, -15*p.mo.scale/2)
				else
					P_InstaThrust(p.mo, p.mo.angle, -15*p.mo.scale)
				end
			elseif p.mo.health
				p.recX = p.mo.momx 
				p.recY = p.mo.momy 
				p.recZ = p.mo.momz
			end
			
			--absolute spaghetti code but it works and that's what matters
			if p.kou.beginmurdering > 0
				--P_TeleportMove(p.mo, p.kou.tx+P_ReturnThrustX(p.mo, p.kou.orbitangle, -15*p.mo.scale), p.kou.ty+P_ReturnThrustY(p.mo, p.kou.orbitangle, -15*p.mo.scale), p.kou.tz)
				--p.mo.angle = p.kou.orbitangle
				--p.drawangle = p.kou.orbitangle
				p.mo.momz = 0
				p.kou.parrytimer = 0
				p.kou.beginmurdering = $+1
				if p.kou.beginmurdering < 20
					--uhhh i was gonna do something else here but i forgot what
					if p == displayplayer or p == secondarydisplayplayer
						local cam = (p == secondarydisplayplayer) and camera2 or camera
						if cam
							P_TryCameraMove(cam, cam.x+P_ReturnThrustX(p.mo, cam.angle, -20*FRACUNIT), cam.y+P_ReturnThrustY(p.mo, cam.angle, -20*FRACUNIT), cam.z)
						end
					end
				elseif p.kou.beginmurdering < 24
					--P_TryCameraMove(camera, camera.x+P_ReturnThrustX(p.mo, camera.angle, 20*FRACUNIT), camera.y+P_ReturnThrustY(p.mo, camera.angle, 20*FRACUNIT), camera.z)
					if p.mo.state != S_PLAY_KOU_PUNCH1
						p.mo.state = S_PLAY_KOU_PUNCH1
					end
					if p.kou.beginmurdering == 21
						p.mo.state = S_PLAY_KOU_PUNCH1
						
						if targ and targ.valid then targ.punchsetup = false end
						if targ and targ.valid then
							targ.flags = $&~MF_NOTHINK
						end
						if p == displayplayer
							P_StartQuake(FRACUNIT*20, 7)
						end
						--print("wtf")
						if targ and targ.valid 
							if (targ.flags & MF_BOSS)
							and not (targ.type == MT_FANG) -- dude you're on a train getting flung by him sucks
							and not (targ.type == MT_METALSONIC_BATTLE) -- this arena is way too small...
								p.bosshitter = true
							else
								p.bosshitter = false
								if targ.health > 1 
									p.kou.enemybounceback = true
								else 
									p.kou.enemybounceback = false 
								end
							end

							if (p.powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL
							or (p.powers[pw_shield] & SH_NOSTACK) == SH_FLAMEAURA then
								p.kou.dofirering = p.kou.charge/3
							end
							if (p.powers[pw_shield] & SH_NOSTACK == SH_ARMAGEDDON)
							and p.kou.charge >= KOU_S_OVERCHARGE then
								p.powers[pw_shield] = SH_NONE
								P_FlashPal(p, PAL_NUKE, 10)
								S_StartSound(p.mo, sfx_bkpoof)
								P_NukeEnemies(p.mo, p.mo, 1500*p.mo.scale)
							end
							p.pflags = $ &~ PF_SHIELDABILITY
							
							local DMG = KOU_UNCHARGED_DAMAGE
							if p.kou.charge >= KOU_S_PEAKCHARGE then
								DMG = KOU_CHARGED_DAMAGE
							--elseif p.koul.charge = KOU_S_OVERCHARGE then
								--DMG = KOU_OVERCHARGE_DAMAGE
							end

							if istree(targ) then DMG = -2 end
							domurder(targ, p.mo, DMG, p.speed)
							if istree(targ) then P_KillMobj(targ) end
							local BOOM = false
							if targ and targ.valid then
								if targ.type == MT_TNTBARREL then
									if p.kou.charge >= KOU_S_OVERCHARGE then
										KouVars(p)
										p.mo.state = S_PLAY_PAIN
										if (p.mo.eflags & MFE_UNDERWATER)
											P_SetObjectMomZ(p.mo, FRACUNIT*15*117/200, false)
											P_InstaThrust(p.mo, p.mo.angle, -targ.scale*15)
										else
											P_SetObjectMomZ(p.mo, FRACUNIT*15, false)
											P_InstaThrust(p.mo, p.mo.angle, -targ.scale*30)
										end
										spawnnormalexplosion(targ, p.mo.scale*10)
										IHATESPIKES(p.mo, 240*p.mo.scale, "big fucking explosion")
										yoshiExplodeFavEdit(p.mo, 245*p.mo.scale, false, true, true)
										P_RemoveMobj(targ)
										BOOM = true
									else
										if (p.mo.eflags & MFE_UNDERWATER)
											P_InstaThrust(targ, p.mo.angle, KOU_S_OVERCHARGE*p.mo.scale/2)
											P_SetObjectMomZ(targ, 15*FRACUNIT*117/200, false)
										else
											P_InstaThrust(targ, p.mo.angle, KOU_S_OVERCHARGE*p.mo.scale)
											P_SetObjectMomZ(targ, 15*FRACUNIT, false)
										end
										targ.murdered = true
									end
								else
									P_DamageMobj(targ, p.mo, p.mo, DMG)
								end
							end

							local kicker = P_SpawnMobjFromMobj(p.mo, 0,0,0, MT_KOU_MISSILEPARTICLE)
							kicker.destscale = $*2
							kicker.scalespeed = p.mo.scale/12
							kicker.color = p.skincolor
							kicker.blendmode = AST_ADD
							kicker.fuse = 6
							--local sounds = p.kou.charge >= KOU_S_PEAKCHARGE and {sfx_spnch1, sfx_spnch2} or {sfx_wpnch1, sfx_wpnch2}
							local sounds = p.kou.charge >= KOU_S_PEAKCHARGE and {sfx_s239} or {sfx_kc3b} --old: the line commented above
							S_StartSound(p.mo, sounds[P_RandomRange(1, #sounds)])
							if not p.bosshitter
							or p.kou.enemybounceback
								if not BOOM then p.kou.punchlagactive = 8 end -- FREEZE
								if p.kou.charge >= KOU_S_PEAKCHARGE then
									if p.kou.charge >= KOU_S_OVERCHARGE then
										p.kou.punchlagactive = $*2
										P_FlashPal(p, PAL_INVERT, 4)
										IHATESPIKES(p.mo, 240*p.mo.scale, "TI...tan?")
										local circ = P_SpawnMobjFromMobj(p.mo, 0, 0, 1, MT_KOUCIRCLE)
										circ.tics = 15
										circ.scale = p.mo.scale + p.mo.scale
										circ.destscale = p.mo.scale * 30
										circ.scalespeed = p.mo.scale * 3
										if targ and targ.valid then spawnnormalexplosion(targ, targ.scale*2) end
										if (p.mo.eflags & MFE_UNDERWATER)
											P_InstaThrust(p.mo, p.mo.angle, -45*p.mo.scale/2)
											P_SetObjectMomZ(p.mo, p.jumpfactor*117/200)
										else
											P_InstaThrust(p.mo, p.mo.angle, -45*p.mo.scale)
											P_SetObjectMomZ(p.mo, p.jumpfactor)
										end
										P_RadiusAttack(p.mo, p.mo, RING_DIST*3/2)
										S_StartSound(p.mo, sfx_bkpoof) --old: silence
									end
									P_NukeEnemies(p.mo, p.mo, 0) --Purely for visual effect
									P_Telekinesis(p, 22*p.mo.scale, 320*p.mo.scale)
								end
							else
								local spd = p.kou.charge >= KOU_S_OVERCHARGE and -90*p.mo.scale or -45*p.mo.scale
								if (p.mo.eflags & MFE_UNDERWATER) then spd = $/2 end
								P_InstaThrust(p.mo, p.mo.angle, spd) --remember: angles were originally p.kou.orbitangle if it falls through
							end
							p.glidetime = 0
							p.kou.hittarget = nil
						end
					end
					if p.kou.beginmurdering > 22
					and (p == displayplayer or p == secondarydisplayplayer)
						local cam = (p == secondarydisplayplayer) and camera2 or camera
						if cam
							P_TryCameraMove(cam, cam.x+P_ReturnThrustX(p.mo, cam.angle, 10*FRACUNIT), cam.y+P_ReturnThrustY(p.mo, cam.angle, 10*FRACUNIT), cam.z)
						end
					end
				elseif p.kou.beginmurdering == 24
					local storeframe = p.mo.frame
					p.mo.state = S_PLAY_KOU_REVERSEROLL_STARTUP
					p.mo.frame = storeframe
					--P_TeleportMove(p.mo, p.kou.tx, p.kou.ty, p.kou.tz+(FRACUNIT*mobjflip))
					if (p.mo.eflags & MFE_UNDERWATER)
						P_SetObjectMomZ(p.mo, 8*FRACUNIT*117/200, false)
					else
						P_SetObjectMomZ(p.mo, 8*FRACUNIT, false)
					end
					
					if p.kou.enemybounceback then
						if (p.mo.eflags & MFE_UNDERWATER)
							P_InstaThrust(p.mo, p.mo.angle, -4*p.mo.scale)
						else
							P_InstaThrust(p.mo, p.mo.angle, -8*p.mo.scale)
						end
					end
					p.kou.beginmurdering = 0
					--p.powers[pw_flashing] = TICRATE/2
				end
			elseif targ and targ.valid then
				targ.flags = $&~MF_NOTHINK
				p.kou.hittarget = nil
			end
		end
	end
end)
addHook("MobjMoveCollide", function(thing, tmthing)
	if thing and thing.valid and thing.health
	and thing.parent and thing.parent.valid and thing.parent.health
	and tmthing and tmthing.valid and tmthing.health
	and (tmthing.flags & MF_SHOOTABLE or tmthing.type == MT_EGGSHIELD)
	and tmthing != thing.parent and collZCheck(thing, tmthing)
		if tmthing.type == MT_EGGSHIELD
			P_KillMobj(tmthing, thing.parent, thing.parent, DMG_NUKE)
		else
			P_DamageMobj(tmthing, thing.parent, thing.parent, KOU_CHARGED_DAMAGE)
		end
		thing.lockedon = nil
		thing.momx = $/4
		thing.momy = $/4
		thing.momx = $*-1
		thing.momy = $*-1
		P_SetObjectMomZ(thing, 10*FRACUNIT, false)
	end
end, MT_KOU_MURDER)
addHook("MobjThinker", function(mo)
	if mo and mo.valid
		if not (mo.isoverlay)
			if mo.parent and mo.parent.valid and mo.parent.health
				mo.lifetime = $ or 0
				mo.lifetime = $+1
				if mo.lifetime == 1 then
					if mo.nohoming then
						P_InstaThrust(mo, mo.angle, mo.speed)
						P_SetObjectMomZ(mo, 15*FRACUNIT, false)
					else
						local found = CobaltEnemyLook(mo.parent.player)
						if found and found.valid and found.health
							mo.lockedon = found
						else
							P_InstaThrust(mo, mo.parent.player.mo.angle, mo.speed) -- originally orbitangle
							P_SetObjectMomZ(mo, 15*FRACUNIT, false)
						end
					end
				end
				if mo.health
					mo.rollangle = $+FixedAngle(25*FRACUNIT)
					if mo.lockedon and mo.lockedon.valid and mo.lockedon.health
					and not (mo.lockedon.flags2 & MF2_FRET)
						P_HomingAttack(mo, mo.lockedon)
						if mo.lifetime < TICRATE*2 then return end
					else
						mo.lockedon = nil
					end
					
					if (P_IsObjectOnGround(mo) and mo.lifetime > 2
					or (FixedHypot(mo.momx, mo.momy) == 0)
					or mo.lifetime > 10*TICRATE)
						local m = P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_KOU_MURDER)
						m.scale = mo.scale
						m.sprite = mo.sprite
						m.sprite2 = mo.sprite2
						m.color = mo.color
						m.rollangle = mo.rollangle
						m.angle = mo.angle
						m.target = mo
						m.isoverlay = true
						m.flags = $|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT
						mo.flags = $|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT
						mo.alreadydead = true
						mo.momx = 0
						mo.momy = 0
						mo.momz = 0
						mo.rollangle = 0
						P_KillMobj(mo)
					end
				end
			end
		else
			--print(leveltime)
			if not (mo.target and mo.target.valid)
				P_RemoveMobj(mo)
			else
				mo.momx = 0
				mo.momz = 0
				mo.momy = 0
				mo.target.momx = 0
				mo.target.momy = 0
				mo.target.momz = 0
				P_MoveOrigin(mo, mo.target.x, mo.target.y, mo.target.z)
				if leveltime % 2 == 0
					mo.flags2 = $|MF2_DONTDRAW
				else
					mo.flags2 = $&~MF2_DONTDRAW
				end
			end
		end
	end
end, MT_KOU_MURDER)
addHook("MobjDeath", function(trg, inf, src)
	if not (inf and inf.valid and inf.health and trg and trg.valid and trg.flags & MF_ENEMY and inf.skin == "kou") return end
	--if trg.health == 1		
	--end
	--return true
end)
addHook("MobjDamage", function(trg, inf, src)
	if inf and src and inf == src and inf.valid and inf.player and inf.player.kou then
		if inf.player.kou.lungetime and not src.player.kou.punchlagactive then
			inf.player.kou.strikecharge = false
			inf.player.kou.diveWaiter = 0
			inf.player.kou.beginmurdering = max($,19) --punch enemies even if they already died to something like invincibility
		end
		inf.player.kou.funnyfall = false
		inf.player.kou.passivefunnyfall = false
	end
end)
--there are probably way more efficient ways of doing this
addHook("MobjThinker", function(barrel)
	if barrel.murdered then
		barrel.rollangle = $+FixedAngle(25*FRACUNIT)
	end
end, MT_TNTBARREL)
addHook("MobjMoveBlocked", function(barrel, thing, line)
	if barrel.murdered then
		P_KillMobj(barrel, thing, thing)
	end
end, MT_TNTBARREL)