--╔════════════════════════════╗═════════════════════════════════════════════════════════════════════════════════════════════════╗
--║     GRIND RAILS SET UP     ║  Grind Rails are made by Golden Shine! The day for rail grinding in SRB2 has finally arrived!   ║
--║════════════════════════════╝═════════════════════════════════════════════════════════════════════════════════════════════════║
--║ This lump and LUA_HOOKS work together to allowing loading duplicate Grind Rails mods without creating issues/lag.			 ║
--║ They will automatically override older Grind Rail versions with a newer versions's features!								 ║
--╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝
if GS_RAILS_V==nil print("\x85"+"FATAL ERROR:\x81"+"GS_RAILS_V was somehow not valid!") return end --
local VERSION = GS_RAILS_V  --Our version table. This shit BETTER be loaded.

--Is a newer version of GS Rails already loaded? Don't make duplicates, only use the newest one!
--It checks if we have an old GS_RAILS table and if that table's version is higher than GS_RAILS_V's version.
--If it is, overwrite GS_RAILS' old functions, WITHOUT adding new hooks.(ThinkFrame, PostThinkFrame, etc)
if not (GS_RAILS) or not (GS_RAILS.version) --There's never BEEN a version, so obviously continue.
or (GS_RAILS.version < VERSION.version) --Version is higher than last added one, continue!

	 --This is the main rail table that we'll be using for most of our operations.
	 --It's jam-packed with functions and variables, and also contains the current version number.
	 --By packing all the functions into this one table, it's easy to completely replace functionality in updates.
	if not (GS_RAILS) or type(GS_RAILS)!="table" or not (GS_RAILS.version)
		rawset(_G,"GS_RAILS",{}) --Make the table!
	end
	local RAIL = GS_RAILS
	if GS_RAILS.version==nil
		VERSION.FirstLoad = true
	else
		VERSION.timer = 7
	end
	RAIL.version = VERSION.version --This is now the newest version.
	RAIL.blockload = false --Since this is new, do not block the Lua lumps from loading.
else
	GS_RAILS.blockload = true
end

local RAIL = GS_RAILS --Shorten GS_RAILS to RAIL for convenience! You can simply call GS_RAILS.ActivateRail if preferred. (if GS_RAILS exists)
RAIL.maxhook = (RAIL.maxhook) and 2 or 1 --Only load hooks once, use the actual editable tables to update the behaviour!
RAIL.LastErrorMessage,RAIL.LastMessage,RAIL.LastPrint = nil

--Global table for Rails Settings. (Currently unused, just forward thinking.)
if GS_RAILS_SETTINGS==nil
	rawset(_G,"GS_RAILS_SETTINGS", {})
end

--The GS_RAILS_SKINS table, allowing coders to create special behavior on characters that will only activates when GS Grind Rails 
--are actually added to SRB2. This saves performance for everyone involved, and allows for a crapton of customizability.
--Most character modders will only need to know how to use the rails version of S_SKIN. Check LUA_CUSTOMCHARS for info on that!
if GS_RAILS_SKINS==nil
	rawset(_G,"GS_RAILS_SKINS",{})
end

--The Rail Director is an object(MT_GSRAILDIRECTOR) that connects all rails in a level, then keeps track of said rails
--using it's own seperate tables. This massively saves performance, and works well for SRB2's netcode. 
--It also means GSGrindRails adds almost zero strain to any levels with no rails them.
--GS_RAILS_DIRECTOR refers to this rail director object once it's spawned. Otherwise it's just an empty table.
if GS_RAILS_DIRECTOR==nil 
	rawset(_G,"GS_RAILS_DIRECTOR", {}) 
end

--This is just a convenience shortcut so you can check if player.powers[pw_carry]==CR_GRINDRAIL, instead of typing 3888.
if (CR_GRINDRAIL!=3888)
	rawset(_G,"CR_GRINDRAIL", 3888)
end

--Error printing. Used to warn of unusual circumstances or if a mapper did something wrong. Sometimes used for debugging.
RAIL.error = function(message)
	if RAIL.LastErrorMessage==message return end --Same message. Don't play.
	print(message)
	if RAIL.LastError==nil or (RAIL.LastError!=leveltime)
		S_StartSound(nil, sfx_skid)
		P_StartQuake(50<<16, 12)
	end
	RAIL.LastErrorMessage = message
	RAIL.LastError = leveltime
end

--════════════════════════════════════════════DEBUG STUFF BELOW════════════════════════════════════════════════════════

--Checks priority before printing, otherwise don't print.
RAIL.print = function(message, priority) 
	local GO = false
	if type(priority)=="string" --If field 1 is "FORCE", then priority IS the message, and the print will always play.
		if ((string.upper(message))!="FORCE") RAIL.error("use the force, dumbass.") return end
		message,priority,GO = priority, ANGLE_180-1, true
	elseif ((priority or 0) > ANG30) 
		GO = true
	else 
		local p = players[0] local NAME = (p and p.name) and string.upper(p.name) or ""
		if NAME=="GOLDEN SHINE" and (p.cmd.buttons & (BT_CUSTOM1|BT_CUSTOM2|BT_CUSTOM3|BT_TOSSFLAG)) GO = true end
	end
	if GO==true and RAIL.LastPrint!=message
		RAIL.LastPrint = message
		print(message) return true
	end --Done!
end

--Spawns a debug toad on X,Y,Z, or on the object if no coordinates are given. Mimmicks object's color unless FORCECOLOR is set.
--If X is a number, but Y and Z are not, X will be "FORCECOLOR" instead, and it'll spawn on the object given.
--In simpler terms, RAIL.SpawnDebug(mo, SKINCOLOR_BLUE) spawns a blue toad on "mo".
RAIL.SpawnDebug = function(s, X,Y,Z, FORCECOLOR)
	if (type(X)=="number") and Y==nil and Z==nil and FORCECOLOR==nil and type(s)=="userdata"
		FORCECOLOR = X --You can use X as the "FORCECOLOR" field if you want.
		X = nil
	end
	if s and type(s)=="userdata"
		if X==nil X,Y,Z = s.x,s.y,s.z end
	elseif X==nil
		return --Input SOMETHING, tho'...
	end
	
	local DEBUG = P_SpawnMobj(X,Y,Z,MT_TOAD)
	DEBUG.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
	DEBUG.dispoffset = 149 
	DEBUG.flags = $|MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOTHINK|MF_SCENERY
	DEBUG.color,DEBUG.colorized = FORCECOLOR or (s and s.color) and s.color or SKINCOLOR_GOLDENROD, true
	DEBUG.spriteyscale = $+(3<<16)
	if s and s.valid
		DEBUG.scale,DEBUG.angle,DEBUG.tracer = s.scale,s.angle,s
		local LAST = s.GSLastToad 
		if LAST and LAST.valid and LAST.x==DEBUG.x and LAST.y==DEBUG.y and LAST.z==z
		and LAST.color==DEBUG.color and LAST.scale==DEBUG.scale and LAST.angle==DEBUG.angle and LAST.tracer==DEBUG.tracer
			P_RemoveMobj(LAST) --No need for identicals, right?
		end
		s.GSLastToad = DEBUG
	end
end