// --------------------------------------
// Lua_Wheel
// Wheel ability!
// --------------------------------------
local wheeltranstime = TICRATE/5
local wheelaccel = FRACUNIT*4
local wheelmax = FRACUNIT*40
local wheelmin = FRACUNIT*25

local function buttonPressed(player, button)
	if(player.cmd.buttons & button)
		and not(player.kvars.prevcmd & button)
		return true
	end
	return false
end

local function dotProduct(x1, y1, x2, y2)
	return FixedMul(x1, x2)+FixedMul(y1, y2)
end

local function bustAtZ(checkz, sector)
	for rover in sector.ffloors()
		if not(rover.flags & FF_EXISTS)
			or not(rover.flags & FF_BUSTUP)
			continue
		end
		
		local topheight = rover.topheight
		local bottomheight = rover.bottomheight
		if(rover.t_slope) topheight = P_GetZAt(rover.t_slope, mobj.x, mobj.y) end
		if(rover.b_slope) bottomheight = P_GetZAt(rover.b_slope, mobj.x, mobj.y) end
		
		if(topheight >= checkz and bottomheight <= checkz)
			// In bustable; break it!
			EV_CrumbleChain(sector, rover)		// Bust it!
			// Run a linedef executor??
			if(rover.master.flags & ML_EFFECT5)
				P_LinedefExecute(P_AproxDistance(rover.master.dx, rover.master.dy)>>FRACBITS, mobj, sector)
			end
		end
	end
end

local function wheelOnLine(mobj, line)
	local cx = mobj.x+mobj.momx
	local cy = mobj.y+mobj.momy
	local pointx, pointy = P_ClosestPointOnLine(cx, cy, line)
	// SRB2 doesn't account for this, fun.
	// --------------
	// Check if we're past the line!
	local refpoint = nil
	
	local rx = cx-line.v1.x
	local ry = cy-line.v1.y
	local px = line.v1.x-line.v2.x
	local py = line.v1.y-line.v2.y
	local 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 = cx-line.v2.x
		ry = cy-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
	
	if(refpoint == nil)
		refpoint = {}
		refpoint.x = pointx
		refpoint.y = pointy
	end
	// --------------
	
	if not(abs(refpoint.x - cx) <= mobj.radius)
		or not(abs(refpoint.y - cy) <= mobj.radius)
		return
	end
	
	local checkz = mobj.z+(mobj.height/2)
	local snum = 0
	while(snum < 2)
		local sector = line.frontsector
		if(snum)
			sector = line.backsector
		end
		if(sector and sector.valid)
			bustAtZ(checkz, sector)
		end
		snum = $1 + 1
	end
end

rawset(_G, "K_Wheel", function(checkflags, destroy, late, player)
	if(checkflags)
		if(player.kvars.ablstate)
			or(player.kvars.ablvar1)
			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.ablvar1)
		and not(player.kvars.ducking)
		and not(player.kvars.floating)
		and not(player.pflags & PF_SLIDING)
		and not(player.powers[pw_carry])
		and not(player.exiting)
		and not(player.pflags & PF_STASIS)
		and not(player.powers[pw_nocontrol])
		and not(player.kvars.industdevil)
		if(player.kvars.ablstate)
			player.kvars.ablstate = 0
			player.pflags = ($1 & !PF_JUMPED)|PF_NOJUMPDAMAGE
			player.kvars.ablvar1 = wheeltranstime
			player.kvars.ablvar2 = 0
		//	if(P_IsObjectOnGround(player.mo))
		//		player.mo.state = S_PLAY_RUN
		//	else
		//		player.mo.state = S_PLAY_FALL
		//	end
			if(P_IsObjectOnGround(player.mo))
				player.mo.momz = 5*player.mo.scale*P_MobjFlip(player.mo)
				P_TeleportMove(player.mo, player.mo.x, player.mo.y, player.mo.z+P_MobjFlip(player.mo))
			end
		else
			player.kvars.ablstate = 1
			player.kvars.ablvar1 = wheeltranstime
			player.kvars.ablvar2 = 0
		end
	end
	
	if(player.kvars.ablstate or player.kvars.ablvar1)
		and((player.pflags & PF_SLIDING) or player.powers[pw_carry])
		player.kvars.ablstate = 0
		player.kvars.ablvar1 = 0
		if(player.mo.state >= S_KIRBY_WHEEL1 and player.mo.state <= S_KIRBY_WHEEL4)
			if(player.pflags & PF_SLIDING)
				player.mo.state = S_PLAY_PAIN
			else
				player.mo.state = S_PLAY_RIDE
			end
		end
	end
	
	local div = 1
	if(player.mo.eflags & MFE_UNDERWATER)
		div = 2
	end
	
	if(player.kvars.ablvar1)
		player.pflags = ($1 & !PF_NOJUMPDAMAGE)|PF_JUMPED
		player.kvars.ablvar1 = $1 - 1
		local tostate = S_KIRBY_WHEELTRANS1
		if((player.kvars.ablvar1 > wheeltranstime-3) and(player.kvars.ablstate))
			or((player.kvars.ablvar1 <= 3) and not(player.kvars.ablstate))
			or(player.kvars.ablvar2)
			tostate = S_KIRBY_WHEELTRANS2
		end
		if not(player.mo.state == tostate)
			player.mo.state = tostate
		end
//		player.mo.momx = 0
//		player.mo.momy = 0
//		player.mo.momz = 0
		player.normalspeed = 0
		player.thrustfactor = 0
		player.drawangle = R_PointToAngle2(0, 0, player.cmd.forwardmove*FRACUNIT, -player.cmd.sidemove*FRACUNIT)+(player.cmd.angleturn<<16)
		if(player.kvars.ablvar1 == 0)
			if(player.kvars.ablstate)
				P_InstaThrust(player.mo, player.drawangle, FixedMul(wheelmax/div, player.mo.scale))
				player.mo.momx = $1 + player.cmomx
				player.mo.momy = $1 + player.cmomy
			else
				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
			end
		end
	elseif(player.kvars.ablstate)
	//	player.charability = CA_GLIDEANDCLIMB
		player.charflags = $1|SF_RUNONWATER
		player.pflags = ($1 & !PF_NOJUMPDAMAGE)|PF_JUMPED
		player.mo.friction = FRACUNIT*95/100
		player.kvars.ablstate = ($1 % 4)+1
		if(player.kvars.ablstate == 1)
			S_StartSound(player.mo, sfx_tornad)
		end
		if not(player.mo.state >= S_KIRBY_WHEEL1 and player.mo.state <= S_KIRBY_WHEEL4)
			player.mo.state = S_KIRBY_WHEEL1
		end
		player.normalspeed = 0
		player.thrustfactor = 0
		local oldspeed = R_PointToDist2(player.cmomx, player.cmomy, player.mo.momx, player.mo.momy)
		local thrust = FixedMul(wheelaccel/div, player.mo.scale)
		if not(P_IsObjectOnGround(player.mo))
			thrust = $1 / 4
		end
		P_Thrust(player.mo, R_PointToAngle2(0, 0, player.cmd.forwardmove*FRACUNIT, -player.cmd.sidemove*FRACUNIT)+(player.cmd.angleturn<<16), thrust)
		local dir = R_PointToAngle2(player.cmomx, player.cmomy, player.mo.momx, player.mo.momy)
		local newspeed = R_PointToDist2(player.cmomx, player.cmomy, player.mo.momx, player.mo.momy)
		player.drawangle = dir
		if(newspeed >= FixedMul(wheelmax/div, player.mo.scale))
			and(oldspeed <= FixedMul(wheelmax/div, player.mo.scale))
			player.mo.friction = FRACUNIT
			P_InstaThrust(player.mo, dir, FixedMul(wheelmax/div, player.mo.scale))
			player.mo.momx = $1 + player.cmomx
			player.mo.momy = $1 + player.cmomy
		elseif(newspeed >= FixedMul(wheelmax/div, player.mo.scale))
			and(oldspeed >= FixedMul(wheelmax/div, player.mo.scale))
			player.mo.friction = FRACUNIT
			P_InstaThrust(player.mo, dir, oldspeed)
			player.mo.momx = $1 + player.cmomx
			player.mo.momy = $1 + player.cmomy
		elseif(newspeed <= FixedMul(wheelmin/div, player.mo.scale))
			and(oldspeed >= FixedMul(wheelmin/div, player.mo.scale))
			player.mo.friction = FRACUNIT
			P_InstaThrust(player.mo, dir, FixedMul(wheelmin/div, player.mo.scale))
			player.mo.momx = $1 + player.cmomx
			player.mo.momy = $1 + player.cmomy
		elseif(newspeed <= FixedMul(wheelmin/div, player.mo.scale))
			and(newspeed < oldspeed)
			player.mo.friction = FRACUNIT
			P_InstaThrust(player.mo, dir, oldspeed)
			player.mo.momx = $1 + player.cmomx
			player.mo.momy = $1 + player.cmomy
		end
		
		if(P_IsObjectOnGround(player.mo))
			if(player.cmd.buttons & BT_JUMP)
				and not(player.kvars.prevcmd & BT_JUMP)
				and not(player.exiting)
				and not(player.pflags & PF_STASIS)
				and not(player.powers[pw_nocontrol])
				and not(player.kvars.industdevil)
				S_StartSound(player.mo, sfx_jump)
				player.mo.momz = 9*player.mo.scale*P_MobjFlip(player.mo)/div
				P_TeleportMove(player.mo, player.mo.x, player.mo.y, player.mo.z+P_MobjFlip(player.mo))
			end
			
			// Dust
			if not(leveltime % 3)
				// MFE_TOUCHLAVA not exposed to lua?
				and not((player.mo.eflags & MFE_TOUCHWATER) and not(player.mo.eflags & MFE_UNDERWATER))
				local particle = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_SPINDUST)
				particle.scale = player.mo.scale
				if(player.mo.eflags & MFE_VERTICALFLIP)
					P_TeleportMove(particle, player.mo.x, player.mo.y, player.mo.z+player.mo.height-particle.scale)
				end
				particle.momz = (((P_RandomByte()*player.mo.scale)/255)+player.mo.scale)*P_MobjFlip(player.mo)
				
				local rundir = R_PointToAngle2(player.cmomx, player.cmomy, player.mo.momx, player.mo.momy)+ANGLE_90
				P_InstaThrust(particle, rundir, (((P_RandomByte()*player.mo.scale*2)/255)-player.mo.scale)*16)
				
				local goodmomx = player.mo.momx-player.cmomx
				local goodmomy = player.mo.momy-player.cmomy
				particle.momx = $1 + player.cmomx + (goodmomx*2/3)
				particle.momy = $1 + player.cmomy + (goodmomy*2/3)
				
				P_TeleportMove(particle, particle.x-particle.momx, particle.y-particle.momy, particle.z+particle.momz)
				
				if(P_MobjFlip(player.mo) < 0)
					particle.eflags = $1|MFE_VERTICALFLIP
					particle.flags2 = $1|MF2_OBJECTFLIP
				end
				
				if(player.mo.eflags & MFE_UNDERWATER)
					particle.state = S_SPINDUST_BUBBLE1
				end
				
				particle.scale = player.mo.scale
				
				particle.tics = 6
			end
		else
			P_InstaThrust(player.mo, dir, oldspeed)
			player.mo.momx = $1 + player.cmomx
			player.mo.momy = $1 + player.cmomy
		end
		
		// Ghost
		if not(leveltime%3)
			local wg = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_WHEELGHOST)
			wg.angle = player.drawangle
			wg.scale = player.mo.scale
			wg.skin = player.mo.skin
			wg.savestate = player.mo.state
			wg.saveframe = player.mo.frame
			wg.savetics = player.mo.tics
			wg.state = wg.savestate
			wg.frame = (wg.saveframe&FF_FRAMEMASK)|FF_TRANS50
			wg.tics = wg.savetics
			wg.color = player.mo.color
		//	wg.colorized = true
			wg.flags2 = $1|MF2_DONTDRAW
			if(player.mo.eflags & MFE_VERTICALFLIP)
				wg.eflags = $1|MFE_VERTICALFLIP
			end
		end
		
		// Check blockmap to bust walls
		local sx = player.mo.x+player.mo.momx
		local sy = player.mo.y+player.mo.momy
		searchBlockmap("lines", wheelOnLine, player.mo, sx-player.mo.radius, sx+player.mo.radius, sy-player.mo.radius, sy+player.mo.radius)
		
		// Check for walls
		local wallchecker = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_GARGOYLE)
		wallchecker.radius = player.mo.radius
		wallchecker.height = player.mo.height
		wallchecker.flags = ($1|MF_NOCLIPTHING) & !(MF_SOLID|MF_PUSHABLE)
		local hit = P_TryMove(wallchecker, player.mo.x+player.mo.momx, player.mo.y+player.mo.momy, true)
		P_RemoveMobj(wallchecker)
		
		if not(hit)
			player.pflags = ($1 & !PF_JUMPED)|PF_NOJUMPDAMAGE
			player.mo.state = S_KIRBY_WHEELTRANS2
			P_TeleportMove(player.mo, player.mo.x, player.mo.y, player.mo.z+P_MobjFlip(player.mo))
			player.mo.momz = 3*player.mo.scale*P_MobjFlip(player.mo)
			P_InstaThrust(player.mo, player.drawangle, -5*player.mo.scale)
			player.mo.momx = $1 + player.cmomx
			player.mo.momy = $1 + player.cmomy
			player.kvars.ablvar1 = wheeltranstime
			player.kvars.ablvar2 = 2
			player.kvars.ablstate = 0
			S_StartSound(player.mo, sfx_stone)
		end
	end
end)

addHook("MobjThinker", function(mo)
	if(mo.wtime == nil)
		mo.wtime = 4
	end
	
	mo.flags2 = $1 & !MF2_DONTDRAW
	
	mo.state = mo.savestate
	mo.frame = (mo.saveframe&FF_FRAMEMASK)|FF_TRANS30
	mo.tics = mo.savetics
	
	mo.wtime = $1 - 1
	if not(mo.wtime)
		P_RemoveMobj(mo)
	end
	return true
end, MT_WHEELGHOST)
