--╔════════════════════════════╗══════════════════════════════════════════════════════════════════════════════════════════════════╗
--║       OBJECT HOOKS &       ║ So thinkers never duplicate with multiple rail mods are added, I'll put hooks & thinkers here
--║         THINKERS           ║ It's a pain in the ass for development, but it'll make Grind Rails permanently future-compatible.
--╚════════════════════════════╝══════════════════════════════════════════════════════════════════════════════════════════════════╝
if GS_RAILS and (GS_RAILS.blockload == true or GS_RAILS.maxhook == 2)
	GS_RAILS.blockload = false
--	print("\x85"+"NOT\x80 LOADING NEW\x81 HOOKS! \x80 RAIL.blockload:\x81 "+RAIL.blockload+"\x80 RAIL.maxhook:\x81 "+RAIL.maxhook)
	return --Okay, we've skipped all loading phases, remove blockload!
end
local RAIL = GS_RAILS

--LUA_RAILMAIN

--Try to make sure the Director won't be "killed" or anything
addHook("ShouldDamage", function(s) if s return false end end, MT_GSRAILDIRECTOR)
addHook("MobjDamage", function(s) if s return true end end, MT_GSRAILDIRECTOR)
addHook("MobjDeath", function(s) if s return true end end, MT_GSRAILDIRECTOR)
addHook("MobjRemoved", function(s) if s RAIL.error("Warning! The Rail Director got removed!") end end, MT_GSRAILDIRECTOR)

--Technically happens before the Main Rail.
addHook("MobjSpawn", function(s)
	RAIL.RailMobjSpawn(s)
end, MT_GSRAIL)

addHook("MapThingSpawn", function(s, mapthing)
--	if RAIL.IsMTValid(s, mapthing)
		RAIL.MapThingSpawn(s, mapthing)
--	end
end, MT_GSRAIL)

addHook("MobjDeath", RAIL.RailBusted, MT_GSRAIL) 
addHook("MobjDamage", RAIL.RailBusted, MT_GSRAIL)

addHook("MobjThinker", function(s) 
	if RAIL.IsValid(s, 1)
		RAIL.AttachedThingThinker(s)
	end
end, MT_GSRAILATTACHEDTHING)

--LUA_RAILDECOR
addHook("MapThingSpawn", function(s, mapthing)
	if RAIL.IsMTValid(s, mapthing)
		RAIL.DyeMapThingSpawn(s,mapthing)
	end
end, MT_GSRAILDYE)
addHook("MobjThinker", function(s) 
	if RAIL.IsValid(s) RAIL.DyeThinker(s) end
end, MT_GSRAILDYE)

addHook("MapThingSpawn", function(s, mapthing) 
	if RAIL.IsMTValid(s, mapthing)
		RAIL.LightMapThingSpawn(s, mapthing)
	end
end, MT_GSRAILLIGHT)
addHook("MobjThinker", function(s)
	if RAIL.IsValid(s)
		RAIL.LightThinker(s)
	end
end, MT_GSRAILLIGHT)

addHook("MapThingSpawn", function(s, mapthing) 
	if RAIL.IsMTValid(s, mapthing)
		RAIL.PoleMapThingSpawn(s, mapthing)
	end
end, MT_GSRAILPOLE)

--addHook("MobjThinker", function(s)
--	if RAIL.IsValid(s) RAIL.LightThinker(s) end
--end, MT_GSRAILPOLE)

--LUA_COLLIDE
addHook("MobjCollide", RAIL.GrindRailCollide, MT_GSRAIL) 
addHook("MobjCollide", RAIL.GrindSegmentCollide, MT_GSRAILSEG)
--addHook("MobjMoveCollide", RAIL.GrindRailCollide, MT_GSRAIL)
--addHook("MobjMoveCollide", RAIL.GrindSegmentCollide, MT_GSRAILSEG)
addHook("MobjCollide", RAIL.RailObjectCollide, MT_GSRAILATTACHEDTHING)
addHook("MobjMoveCollide", RAIL.RailObjectCollide, MT_GSRAILATTACHEDTHING)

--LUA_RAILGFX
addHook("MobjThinker", function(s)
	if RAIL.IsValid(s)  
		RAIL.autoframe(s) 
	end
end, MT_GSRAILGFX)


--LUA_GRINDMOBJ
addHook("PreThinkFrame", do --Use PreThinkframe for menu stuff and to prevent jank interactions!
	if (gamestate!=GS_LEVEL) return end
	for p in players.iterate
		if not (p and p.valid) continue end local s = p.mo if not (s and s.valid) continue end 
		local GS = s.GSgrind	
		
		if s.GSRailMenu
			RAIL.MenuThinker(p, s, s.GSRailMenu) continue --The rest doesn't matter rn.
		end 
		
		if GS
		/*	if (leveltime%71==0) and not (p.bot)
				if GS.playerskin!=s.skin and p.GSRO and s.skin!=p.GSRO.playerskin and p.GSRO.playerskin!=nil
					p.GSRO.hasloaded = nil
					RAIL.SaveLoad(p, false, true, nil, s.skin)
				end
				GS.playerskin = s.skin 
			end */
			if GS.forcemove --Forced analog input for a specified amount of time.
				GS.forcemove.timer = $-1
				if GS.forcemove.forwardmove!=nil
					p.cmd.forwardmove = GS.forcemove.forwardmove
				end
				if GS.forcemove.sidemove!=nil
					p.cmd.sidemove = GS.forcemove.sidemove
				end				
				if not GS.forcemove.timer
					GS.forcemove = nil
				end
			end
			if GS.grinding and GS.myrail and GS.myrail.valid
				s.friction = FRACUNIT
				if not GS.leftrail 		
					GS.side = p.cmd.sidemove or 0
					GS.forward = p.cmd.forwardmove or 0
					if p.panim==PA_IDLE p.panim = PA_ETC end --Never allow that panim
					s.eflags = $ & ~(MFE_JUSTHITFLOOR|MFE_APPLYPMOMZ|MFE_ONGROUND)
					p.pflags = $ & ~(PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SPINNING|PF_STARTDASH|PF_SLIDING|PF_SHIELDABILITY)
					p.speed = max(1, abs(GS.railspeed or 0))
					if (GS.myrail.GSgrindflags & 131072) --Don't jump out of the cinematic "nomove" part.
						GS.railjump = 99<<16
						p.pflags = $|PF_JUMPDOWN
					end
					if GS.myrail.GSwalljumprail
						p.cmd.sidemove,p.cmd.forwardmove = 0,0
					end
				end
			elseif GS.railswitching 
				if (p.pflags & (PF_JUMPED|PF_NOJUMPDAMAGE|PF_BOUNCING)) 
					if (GS.railswitching > 2) and not P_IsObjectOnGround(s)
						if (GS.railswitching > 10)
							p.cmd.sidemove = 0
						elseif (abs(p.cmd.sidemove) > 3)
							p.cmd.sidemove = $/3						
						end
						if (abs(p.cmd.forwardmove) > 3)
							p.cmd.forwardmove = $/3
						end
						p.cmd.buttons = $ & ~(BT_CAMLEFT|BT_CAMRIGHT)
						p.pflags = $|PF_JUMPDOWN|PF_USEDOWN & ~(PF_STARTJUMP|PF_SPINNING|PF_SHIELDABILITY)
					else
						p.pflags = $ & ~PF_SHIELDABILITY
					end
				end
			elseif p.homing and s.tracer --Immediately unstuck "Cannotgrind value"
				if (s.tracer.GShomingtarget) or s.tracer.railobject
					GS.cannotgrind,p.homing = 0, min(p.homing, 40)
				end
			end
			if GS.walljump and GS.walljump.timer --Gradually ease back into full control
				if GS.walljump.loopcam
					if (GS.walljump.init < 52)
						p.cmd.forwardmove = 0
					elseif (GS.walljump.init < 60)
						p.cmd.forwardmove = $/(60-GS.walljump.init)
					end
				end
				if (GS.walljump.init < 8)
					local DIV = max(1, 8-GS.walljump.init)
					p.cmd.forwardmove,p.cmd.sidemove = $/DIV,$/DIV
					if (GS.walljump.init < 5)
						p.pflags = $|PF_JUMPDOWN|PF_USEDOWN
					end
				end
			elseif ( GS.grinding or ((GS.leftrail or 0) > 1) ) and (P_IsObjectOnGround(s) or (s.eflags & MFE_JUSTHITFLOOR))
				if not p.cmd.sidemove and not p.cmd.forwardmove and not (p.pflags & PF_SPINNING)
					if GS.LastRail and GS.LastRail.GSloopflags and (GS.LastRail.GSloopflags & (262144|524288))
						p.cmd.sidemove = (s.momy>0) and 1 or -1
					else
						p.cmd.forwardmove = 1 --SRB2 will forcibly stop all momentum otherwise...
					end
				end
			end			
		end
		if p.WasInRailMenu --Reset the music volume.
			if p.WasInRailMenu==gamemap S_SetInternalMusicVolume(100, p) end
			p.pflags,p.WasInRailMenu = $ & ~PF_FORCESTRAFE, nil		
		end
	end
end)

addHook("ThinkFrame", do --Use Thinkframes for most other stuff, to ensure animations and cameras won't break easily!
	if (gamestate!=GS_LEVEL) return end
	for p in players.iterate
		if p and p.valid RAIL.PlayerThink(p) end
	end
end)

local VERSION = GS_RAILS_V
local DIRECTOR = GS_RAILS_DIRECTOR

addHook("PostThinkFrame", do --Use PostThinkframes for animations, cameras, and the director behaviour.
	if (gamestate!=GS_LEVEL) return end --ALWAYS be in a level first.
	
	if not ((DIRECTOR) and type(DIRECTOR)=="userdata" and userdataType(DIRECTOR)=="mobj_t" and DIRECTOR.valid)
		local NETJOIN = nil
		if netgame==true and consoleplayer==displayplayer and ((consoleplayer.jointime or 0) < 9) and not (consoleplayer.bot)
			NETJOIN = consoleplayer
		end
		local FOUNDDIRECTOR = false --Let our quest begin!
		if (NETJOIN!=nil)
			for z in players.iterate
				if z and z.valid and (z.GS_RAILDIRECTOR) and type(z.GS_RAILDIRECTOR)=="userdata" 
				and userdataType(z.GS_RAILDIRECTOR)=="mobj_t" and z.GS_RAILDIRECTOR.valid
				and (z.GS_RAILDIRECTOR.type==MT_GSRAILDIRECTOR)
					DIRECTOR = z.GS_RAILDIRECTOR
					NETJOIN.GS_RAILDIRECTOR = DIRECTOR
					GS_RAILS_DIRECTOR = DIRECTOR
					FOUNDDIRECTOR = true break --Our fellow players were so kind as to redirect us.
				end
			end
		end
		if FOUNDDIRECTOR==false
			if (NETJOIN!=nil) and (RAIL.MapReady) and (leveltime > 2) --Is there one anywhere in the stage...??
				for m in mobjs.iterate(mobjs)
					if m and m.type==MT_GSRAILDIRECTOR
						GS_RAILS_DIRECTOR = m 
						FOUNDDIRECTOR = true break --
					end
				end
			end
			if FOUNDDIRECTOR==false and (leveltime > 1)
				if (netgame!=true) or (consoleplayer==displayplayer) --Aight, make a new one.
					GS_RAILS_DIRECTOR = P_SpawnMobj(995, 995, 22999<<16, MT_GSRAILDIRECTOR)
					DIRECTOR = GS_RAILS_DIRECTOR -- print("SPAWNED\x83 NEW\x80 DIRECTOR")
				/*	for z in players.iterate
						if z and z.valid 
							z.GS_RAILDIRECTOR = DIRECTOR
						end
					end */
				end
			end
		end
	else
		RAIL.DirectorThinker(DIRECTOR)
	end

	for p in players.iterate
		if not (p and p.valid) continue end local s = p.mo if not (s and s.valid) continue end 
		local GS = s.GSgrind
		if GS
			if GS.animcall or GS.forcesprite!=nil
				RAIL.CustomAnim(p, s, GS)
			end
			if GS.forceangle!=nil
				p.drawangle = GS.forceangle
				GS.forceangle = nil
			end
			if ((GS.railswitching or 0)> 2) 
				if (GS.switchCAMang!=nil) and not ((s.flags2 & MF2_TWOD) or twodlevel)
					p.cmd.angleturn = GS.switchCAMang
				end
			else
				if GS.grinding
					s.friction = FRACUNIT
				--	if GS.ropesnap and GS.ropesnap==GS.myrail and not (GS.myrail.GSloopflags & (262144|524288))	
						--
				--	else
						local rail = GS.myrail or GS.LastRail
						local RSTATS = GS.MyRailStats
						if rail
							if rail.GSVertRail or RSTATS and RSTATS.VERTRAIL
								if rail and rail.valid and rail.GSnextrail
									if not (rail.GSloopflags & 8388608)
										s.frame = $ & ~FF_VERTICALFLIP
										if (s.sprite2 & ~FF_SPR2SUPER)==SPR2_ROLL
											s.rollangle = 0
										else
											local TOANG = RAIL.ANG(rail)+ANGLE_180 --R_PointToAngle2(rail.x, rail.y, rail.GSnextrail.x, rail.GSnextrail.y) 
											if (rail.GSnextrail.z < rail.z) TOANG = $+ANGLE_180 end
											local MYANG =  p.cmd.angleturn<<16
											if displayplayer --and netgame==true
												MYANG = displayplayer.cmd.angleturn<<16
											end
											local ANGDIFF,PLUS = RAIL.AngleDifference(MYANG, TOANG, 2, ANGLE_90, 2)
											if (ANGDIFF >= ANG10) and not (ANGDIFF >  ANG1*85)
												PLUS = $*((s.mirrored) and -1 or 1) --*((ANGDIFF > ANG60) and -1 or 1)
												ANGDIFF = min(ANG2*26, max(ANG2*17, $))
												local LOOP = RAIL.LearnLoopType(rail) 
												if LOOP and LOOP.walljump
													local SKIN = GS_RAILS_SKINS[s.skin] or nil
													local STATS = (SKIN) and SKIN["S_SKIN"] or nil										
													if STATS and STATS.WALLCLINGSPRITE --Special sprite just for this? No rollangle, then.
														s.rollangle = 0
													else
														s.rollangle = (ANGDIFF/3)*PLUS
													end
												else
													s.rollangle = ANGDIFF*PLUS*2
												end
											else
												s.rollangle = 0
											end
										end
									end
									if not (rail.GSLoopType) and not (rail.GSloopflags & (262144|524288|4194304))
									and not (rail.GSgrindflags & 131072) and (GS.TWOD!=true) and RSTATS
									and (RSTATS.ZSPEED) and not p.awayviewtics
									and not ((GS.justtouchedrailthing or 0)>4) and not (GS.forceVaim) and GS.OGAIM==nil 
										GS.OGAIM = p.aiming --Auto cam on vertical rails?
										local ZSPEED = (RSTATS.ZSPEED/(s.scale/3))*ANG1
										if (RSTATS.ZSPEED > 0)
											p.aiming = min(ZSPEED, ANG2*29)
										else
											p.aiming = max(ZSPEED, -ANG2*29)
										end
									end					
								end
							end
							if GS.railsparks
								local ghs
								for i = -1,1,2 --Adjust rail sparks to where they should be.
									ghs = GS.railsparks[i]
									if ghs and ghs.valid
										if GS.MyRailStats and GS.MyRailStats.VERTRAIL
											P_MoveOrigin(ghs, s.x, s.y, ghs.z)
										else
											ghs.z = s.z+((s.eflags&MFE_VERTICALFLIP) and abs(s.height) or 0)	
										end
									end
								end				
							end
							if GS.railcam and GS.railcam.valid
								if GS.railcam.PostANG=="face"
									GS.railcam.angle = R_PointToAngle2(GS.railcam.x, GS.railcam.y, s.x, s.y)
									GS.railcam.PostANG = nil
								end
							/*	if not ((rail.GSloopflags or 0) & 8388608)
									local CAM = GS.railcam
									if p.awayviewmobj and p.awayviewmobj.valid
										if (p.awayviewmobj!=GS.railcam)
											CAM = p.awayviewmobj
											P_MoveOrigin(GS.railcam, CAM.x, CAM.y, CAM.z)
											GS.railcam.angle = CAM.angle
											GS.railcam.momx,GS.railcam.momy, GS.railcam.momz = CAM.momx,CAM.momy,CAM.momz
										elseif p.solcam and mapheaderinfo[gamemap].solcam=="on"
											if p.solcam.mode=="default" and not p.solcam.activetimer and not p.solcam.transitionrem
												p.solcam = nil --Let it remake itself later.
											else
												if p.solcam.mobj and p.solcam.mobj.valid --Solcam, but not the cam we're using?
													local CAM = GS.railcam local SOL = p.solcam.mobj
													P_MoveOrigin(SOL, CAM.x, CAM.y, CAM.z) --Adjust it.
													SOL.angle = CAM.angle
												end 
											end
										end
									end
									if (netgame!=true) and p==consoleplayer and camera and camera.chase
										P_TeleportCameraMove(camera, CAM.x, CAM.y, CAM.z)
										camera.momx,camera.momy,camera.momz = CAM.momx,CAM.momy,CAM.momz
										camera.angle = CAM.angle
									end
								end */
							end					
						end
				--	end
				else
					if GS.JumpRamp and GS.JumpRamp.rollangle!=nil --Enforce rollangle strictly.
						s.rollangle = GS.JumpRamp.rollangle
					elseif GS.FuckingROLL --When gentle persuasion refuses to work...
						if not GS.cannotgrind
							GS.FuckingROLL = nil
						elseif not p.powers[pw_carry] or P_PlayerInPain(p)
							GS.FuckingROLL = max($-1,0)
							p.pflags = $|PF_SPINNING|PF_USEDOWN|PF_JUMPDOWN & ~PF_THOKKED
							s.state = S_PLAY_ROLL
							if not P_IsObjectOnGround(s)
								p.pflags = $|PF_JUMPED
							end
							if not GS.FuckingROLL
								GS.FuckingROLL = nil
							end
						end
					end
				end
				if GS.speedtrail --Do an adventure-style spintrail?
					RAIL.SpeedTrail(s, GS)
					if type(GS.speedtrail)=="number"
						GS.speedtrail = max($-1,0)
					else
						GS.speedtrail = nil
					end
				end					
				GS.LastXYZ = {X = s.x, Y = s.y, Z = s.z}	
			end	
			local SKIN = GS_RAILS_SKINS[s.skin]
			if SKIN and SKIN["FinalThinker"]
				SKIN["FinalThinker"](p, s, GS) --The final thinker that goes after EVERYTHING else.
			end				
		end
	end
	if VERSION.timer
		VERSION.timer = max($-1,0)
		if not VERSION.timer
			if consoleplayer --Print a message on load!
				local TEXT,SRB2V = "\x86", "SRB2 "
				local VTEXT = VERSION.versionstring or "???"
				local VTEXT = "\x85"+"SATv0.77." --What to dub this version. Purely cosmetic.
				if ((SUBVERSION or 0) < 13) or type(VERSIONSTRING)!="string"
					SRB2V = $+"\x85"+"v2.2.13+"+"("+"\x89"+"This SRB2 version might not work!)"+"\x80"
				else
					SRB2V = $+"\x83"+VERSIONSTRING+"+"+"\x80"
				end
				if VERSION.FirstLoad==true
					TEXT = $+"═════════════════"+"\n"+"\x82"
					+"GS Grind Rails\x80 ["+VTEXT+GS_RAILS.version+"] for ["+SRB2V+"] by\x82 Golden Shine!"
				else
					TEXT = $+"═════════════════"+"\n"+"\x83"
					+"<═UPDATED═>"+"\x82 GS Grind Rails\x80 to ["+VTEXT+GS_RAILS.version+"\x80"+"]"
				end
				CONS_Printf(consoleplayer, TEXT) chatprintf(consoleplayer, TEXT, false)
			end
			VERSION.FirstLoad,VERSION.timer = nil 
		end
	end	
end)


addHook("ShouldDamage", RAIL.PlayerShouldDamage, MT_PLAYER) --Players are immune to crush damage when grinding!
addHook("MobjDamage", RAIL.PlayerDamage, MT_PLAYER) --Actions when players get damaged on a rail
addHook("MobjDeath", RAIL.PlayerDeath, MT_PLAYER) --Actions when players die on a rail

addHook("MapChange", RAIL.MapChange) --Reset RAIL Director and other values!

--Let jetty's call linedef executors when killed with their "Can Move" flag set to no! Feel free to add this function to any mobj!
addHook("MobjDeath", RAIL.JettDeath, MT_JETTBOMBER)
addHook("MobjDeath", RAIL.JettDeath, MT_JETTGUNNER)

addHook("MapLoad", function(gamemap)
	if gamemap!=nil and leveltime!=nil
		RAIL.MapLoad(gamemap)
	end
end)

hud.add(function(v, p)
	RAIL.HUDdraw(v,p)
end)

--Lastly, sync some stuff.
addHook("NetVars", function(network)
	GS_RAILS_V = network($)
	
--	GS_RAILS.blockload = network($)
	GS_RAILS.MapReady = network($)
--	GS_RAILS.maxhook = network($)
	
--	GS_RAILS_SKINS = network($)
	CR_GRINDRAIL = network($)
	GS_RAILS_SETTINGS = network($)
	GS_RAILS_DIRECTOR = network($)
	GS_LOOP_TYPES = network($)
	GS_RAILS_TYPES	= network($)
end)

--print("\x82"+"LOADING ALL HOOKS")