-- Voidy's Stuff --
local impacttimer = 0
local dodgeroll = 0
local attackcount = 0
local attacktimer = 0
local finishertimer = 0
local voidycharge = 0
local stilltimer = 0
local attacking = 0
local lifeforce = 0
local LIFEFORCE = "client/VoidyChars/voidy.dat"
local transforming = 0
local frenzied = 0

rawset(_G, "P_IsClimbingValid",function(player, angle)
	local platx, platy
	local glidesector
	local floorz, ceilingz
	local mo = player.mo
	local rover
	platx = P_ReturnThrustX(mo, angle, mo.radius + FixedMul(8*FRACUNIT, mo.scale))
	platy = P_ReturnThrustY(mo, angle, mo.radius + FixedMul(8*FRACUNIT, mo.scale))
	glidesector = R_PointInSubsector(mo.x + platx, mo.y + platy).sector
	floorz = glidesector.floorheight
	ceilingz = glidesector.ceilingheight
	if (glidesector ~= mo.subsector.sector)
		local floorclimb = false
		local topheight, bottomheight
		for rover in glidesector.ffloors()
			if (not (rover.flags & FF_EXISTS) or not (rover.flags & FF_BLOCKPLAYER))
				continue
			end
			topheight = rover.topheight
			bottomheight = rover.bottomheight
			floorclimb = true
			if (mo.eflags & MFE_VERTICALFLIP)
				if ((topheight < mo.z + mo.height) and ((mo.z + mo.height + mo.momz) < topheight))
					floorclimb = true
				end
				if (topheight < mo.z)
					floorclimb = false
				end
				if (bottomheight > mo.z + mo.height - FixedMul(16*FRACUNIT,mo.scale))
					floorclimb = false
				end
			else
				if ((bottomheight > mo.z) and ((mo.z - mo.momz) > bottomheight))
					floorclimb = true
				end
				if (bottomheight > mo.z + mo.height)
					floorclimb = false
				end
				if (topheight < mo.z + FixedMul(16*FRACUNIT,mo.scale))
					floorclimb = false
				end
			end
			if (floorclimb)
				break
			end
		end
		if (mo.eflags & MFE_VERTICALFLIP)
			if ((floorz <= mo.z + mo.height)
				and ((mo.z + mo.height - mo.momz) <= floorz))
				floorclimb = true
			end
			if ((floorz > mo.z)
				and glidesector.floorpic == "F_SKY1")
				return false
			end
			if ((mo.z + mo.height - FixedMul(16*FRACUNIT,mo.scale) > ceilingz)
				or (mo.z + mo.height <= floorz))
				floorclimb = true
			end
		else
			if ((ceilingz >= mo.z)
				and ((mo.z - mo.momz) >= ceilingz))
				floorclimb = true
			end
			if ((ceilingz < mo.z+mo.height)
				and glidesector.ceilingpic == "F_SKY1")
				return false
			end
			if ((mo.z + FixedMul(16*FRACUNIT,mo.scale) < floorz)
				or (mo.z >= ceilingz))
				floorclimb = true
			end
		end
		if (not floorclimb)
			return false
		end
		return true
	else
		return true
	end
	return false
end)

rawset(_G,"PTR_GlideClimbTraverse",function(slidemo,li)
	local checkline = li
	local rover
	local topheight, bottomheight
	local fofline = false
	local checksector = (li.backsector and not P_PointOnLineSide(slidemo.x, slidemo.y, li)) and li.backsector or li.frontsector
	if (checksector.ffloors)
		for rover in checksector.ffloors()
			if (not(rover.flags & FF_EXISTS) or not(rover.flags & FF_BLOCKPLAYER) or ((rover.flags & FF_BUSTUP) and (slidemo.player.charflags & SF_CANBUSTWALLS)))
				continue
			end
			topheight = rover.topheight
			bottomheight = rover.bottomheight
			if (topheight < slidemo.z)
				continue
			end
			if (bottomheight > slidemo.z + slidemo.height)
				continue
			end
			if (rover.master.flags & ML_TFERLINE)
				local linenum = checksector.lines[0]
				checkline = rover.master.frontsector.lines[0] + linenum
				fofline = true
			end
			break
		end
	end
	local canclimb
	local climbangle, climbline
	local whichside = P_PointOnLineSide(slidemo.x, slidemo.y, li)
	climbangle = R_PointToAngle2(li.v1.x, li.v1.y, li.v2.x, li.v2.y)
	climbline = climbangle
	if (whichside)
		climbline = $+ ANGLE_180
	end
	climbangle = $+ (ANGLE_90 * (whichside and -1 or 1))
	canclimb = (li.backsector and P_IsClimbingValid(slidemo.player, climbangle) or true)
	if (((not slidemo.player.climbing and abs((slidemo.angle - ANGLE_90 - climbline)) < ANGLE_45)
	or (slidemo.player.climbing == 1 and abs((slidemo.angle - climbline)) < ANGLE_135))
	and canclimb) 
		return true,climbangle
	end
	return false,0
end)

local function VoidyCheckAndCrumble(mo, sec)
  for fof in sec.ffloors()
    if not (fof.flags & FF_EXISTS) then continue end
    if not (fof.flags & FF_BUSTUP) then continue end
    if mo.z + mo.height < fof.bottomheight then continue end 
    if mo.z > fof.topheight then continue end
    EV_CrumbleChain(fof)
  end
end

local function VoidyCrumbleCollide(mo, line)
	for _, sec in ipairs({line.frontsector, line.backsector})
		VoidyCheckAndCrumble(mo, sec)
	end
end

local function VoidyBlockMap(fuckyou)
	if (fuckyou.flags & (MF_MISSILE|MF_NOBLOCKMAP)) == (MF_MISSILE|MF_NOBLOCKMAP) 
		fuckyou.flags = $ & ~MF_NOBLOCKMAP
	end
end

local function BasicShredderThink(atomshredder)
	for player in players.iterate do
		local p = player.mo
		if atomshredder and atomshredder.valid and p and p.valid and p.skin == "voidy"
			local fx = atomshredder.x
			local fy = atomshredder.y
			local fr = atomshredder.radius
			searchBlockmap("objects", function(atomshredder, found)
				if found.valid
					if found.flags & MF_SHOOTABLE
					and found.z - found.scale <= atomshredder.z + atomshredder.height
					and found.z + found.height + found.scale >= atomshredder.z
						P_DamageMobj(found, atomshredder, p, 1)
					end
				end
				if (found.valid and found.flags & MF_MISSILE)
				and found.z - found.scale <= atomshredder.z + atomshredder.height
				and found.z + found.height + found.scale >= atomshredder.z
					P_RemoveMobj(found)
				end
				if (found.valid and found.type == MT_EGGSHIELD) 
				and found.z - found.scale <= atomshredder.z + atomshredder.height
				and found.z + found.height + found.scale >= atomshredder.z
					P_KillMobj(found, atomshredder, p)
				end
				if (found.valid and found.type == MT_METALSONIC_BATTLE) 
				and found.state == found.info.spawnstate
				and not (found.flags2 & MF2_CLASSICPUSH) 
				and found.z - found.scale <= atomshredder.z + atomshredder.height
				and found.z + found.height + found.scale >= atomshredder.z
					found.flags2 = $ | MF2_CLASSICPUSH
				end
			end, atomshredder, fx-fr, fx+fr, fy-fr, fy+fr)
			VoidyCheckAndCrumble(atomshredder, atomshredder.subsector.sector)
		end
	end
end

local function VoidyKillSpikes(thing, tmthing)
	if thing and thing.valid and tmthing and tmthing.valid
	and (tmthing.type == MT_IMPACTSHOCKWAVE or tmthing.type == MT_RIGHTSHREDDER or tmthing.type == MT_LEFTSHREDDER or tmthing.type == MT_DUALSHREDDER or tmthing.type == MT_ENERGYPILLAR 
	or tmthing.type == MT_FRENZY_RIGHTSHREDDER or tmthing.type == MT_FRENZY_LEFTSHREDDER or tmthing.type == MT_FRENZY_DUALSHREDDER or tmthing.type == MT_FRENZY_ENERGYBALL or tmthing.type == MT_FRENZY_ENERGYPILLAR 
	or tmthing.type == MT_VOIDYBLACKHOLE or (srb2p and tmthing.player and tmthing.skin == "voidy" and tmthing.state == S_PLAY_GLIDE)) 
	and thing.z - thing.scale <= tmthing.z + tmthing.height + (tmthing and P_MobjFlip(tmthing) or tmthing.momz)
	and thing.z + thing.height + thing.scale >= tmthing.z + (tmthing and P_MobjFlip(tmthing) or tmthing.momz)
		P_KillMobj(thing, tmthing)
		return true
	end
end

local function VoidyFakeNoClip(mo)
	P_SetOrigin(mo, mo.x + mo.momx, mo.y + mo.momy, mo.z + mo.momz)
    return
	true
end

local function ValidVoidyObject(mobj)
	if (mobj.type == MT_RING 
	or mobj.type == MT_FLINGRING 
	or mobj.type == MT_COIN 
	or mobj.type == MT_FLINGCOIN 
	or mobj.type == MT_BLUESPHERE 
	or mobj.type == MT_FLINGBLUESPHERE 
	or mobj.flags & MF_ENEMY 
	or mobj.flags & MF_MONITOR 
	or mobj.flags & MF_MISSILE)
		return true
	else
		return false
	end
end

local function VoidyAttract(mo, target, speed, xoffs, yoffs, zoffs)
	xoffs = $ or 0
	yoffs = $ or 0
	zoffs = $ or 0
	local newx = target.x + xoffs
	local newy = target.y + yoffs
	local newz = target.z + zoffs
	local dist = R_PointToDist2(0, 0, R_PointToDist2(mo.x, mo.y, newx, newy), newz - mo.z)
	if speed < dist
		mo.momx = FixedMul(FixedDiv(newx - mo.x, dist), speed)
		mo.momy = FixedMul(FixedDiv(newy - mo.y, dist), speed)
		mo.momz = FixedMul(FixedDiv(newz - mo.z, dist), speed)
	elseif speed > dist
		mo.momx = 0
		mo.momy = 0
		mo.momz = 0
		P_SetOrigin(mo, newx, newy, newz)
	end
end

local function FrenzyShredderThink(atomshredder)
	for player in players.iterate do
		local p = player.mo
		if atomshredder and atomshredder.valid and p and p.valid and p.skin == "voidy"
			local fx = atomshredder.x
			local fy = atomshredder.y
			local fr = atomshredder.radius
			searchBlockmap("objects", function(atomshredder, found)
				if found.valid
					if found.flags & MF_SHOOTABLE
					and found.z - found.scale <= atomshredder.z + atomshredder.height
					and found.z + found.height + found.scale >= atomshredder.z
						P_DamageMobj(found, atomshredder, p, 2)
					end
				end
				if (found.valid and found.flags & MF_MISSILE)
				and found.z - found.scale <= atomshredder.z + atomshredder.height
				and found.z + found.height + found.scale >= atomshredder.z
					P_RemoveMobj(found)
				end
				if (found.valid and found.type == MT_EGGSHIELD) 
				and found.z - found.scale <= atomshredder.z + atomshredder.height
				and found.z + found.height + found.scale >= atomshredder.z
					P_KillMobj(found, atomshredder, p)
				end
				if (found.valid and found.type == MT_METALSONIC_BATTLE) 
				and found.state == found.info.spawnstate
				and not (found.flags2 & MF2_CLASSICPUSH) 
				and found.z - found.scale <= atomshredder.z + atomshredder.height
				and found.z + found.height + found.scale >= atomshredder.z
					found.flags2 = $ | MF2_CLASSICPUSH
				end
			end, atomshredder, fx-fr, fx+fr, fy-fr, fy+fr)
			VoidyCheckAndCrumble(atomshredder, atomshredder.subsector.sector)
		end
	end
end

local frenzyspritechange = {}
frenzyspritechange[SPR2_GLID] = SPR2_FGLI
frenzyspritechange[SPR2_JUMP] = SPR2_FJUM
frenzyspritechange[SPR2_FALL] = SPR2_FFAL
frenzyspritechange[SPR2_LAND] = SPR2_FLAN
frenzyspritechange[SPR2_SPNG] = SPR2_FSPN
frenzyspritechange[SPR2_ROLL] = SPR2_FROL
frenzyspritechange[SPR2_PAIN] = SPR2_FPAI
frenzyspritechange[SPR2_GASP] = SPR2_FGAS
frenzyspritechange[SPR2_RIDE] = SPR2_FRID
frenzyspritechange[SPR2_STND] = SPR2_FSTN
frenzyspritechange[SPR2_WAIT] = SPR2_FSTN
frenzyspritechange[SPR2_WALK] = SPR2_FWAL
frenzyspritechange[SPR2_RUN_] = SPR2_FRU_
frenzyspritechange[SPR2_EDGE] = SPR2_FEDG
local frenzyspriterestore = {}
frenzyspriterestore[SPR2_FGLI] = SPR2_GLID
frenzyspriterestore[SPR2_FJUM] = SPR2_JUMP
frenzyspriterestore[SPR2_FFAL] = SPR2_FALL
frenzyspriterestore[SPR2_FLAN] = SPR2_LAND
frenzyspriterestore[SPR2_FSPN] = SPR2_SPNG
frenzyspriterestore[SPR2_FROL] = SPR2_ROLL
frenzyspriterestore[SPR2_FPAI] = SPR2_PAIN
frenzyspriterestore[SPR2_FGAS] = SPR2_GASP
frenzyspriterestore[SPR2_FRID] = SPR2_RIDE
frenzyspriterestore[SPR2_FSTN] = SPR2_STND
frenzyspriterestore[SPR2_FWAL] = SPR2_WALK
frenzyspriterestore[SPR2_FRU_] = SPR2_RUN_
frenzyspriterestore[SPR2_FEDG] = SPR2_EDGE

local function VoidySuperTrans(player)
    if ((player.mo.state == S_PLAY_SUPER_TRANS1) 
	or (player.mo.state == S_PLAY_SUPER_TRANS2) 
	or (player.mo.state == S_PLAY_SUPER_TRANS3) 
	or (player.mo.state == S_PLAY_SUPER_TRANS4) 
	or (player.mo.state == S_PLAY_SUPER_TRANS5) 
	or (player.mo.state == S_PLAY_SUPER_TRANS6))
        return true
    else
        return false
    end
end

local function VoidySuperCheck(player)
    if not P_IsObjectOnGround(player.mo) and player.rings >= 50 and All7Emeralds(emeralds) and not player.powers[pw_super] and (player.playerstate == PST_LIVE)
    and (skins[player.mo.skin].flags & SF_SUPER)
    and not (player.cmd.buttons & BT_USE)
    and not (player.pflags & PF_THOKKED) and not (mapheaderinfo[gamemap].typeoflevel & TOL_NIGHTS) and not (player.pflags & PF_SHIELDABILITY) and (player.pflags & PF_JUMPED) and not VoidySuperTrans(player)
    and not VoidySuperTrans(player)
    and ((player.powers[pw_shield] == 0) or (player.powers[pw_shield] > 0)) 
	and (player.frenzied == 0)
    then
        return true
    else
        return false
    end
end

local function VoidySuper(player)
    player.pflags = $ & ~PF_JUMPED
    player.charflags = $1 | SF_SUPER
	P_DoSuperTransformation(player, false)
end

local function VoidyVariableReset(player, health)
	if (player.voidyvars == nil)
		player.voidyvars = {}
	end
	player.impacttimer = 0
	player.dodgeroll = 0
	player.attackcount = 0
	player.attacktimer = 0
	player.finishertimer = 0
	player.voidycharge = 0
	player.stilltimer = 0
	player.attacking = 0
	if io and player == consoleplayer
		local file = io.openlocal(LIFEFORCE)
		if file
			player.lifeforce = file:read("*n")
			file:close()
		else
			player.lifeforce = 0
			if io and player == consoleplayer
				local file = io.openlocal(LIFEFORCE, "w+")
				file:write(player.lifeforce)
				file:close()
			end
		end
	end
	player.transforming = 0
	player.frenzied = 0
	player.frenzyfallframe = 0
	player.frenzyrollframe = 0
	player.frenzystandframe = 0
	player.frenzywalkframe = 0
	player.frenzyrunframe = 0
	player.frenzyedgeframe = 0
end

addHook("MapLoad", function()
	for player in players.iterate
		VoidyVariableReset(player, false)
	end
end)

addHook("JumpSpecial", function(player)
	local mo = player.mo
	if mo.skin == "voidy"
		if player.powers[pw_super] and player.pflags & PF_THOKKED
			P_SetObjectMomZ(mo, 0*FRACUNIT)
		end
	end
end)

addHook("SpinSpecial", function(player)
	local mo = player.mo
	if mo.skin == "voidy"
		if player.powers[pw_super] and player.pflags & PF_JUMPED
			P_SetObjectMomZ(mo, 10*FRACUNIT)
			mo.state = S_PLAY_JUMP
		end
	end
end)

addHook("AbilitySpecial", function(player)
	local mo = player.mo
	if mo.skin == "voidy"
		if not player.powers[pw_super] 
		and not (player.mo.eflags & MFE_GOOWATER)
			mo.state = S_PLAY_GLIDE
			return true
		end
	end
end)

addHook("PlayerThink", function(player)
	if player.mo and player.mo.skin == "voidy"
		if (player.voidyvars == nil)
			VoidyVariableReset(player, true)
		end
		player.powers[pw_spacetime] = 0
		if player.powers[pw_super]
			player.charability = CA_FLOAT
			player.charflags = $|SF_MULTIABILITY
			player.powers[pw_shield] = SH_NONE|SH_PROTECTWATER
		else
			player.charability = CA_NONE
			player.charflags = $ & ~SF_MULTIABILITY
		end
		if (player.mo.state == S_PLAY_GLIDE) and not (player.mo.eflags & MFE_GOOWATER)	
			local trail = P_SpawnGhostMobj(player.mo)
			trail.colorized = true
			trail.blendmode = AST_ADD
			trail.color = player.mo.color
			player.pflags = $|PF_THOKKED
			player.powers[pw_nocontrol] = 1
			if not srb2p
				player.powers[pw_strong] = STR_ANIM|STR_HEAVY|STR_SPRING|STR_SPIKE|STR_ATTACK
			end
			P_InstaThrust(player.mo, player.drawangle, 50*FRACUNIT)
			P_SetObjectMomZ(player.mo, -20*FRACUNIT)
			player.diveconfirm = true
		elseif player.diveconfirm and (P_IsObjectOnGround(player.mo) or player.mo.eflags & MFE_GOOWATER)
			if not (player.playerstate == PST_DEAD)
				player.mo.state = S_PLAY_IMPACTLAND
			end
			player.powers[pw_nocontrol] = 1
			player.mo.momx = 0*FRACUNIT
			player.mo.momy = 0*FRACUNIT
			P_StartQuake(10*FRACUNIT, TICRATE*1/3)
			player.impacttimer = $+1
			S_StartSound(player.mo, sfx_s3k9b)
			for vx = player.mo.x, player.mo.x + player.mo.radius, player.mo.radius
				for vy = player.mo.y, player.mo.y + player.mo.radius, player.mo.radius
					local ss = R_PointInSubsector(vx,vy)
					for fof in ss.sector.ffloors()
						if (fof.flags & FF_BUSTUP) and (fof.flags & FF_EXISTS)
							EV_CrumbleChain(ss.sector, fof)
						end
					end
				end
			end
			if player.impacttimer == 1
				if player.powers[pw_shield] == SH_ARMAGEDDON
					P_BlackOw(player)
				else
					if player.frenzied == 0
						for i = 1, 5
							player.wave = P_SPMAngle(player.mo, MT_IMPACTSHOCKWAVE, player.mo.angle-ANG10*i, 0)
							player.wave = P_SPMAngle(player.mo, MT_IMPACTSHOCKWAVE, player.mo.angle, 0)
							player.wave = P_SPMAngle(player.mo, MT_IMPACTSHOCKWAVE, player.mo.angle+ANG10*i, 0)
						end
					else
						for i = 1, 50
							player.wave = P_SPMAngle(player.mo, MT_IMPACTSHOCKWAVE, player.mo.angle-ANG10*i, 0)
							player.wave = P_SPMAngle(player.mo, MT_IMPACTSHOCKWAVE, player.mo.angle, 0)
							player.wave = P_SPMAngle(player.mo, MT_IMPACTSHOCKWAVE, player.mo.angle+ANG10*i, 0)
						end
					end
				end
			end
			if player.impacttimer <= 50
				player.diveconfirm = false
				player.impacttimer = 0
			end
		end
		if (player.mo.eflags & MFE_SPRUNG)
			player.diveconfirm = false
			player.airconfirm = false
		end
		if not (player.exiting or srb2p or player.transforming == 1)
			if not (P_PlayerInPain(player) or player.playerstate == PST_DEAD)
				if ((player.powers[pw_carry] == CR_NONE) and not (player.pflags & PF_SPINNING))
					if (player.cmd.buttons & BT_USE)
						player.spintapping = true
						player.spintapready = false
					elseif player.spintapping
						player.spintapready = true
						player.spintapping = false
					else
						player.spintapready = false
					end
					if player.spintapready and player.mo.state == S_PLAY_IMPACTLAND and not player.airconfirm
						player.spinconfirm = true
					end
					if player.spinconfirm and not player.weapondelay and player.impacttimer == 0
						player.mo.state = S_PLAY_ROLL
						player.pflags = $|PF_SPINNING
						S_StartSound(player.mo, sfx_zoom)
						local circle = P_SpawnMobjFromMobj(player.mo, 0, 0, player.mo.scale * 19, MT_REBOUND)
						circle.angle = player.mo.angle + ANGLE_90
						circle.scale = 0
						circle.destscale = 10*FRACUNIT
						circle.scalespeed = FRACUNIT*1/5
						circle.color = player.mo.color
						circle.colorized = true
						P_InstaThrust(player.mo, player.mo.angle, 30*FRACUNIT)
						circle.fuse = 10
						player.weapondelay = TICRATE*1
						player.dodgeroll = 1
					end
					if player.spintapping 
					and not (player.diveconfirm or player.spinconfirm) 
					and not player.powers[pw_super]
						player.voidycharge = $ + 1
					end
					if player.spintapready and player.voidycharge < 50
						player.voidycharge = 0
					end
					if player.spintapready 
					and not (player.diveconfirm or player.spinconfirm or player.airconfirm) 
					and not player.powers[pw_super]
						if P_IsObjectOnGround(player.mo)
							if player.voidycharge >= 50
								if not player.succconfirm
									if not (player.mo.state == S_PLAY_EARTHSPIKER1)	
										player.mo.state = S_PLAY_EARTHSPIKER1
									end
									if player.frenzied == 0
										P_StartQuake(10*FRACUNIT, TICRATE*1/3)
										S_StartSound(player.mo, sfx_pstop)
										S_StartSound(player.mo, sfx_s255)
										player.spike = P_SpawnMobjFromMobj(player.mo, FixedMul(150*FRACUNIT, cos(player.drawangle)), FixedMul(150*FRACUNIT, sin(player.drawangle)), 0, MT_VOIDYSPIKE)
									else
										P_StartQuake(40*FRACUNIT, TICRATE*1/3)
										S_StartSound(player.mo, sfx_pstop)
										S_StartSound(player.mo, sfx_brakrx)
										player.spike = P_SPMAngle(player.mo, MT_EARTHSCORCHER, player.mo.angle)
									end
									player.attacktimer = 20
								else
									if not (player.mo.state == S_PLAY_VACUUMREVERT1)	
										player.mo.state = S_PLAY_VACUUMREVERT1
									end
									player.attacktimer = 20
									player.succconfirm = false
								end
								player.stilltimer = 0
								player.voidycharge = 0
							else
								if player.attackcount == 0 and (player.attacktimer == 0)
									if not (player.mo.state == S_PLAY_GROUNDBASICR1)	
										player.mo.state = S_PLAY_GROUNDBASICR1
									end
									player.attacktimer = 20
								elseif player.attackcount == 1 and (player.attacktimer > 0 and player.attacktimer <= 10)
									if not (player.mo.state == S_PLAY_GROUNDBASICL1)	
										player.mo.state = S_PLAY_GROUNDBASICL1
									end
									player.attacktimer = 20
								elseif player.attackcount == 2 and (player.attacktimer > 0 and player.attacktimer <= 10)
									if not (player.mo.state == S_PLAY_GROUNDBASICD1)	
										player.mo.state = S_PLAY_GROUNDBASICD1
									end
									if player.frenzied == 0	
										S_StartSound(player.mo, sfx_claw2)
										player.shredder = P_SPMAngle(player.mo, MT_DUALSHREDDER, player.mo.angle, 0)
									else
										S_StartSound(player.mo, sfx_claw2)
										player.shredder = P_SpawnMobjFromMobj(player.mo, 0, 0, 0, MT_FRENZY_DUALSHREDDER)
										P_StartQuake(20*FRACUNIT, TICRATE*1/3)
									end
									player.attacktimer = 20
								end
							end
						else
							if player.voidycharge >= 50
								player.airconfirm = true
								player.chargedairconfirm = true
								player.voidycharge = 0
							else
								if player.attackcount == 0 and (player.attacktimer == 0)
									if not (player.mo.state == S_PLAY_AIRBASICR1)	
										player.mo.state = S_PLAY_AIRBASICR1
									end
									player.attacktimer = 20
								elseif player.attackcount == 1 and (player.attacktimer > 0 and player.attacktimer <= 10)
									if not (player.mo.state == S_PLAY_AIRBASICL1)
										player.mo.state = S_PLAY_AIRBASICL1
									end
									player.attacktimer = 20
								elseif player.attackcount == 2 and (player.attacktimer > 0 and player.attacktimer <= 10)
									if not (player.mo.state == S_PLAY_AIRBASICD1)	
										player.mo.state = S_PLAY_AIRBASICD1
									end
									player.attacktimer = 20
								end
							end
						end
					end
					if player.weapondelay
						player.spinconfirm = false
					end
					if player.attacktimer > 0
						player.attacktimer = $-1
					end
					if player.attacktimer <= 0
						player.attackcount = 0
						player.attacktimer = 0
					end
					if player.mo.state == S_PLAY_GROUNDBASICR1 
					or player.mo.state == S_PLAY_AIRBASICR1
						player.attackcount = 1
						if player.frenzied == 0
							S_StartSound(player.mo, sfx_claw1)
							player.shredder = P_SpawnMobjFromMobj(player.mo, FixedMul(50*FRACUNIT, cos(player.drawangle)), FixedMul(50*FRACUNIT, sin(player.drawangle)), 0, MT_RIGHTSHREDDER)
						else
							S_StartSound(player.mo, sfx_xclaw1)
							player.shredder = P_SpawnMobjFromMobj(player.mo, FixedMul(100*FRACUNIT, cos(player.drawangle)), FixedMul(100*FRACUNIT, sin(player.drawangle)), -100*FRACUNIT, MT_FRENZY_RIGHTSHREDDER)
							P_StartQuake(10*FRACUNIT, TICRATE*1/3)
						end
					end
					if player.mo.state == S_PLAY_GROUNDBASICL1 
					or player.mo.state == S_PLAY_AIRBASICL1
						player.attackcount = 2
						if player.frenzied == 0
							S_StartSound(player.mo, sfx_claw1)
							player.shredder = P_SpawnMobjFromMobj(player.mo, FixedMul(50*FRACUNIT, cos(player.drawangle)), FixedMul(50*FRACUNIT, sin(player.drawangle)), 0, MT_LEFTSHREDDER)
						else
							S_StartSound(player.mo, sfx_xclaw1)
							player.shredder = P_SpawnMobjFromMobj(player.mo, FixedMul(100*FRACUNIT, cos(player.drawangle)), FixedMul(100*FRACUNIT, sin(player.drawangle)), -100*FRACUNIT, MT_FRENZY_LEFTSHREDDER)
							P_StartQuake(10*FRACUNIT, TICRATE*1/3)
						end
					end
					if player.mo.state == S_PLAY_GROUNDBASICD1 
					or player.mo.state == S_PLAY_AIRBASICD1
						player.attackcount = 3
					end
					if player.mo.state == S_PLAY_AIRBASICR1 
					or player.mo.state == S_PLAY_AIRBASICR2 
					or player.mo.state == S_PLAY_AIRBASICR3 
					or player.mo.state == S_PLAY_AIRBASICL1 
					or player.mo.state == S_PLAY_AIRBASICL2 
					or player.mo.state == S_PLAY_AIRBASICL3 
						player.mo.momx = 0*FRACUNIT
						player.mo.momy = 0*FRACUNIT
						player.mo.momz = 0*FRACUNIT
					end
					if player.mo.state == S_PLAY_AIRBASICD1
						P_SetObjectMomZ(player.mo, 5*FRACUNIT)
						player.mo.momx = 0*FRACUNIT
						player.mo.momy = 0*FRACUNIT
						player.airconfirm = true
					end
					if player.mo.state == S_PLAY_AIRBASICD2 
					or player.mo.state == S_PLAY_AIRBASICD3
						P_SetObjectMomZ(player.mo, -30*FRACUNIT)
						player.mo.momx = 0*FRACUNIT
						player.mo.momy = 0*FRACUNIT
						local trail = P_SpawnGhostMobj(player.mo)
						trail.colorized = true
						trail.blendmode = AST_ADD
						trail.color = player.mo.color
					end
					if player.airconfirm
						if not player.chargedairconfirm
							if P_IsObjectOnGround(player.mo)
								if not (player.playerstate == PST_DEAD)
									player.mo.state = S_PLAY_FINISHERLAND
								end
								player.powers[pw_nocontrol] = 1
								player.mo.momx = 0*FRACUNIT
								player.mo.momy = 0*FRACUNIT
								player.finishertimer = $+1
								for vx = player.mo.x, player.mo.x + player.mo.radius, player.mo.radius
									for vy = player.mo.y, player.mo.y + player.mo.radius, player.mo.radius
										local ss = R_PointInSubsector(vx,vy)
										for fof in ss.sector.ffloors()
											if (fof.flags & FF_BUSTUP) and (fof.flags & FF_EXISTS)
												EV_CrumbleChain(ss.sector, fof)
											end
										end
									end
								end
								if player.frenzied == 0
									if player.finishertimer == 1
										player.pillar = P_SpawnMobjFromMobj(player.mo, FixedMul(100*FRACUNIT, cos(player.drawangle)), FixedMul(100*FRACUNIT, sin(player.drawangle)), 0, MT_ENERGYPILLAR)
										S_StartSound(player.mo, sfx_kc40)
										P_StartQuake(10*FRACUNIT, TICRATE*1/3)
									elseif player.finishertimer == 10
										player.pillar = P_SpawnMobjFromMobj(player.mo, FixedMul(150*FRACUNIT, cos(player.drawangle)), FixedMul(150*FRACUNIT, sin(player.drawangle)), 0, MT_ENERGYPILLAR)
										S_StartSound(player.mo, sfx_kc40)
										P_StartQuake(10*FRACUNIT, TICRATE*1/3)
									elseif player.finishertimer == 20
										player.pillar = P_SpawnMobjFromMobj(player.mo, FixedMul(200*FRACUNIT, cos(player.drawangle)), FixedMul(200*FRACUNIT, sin(player.drawangle)), 0, MT_ENERGYPILLAR)
										S_StartSound(player.mo, sfx_kc40)
										P_StartQuake(10*FRACUNIT, TICRATE*1/3)
									end
								else
									if player.finishertimer == 1
										for i = 1,6	
											player.ball = P_SpawnMobjFromMobj(player.mo, FixedMul(50*FRACUNIT, cos(player.drawangle)), FixedMul(50*FRACUNIT, sin(player.drawangle)), 5*FRACUNIT, MT_FRENZY_ENERGYBALL)
											P_InstaThrust(player.ball, player.mo.angle+ANG60*i, 9*FRACUNIT)
											player.ball.target = player.mo
											P_SetObjectMomZ(player.ball, 9*FRACUNIT)
										end
										S_StartSound(player.mo, sfx_bexpld)
										S_StartSound(player.mo, sfx_xfinsh)
										P_StartQuake(20*FRACUNIT, TICRATE*1/3)
									end
								end
							end
							if not srb2p
								player.powers[pw_strong] = STR_ANIM|STR_HEAVY|STR_SPRING
							end
						else
							player.finishertimer = $+1
							if player.finishertimer == 1
								if not (player.mo.state == S_PLAY_CRESCENTDANCER1)	
									player.mo.state = S_PLAY_CRESCENTDANCER1
								end
								P_SetObjectMomZ(player.mo, 0*FRACUNIT)
								if player.frenzied == 0	
									player.crescent = P_SPMAngle(player.mo, MT_CRESCENTDANCER, player.mo.angle)
									S_StartSound(player.mo, sfx_cdance)
								else
									player.reaper = P_SPMAngle(player.mo, MT_CRESCENTREAPER, player.mo.angle)
									S_StartSound(player.mo, sfx_rail1)
									P_StartQuake(40*FRACUNIT, TICRATE*1/3)
								end
							elseif player.finishertimer == 2
								P_SetObjectMomZ(player.mo, 10*FRACUNIT)
								if player.frenzied == 1
									P_InstaThrust(player.mo, player.mo.angle, -20*FRACUNIT)
								end
							elseif player.finishertimer == 20
								if not (player.mo.state == S_PLAY_CRESCENTDANCER1)	
									player.mo.state = S_PLAY_CRESCENTDANCER1
								end
								P_SetObjectMomZ(player.mo, 0*FRACUNIT)
								if player.frenzied == 0	
									player.crescent = P_SPMAngle(player.mo, MT_CRESCENTDANCER, player.mo.angle)
									S_StartSound(player.mo, sfx_cdance)
								else
									player.reaper = P_SPMAngle(player.mo, MT_CRESCENTREAPER, player.mo.angle)
									S_StartSound(player.mo, sfx_rail1)
									P_StartQuake(40*FRACUNIT, TICRATE*1/3)
								end
							elseif player.finishertimer == 21
								P_SetObjectMomZ(player.mo, 10*FRACUNIT)
								if player.frenzied == 1
									P_InstaThrust(player.mo, player.mo.angle, -20*FRACUNIT)
								end
							end
						end
						if player.finishertimer >= 21
							player.airconfirm = false
							player.chargedairconfirm = false
							player.finishertimer = 0
						end
					end
					if not (player.cmd.buttons & BT_CUSTOM1)
						player.cust1tapready = true
						player.cust1tapping = false
					elseif player.cust1tapready
						player.cust1tapping = true
						player.cust1tapready = false
					else
						player.cust1tapping = false
					end
					if player.lifeforce == 80
						if P_IsObjectOnGround(player.mo) 
						and not player.powers[pw_super] 
						and player.attacking == 0 
						and player.transforming == 0
						and player.frenzied == 0 
						and player.cust1tapping
							if not (player.mo.state == S_PLAY_FRENZY_INTRO1)	
								player.mo.state = S_PLAY_FRENZY_INTRO1
							end
							S_StartSound(player.mo, sfx_frenzy)
							S_StopMusic(player)
						end
					end
				end
			end
		end
		if player.dodgeroll == 1 and (player.pflags & PF_SPINNING)
			local trail = P_SpawnGhostMobj(player.mo)
			trail.colorized = true
			trail.blendmode = AST_ADD
			trail.color = player.mo.color
		elseif player.dodgeroll == 1 and not (player.pflags & PF_SPINNING)
			player.dodgeroll = 0
		end
		if player.voidycharge == 10
			S_StartSound(player.mo, sfx_aschrg)
			player.aura = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_VOIDYCHARGEAURA)
			player.aura.scale = 5*FRACUNIT
			player.aura.destscale = 0*FRACUNIT
			player.aura.scalespeed = FRACUNIT*1/8
			player.aura.color = SKINCOLOR_NEON
			player.aura.colorized = true
		end
		if player.voidycharge >= 10 
		and P_IsObjectOnGround(player.mo) 
		and player.speed == 0
			player.stilltimer = $ + 1
			if player.stilltimer < 50
				player.mo.state = S_PLAY_CHARGESTAND
			end
		else
			player.stilltimer = 0
		end
		if player.voidycharge == 0 or player.voidycharge >= 50
			S_StopSoundByID(player.mo, sfx_aschrg)
		end
		if player.voidycharge == 50
			S_StartSound(player.mo, sfx_s3k5c)
			S_StartSound(player.mo, sfx_aschgd)
			player.flash = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_VOIDYCHARGEFLASH)
			player.flash.scale = 3*FRACUNIT
			player.flash.color = SKINCOLOR_NEON
			player.flash.colorized = true
		end
		if player.stilltimer == 50
			player.mo.state = S_PLAY_VACUUMGULLET1
			player.hitbox = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_VACCHITBOX)
			player.hitbox.target = player.mo
			player.succconfirm = true
		end
		if player.stilltimer >= 50
			player.mo.momx = 0*FRACUNIT
			player.mo.momy = 0*FRACUNIT
			player.pflags = $|PF_JUMPSTASIS
			if player.mo.eflags & MFE_UNDERWATER	
				player.powers[pw_shield] = SH_NONE|SH_PROTECTWATER
				P_RestoreMusic(player)
			end
			if (player.mo.state == S_PLAY_VACUUMGULLET4)
				if player.frenzied == 0
					player.succ = P_SpawnMobj(player.mo.x + FixedMul(500*FRACUNIT, cos(player.drawangle)), player.mo.y + FixedMul(500*FRACUNIT, sin(player.drawangle)), player.mo.z, MT_SUCCFX)
					player.succ.fuse = 24
					player.succ.scale = 4*FRACUNIT
					player.succ.destscale = 1*FRACUNIT
					player.succ.scalespeed = FRACUNIT*1/7
					S_StartSound(player.mo, sfx_s3ka0)
					P_InstaThrust(player.succ, player.mo.angle, -20*FRACUNIT)
					player.succ.angle = player.mo.angle + ANGLE_90
				else
					player.succ = P_SpawnMobjFromMobj(player.mo, 0, 0, 550*FRACUNIT, MT_FRENZY_SUCCFX)
					player.succ.fuse = 21
					player.succ.scale = 8*FRACUNIT
					player.succ.destscale = 1*FRACUNIT
					player.succ.scalespeed = FRACUNIT*1/3
					P_SetObjectMomZ(player.succ, -5*FRACUNIT)
				end
				player.succ.color = SKINCOLOR_NEON
				player.succ.colorized = true
			end
			if player.frenzied == 1
				P_StartQuake(30*FRACUNIT, TICRATE*1/3)
				if (leveltime%13 == 0)
					S_StartSound(player.mo, sfx_succ)
				end
			end
			if player.lifeforce < 80
			and (leveltime%22 == 0)
				player.lifeforce = $ + 1
			end
		end
		if (player.mo.state == S_PLAY_EARTHSPIKER1 
		or player.mo.state == S_PLAY_CRESCENTDANCER1)
			player.mo.frame = J
		elseif (player.mo.state == S_PLAY_EARTHSPIKER2 
		or player.mo.state == S_PLAY_CRESCENTDANCER2)
			player.mo.frame = K
		elseif (player.mo.state == S_PLAY_EARTHSPIKER3 
		or player.mo.state == S_PLAY_CRESCENTDANCER3)
			player.mo.frame = L
		elseif (player.mo.state == S_PLAY_CRESCENTDANCER4 
		or player.mo.state == S_PLAY_VACUUMGULLET1)
			player.mo.frame = M
		end
		if player.mo.state == S_PLAY_VACUUMGULLET2
			player.mo.frame = N
		end
		if player.mo.state == S_PLAY_VACUUMGULLET3
			player.mo.frame = O
		end
		if player.mo.state == S_PLAY_VACUUMGULLET4
			player.mo.frame = O
		end
		if player.mo.state == S_PLAY_VACUUMREVERT1
			player.mo.frame = O
		end
		if player.mo.state == S_PLAY_VACUUMREVERT2
			player.mo.frame = N
		end
		if player.mo.state == S_PLAY_VACUUMREVERT3
			player.mo.frame = M
		end
		if player.mo.state == S_PLAY_VACUUMREVERT1 
		or player.mo.state == S_PLAY_VACUUMREVERT2 
		or player.mo.state == S_PLAY_VACUUMREVERT3
			player.pflags = $|PF_FULLSTASIS
			if player.mo.eflags & MFE_UNDERWATER	
				player.powers[pw_shield] = SH_NONE
			end
		end
		if player.lifeforce >= 80 
		or player.powers[pw_super]
			player.lifeforce = 80
		end
		if player.lifeforce == 80
			if player.frenzied == 0 
			and not player.frenzynotified
				S_StartSound(player.mo, sfx_fready)
				player.frenzynotified = true
			end
		else
			player.frenzynotified = false
		end
		if player.attacktimer > 0 
		or player.voidycharge > 0 
		or (player.mo.state == S_PLAY_IMPACTLAND or player.mo.state == S_PLAY_FINISHERLAND)
			player.attacking = 1
		else
			player.attacking = 0
		end
		if player.mo.state == S_PLAY_FRENZY_INTRO1 
		or player.mo.state == S_PLAY_FRENZY_INTRO2 
		or player.mo.state == S_PLAY_FRENZY_TRANS1 
		or player.mo.state == S_PLAY_FRENZY_TRANS2 
		or player.mo.state == S_PLAY_FRENZY_TRANS3 
		or player.mo.state == S_PLAY_FRENZY_TRANS4 
		or player.mo.state == S_PLAY_FRENZY_TRANS5 
		or player.mo.state == S_PLAY_FRENZY_TRANS6 
		or player.mo.state == S_PLAY_SUPER_INTRO1 
		or player.mo.state == S_PLAY_SUPER_INTRO2 
		or player.mo.state == S_PLAY_SUPER_INTRO3 
		or player.mo.state == S_PLAY_SUPER_TRANS1 
		or player.mo.state == S_PLAY_SUPER_TRANS2 
		or player.mo.state == S_PLAY_SUPER_TRANS3 
		or player.mo.state == S_PLAY_SUPER_TRANS4 
		or player.mo.state == S_PLAY_SUPER_TRANS5 
		or player.mo.state == S_PLAY_SUPER_TRANS6 
			player.transforming = 1
			player.powers[pw_nocontrol] = 1
			player.mo.momx = 0*FRACUNIT
			player.mo.momy = 0*FRACUNIT
		else
			player.transforming = 0
		end
		if player.mo.state == S_PLAY_FRENZY_TRANS3
			player.frenzied = 1
			S_StartSound(player.mo, sfx_s3k4e)
			P_StartQuake(50*FRACUNIT, TICRATE*1/3)
			P_PlayJingleMusic(player, "FRENZY", nil, true, JT_OTHER)
		end
		if player.frenzied == 1
			if player.transforming == 0
			and (leveltime%11 == 0)
				player.lifeforce = $ - 1
			end
			if P_PlayerInPain(player)
				player.mo.momx = 0
				player.mo.momy = 0
			end
			if player.lifeforce <= 0 
			or player.playerstate == PST_REBORN
				player.frenzied = 0
				P_RestoreMusic(player)
				if player.mo.state == S_PLAY_STND 
				or player.mo.state == S_PLAY_WAIT
					player.mo.state = S_PLAY_STND
				end
				if player.mo.state == S_PLAY_EDGE
					player.mo.state = S_PLAY_EDGE
				end
			end	
		end
		if player.exiting == 3
			if io and player == consoleplayer
				local file = io.openlocal(LIFEFORCE, "w+")
				file:write(player.lifeforce)
				file:close()
			end
		end
		if (All7Emeralds(emeralds)) and not player.voidynotified
			player.voidynotified = true
			S_StartSound(player.mo, sfx_s3k68)
		end
		if not G_EnoughPlayersFinished()
			if not player.powers[pw_super]
				player.charflags = $ & ~SF_SUPER
			end
			if (player.cmd.buttons & BT_CUSTOM1) and VoidySuperCheck(player) and player.transforming == 0
				if not (player.mo.state == S_PLAY_SUPER_INTRO1)	
					player.mo.state = S_PLAY_SUPER_INTRO1
				end
				S_StartSound(player.mo, sfx_void)
				S_StopMusic(player)
			end
			if P_PlayerTouchingSectorSpecial(player, 4, 6) and All7Emeralds(emeralds) and not player.powers[pw_super] then
				player.rings = 50
				VoidySuper(player)
			end
        end
		if player.mo.state == S_PLAY_SUPER_INTRO1 
		or player.mo.state == S_PLAY_SUPER_INTRO2 
		or player.mo.state == S_PLAY_SUPER_INTRO3
			P_SetObjectMomZ(player.mo, 0*FRACUNIT)
		end
		if player.mo.state == S_PLAY_SUPER_INTRO3
			VoidySuper(player)
		end
		if player.mo.state == S_PLAY_SUPER_TRANS3
			player.blackhole = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_VOIDYBLACKHOLE)
			player.blackhole.destscale = 5*FRACUNIT
			player.blackhole.scalespeed = FRACUNIT*1/3
			player.blackhole.color = SKINCOLOR_BLACK
			player.blackhole.target = player.mo
			S_StartSound(player.mo, sfx_bhole)
		end
		if player.powers[pw_super] 
		or (player.stilltimer >= 50 and player.frenzied == 1)
			local m = player.realmo
			local range = 1000*FRACUNIT
			local range2 = range - (64*FRACUNIT)
			searchBlockmap("objects", function(ref, found)
				if ValidVoidyObject(found) and found.health then
					if R_PointToDist2(m.x, m.y, found.x, found.y) <= range2 then
						if found.type == MT_RING or found.type == MT_COIN or found.type == MT_BLUESPHERE then
							found.tracer = m
						end
						local dist = R_PointToDist2(m.x, m.y, found.x, found.y)
						if dist < FRACUNIT then
							dist = FRACUNIT
						end
						local PullForce = range/dist * 3
						if PullForce > 30 then
							PullForce = 30
						end
						PullForce = $ * FRACUNIT
						VoidyAttract(found, m, PullForce)
					end
				end
				if found.flags & MF_BOSS
					if R_PointToDist2(m.x, m.y, found.x, found.y) <= range2 then
						P_DamageMobj(found, player.mo, player.mo, 1)
					end
				end
				if (found.valid and found.type == MT_EGGSHIELD)
					if R_PointToDist2(m.x, m.y, found.x, found.y) <= range2 then
						P_KillMobj(found, player.mo, player.mo)
					end
				end
				if (found.valid and found.flags & MF_MISSILE)
					if R_PointToDist2(m.x, m.y, found.x, found.y) <= range2 then
						P_RemoveMobj(found)
					end
				end
				if (found.valid and found.type == MT_METALSONIC_BATTLE) 
				and found.state == found.info.spawnstate 
				and not (found.flags2 & MF2_CLASSICPUSH)
					if R_PointToDist2(m.x, m.y, found.x, found.y) <= range2 then
						found.flags2 = $ | MF2_CLASSICPUSH
					end
				end
			end, m, m.x-range, m.x+range, m.y-range, m.y+range)
		end
		if (player.mo.state == S_PLAY_FLOAT or player.mo.state == S_PLAY_FLOAT_RUN) 
		and not player.powers[pw_super]
			P_ResetPlayer(player)
			player.mo.state = S_PLAY_FALL
		end
	end
end)

addHook("ThinkFrame", function()
	for player in players.iterate
		if not(player.mo and player.mo.valid and(player.mo.skin == "voidy"))
			continue
		end
		if (player.frenzied == 1) and (player.mo.sprite == SPR_PLAY)
			if (frenzyspritechange[player.mo.sprite2])
				player.mo.sprite2 = frenzyspritechange[$1]
			end
			if (player.mo.sprite2 == SPR2_FFAL)
				if (player.mo.tics == 1)
					player.frenzyfallframe = ($1 + 1)%2
				end
				player.mo.frame = player.frenzyfallframe
			else
				player.frenzyfallframe = 0
			end
			if (player.mo.sprite2 == SPR2_FROL)
				if (player.mo.tics == 1)
					player.frenzyrollframe = ($1 + 1)%4
				end
				player.mo.frame = player.frenzyrollframe
			else
				player.frenzyrollframe = 0
			end
			if (player.mo.sprite2 == SPR2_FSTN)
				if (leveltime%7 == 0)
					player.frenzystandframe = ($1 + 1)%4
				end
				player.mo.frame = player.frenzystandframe
			else
				player.frenzystandframe = 0
			end
			if (player.mo.sprite2 == SPR2_FWAL)
				if (player.mo.tics == 1)
					player.frenzywalkframe = ($1 + 1)%8
				end
				player.mo.frame = player.frenzywalkframe
			else
				player.frenzywalkframe = 0
			end
			if (player.mo.sprite2 == SPR2_FRU_)
				if (player.mo.tics == 1)
					player.frenzyrunframe = ($1 + 1)%8
				end
				player.mo.frame = player.frenzyrunframe
			else
				player.frenzyrunframe = 0
			end
			if (player.mo.sprite2 == SPR2_FEDG)
				if (leveltime%12 == 0)
					player.frenzyedgeframe = ($1 + 1)%2
				end
				player.mo.frame = player.frenzyedgeframe
			else	
				player.frenzyedgeframe = 0
			end
		elseif (frenzyspriterestore[player.mo.sprite2])
			player.mo.sprite2 = frenzyspriterestore[player.mo.sprite2]
		end
	end
end)

addHook("MobjMoveBlocked", function(mo,mobj,line)
	if mo and mo.valid
		if line and line.valid
			if PTR_GlideClimbTraverse(mo,line)
			and mo.player and mo.player.valid 
			and mo.skin == "voidy"
				local real,ang,sec = PTR_GlideClimbTraverse(mo,line)
				if P_IsClimbingValid(mo.player,ang) and (mo.state == S_PLAY_GLIDE) and not (mo.eflags & MFE_GOOWATER)
					mo.angle = ang
					P_BounceMove(mo)
					P_SetObjectMomZ(mo, 20*FRACUNIT)
					mo.player.drawangle = ang + ANGLE_180
					mo.state = S_PLAY_ROLL
					local circle = P_SpawnMobjFromMobj(mo, 0, 0, mo.scale * 19, MT_REBOUND)
					circle.angle = ang + ANGLE_90
					circle.scale = 0
					circle.destscale = 10*FRACUNIT
					circle.color = mo.color
					circle.colorized = true
					P_StartQuake(5*FRACUNIT, TICRATE*1/3)
					S_StartSound(mo, sfx_vowall)
					circle.fuse = 9
				end
			end
		end
	end
end, MT_PLAYER)

addHook("PlayerCanDamage", function(player, mohit)
	if player.mo.skin == "voidy"
		if player.mo.state == S_PLAY_GLIDE
			if mohit.flags & MF_ENEMY
			or mohit.flags & MF_MONITOR
			or mohit.flags & MF_SHOOTABLE
			or mohit.flags & MF_BOSS
				return true
			end
		end
	end
end)

addHook("ShouldDamage", function(mo, inf, src, dmg, dmgt)
	if (mo and (mo.skin == "voidy") and mo.player)
		if ((inf and inf.valid) or (src and src.valid))
			if mo.state == S_PLAY_GLIDE 
			or mo.player.dodgeroll == 1 
			or mo.player.transforming == 1 
			or (mo.player.succconfirm and mo.player.frenzied == 1)
				return false
			end
		end
	end
end, MT_PLAYER)

addHook("MobjLineCollide", VoidyCrumbleCollide, MT_RIGHTSHREDDER)
addHook("MobjThinker", BasicShredderThink, MT_RIGHTSHREDDER)

addHook("MobjLineCollide", VoidyCrumbleCollide, MT_LEFTSHREDDER)
addHook("MobjThinker", BasicShredderThink, MT_LEFTSHREDDER)

addHook("MobjLineCollide", VoidyCrumbleCollide, MT_DUALSHREDDER)
addHook("MobjThinker", BasicShredderThink, MT_DUALSHREDDER)
addHook("MobjMoveBlocked", VoidyFakeNoClip, MT_DUALSHREDDER)

addHook("MobjLineCollide", VoidyCrumbleCollide, MT_ENERGYPILLAR)
addHook("MobjThinker", BasicShredderThink, MT_ENERGYPILLAR)

addHook("MobjThinker", function(voidyaura)
	for player in players.iterate do
		local p = player.mo
		if voidyaura and voidyaura.valid and p and p.valid	 and p.skin == "voidy"
			if (p.eflags & MFE_VERTICALFLIP)
				P_SetOrigin(voidyaura, p.x, p.y, p.z - 10*FRACUNIT)
			else
				P_SetOrigin(voidyaura, p.x, p.y, p.z + 50*FRACUNIT)
			end
			if player.voidycharge == 0 or player.voidycharge == 50
				voidyaura.state = S_NULL
			end
		end
	end
end, MT_VOIDYCHARGEAURA)

addHook("MobjThinker", function(voidyflash)
	for player in players.iterate do
		local p = player.mo
		if voidyflash and voidyflash.valid and p and p.valid and p.skin == "voidy"
			if (p.eflags & MFE_VERTICALFLIP)
				P_SetOrigin(voidyflash, p.x, p.y, p.z - 70*FRACUNIT)
			else
				P_SetOrigin(voidyflash, p.x, p.y, p.z - 10*FRACUNIT)
			end
		end
	end
end, MT_VOIDYCHARGEFLASH)

addHook("MobjThinker", function(voidyspike)
	for player in players.iterate do
		local p = player.mo
		if voidyspike and voidyspike.valid and p and p.valid and p.skin == "voidy"
			local fx = voidyspike.x
			local fy = voidyspike.y
			local fr = voidyspike.radius
			searchBlockmap("objects", function(voidyspike, found)
				if found.valid
					if found.flags & MF_SHOOTABLE
					and found.z - found.scale <= voidyspike.z + voidyspike.height
					and found.z + found.height + found.scale >= voidyspike.z
						P_DamageMobj(found, voidyspike, p, 2)
					end
				end
				if (found.valid and found.type == MT_EGGSHIELD) 
				and found.z - found.scale <= voidyspike.z + voidyspike.height
				and found.z + found.height + found.scale >= voidyspike.z
					P_KillMobj(found, voidyspike, p)
				end
				if (found.valid and found.type == MT_METALSONIC_BATTLE) 
				and found.state == found.info.spawnstate
				and not (found.flags2 & MF2_CLASSICPUSH) 
				and found.z - found.scale <= voidyspike.z + voidyspike.height
				and found.z + found.height + found.scale >= voidyspike.z
					found.flags2 = $ | MF2_CLASSICPUSH
				end
			end, voidyspike, fx-fr, fx+fr, fy-fr, fy+fr)
		end
	end
end, MT_VOIDYSPIKE)

addHook("MobjThinker", function(crescentdancer)
	for player in players.iterate do
		local p = player.mo
		if crescentdancer and crescentdancer.valid and p and p.valid and p.skin == "voidy"
			P_SetObjectMomZ(crescentdancer, -40*FRACUNIT)
		end
	end
end, MT_CRESCENTDANCER)
addHook("MobjMoveCollide", function(mo, mohit)
	if (mo.z < mohit.z - 70*FRACUNIT) or (mo.z >= mohit.z + 70*FRACUNIT) then
		return
	end
	if mohit and mohit.valid then
		if mohit.flags & MF_ENEMY
		or mohit.flags & MF_SHOOTABLE
		or mohit.flags & MF_BOSS then
			P_DamageMobj(mohit, mo, mo.target, 2)
		end
	end
end, MT_CRESCENTDANCER)

addHook("MobjThinker", function(voidysucky)
	for player in players.iterate do
		local p = player.mo
		if voidysucky and voidysucky.valid and p and p.valid and p.skin == "voidy"
			if voidysucky.scale >= 3*FRACUNIT
				if (p.eflags & MFE_VERTICALFLIP)
					voidysucky.z = p.z - 200*FRACUNIT
				else	
					voidysucky.z = p.z + 15*FRACUNIT
				end
			elseif voidysucky.scale > 2*FRACUNIT
				if (p.eflags & MFE_VERTICALFLIP)
					voidysucky.z = p.z - 150*FRACUNIT
				else	
					voidysucky.z = p.z + 20*FRACUNIT
				end
			elseif voidysucky.scale > 1*FRACUNIT
				if (p.eflags & MFE_VERTICALFLIP)
					voidysucky.z = p.z - 85*FRACUNIT
				else
					voidysucky.z = p.z + 25*FRACUNIT
				end
			elseif voidysucky.scale == 1*FRACUNIT
				if (p.eflags & MFE_VERTICALFLIP)
					voidysucky.z = p.z - 80*FRACUNIT
				else
					voidysucky.z = p.z + 30*FRACUNIT
				end
			end
			local fx = voidysucky.x
			local fy = voidysucky.y
			local fr = voidysucky.radius
			searchBlockmap("objects", function(voidysucky, found)
				if found.valid
					if found.flags & MF_BOSS
					and found.z - found.scale <= voidysucky.z + voidysucky.height
					and found.z + found.height + found.scale >= voidysucky.z
						P_DamageMobj(found, voidysucky, p, 1)
					end
					if ValidVoidyObject(found) and found.health
						if R_PointToDist2(p.x, p.y, found.x, found.y)
							if found.type == MT_RING or found.type == MT_COIN or found.type == MT_BLUESPHERE
								found.tracer = p
							end
							local range = 700*FRACUNIT
							local dist = R_PointToDist2(p.x, p.y, found.x, found.y)
							if dist < FRACUNIT
								dist = FRACUNIT
							end
							local PullForce = range/dist * 3
							if PullForce > 30
								PullForce = 30
							end
							PullForce = $ * FRACUNIT
							VoidyAttract(found, p, PullForce)
						end
					end
				end
				if (found.valid and found.type == MT_EGGSHIELD) 
				and found.z - found.scale <= voidysucky.z + voidysucky.height
				and found.z + found.height + found.scale >= voidysucky.z
					P_KillMobj(found, voidysucky, p)
				end
				if (found.valid and found.flags & MF_MISSILE) 
				and found.z - found.scale <= voidysucky.z + voidysucky.height
				and found.z + found.height + found.scale >= voidysucky.z
					P_RemoveMobj(found)
				end
				if (found.valid and found.type == MT_METALSONIC_BATTLE) 
				and found.state == found.info.spawnstate
				and not (found.flags2 & MF2_CLASSICPUSH) 
				and found.z - found.scale <= voidysucky.z + voidysucky.height
				and found.z + found.height + found.scale >= voidysucky.z
					found.flags2 = $ | MF2_CLASSICPUSH
				end
			end, voidysucky, fx-fr, fx+fr, fy-fr, fy+fr)
		end
	end
end, MT_SUCCFX)
addHook("MobjMoveBlocked", VoidyFakeNoClip, MT_SUCCFX)

addHook("MobjThinker", function(succhit)
	for player in players.iterate do
		local p = player.mo
		if succhit and succhit.valid and p and p.valid and p.skin == "voidy"
			if player.frenzied == 0	
				P_SetOrigin(succhit, p.x + FixedMul(80*FRACUNIT, cos(p.angle)), p.y + FixedMul(80*FRACUNIT, sin(p.angle)), p.z)
			else
				P_SetOrigin(succhit, p.x, p.y, p.z)
			end
			if not player.succconfirm
				succhit.state = S_NULL
			end
		end
	end
end, MT_VACCHITBOX)
addHook("MobjMoveBlocked", VoidyFakeNoClip, MT_VACCHITBOX)
addHook("MobjMoveCollide", function(mo, mohit)
	if mohit and mohit.valid
		if mohit.flags & MF_ENEMY
		or mohit.flags & MF_MONITOR
			P_DamageMobj(mohit, mo, mo.target, 1, DMG_INSTAKILL)
		end
	end
end, MT_VACCHITBOX)

addHook("MobjThinker", function(frenzyaura)
	for player in players.iterate do
		local p = player.mo
		if frenzyaura and frenzyaura.valid and p and p.valid and p.skin == "voidy"
			if p.state == S_PLAY_FRENZY_TRANS3
				frenzyaura.state = S_FRENZYAURA1
				frenzyaura.color = SKINCOLOR_NEON
				frenzyaura.scale = FRACUNIT*3/2
				S_StartSound(p, sfx_drkaur)
			end
			if (player.frenzied == 0)
			or player.powers[pw_super]
				frenzyaura.state = S_INVISIBLE
			end
		end
	end
end, MT_FRENZYAURA)

addHook("MobjLineCollide", VoidyCrumbleCollide, MT_FRENZY_RIGHTSHREDDER)
addHook("MobjThinker", FrenzyShredderThink, MT_FRENZY_RIGHTSHREDDER)

addHook("MobjLineCollide", VoidyCrumbleCollide, MT_FRENZY_LEFTSHREDDER)
addHook("MobjThinker", FrenzyShredderThink, MT_FRENZY_LEFTSHREDDER)

addHook("MobjLineCollide", VoidyCrumbleCollide, MT_FRENZY_DUALSHREDDER)
addHook("MobjThinker", FrenzyShredderThink, MT_FRENZY_DUALSHREDDER)

addHook("MobjLineCollide", VoidyCrumbleCollide, MT_FRENZY_ENERGYBALL)
addHook("MobjThinker", function(energyball)
	for player in players.iterate do
		local p = player.mo
		if energyball and energyball.valid and p and p.valid and p.skin == "voidy"	
			VoidyCheckAndCrumble(energyball, energyball.subsector.sector)
			if energyball.state == S_FRENZY_ENERGYBALLEXPL1
				energyball.laser = P_SpawnMobjFromMobj(energyball, 0, 0, 0, MT_FRENZY_ENERGYPILLAR)
				energyball.laser.scale = 3*FRACUNIT
				energyball.laser.color = SKINCOLOR_NEON
				energyball.laser.blendmode = AST_ADD
				S_StartSound(energyball, sfx_s3k54)
				P_StartQuake(30*FRACUNIT, TICRATE*1/3)
			end
		end
	end
end, MT_FRENZY_ENERGYBALL)
addHook("MobjMoveCollide", function(mo, mohit)
	if (mo.z < mohit.z - 70*FRACUNIT) or (mo.z >= mohit.z + 70*FRACUNIT) then
		return
	end
	if mohit and mohit.valid then
		if mohit.flags & MF_ENEMY
		or mohit.flags & MF_SHOOTABLE
		or mohit.flags & MF_BOSS then
			P_DamageMobj(mohit, mo, mo.target, 2)
			return false
		end
	end
end, MT_FRENZY_ENERGYBALL)

addHook("MobjLineCollide", VoidyCrumbleCollide, MT_FRENZY_ENERGYPILLAR)
addHook("MobjThinker", FrenzyShredderThink, MT_FRENZY_ENERGYPILLAR)
addHook("MobjMoveBlocked", VoidyFakeNoClip, MT_FRENZY_ENERGYPILLAR)

addHook("MobjThinker", function(scorcher)
	for player in players.iterate do
		local p = player.mo
		if scorcher and scorcher.valid and p and p.valid	 and p.skin == "voidy"
			if (leveltime%2 == 0)
				scorcher.flame = P_SpawnMobjFromMobj(scorcher, 0, 0, 0, MT_EARTHSCORCHER_FLAME)
				scorcher.flame.scale = 2*FRACUNIT
				scorcher.flame.target = p
			end
		end
	end
end, MT_EARTHSCORCHER)
addHook("MobjMoveCollide", function(mo, mohit)
	if mohit and mohit.valid
		if mohit.flags & MF_ENEMY
		or mohit.flags & MF_SHOOTABLE
		or mohit.flags & MF_BOSS then
			P_DamageMobj(mohit, mo, mo.target, 4)
			return false
		end
	end
end, MT_EARTHSCORCHER)

addHook("MobjMoveCollide", function(mo, mohit)
	if mohit and mohit.valid
		if mohit.flags & MF_ENEMY
		or mohit.flags & MF_SHOOTABLE
		or mohit.flags & MF_BOSS then
			P_DamageMobj(mohit, mo, mo.target, 2)
		end
	end
end, MT_EARTHSCORCHER_FLAME)

addHook("MobjSpawn", function(creap)
	for player in players.iterate do
		local p = player.mo
		if creap and creap.valid and p and p.valid and p.skin == "voidy"
			if (p.eflags & MFE_VERTICALFLIP)	
				creap.z = p.z - 150*FRACUNIT
			else
				creap.z = p.z - 100*FRACUNIT
			end
		end
	end
end, MT_CRESCENTREAPER)
addHook("MobjThinker", function(crescent)
	for player in players.iterate do
		local p = player.mo
		if crescent and crescent.valid and p and p.valid and p.skin == "voidy"
			local fx = crescent.x
			local fy = crescent.y
			local fr = crescent.radius
			searchBlockmap("objects", function(crescent, found)
				if found.valid
					if found.flags & MF_SHOOTABLE
					and found.z - found.scale <= crescent.z + crescent.height
					and found.z + found.height + found.scale >= crescent.z
						P_DamageMobj(found, crescent, p, 4)
					end
				end
				if (found.valid and found.flags & MF_MISSILE)
				and found.z - found.scale <= crescent.z + crescent.height
				and found.z + found.height + found.scale >= crescent.z
					P_RemoveMobj(found)
				end
				if (found.valid and found.type == MT_EGGSHIELD) 
				and found.z - found.scale <= crescent.z + crescent.height
				and found.z + found.height + found.scale >= crescent.z
					P_KillMobj(found, crescent, p)
				end
				if (found.valid and found.type == MT_METALSONIC_BATTLE) 
				and found.state == found.info.spawnstate
				and not (found.flags2 & MF2_CLASSICPUSH) 
				and found.z - found.scale <= crescent.z + crescent.height
				and found.z + found.height + found.scale >= crescent.z
					found.flags2 = $ | MF2_CLASSICPUSH
				end
			end, crescent, fx-fr, fx+fr, fy-fr, fy+fr)
		end
	end
end, MT_CRESCENTREAPER)

addHook("MobjLineCollide", VoidyCrumbleCollide, MT_VOIDYBLACKHOLE)
addHook("MobjThinker", function(bh)
	for player in players.iterate do
		local p = player.mo
		if bh and bh.valid and p and p.valid	
			if (p.eflags & MFE_VERTICALFLIP)
				P_SetOrigin(bh, p.x, p.y, p.z - 150*FRACUNIT)
			else
				P_SetOrigin(bh, p.x, p.y, p.z - 70*FRACUNIT)
			end
			VoidyCheckAndCrumble(bh, bh.subsector.sector)
			if not player.powers[pw_super]
				bh.state = S_NULL
			end
		end
	end
end, MT_VOIDYBLACKHOLE)
addHook("MobjMoveCollide", function(mo, mohit)
	if mohit and mohit.valid
		if mohit.flags & MF_ENEMY
		or mohit.flags & MF_MONITOR
			P_DamageMobj(mohit, mo, mo.target, 1, DMG_INSTAKILL)
		end
	end
end, MT_VOIDYBLACKHOLE)

addHook("MobjCollide", VoidyKillSpikes, MT_SPIKE)
addHook("MobjCollide", VoidyKillSpikes, MT_WALLSPIKE)

addHook("MobjSpawn", VoidyBlockMap)

addHook("MobjDeath", function(target, attack, attacker)
	if target and target.valid
		if attacker and attacker.valid and attacker.skin == "voidy"
			if (target.type == MT_FLINGRING 
			or target.type == MT_FLINGCOIN 
			or target.type == MT_FLINGBLUESPHERE) 
			and attacker.player.succconfirm
				if attacker.player.lifeforce < 80
					attacker.player.lifeforce = $ + 1
				end
			end
			if target.type == MT_RING 
			or target.type == MT_COIN 
			or target.type == MT_BLUESPHERE
				if attacker.player.lifeforce < 80
					attacker.player.lifeforce = $ + 2
				end
			end
			if target.flags & MF_ENEMY
				if attacker.player.lifeforce < 80
					attacker.player.lifeforce = $ + 4
				end
			end
			if target.flags & MF_BOSS
				if attacker.player.lifeforce < 80
					attacker.player.lifeforce = $ + 8
				end
			end
		end
	end
end)

hud.add(function(v, player)
	if player.mo == nil or player.mo.skin ~= "voidy" or srb2p
		return
	end
	if player.mo.state == S_PLAY_GLIDE
		if (leveltime % 2 == 0)
			v.drawScaled(0, 0, 65355, v.cachePatch("VDYZOOM"), V_SNAPTOLEFT|V_SNAPTOTOP|V_70TRANS, v.getColormap(nil,SKINCOLOR_WHITE))
		else
			v.drawScaled(320*FRACUNIT, 0, 65355, v.cachePatch("VDYZOOM"), V_SNAPTORIGHT|V_SNAPTOTOP|V_80TRANS|V_FLIP, v.getColormap(nil,SKINCOLOR_WHITE))
		end
	end
	if player.mo.state == S_PLAY_FRENZY_INTRO1 
	or player.mo.state == S_PLAY_SUPER_INTRO1
		v.drawScaled(0, 0, 65355, v.cachePatch("VDYZOOM"), V_SNAPTOLEFT|V_SNAPTOTOP|V_70TRANS, v.getColormap(nil,SKINCOLOR_WHITE))
	end
	local huddisplay = v.cachePatch("VDYHUD1")
	local frenzysleep = v.cachePatch("VDYHUD2")
	local iconflash = v.cachePatch("VDYHUD3")
	local frenzyactive = v.cachePatch("VDYHUD4")
	local voidinstall = v.cachePatch("VDYHUD5")
	local hudbg = v.cachePatch("VDYHUD6")
	if leveltime >= 50
		v.draw(170, 135, hudbg, V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_PERPLAYER, v.getColormap(nil,player.mo.color))
		v.drawFill(191, 165, 80, 8, 31|V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_PERPLAYER)
		v.drawFill(191, 165, player.lifeforce, 8, 163|V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_PERPLAYER)
		if player.lifeforce == 80 and player.frenzied == 0
			if not (player.exiting) 
			and not (P_PlayerInPain(player) or player.playerstate == PST_DEAD) 
			and ((player.powers[pw_carry] == CR_NONE) and not (player.pflags & PF_SPINNING)) 
			and (P_IsObjectOnGround(player.mo)) 
			and not (player.powers[pw_super]) 
			and (player.attacking == 0) 
			and (player.transforming == 0)
				v.drawString(195, 166, "Ready! (Custom 1)", V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_PERPLAYER|V_INVERTMAP, "thin")
			else
				v.drawString(195, 166, "Ready! (Custom 1)", V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_PERPLAYER|V_INVERTMAP|V_50TRANS, "thin")
			end
		end
		v.draw(130, 135, huddisplay, V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_PERPLAYER, v.getColormap(nil,player.mo.color))
		if player.powers[pw_super]
			v.draw(130, 135, voidinstall, V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_PERPLAYER)
		else
			if player.frenzied == 1
				v.draw(130, 135, frenzyactive, V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_PERPLAYER)
			else
				v.draw(130, 135, frenzysleep, V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_PERPLAYER)
			end
		end
		if player.mo.state == S_PLAY_FRENZY_TRANS1 
		or player.mo.state == S_PLAY_FRENZY_TRANS2 
		or player.mo.state == S_PLAY_FRENZY_TRANS3 
		or player.mo.state == S_PLAY_SUPER_TRANS1 
		or player.mo.state == S_PLAY_SUPER_TRANS2 
		or player.mo.state == S_PLAY_SUPER_TRANS3
			v.draw(130, 135, iconflash, V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_PERPLAYER)
		end
		if player.mo.state == S_PLAY_FRENZY_TRANS4 
		or player.mo.state == S_PLAY_SUPER_TRANS4
			v.draw(130, 135, iconflash, V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_PERPLAYER|V_40TRANS)
		end
		if player.mo.state == S_PLAY_FRENZY_TRANS5 
		or player.mo.state == S_PLAY_SUPER_TRANS5
			v.draw(130, 135, iconflash, V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_PERPLAYER|V_80TRANS)
		end
	end
	local emerdudes3 = v.cachePatch('VHUDA0')
	if (All7Emeralds(emeralds))
		if (leveltime > 0) and (leveltime < 150)
			v.draw(70, 60, emerdudes3, V_PERPLAYER)
			v.drawString(5, 150, "Voidy can now transform into Abyssal Voidy", V_PERPLAYER)
			v.drawString(50, 160, "by pressing Custom 1 in midair!", V_PERPLAYER)
		end
	end
end)