local linelist = {} //stores all halfpipe line in this table, better than looking for them with block map search
local funcs = nil 


local function ati(a, addtn) //was desperetaed there LOL
	//if not add then add = 0 end
	a = a / ANG1
	
	if a < 0 then
		a = a + 360
	end
	if a > 360 then
		a = a - 360
	end

	return a
end

local function clamp(val, mi, ma) // :skull:
	return max(mi,min(ma,val)) 
end

//backported from source, why is not exposed to lua??? very useful
local function localP_PointOnLineSide(x, y, line)
	local v1 = line.v1
	local dx, dy, left, right

	if not line.dx then
		if x <= v1.x then
			return (line.dy > 0)
		end
	
		return (line.dy < 0)
	end
	if not line.dy then
		if y <= v1.y then
			return (line.dx < 0)
		end

		return (line.dx > 0)
	end

	dx = x - v1.x
	dy = y - v1.y

	left = FixedMul(line.dy / FRACUNIT, dx)
	right = FixedMul(dy, line.dx / FRACUNIT)

	if right < left then
		return 0 // front side
	end
	return 1 // back side

end

//do i REALLY HAVE to explain

local function getlinemiddle(line)
	local lang, ldist = R_PointToAngle2(line.v1.x, line.v1.y, line.v2.x, line.v2.y), FixedHypot(line.v1.x - line.v2.x, line.v1.y - line.v2.y)
	local pos = {}
	pos.x = line.v1.x + P_ReturnThrustX(nil, lang, ldist / 2)
	pos.y = line.v1.y + P_ReturnThrustY(nil, lang, ldist / 2)

	
	return pos.x, pos.y
end

//multi purpose function
//gives the direction the front of the wall faces
//the position towards from the linedef's middle point
//the floor height at this position
//and can retrieve the back side instead if desired
local function getwalldirection(line, back)
	local lang, ldist = R_PointToAngle2(line.v1.x, line.v1.y, line.v2.x, line.v2.y), FixedHypot(line.v1.x - line.v2.x, line.v1.y - line.v2.y)
	local pos = {}
	local direction = ANGLE_90
	pos.x, pos.y = getlinemiddle(line)
	
	local test = {}
	test.x, test.y = pos.x, pos.y
	//test to see which side is the front side, using this cuz it wanna return the position
	test.x = $ + P_ReturnThrustX(nil, lang + direction, 8*FRACUNIT)
	test.y = $ + P_ReturnThrustY(nil, lang + direction, 8*FRACUNIT)
	
	local lst = localP_PointOnLineSide(test.x, test.y, line)
	//if this returns true or 1, were in the back side so we invert the direction
	if ((lst == true or lst == 1) and (back == nil or back == false)) //is in backside, invert, if you want the front
	or ((lst == false or lst == 0) and back == true) then 	//is in the front, invert because you want the back
		direction = -ANGLE_90
	end
	
	//the final position
	//move towards the line facing direction
	pos.x = $ + P_ReturnThrustX(nil, lang + direction, 8*FRACUNIT)
	pos.y = $ + P_ReturnThrustY(nil, lang + direction, 8*FRACUNIT)
	
	//wall facing direction, the position, the floor height at this position
	return lang + direction, pos.x, pos.y, P_FloorzAtPos(pos.x, pos.y, R_PointInSubsector(pos.x, pos.y).sector.floorheight, 32*FRACUNIT) 

end
local point, wall = {} //player uses those, has to be kept between frames


addHook("ThinkFrame",do
	if not HalfPipe then return end //F
	linelist = HalfPipe.HPLines
	funcs = HalfPipe.funcs
	if not linelist then return end //FUCK GO BACK 
	for p in players.iterate do
		if not (p.mo and p.mo.valid) then continue end		
		
		//try to retrieve a halfpipe line
		local jnk, rad  
		rad = p.mo.radius + FixedHypot(p.mo.momx, p.mo.momy) + (16*FRACUNIT)
		if (p.halfpipelaunched and p.halfpipelaunched == true) then
			//rad = $ + (94*FRACUNIT)
		end
		jnk = funcs.gethalfpipeline(p.mo.x, p.mo.y, 1, rad, {p.currenthalfpipewall, p.previoushalfpipewall})//,{p.currenthalfpipewall, p.previoushalfpipewall})

		
		if (p.halfpipelaunched and p.halfpipelaunched == true) then
			//didnt find a half pipe line to collide
			//which means it might have a wall that goes way from you
			//extend the range a bit to try again
			if not (jnk and jnk ~= nil) then
				//print("NOT FOUND")
				rad = $ + (32*FRACUNIT)
				jnk = funcs.gethalfpipeline(p.mo.x, p.mo.y, 1, rad, { p.previoushalfpipewall})	
			else
				//collided BUT for walls tha tcurves inside, you'll bonk them
				//because it wont ignore your current half pipe line, so doing this it fixes
				jnk = funcs.gethalfpipeline(p.mo.x, p.mo.y, 1, rad, {p.currenthalfpipewall})
		
			end
		end

		wall = nil
		point = nil
		if jnk 
		or (jnk 
		and jnk ~= wall 
		and jnk ~= p.currenthalfpipewall
		and jnk ~= p.previoushalfpipewall
		and p.halfpipelaunched == true) then
			wall = jnk
			point = funcs.pointonline(p.mo.x, p.mo.y, jnk)
		end
		
		//print("momz "..p.mo.momz)
		//print("flip "..P_MobjFlip(p.mo))
		
		//land and reset
		if P_IsObjectOnGround(p.mo) 
		and (p.halfpipelaunched and p.halfpipelaunched == true ) then
		//and ((p.mo.momz == 0 and P_MobjFlip(p.mo) == p.launchgravdir) 
		//or (funcs.sign(p.mo.momz) ~= P_MobjFlip(p.mo) and P_MobjFlip(p.mo) ~= p.launchgravdir))  then
			//this is a bit different, changes your direction
			if p.mo.standingslope then
				P_Thrust(p.mo, getwalldirection(p.currenthalfpipewall, false), (p.halfpipestorezmom*-P_MobjFlip(p.mo)) / 2)
			end
			if P_MobjFlip(p.mo) ~= p.launchgravdir
			and p.mo.standingslope then
				p.mo.angle = getwalldirection(p.currenthalfpipewall, false)
				P_InstaThrust(p.mo, p.mo.angle, (p.halfpipestorezmom*-P_MobjFlip(p.mo))/ 2)
			end
			p.halfpipelaunched = nil
			p.currenthalfpipewall = nil
			p.previoushalfpipewall = nil
			if p.mo.rollangle ~= nil then p.mo.rollangle = 0 end //hello again
			//update your speed rn
			p.speed = FixedHypot(p.mo.momx, p.mo.momy)
			wall = nil
			point = nil
			
		end
		p.mo.flags = $ & ~(MF_NOCLIP|MF_NOCLIPHEIGHT)
	
		if p.mo.standingslope then
			//print("zang "..ati(p.mo.standingslope.zangle))
		end
		//if you near a halfpipe wall, continue
		//also means youre about to collide so you can be launched
		if (wall and wall ~= nil)
		and not (p.halfpipelaunched and p.halfpipelaunched == true)
		//and (p.currenthalfpipewall and p.currenthalfpipewall ~= wall)
		and p.mo.standingslope
		and p.speed > 1 * FRACUNIT //i dunno might change this later
		then
			//point.x = $ + FixedMul(p.mo.radius + 9*FRACUNIT, cos(getwalldirection(wall)))
			//point.y = $ + FixedMul(p.mo.radius + 9*FRACUNIT, sin(getwalldirection(wall)))
			//cancel if youre to ofar from the wall yer
			//so it doesnt change your half pine line YET
			//if FixedHypot(p.mo.x - point.x , p.mo.y - point.y) > p.mo.radius + (9*FRACUNIT) then return end
			
			//print("LAUNCHED")
			local angdiff = R_PointToAngle2(0,0, p.mo.momx, p.mo.momy) - funcs.wallfrontangle(wall, true) 
			local wang = R_PointToAngle2(wall.v1.x, wall.v1.y, wall.v2.x, wall.v2.y)  //wall angle 
			local mang = R_PointToAngle2(0,0, p.mo.momx, p.mo.momy) //player moving angle
			//print("mang "..ati(mang))
			//print("wang "..ati(getwalldirection(wall, true)))
			//print("diff "..abs(angdiff)/ANG1)
			
			if abs(angdiff) > 64*ANG1 then return end
			
			if P_MobjFlip(p.mo) > 0
			and (ati(p.mo.standingslope.zangle) < 32 
			and ati(p.mo.standingslope.zangle) > 360 - 32)
			or P_MobjFlip(p.mo) < 0
			and abs(ati(p.mo.standingslope.zangle) - 180) < 32
				return 
			end
			
			
			
			
			p.halfpipelaunched = true
			
			//let us know if you should change direction
			
			p.launchgravdir = P_MobjFlip(p.mo)

			p.halfpipemovedir = -funcs.sign(angdiff) //the direction you wanna move
			//the sideways speed, the more aligned you move to the wall, faster you go
			p.halfpipespeed = FixedMul(p.speed , FixedDiv( abs(angdiff) - ANG1 ,(64*ANG1) - ANG1))
			p.halfpipeverticalspeed = p.speed * (P_MobjFlip(p.mo)) //grav flip.
			//stops any current movement
			p.mo.momx, p.mo.momy, p.mo.momz = 0,0,0
			p.speed = 0
			
			
			//now reposition the player to the wall and set the sideways/vertical momentum
			//doing this once is easier and more natural if you bonk or shenanigans happens

			p.mo.flags = $ | (MF_NOCLIPHEIGHT)
			
			
			P_TryMove(p.mo,
			point.x + FixedMul(p.mo.radius + 9*FRACUNIT, cos(getwalldirection(wall))),
			point.y + FixedMul(p.mo.radius + 9*FRACUNIT, sin(getwalldirection(wall))),
			true)
			p.mo.z = p.mo.floorz + (P_MobjFlip(p.mo)*2)
			
			p.currenthalfpipewall = wall //set this for switching half pipe lines
			p.previoushalfpipewall = nil //avoid to keep switching back and forth betwen half pipe lines
			p.mo.momz = p.halfpipeverticalspeed //vertical movement
			//sideays movement
			P_InstaThrust(p.mo, wang, p.halfpipespeed * p.halfpipemovedir)
			//update your speed rn
			p.speed = FixedHypot(p.mo.momx, p.mo.momy)
			wall = nil
			point = nil
		end
		
		
		
		if (p.halfpipelaunched and p.halfpipelaunched == true)  then
			//hello there fellow saturn user
			if p.mo.rollangle ~= nil then
				local wang = getwalldirection(p.currenthalfpipewall, true)
				p.mo.rollangle = FixedMul(ANGLE_90, FixedRound(sin(R_PointToAngle(p.mo.x, p.mo.y) - wang)))
				//dont mind me, just adding some neat extra flair
			end
			if not P_IsObjectOnGround(p.mo)  then
				p.halfpipestorezmom = p.mo.momz
			end
		end
		
		
		//transfer to another halfpipe line if near enough 
		if (p.halfpipelaunched and p.halfpipelaunched == true)
		and (wall and wall ~= p.currenthalfpipewall) then
			point.x = $ + FixedMul(p.mo.radius + 9*FRACUNIT, cos(getwalldirection(wall)))
			point.y = $ + FixedMul(p.mo.radius + 9*FRACUNIT, sin(getwalldirection(wall)))
			//cancel if youre to ofar from the wall yer
			//so it doesnt change your half pine line YET
			if FixedHypot(p.mo.x - point.x , p.mo.y - point.y) > p.mo.radius + (9*FRACUNIT) then return end
			local angdiff = R_PointToAngle2(0,0, p.mo.momx, p.mo.momy) - funcs.wallfrontangle(wall, true) 
			local wang = R_PointToAngle2(wall.v1.x, wall.v1.y, wall.v2.x, wall.v2.y)  //wall angle 
			local mang = R_PointToAngle2(0,0, p.mo.momx, p.mo.momy) //player moving angle
			
			p.mo.flags = $ | (MF_NOCLIPHEIGHT)
	
			P_TryMove(p.mo,
			point.x,
			point.y,
			true)
			
			//but then store this current line as the previous
			p.previoushalfpipewall = p.currenthalfpipewall 
			p.currenthalfpipewall = wall //set this for later use
			
			p.halfpipemovedir = -funcs.sign(angdiff)
			p.halfpipespeed = p.speed//FixedMul(p.speed , FixedDiv( abs(angdiff) - ANG1 ,(64*ANG1) - ANG1))
			P_InstaThrust(p.mo, wang, p.halfpipespeed * p.halfpipemovedir)
			//update your speed rn
			p.speed = FixedHypot(p.mo.momx, p.mo.momy)
			wall = nil
			point = nil
		end
		
		
		//explaining this
		//in the source code, when you leave the ground and have a field named standingslope set to a slope
		//it will assume you want to be launched with the slope physics
		//so if you acquire any vertical momentum, is calculated to use the slope slopyness
		//the down side is: IT MAY SEND YOU BACKWRDS OR SIDEAYS, but you jsut want to make it go vertically only
		//yo ucannot set this field to nil
		//you cant set the slope flag to have no physics
		//the only three options are
		//use p_trymove giving the player some flags that during the function sets standingslope to null
		//use a spring (WHY)
		//use a gas jet (WHY²???????)
		if funcs.sign(p.mo.momz) == P_MobjFlip(p.mo)
		and p.mo.momz ~= 0
		and p.halfpipelaunched
		then
			//p.mo.flags = $ | (MF_NOCLIP|MF_NOCLIPHEIGHT)
			//p.mo.flags = $ | (MF_NOCLIPHEIGHT)
			//P_TryMove(p.mo, p.mo.x, p.mo.y, false)
			
		else
			//p.mo.flags = $ & ~(MF_NOCLIP|MF_NOCLIPHEIGHT)
			
		end
		
	end

end)
