// --------------------------------------
// Lua_Needle
// Needle ability!
// --------------------------------------
// BUGS:
// Should move with the most speedy wall
// --------------------------------------
local function buttonPressed(player, button)
	if(player.cmd.buttons & button)
		and not(player.kvars.prevcmd & button)
		return true
	end
	return false
end

local function lockControl(player)
	if(player.powers[pw_nocontrol] <= 2)
		player.powers[pw_nocontrol] = 2
	end
	player.pflags = $1|PF_STASIS
end

// NOTE: Moves with texture offsets, not actual scrolling... oh well
local function moveWithSide(mobj, side, normalx, normaly)
	// Init side tracking
	if not(mobj.needlewall == side)
		mobj.needlewallx = side.textureoffset
		mobj.needlewally = side.rowoffset
	end
	mobj.needlewall = side
	
	local offx = FixedMul(mobj.needlewallx-side.textureoffset, normalx)
	local offy = FixedMul(mobj.needlewallx-side.textureoffset, normaly)
	local offz = mobj.needlewally-side.rowoffset
	
	mobj.needlewallx = side.textureoffset
	mobj.needlewally = side.rowoffset
	
	return {offx, offy, offz}
end

local function checkInFoF(startz, endz, sector, x, y)
	for rover in sector.ffloors()
		if not(rover.flags & FF_EXISTS)
			or not(rover.flags & FF_BLOCKPLAYER)
			continue
		end
		
		local topheight = rover.topheight
		local bottomheight = rover.bottomheight
		if(rover.t_slope) topheight = P_GetZAt(rover.t_slope, x, y) end
		if(rover.b_slope) bottomheight = P_GetZAt(rover.b_slope, x, y) end
		
		if(topheight > startz and bottomheight < endz)	// Not >= because we dont stick to the ground!
			return true
		end
		
		// rover->master->frontsector->floorspeed
		// if (rover->master->frontsector->floorspeed && rover->master->frontsector->ceilspeed == 42)
	end
	return false
end

local checkradius
local retval = false

local function checkForFlats(mobj, sector)
	// Check floor/ceiling
	local ceilingz
	local floorz
	local ceilingheight
	local floorheight
	if(sector == nil)
		ceilingz = mobj.ceilingz*P_MobjFlip(mobj)
		floorz = mobj.floorz*P_MobjFlip(mobj)
	else
		if(sector.c_slope) ceilingheight = P_GetZAt(sector.c_slope, mobj.x, mobj.y) else ceilingheight = sector.ceilingheight end
		if(sector.f_slope) floorheight = P_GetZAt(sector.f_slope, mobj.x, mobj.y) else floorheight = sector.floorheight end
		ceilingz = ceilingheight*P_MobjFlip(mobj)
		floorz = floorheight*P_MobjFlip(mobj)
	end
	local centerz = (mobj.z+(mobj.height/2))*P_MobjFlip(mobj)
	local topz = (mobj.z+mobj.height)*P_MobjFlip(mobj)
	if(mobj.eflags & MFE_VERTICALFLIP)
		if(sector == nil)
			floorz = mobj.ceilingz*P_MobjFlip(mobj)
			ceilingz = mobj.floorz*P_MobjFlip(mobj)
		else
			floorz = ceilingheight*P_MobjFlip(mobj)
			ceilingz = floorheight*P_MobjFlip(mobj)
		end
		topz = mobj.z*P_MobjFlip(mobj)
	end
	
	if(ceilingz <= centerz+checkradius)
		or(floorz >= centerz)
		return true
	end
	return false
end

local function checkForWalls(mobj, line)
	if(retval)
		return
	end
	
	// Get line normal
	local lnx = (line.v1.y-line.v2.y)
	local lny = -(line.v1.x-line.v2.x)
	local mag = R_PointToDist2(0, 0, lnx, lny)
	lnx = FixedDiv($1, mag)
	lny = FixedDiv($1, mag)
	
	// Check if we're past the line!
	local refpoint = nil
	
	local rx = mobj.x-line.v1.x
	local ry = mobj.y-line.v1.y
	local px = line.v1.x-line.v2.x
	local py = line.v1.y-line.v2.y
	mag = R_PointToDist2(0, 0, px, py)
	px = FixedDiv($1, mag)
	py = FixedDiv($1, mag)
	if(FixedMul(px, rx)+FixedMul(py, ry) > 0)
		refpoint = line.v1
	else
		rx = mobj.x-line.v2.x
		ry = mobj.y-line.v2.y
		px = line.v2.x-line.v1.x
		py = line.v2.y-line.v1.y
		mag = R_PointToDist2(0, 0, px, py)
		px = FixedDiv($1, mag)
		py = FixedDiv($1, mag)
		if(FixedMul(px, rx)+FixedMul(py, ry) > 0)
			refpoint = line.v2
		end
	end
	
	local hit = false
	if(refpoint)
		if(R_PointToDist2(refpoint.x, refpoint.y, mobj.x, mobj.y) <= checkradius)
			hit = true
		end
	elseif(abs(FixedMul(lnx, mobj.x-line.v1.x)+FixedMul(lny, mobj.y-line.v1.y)) <= checkradius)
		hit = true
	end
	
	// Check if line collided with mobj in checkradius
	if(hit)
		// Check for walls!
		local snum = 0
		while(snum < 2)
			and not(retval)
			local sector = line.frontsector
			if(snum)
				sector = line.backsector
			end
			if(sector and sector.valid)
				and not(sector == mobj.subsector.sector)	// Don't stick to our own sector!
				
				if(line.backside and line.backside.valid)
					if(mobj.eflags & MFE_VERTICALFLIP)
						retval = checkInFoF(mobj.z+(mobj.height/2)-(checkradius/2), mobj.z, sector, mobj.x, mobj.y)
					else
						retval = checkInFoF(mobj.z, mobj.z+(mobj.height/2)+(checkradius/2), sector, mobj.x, mobj.y)
					end
					
					if(checkForFlats(mobj, sector))
						retval = true
					end
				else
					retval = true	// Always collide with one sided lines!
					snum = $1 + 1
				end
				
				// Get line info for scrolling
				if(retval)
					if(snum == 1)		// Checking side 0 means we're using the front sector, so we're on the back side; we collided with the back side of the line!
						mobj.needlelastside = line.frontside
						mobj.needlelastnx = (line.v1.x-line.v2.x)
						mobj.needlelastny = (line.v1.y-line.v2.y)
					else
						mobj.needlelastside = line.backside
						mobj.needlelastnx = (line.v2.x-line.v1.x)
						mobj.needlelastny = (line.v2.y-line.v1.y)
					end
					mag = R_PointToDist2(0, 0, mobj.needlelastnx, mobj.needlelastny)
					mobj.needlelastnx = FixedDiv($1, mag)
					mobj.needlelastny = FixedDiv($1, mag)
				end
			end
			snum = $1 + 1
		end
	end
end

local function enemyPlayer(player1, player2)
	if(gametyperules & GTR_FRIENDLY)
		or((gametyperules & GTR_TEAMS) and(player2.ctfteam == 0 or player2.ctfteam == player1.ctfteam))
		return false
	end
	
	return true
end

local function checkForEnemies(mobj, othermo)
	if not(othermo.flags & MF_ENEMY)
		and not(othermo.flags & MF_MONITOR)
		and not(othermo.player and othermo.player.valid and enemyPlayer(mobj.player, othermo.player))
		return
	end
	
	if(R_PointToDist2(mobj.x, mobj.y, othermo.x, othermo.y) < checkradius+othermo.radius)
		local startz
		local endz
		if(mobj.eflags & MFE_VERTICALFLIP)
			endz = mobj.z+(mobj.height/2)
			startz = endz-(checkradius/2)
		else
			startz = mobj.z+(mobj.height/2)
			endz = startz+(checkradius/2)
		end
		if(othermo.z <= endz)
			and(othermo.z+othermo.height >= startz)
			P_DamageMobj(othermo, mobj, mobj)
		end
	end
end

rawset(_G, "K_Needle", function(checkflags, destroy, late, player)
	if(checkflags)
		if(player.kvars.ablstate)
			return AF_NOFLOAT|AF_NODUCK|AF_NOJUMP|AF_NOSWIM|AF_NODROP
		end
		return 0
	end
	if(destroy)
		return
	end
	if(late)
		return
	end
	
	if(buttonPressed(player, BT_USE))
		and not(player.kvars.ducking)
		and not(player.kvars.floating)
		and not(player.pflags & PF_SLIDING)
		and not(player.powers[pw_carry] or player.kvars.prevcarry)
		and not(player.exiting)
		and not(player.pflags & PF_STASIS)
		and not(player.kvars.industdevil)
		player.kvars.ablstate = 1
		player.mo.state = S_KIRBY_NEEDLE1
	end
	
	if(player.kvars.ablstate)
		if(player.pflags & PF_SLIDING)
			or(player.powers[pw_carry] or player.kvars.prevcarry)
			or(player.exiting)
			or(player.pflags & PF_STASIS)
			or not(player.cmd.buttons & BT_USE)
			player.kvars.ablstate = 0
			player.pflags = ($1 & !PF_JUMPED)|PF_NOJUMPDAMAGE
			if(P_IsObjectOnGround(player.mo))
				player.mo.state = S_PLAY_STND
			else
				player.mo.state = S_PLAY_FALL
			end
		else
			if not(player.mo.state >= S_KIRBY_NEEDLE1 and player.mo.state <= S_KIRBY_NEEDLE4)
				player.mo.state = S_KIRBY_NEEDLE4
			end
			
			player.mo.needlelastside = nil
			player.mo.needlelastnx = nil
			player.mo.needlelastny = nil
			retval = false
			checkradius = player.mo.scale*40
			searchBlockmap("lines", checkForWalls, player.mo, player.mo.x-checkradius, player.mo.x+checkradius, player.mo.y-checkradius, player.mo.y+checkradius)
			if(retval)
				or(checkForFlats(player.mo))
				player.mo.momx = 0
				player.mo.momy = 0
				player.mo.momz = 0
				if not(player.kvars.ablvar2)
					S_StartSound(player.mo, sfx_spkdth)
				end
				player.kvars.ablvar2 = 1
				// Scrolling!
				if(player.mo.needlelastside)
					local n = moveWithSide(player.mo, player.mo.needlelastside, player.mo.needlelastnx, player.mo.needlelastny)
					player.mo.momx = -n[1]
					player.mo.momy = -n[2]
					player.mo.momz = -n[3]
				else
					player.mo.needlewall = nil
				end
			else
				player.mo.needlewall = nil
				if(P_IsObjectOnGround(player.mo))
					if not(player.kvars.ablvar2)
						player.kvars.ablvar2 = 1
						S_StartSound(player.mo, sfx_spkdth)
						player.mo.momx = player.cmomx
						player.mo.momy = player.cmomy
					end
				else
					player.kvars.ablvar2 = 0
				end
			end
			searchBlockmap("objects", checkForEnemies, player.mo, player.mo.x-checkradius, player.mo.x+checkradius, player.mo.y-checkradius, player.mo.y+checkradius)
			
			player.pflags = ($1|PF_JUMPED) & !PF_NOJUMPDAMAGE
			
			lockControl(player)
		end
	else
		player.mo.needlewall = nil
		player.kvars.ablvar2 = 0
	end
end)
