//main lua file for tempest (the character) by tempest (the person)

//airstep function (midair jump ability):
local function airStep(p,sound,momz1,momz2,wall,spintimejump,momxy1,momxy2)
	if p.playerstate == PST_DEAD then return end
	if p.exiting then return end
	S_StartSound(p.mo,sound)
	p.mo.state = S_PLAY_ROLL
	p.pflags = $|PF_JUMPED
	local extraspeed = 0
	if not p.mo.wallstepped
	and wall then
		extraspeed = 4 --factor that makes the first wallstep stronger
		S_StartSound(p.mo,sfx_kc5b)
	end
	if (p.mo.eflags & MFE_UNDERWATER) then --less powerful underwater
		momz1 = $*3/4
	end
	P_SetObjectMomZ(p.mo,max((momz1 + extraspeed)<<16,p.mo.momz + abs(p.mo.momz/(momz2 - extraspeed/4))))
	if not wall then
		p.mo.momx = ($*momxy1)/(momxy2-min(p.powers[pw_sneakers] + p.powers[pw_super],1))
		p.mo.momy = ($*momxy1)/(momxy2-min(p.powers[pw_sneakers] + p.powers[pw_super],1)) --no speed penalty if you have the drip sneakers
	elseif p.powers[pw_super] == 0 then
		//throw player 22.5 degrees away from the wall in the direction they're holding
		P_InstaThrust(p.mo,(p.mo.wallangle + ANGLE_180) - (p.mo.inputangle - p.mo.wallangle)/2,(24 + extraspeed*4)<<16)
		if (p.pflags & PF_THOKKED) then
			p.pflags = $ - PF_THOKKED
		end
		local stepwave = P_SpawnMobj(p.mo.x + P_ReturnThrustX(p.mo,p.mo.wallangle,p.mo.radius*7/8),p.mo.y + P_ReturnThrustY(p.mo,p.mo.wallangle,p.mo.radius*7/8),p.mo.z,MT_STEPWAVE) --wave effect
		stepwave.color = p.mo.color
		stepwave.angle = p.mo.wallangle + ANGLE_90
		stepwave.scale = p.mo.scale*3/4
		stepwave.destscale = stepwave.scale*2
		stepwave.scalespeed = (stepwave.destscale - stepwave.scale)/10
		p.mo.wallstepped = true
	else
		P_InstaThrust(p.mo,p.mo.inputangle,42<<16)
		if (p.pflags & PF_THOKKED) then
			p.pflags = $ - PF_THOKKED
		end
	end
	p.mo.spintime = 10 --how long to spin
	p.mo.spintimejump = spintimejump --whether or not to put the player in jump animation after spinning (if it's a wallstep, no)
	p.mo.abiltimer = 0
end

addHook("PlayerThink", function(p)
	if p == nil then return end
	if p.mo == nil then return end
	if p.mo and p.mo.skin ~= "tempest" then return end
	if p.powers[pw_carry] == CR_NIGHTSMODE then return end
	p.mo.abiltimer = $ or 0 --airstep charge timer
	p.mo.spintime = $ or 0 --amount of frames to be spinning
	p.mo.wallstepped = $ or false --makes the first wallstep more powerful
	p.mo.inputangle = p.cmd.angleturn<<16 + R_PointToAngle2(0,0,p.cmd.forwardmove<<16,-p.cmd.sidemove<<16)
	p.mo.conduitstate = $ or 0 --multipurpose variable for conduit throw
	p.mo.conduittimer = $ or 0
	p.mo.downthrow = $ or false
	p.smoketimer = $ or 0
	p.snappressed = $ or false
	p.undobreak = $ or {}
	p.undobreaktimer = $ or {}
	p.snapped = $ or false
	p.statuetimer = $ or 0
	p.statuemode = $ or false
	if p.powers[pw_super] ~= 0
	and p.mo.conduitstate == 3 then
		p.mo.conduitstate = 0
	end
	if P_IsObjectOnGround(p.mo)
	or (p.mo.eflags & MFE_SPRUNG)
	or P_PlayerInPain(p)
	or (p.pflags & PF_SLIDING)
	or p.powers[pw_carry] == CR_PLAYER
	or p.powers[pw_carry] == CR_MINECART
	or p.powers[pw_carry] == CR_ROPEHANG
	or p.powers[pw_carry] == CR_ROLLOUT
	or p.powers[pw_carry] == CR_PTERABYTE
	or p.playerstate == PST_DEAD then
		p.mo.abiltimer = 0
		p.mo.conduittimer = 0
		if p.mo.conduitstate == 1 then
			p.mo.conduitstate = 0 --no dumb animations
		end
		p.mo.wallstepped = false
	end
	//charging airstep
	if p.mo.abiltimer > 0 then
		if p.powers[pw_super] == 0 then
			p.pflags = $|PF_THOKKED
		end
		if p.mo.state ~= S_TEMPEST_CHARGE
		and p.powers[pw_carry] ~= CR_DUSTDEVIL then
			p.mo.state = S_TEMPEST_CHARGE
		end
		if p.mo.abiltimer < 21 then
			if (p.cmd.buttons & BT_JUMP) then
				p.mo.abiltimer = $+1+min(p.powers[pw_sneakers] + p.powers[pw_super],1)
				if p.mo.abiltimer >= 21 then
					S_StartSound(p.mo,sfx_kc4d)
				end
			elseif p.powers[pw_super] == 0 then --weak airstep
				airStep(p,sfx_thok,7,6,false,false,3,4)
				if (p.pflags & PF_STARTJUMP) then
					p.pflags = $ - PF_STARTJUMP
				end
			else --super thokstep, idk man i'm out of good names :(
				airStep(p,sfx_thok,6,5,true,false)
				if (p.pflags & PF_STARTJUMP) then
					p.pflags = $ - PF_STARTJUMP
				end
			end
		else
			if (p.cmd.buttons & BT_JUMP) then
				if (leveltime % 2)
				and p.powers[pw_super] == 0 then
					p.mo.colorized = true --flashing
				else
					p.mo.colorized = false
				end
			else  --stronk airstep
				airStep(p,sfx_kc5b,17,3,false,true,1,3)
				if (p.pflags & PF_STARTJUMP) then
					p.pflags = $ - PF_STARTJUMP
				end
			end
		end
		p.mo.momz = $ + P_GetMobjGravity(p.mo)/3 --for balance reasons, become heavier while charging
	else
		p.mo.colorized = false
	end
	//pre-conduit throw
	if p.mo.conduittimer > 0 then
		if p.mo.conduitstate < 2 then
			if (p.cmd.buttons & BT_SPIN) then
				if p.mo.conduittimer < 35 then
					p.mo.conduittimer = $+1+min(p.powers[pw_sneakers] + p.powers[pw_super],1)
					if p.mo.conduittimer >= 35 then
						S_StartSound(p.mo,sfx_kc4d)
					end
				end
			else
				p.mo.conduitstate = 1
			end
			if leveltime % (max(2,6-p.mo.conduittimer/5)) == 1 then --flash quicker when charged
				p.mo.colorized = true
			else
				p.mo.colorized = false
			end
		elseif p.mo.conduit ~= nil
		and p.mo.conduit.valid
		and not (p.mo.conduit.flags & MF_NOCLIPTHING) then
			//pull yourself to the conduit
			if p.mo.conduittimer < 12 then
				if not S_SoundPlaying(p.mo,sfx_s3kd9s) then
					S_StartSound(p.mo,sfx_s3kd9s)
				end
				if (p.cmd.buttons & BT_SPIN) then
					p.mo.target = p.mo.conduit
					A_HomingChase(p.mo,38<<16,0)
					if p.powers[pw_super] == 0 then
						p.mo.conduittimer = $ + 1
					end
					if p.mo.conduittimer >= 9 then
						p.mo.conduitstate = 3 --couldn't pull, so despawn the conduit when you touch the ground
					end
					p.mo.state = S_PLAY_ROLL
					p.mo.spintime = 4
					p.mo.abiltimer = 0
				else
					p.mo.conduittimer = 0
					if p.powers[pw_super] == 0 then
						P_KillMobj(p.mo.conduit)
						p.mo.conduitstate = 3
						p.mo.momx = $/2
						p.mo.momy = $/2
						p.mo.momz = $*3/5
					end
				end
			end
		end
	end
	//funi roll animation after moves
	if p.mo.spintime > 0 then
		if leveltime % 3 == 1
		and not (p.mo.eflags & MFE_UNDERWATER) then
			local trail = P_SpawnMobj(p.mo.x,p.mo.y,p.mo.z,MT_ELECTRAIL)
			trail.color = p.mo.color
			trail.scale = p.mo.scale
		end
		if (p.pflags & PF_NOJUMPDAMAGE) then
			p.pflags = $ - PF_NOJUMPDAMAGE --do spin damage when spinning so you don't softlock against metal sonic
		end
		p.mo.spintime = $ - 1
		if p.mo.spintime == 0
		and p.mo.state == S_PLAY_ROLL
		and p.powers[pw_carry] ~= CR_ZOOMTUBE
		and p.powers[pw_carry] ~= CR_MACESPIN
		and p.powers[pw_carry] ~= CR_PTERABYTE --probably pointless but just in case
		and p.powers[pw_carry] ~= CR_ROPEHANG then --also probably pointless
			if p.mo.spintimejump
			and p.mo.momz > 3<<16 then
				p.mo.state = S_PLAY_SPRING
			else
				p.mo.state = S_PLAY_FALL
			end
			p.pflags = $|PF_NOJUMPDAMAGE
		end
	end
	//conduit thinker... is that the right name?
	if p.mo.conduitstate == 1 then
		if p.mo.state == S_TEMPEST_CHARGE_CONDUIT then --conduit aiming
			if (p.cmd.buttons & BT_SPIN) then
				p.mo.downthrow = true
			end
		else
			//guess what? i had to move all the code here to a postthinkframe hook because of a 1 frame visual error!
			if p.mo.conduitstate == 1 then
				p.mo.state = S_TEMPEST_CHARGE_CONDUIT
			end
		end
	elseif p.mo.conduitstate == 2 then
		if p.mo.conduit ~= nil
		and p.mo.conduit.valid then
			p.mo.conduit.momx = $*9/10
			p.mo.conduit.momy = $*9/10
			p.mo.conduit.momz = $*9/10
			if p.mo.conduit.state == S_CONDUIT_MOVING
			and ((p.mo.conduit.eflags & MFE_JUSTHITFLOOR)
			or (p.mo.conduit.eflags & MFE_JUSTSTEPPEDDOWN)) then
				p.mo.conduit.state = S_CONDUIT_FLASH_PRE --automatically activate the conduit if it hits the ground
			end
			if p.mo.conduit.state == S_CONDUIT then
				P_InstaThrust(p.mo.conduit,0,0)
				p.mo.conduit.momz = 0
			end
		end
	elseif p.mo.conduitstate == 3
	and P_IsObjectOnGround(p.mo) then
		if p.mo.conduit ~= nil
		and p.mo.conduit.valid then
			P_KillMobj(p.mo.conduit)
		end
		p.mo.conduitstate = 0
	end
	if P_IsObjectOnGround(p.mo)
	and p.mo.conduit ~= nil
	and not p.mo.conduit.valid then
		p.mo.conduitstate = 0
	end
	if p.powers[pw_carry] == CR_MACESPIN
	or p.powers[pw_carry] == CR_ROPEHANG then
		p.mo.conduittimer = 0
		p.mo.abiltimer = 0
		if p.mo.conduitstate == 3 then
			p.mo.conduitstate = 0
		end
	end
	if p.smoketimer > 0 then --fancy smoke for a well timed conduit
		if leveltime % 3 == 1 then
			for x = 0,1
				local anglechange = ANGLE_180*x
				local ptype = MT_CONDUIT_SPARK1
				local framediff = 0
				if P_RandomChance(FRACUNIT/2) then
					ptype = MT_CONDUIT_SPARK2
					framediff = 5
				end
				local particle = P_SpawnMobj(p.mo.x,p.mo.y,p.mo.z,ptype)
				P_TeleportMove(particle, particle.x + P_ReturnThrustX(particle,p.drawangle + ANGLE_90 + anglechange,p.mo.radius/2), particle.y + P_ReturnThrustY(particle,p.drawangle + ANGLE_90 + anglechange,p.mo.radius/2), particle.z)
				particle.frame = (1+framediff)|FF_ANIMATE
				particle.scale = p.mo.scale*2
				particle.destscale = $/2
				particle.scalespeed = FRACUNIT/4
			end
		end
		p.smoketimer = $ - 1
	end
	if p.mo.conduitstate ~= 3 then
		p.smoketimer = 0
	end
	if p.undobreak ~= {} then
		for x = 1,table.maxn(p.undobreak) do
			if p.undobreak[x] ~= nil then
				if p.undobreak[x].valid then
					if p.undobreaktimer[x] <= 0
					and (p.undobreak[x].flags & FF_SHATTER) then
						p.undobreak[x].flags = $ - FF_SHATTER
						table.remove(p.undobreak,x)
						table.remove(p.undobreaktimer,x)
					else
						p.undobreaktimer[x] = $ - 1
					end
				else --remove the FOF from the table if it's not valid
					table.remove(p.undobreak,x)
					table.remove(p.undobreaktimer,x)
				end
			end
		end
	end
	if p.mo.state == S_TEMPEST_SNAP
	or p.mo.state == S_TEMPEST_BOOF then
		if not P_IsObjectOnGround(p.mo) then
			p.mo.state = S_PLAY_FALL
		else
			if (p.cmd.buttons & BT_SPIN)
			and not p.snappressed then
				P_InstaThrust(p.mo,0,0)
			end
			if p.mo.frame == 2|FF_ANIMATE
			and p.mo.floorrover ~= nil
			and (p.mo.floorrover.flags & FF_BUSTUP) then
				//make bustable grounds break on contact while snapping
				//to avoid screwing up maps, remove the "bust on contact" flag after 20 frames
				if not (p.mo.floorrover.flags & FF_SHATTER) then
					p.undobreak[table.maxn(p.undobreak) + 1] = p.mo.floorrover
					p.undobreaktimer[table.maxn(p.undobreaktimer) + 1] = 20
				end
				p.mo.floorrover.flags = $|FF_SHATTER
			end
		end
	end
	if not (p.cmd.buttons & BT_SPIN) then
		p.snappressed = false
	end
	//curious as to what this does? hold custom3 for five seconds :>
	if p.statuetimer > 125
	and p.funkin == -1 then
		if not p.statuemode then
			p.statuemode = true
		end
	elseif (p.statuetimer == 0
	and p.statuemode
	and (p.cmd.buttons & BT_CUSTOM3))
	or p.funkin ~= -1 then
		p.statuemode = false
		S_StopSoundByID(p.mo,sfx_cement)
	end
	if (p.cmd.buttons & BT_CUSTOM3)
	and P_IsObjectOnGround(p.mo) then
		p.statuetimer = $ + 1
	else
		p.statuetimer = 0
	end
end)

//detecting walls
addHook("MobjLineCollide", function(mo,line)
	if mo.type ~= MT_PLAYER
	and mo.type ~= MT_CONDUIT then
		return
	end
	mo.wall = line
end)

addHook("MobjMoveBlocked", function(mo)
	if mo.type == MT_PLAYER then
		if mo and mo.skin ~= "tempest" then return end
		if P_IsObjectOnGround(mo) then return end
		if mo.abiltimer == 0 then return end
		if mo.wall == nil then return end
		if mo.player.powers[pw_super] ~= 0 then return end
		if mo.abiltimer ~= nil
		and mo.abiltimer >= 21 then
			mo.inputangle = mo.player.cmd.angleturn<<16 + R_PointToAngle2(0,0,mo.player.cmd.forwardmove<<16,-mo.player.cmd.sidemove<<16)
			mo.wallangle = R_PointToAngle2(mo.x,mo.y,P_ClosestPointOnLine(mo.x,mo.y,mo.wall))
			airStep(mo.player,sfx_jump,6,5,true,false)
		end
	elseif mo.type == MT_CONDUIT then
		mo.wallangle = R_PointToAngle2(mo.x,mo.y,P_ClosestPointOnLine(mo.x,mo.y,mo.wall))
	end
end)

addHook("PostThinkFrame", do
	for p in players.iterate
		if p == nil then continue end
		if p.mo == nil then continue end
		if p.mo and p.mo.skin ~= "tempest" then return end
		if p.mo.state == S_PLAY_STND --really wish i knew a compact way to do this
		or p.mo.state == S_PLAY_WAIT
		or p.mo.state == S_PLAY_JUMP
		or p.mo.state == S_PLAY_SPRING
		or p.mo.state == S_PLAY_FALL
		or p.mo.state == S_PLAY_RIDE
		or p.mo.state == S_PLAY_EDGE
		or p.mo.state == S_PLAY_DRWN
		or p.mo.state == S_PLAY_DEAD
		or p.mo.state == S_TEMPEST_CHARGE
		or p.mo.state == S_TEMPEST_FUNKIN
		or p.mo.state == S_TEMPEST_FUNKIN_PRE
		or p.mo.state == S_TEMPEST_GRINDING
		or p.statuemode then
			p.mo.frame = (leveltime%15)/5|FF_ANIMATE --animates a few sprites and makes the transition between them smooth
		end
		//change animation depending on how you aimed the conduit
		if p.mo.conduitstate == 1
		and p.mo.state == S_TEMPEST_CONDUIT then
			local horiFactor = 5 --factor for how fast the conduit should go horizontally
			if p.mo.downthrow then
				p.mo.state = S_TEMPEST_CONDUIT_DOWN
				horiFactor = 3
			end
			p.mo.conduit = P_SpawnMobjFromMobj(p.mo,0,0,0,MT_CONDUIT)
			P_InstaThrust(p.mo.conduit,p.mo.angle,((42 + p.mo.conduittimer*2)*p.mo.scale)*horiFactor/5)
			if p.mo.downthrow then
				P_SetObjectMomZ(p.mo.conduit, -((18 + p.mo.conduittimer)*p.mo.scale))
				P_SetObjectMomZ(p.mo,abs(min(p.mo.momz/2,0)),true) --cut the player's downward speed if there's any
			end
			p.mo.conduit.teamcolor = p.ctfteam
			p.mo.conduittimer = 0
			p.mo.conduitstate = 2
			p.mo.conduit.scale = p.mo.scale/10*12
			p.mo.conduit.thrower = p
			P_SetObjectMomZ(p.mo,5<<16,true) --give the player a little extra bounce
		end
		if (p.mo.eflags & MFE_UNDERWATER)
		and (p.pflags & PF_JUMPED) then
			p.mo.spintime = max($,2)
			if p.mo.state ~= S_PLAY_SWIM
			and p.mo.state ~= S_PLAY_ROLL
			and p.mo.state ~= S_TEMPEST_CHARGE
			and p.mo.state ~= S_TEMPEST_CHARGE_CONDUIT
			and p.mo.state ~= S_TEMPEST_CONDUIT
			and p.mo.state ~= S_TEMPEST_CONDUIT_DOWN then
				p.mo.state = S_PLAY_ROLL
			end
		end
		//this whole section here is dedicated to making sure that snap doesn't interfere with rail grinding
		if p.mo.state == S_TEMPEST_SNAP
		and p.mo.frame == 1|FF_ANIMATE
		and not p.snapped
		and (p.rail == nil or not (p.rail.flags & ML_EFFECT4))then
			local randomfunny = P_RandomKey(420) --you're better off farming a rod of discord :p
			for x = 0,7 do
				local spark = P_SpawnMobj(p.mo.x + P_ReturnThrustX(p.mo,p.drawangle + ANGLE_45*x,64<<16),p.mo.y + P_ReturnThrustY(p.mo,p.drawangle + ANGLE_45*x,72<<16),p.mo.z,MT_CONDUIT_SPARK1)
				spark.snapspark = "this value is irrelevant!"
				if randomfunny == 0 then
					spark.snapspark = "random boof event"
				end
				spark.scale = p.mo.scale*2
				spark.throwercoords = {p.mo.x,p.mo.y}
			end
			if p.mo.conduit ~= nil
			and p.mo.conduit.valid then
				P_KillMobj(p.mo.conduit)
			end
			if randomfunny ~= 0 then
				S_StartSound(p.mo,sfx_s3k77)
			else
				S_StartSound(p.mo,sfx_boof)
				p.mo.state = S_TEMPEST_BOOF
			end
			local function doPush(pmo,enemy)
				if enemy.valid --probably redudant but y'know
				and ((enemy.flags & MF_ENEMY)
				or (enemy.type == MT_PLAYER
				and enemy.player ~= nil
				and (gametype == GT_MATCH
				or (gametype == GT_TEAMMATCH and enemy.player.ctfteam ~= 0 and enemy.player.ctfteam ~= pmo.player.ctfteam)
				or (gametype == GT_CTF and enemy.player.ctfteam ~= 0 and enemy.player.ctfteam ~= pmo.player.ctfteam))))
				and R_PointToDist2(pmo.x,pmo.y,enemy.x,enemy.y) <= 128<<16 then
					P_Thrust(enemy,R_PointToAngle2(pmo.x,pmo.y,enemy.x,enemy.y),(128<<16 - R_PointToDist2(pmo.x,pmo.y,enemy.x,enemy.y))/4)
				end
			end
			searchBlockmap("objects", doPush, p.mo,p.mo.x - 128<<16, p.mo.x + 128<<16, p.mo.y - 128<<16, p.mo.y + 128<<16)
			p.snapped = true
		end
		if p.statuemode then
			p.mo.sprite2 = SPR2_STND
			if P_IsObjectOnGround(p.mo)
			and (abs(p.mo.momx) > 0
			or abs(p.mo.momy) > 0) then
				if not S_SoundPlaying(p.mo,sfx_cement) then
					S_StartSound(p.mo,sfx_cement)
				end
				if leveltime % 4 == 0 then
					P_SpawnMobj(p.mo.x,p.mo.y,p.mo.z,MT_SPINDUST)
				end
			elseif S_SoundPlaying(p.mo,sfx_cement)
				S_StopSoundByID(p.mo,sfx_cement)
			end
		end
	end
end)

addHook("AbilitySpecial", function(p)
	if p.mo and p.mo.skin ~= "tempest" then return end
	if not (p.mo.eflags & MFE_UNDERWATER)
	and (p.pflags & PF_JUMPED)
	and not (p.pflags & PF_THOKKED)
	and not p.exiting
	and (p.mo.conduittimer == 0 or p.mo.conduitstate == 3) then
		p.mo.abiltimer = 1 --trigger airstep
		p.pflags = $|PF_NOJUMPDAMAGE
	end
end)

addHook("JumpSpinSpecial", function(p)
	if p.mo and p.mo.skin ~= "tempest" then return end
	if (p.mo.conduitstate == 0 or p.mo.conduitstate == 2)
	and p.mo.conduittimer == 0
	and (p.mo.abiltimer == 0 or p.mo.conduitstate == 2)
	and not (p.mo.eflags & MFE_UNDERWATER)
	and (p.pflags & PF_JUMPED)
	and (not (p.pflags & PF_THOKKED) or p.mo.abiltimer ~= 0)
	and not p.exiting
	and not (p.pflags & PF_SPINDOWN) then
		p.mo.conduittimer = 1 --timer to charge conduit
		p.mo.downthrow = false
	end
end)

local function conduShareMsg(sharedconduits)
	for everyone in players.iterate
		if sharedconduits.value == 0 then
			CONS_Printf(everyone,"Conduits can now only be activated by their owners.")
		elseif sharedconduits.value == 1 then
			CONS_Printf(everyone,"Conduits can now be activated by any ally Tempests!")
		elseif sharedconduits.value == 2 then
			CONS_Printf(everyone,"Conduits can now be activated by any friendly players!")
		elseif sharedconduits.value == 3 then
			CONS_Printf(everyone,"Conduits can now be activated by anyone!")
		end
	end
end

local sharedconduits = CV_RegisterVar({"conduitshare",1,CV_CALL|CV_NETVAR|CV_CHEAT,{MIN=0,MAX=3},conduShareMsg})

//bounce when touching conduit
addHook("TouchSpecial", function(c,mo)
	if not mo.player then return end
	if (mo.conduit ~= nil and c == mo.conduit)
	or sharedconduits.value > 0 then
		if mo.skin == "tempest"
		or sharedconduits.value > 1 then
			if mo.player.ctfteam == c.teamcolor
			or sharedconduits.value > 2 then
				c.momx = 0
				c.momy = 0
				c.momz = 0
				local extraboost = 0
				if c.state == S_CONDUIT_FLASH
				or mo.player.powers[pw_super] > 0 then
					extraboost = 3<<16
					mo.player.smoketimer = 35 --spawn particles under the player's feet for a few frames
				end
				P_SetObjectMomZ(mo,max(15<<16,abs(mo.momz*3/5)) + extraboost)
				mo.momx = $/2 --believe me this ability would be overpowered without this
				mo.momy = $/2
				if mo.skin == "tempest" then
					mo.spintime = 10
					mo.conduittimer = 0
				end
				mo.state = S_PLAY_ROLL
				mo.player.pflags = $|PF_JUMPED
				if (mo.player.pflags & PF_THOKKED) then
					mo.player.pflags = $ - PF_THOKKED
				end
				if (mo.conduit ~= nil and c == mo.conduit) then
					mo.conduitstate = 3
				end
				if mo.conduit == 0 then
					mo.conduit = nil
				end
			end
		end
	end
end, MT_CONDUIT)

addHook("MobjThinker", function(c)
	c.color = SKINCOLOR_WHITE
	if sharedconduits.value < 3 then
		if c.teamcolor == 1 then
			c.color = SKINCOLOR_RED
		elseif c.teamcolor == 2 then
			c.color = SKINCOLOR_BLUE
		end
		if sharedconduits.value < 2 then
			c.color = c.thrower.mo.color
		end
	end
	//this here will spawn those spark particles
	if (leveltime % 2)
	and c.state ~= S_CONDUIT_ACTIVATED then
		local sparktype = MT_CONDUIT_SPARK1
		if P_RandomChance(FRACUNIT/2) then
			sparktype = MT_CONDUIT_SPARK2
		end
		local spark = P_SpawnMobj(c.x,c.y,c.z + c.height/2,sparktype)
		local speed = P_RandomFixed()*7
		local vertspeed = 7<<16 - speed
		if P_RandomChance(FRACUNIT/2) then
			vertspeed = -$
		end
		P_Thrust(spark,P_RandomRange(0,359)*ANG1,speed)
		P_SetObjectMomZ(spark,vertspeed)
		local sizechange = c.scale
		if c.state == S_CONDUIT_MOVING then
			sizechange = c.scale/2
			spark.momx = $/8
			spark.momy = $/8
			spark.momz = $/8
		end
		spark.scale = c.scale+sizechange
		spark.destscale = spark.scale/4
		spark.scalespeed = FRACUNIT/16
		if c.thrower.mo.target == c
		and c.thrower.mo.conduitstate < 3 then
			local trail = P_SpawnMobj(c.x,c.y,c.z,MT_ELECTRAIL)
			trail.color = c.color
			trail.scale = c.scale
			trail.target = c.thrower.mo
			A_HomingChase(trail,45*c.scale,0)
		end
	end
end, MT_CONDUIT)

//snap ability!
//breaks weak floors under you and resets the conduit
addHook("SpinSpecial", function(p)
	if p.mo == nil then return end
	if not p.mo.valid then return end
	if p.mo.skin ~= "tempest" then return end
	if p.exiting then return end
	if P_IsObjectOnGround(p.mo)
	and not p.snappressed
	and p.mo.state ~= S_TEMPEST_SNAP then
		p.mo.state = S_TEMPEST_SNAP
		p.snapped = false
	end
	p.snappressed = true
end)

addHook("MobjThinker", function(s)
	if s.snapspark == nil then return end
	if s.snapspark ~= "random boof event" then
		P_InstaThrust(s,R_PointToAngle2(s.x,s.y,s.throwercoords[1],s.throwercoords[2]) + (R_PointToDist2(s.x,s.y,s.throwercoords[1],s.throwercoords[2])/72) * (ANG60/FRACUNIT),8<<16)
		if R_PointToDist2(s.x,s.y,s.throwercoords[1],s.throwercoords[2]) <= 4<<16 then
			P_KillMobj(s)
		end
	else
		P_InstaThrust(s,R_PointToAngle2(s.x,s.y,s.throwercoords[1],s.throwercoords[2]) + ANGLE_90,8<<16)
		P_SetObjectMomZ(s,FRACUNIT/2,true)
	end
end, MT_CONDUIT_SPARK1)

addHook("PreThinkFrame", do
	for p in players.iterate
		if p.mo == nil then return end
		if not p.mo.valid then return end
		if p.mo.skin ~= "tempest" then return end
		if p.mo.state == S_TEMPEST_SNAP
		or p.mo.state == S_TEMPEST_BOOF then
			p.pflags = $|PF_FULLSTASIS
		end
	end
end)