--Welp, this is a giant pain, idk why I'm bothering porting this when I could just get the ported SF-64 NPCs.

freeslot("MT_RJNPC","MT_PRJNPC", "SPR_SRBM")
//"S_NPCKO5","S_NPCKO3","S_NPCKO6","S_NPCJUMP2", "S_NPCJUMP3", "S_NPCJUMP4"
/*"S_NPCKO1","S_NPCDEATH1",
"S_NPCSPRING1","S_NPCSPRING2","S_NPCSPRING3",
"S_NPCANGLE1", "S_NPCANGLE2", "S_NPCANGLE3", "S_NPCANGLE4", "S_NPCANGLE5")*/

--"S_NPCWALK1","S_NPCWALK2","S_NPCWALK3","S_NPCWALK4","S_NPCWALK5","S_NPCWALK6","S_NPCWALK7","S_NPCWALK8",
--"S_NPCWALK1T","S_NPCWALK2T","S_NPCWALK3T","S_NPCWALK4T","S_NPCWALK5T","S_NPCWALK6T","S_NPCWALK7T","S_NPCWALK8T",

--Just gonna have Lua do this....
local function RJNPCrngstate(mo)
	local s = mo
	mo.state = S_NPCANGLE1+P_RandomRange(0,4)
	mo.tics = 1
	mo.sprite = SPR_PLAY
end

--Easier this way.
local function RJNPCsetskin(mo)

	local s = mo
	local extracast = {"shadow", "cacee", "skip", "jana", "xsonic", "willo", "milne", "marine", "whisper", "surge", "silver", "nasya"}
	local generic = {"sonic", "metalsonic", "knuckles", "amy", "fang", "amy"} --Avoid Tails with his followobject.
	local SAcast = {"adventuresonic", "adventuresonic", "adventureknuckles", "adventureamy", "adventureshadow", "adventureamy"}
	local rng = P_RandomRange(1,12)
	
	if not mo.color --Assign a special color.
		local color = P_RandomRange(1, #skincolors-1)
		if not color
		or (color >= SKINCOLOR_SUPERSILVER1) and (color <= SKINCOLOR_SUPERTAN5)
			color = P_RandomRange(1, SKINCOLOR_SUPERSILVER1-1)
		end
		mo.color = color
	end
	if P_RandomRange(0,1) and skins[extracast[rng]] --Load some cool extra skins we've added?
		mo.skin = extracast[rng]
		return true
	else
		rng = P_RandomRange(1,5)
		if skins[SAcast[rng]] --If you've added Adventure characters, we prioritize them.
			mo.skin = SAcast[rng]
			mo.SAcastcharacter = true
			return true
		elseif skins[generic[rng]]
			mo.skin = generic[rng]
			return true
		end
	end
	return false
end


--Moving all these thousands of Collide" hooks to 1 local function. No, btw, I can't be arsed to clean it up any further.
local function rjNPCcollide(mo, touched)
local s = mo
local v = touched

	if not (v and v.valid)
		return false
	end
	
	--Gain ability to hitch a ride on NPCs head
	if v.player
		local p = v.player
		local go = nil
		
			if p.RJplayernum and mo.moveguy[p.RJplayernum]
			and mo.moveguy[p.RJplayernum] == v
			
				go = 2
				
			elseif not (s.eflags & MFE_VERTICALFLIP) and not (v.eflags & MFE_VERTICALFLIP) and ((v.z) > (s.z+(s.height/2)))
		
			or (s.eflags & MFE_VERTICALFLIP) and (v.eflags & MFE_VERTICALFLIP) and v.z < s.z-(s.height/2)
				if not ((v.z) > (s.z+s.height+s.momz+3*FRACUNIT)) --Slightly different height checks for players.
				and not ((s.z) > (v.z+v.height+v.momz+3*FRACUNIT))
					go = 1
				end
			end
			if go
			and p.panim != PA_JUMP
			and p.panim != PA_ROLL
			and p.panim != PA_FALL
			and not p.cmd.sidemove
			and not p.cmd.forwardmove
			and not (p.pflags & PF_JUMPED)		
				if p.RJplayernum == nil --Assign number for convenience.
					for i = 0,31
						if p == players[i] p.RJplayernum = i break end
					end
				end
				v.momz = 0
				mo.moveguy[p.RJplayernum] = v
				return false
			end
			return
	end

	if v.z > s.z+s.height --Height and validity checks...
	or s.z > v.z+v.height
		return
	end
	
	--Spring off springs
	if (v.flags & MF_SPRING)
		if touched.info.damage
			P_InstaThrust(mo, touched.angle, touched.info.damage)
		end
		if touched.info.mass
			mo.momz = touched.info.mass*P_MobjFlip(touched)
		end
		touched.state = touched.info.raisestate
		S_StartSound(touched, touched.info.painsound)
		mo.angle = touched.angle
		mo.state = S_NPCSPRING1
		return
	end	
	
	--Hurt enemies
	if v.flags & (MF_ENEMY|MF_BOSS) and (touched.flags & MF_SHOOTABLE) and v.health
	and (mo.state == S_NPCJUMP2 or mo.state == S_NPCJUMP3 or mo.state == S_NPCJUMP4)
		if touched.flags & (MF_ENEMY)
			A_ZThrust(mo,10,(0<<16)+0)
		end
		P_DamageMobj(touched, mo, 1)
		return

	--Special damage behaviour?
	
	elseif touched.flags & (MF_ENEMY|MF_BOSS|MF_PAIN|MF_FIRE) and v.health
	
		if (s.state >= S_NPCWALK1) and (s.state <= S_NPCWALK8)
		or (s.state >= S_NPCWALK1T) and (s.state <= S_NPCWALK8T)
			if s.health > 1
				P_DamageMobj(mo,touched,1)
				A_ZThrust(mo,7,(1<<16)+0)
				A_Thrust(mo,-5,1)
				A_NapalmScatter(mo,(mo.extravalue1<<16) + MT_FLINGRING,(35<<16)+96)
				mo.health = 1
				mo.extravalue1 = 0
				return
			else
				P_DamageMobj(mo,touched,1)
				A_ZThrust(mo,10,(1<<16)+1)
				A_SetObjectFlags(mo,MF_NOGRAVITY,1)
				return
			end
		end
	end
	
	--Die from missiles? Idfk what this code is trying to accomplish, tbh.
	if s.health and v.health
		if (v.flags & MF_MISSILE)
		and not (v.target and v.target.player)
		and not (v.target == s)
		and not (v.flags2 & (MF2_DONTDRAW|MF2_DEBRIS))
		and v.sonicang == nil		
			mo.health = $1 + 1
			mo.extravalue1 = $1 + 1
			P_KillMobj(touched)
			S_StartSound(touched, sfx_itemup)
			return
		end
	end
end
addHook("MobjCollide", rjNPCcollide, MT_RJNPC)
addHook("MobjMoveCollide", rjNPCcollide, MT_RJNPC)
addHook("MobjCollide", rjNPCcollide, MT_PRJNPC)
addHook("MobjMoveCollide", rjNPCcollide, MT_PRJNPC)

--Prevent weirdness.
local function rjNPCblocked(s,m,line)
	if m and m.player and m.player.RJplayernum != nil
		local num = m.player.RJplayernum
		if s.moveguy[num] and s.moveguy[num] == m
			return false
		end
	end
end
addHook("MobjMoveBlocked", rjNPCblocked, MT_RJNPC)
addHook("MobjMoveBlocked", rjNPCblocked, MT_PRJNPC)

--Golden Shine: Slightly better walking function, among some minor additions
local function rjNPCwalk(mo)
	local s = mo
	if s.valid and s.skin and skins[s.skin]
	
		--Spring Frames for jump?
		if (skins[s.skin].flags & SF_NOJUMPSPIN) or s.SAcastcharacter
			if (s.sprite2 == SPR2_ROLL)
			and s.momz*P_MobjFlip(s) > 0
				s.state = S_NPCSPRING1
			end
		end
		
		--Walking!
		if ((mo.state >= S_NPCWALK1T) and (mo.state <= S_NPCWALK8T))
		or ((mo.state >= S_NPCWALK1) and (mo.state <= S_NPCWALK8))
			local accel = skins[s.skin].acceleration or 40
			local maxspeed = skins[s.skin].normalspeed or 12*FRACUNIT
			if abs(s.momx)+abs(s.momy) < maxspeed*3/4
				P_Thrust(s, s.angle, accel*1000)
			end
		end

		--Help some guys ride your head.
		if s.moveguy != nil
			for i = 0,31

				local erase = true

				if s.moveguy[i] and s.moveguy[i].valid and s.moveguy[i].player

				local v = s.moveguy[i]
				local p = s.moveguy[i].player

					if p.panim != PA_JUMP
					and p.panim != PA_ROLL
					and p.panim != PA_FALL
					and not (p.pflags & PF_JUMPED)
						v.momx = $/2 v.momy = $/2
						v.momz = 0
						v.momx = $+s.momx v.momy = $+s.momy
						v.momz = s.momz
						v.eflags = $ & ~MFE_JUSTHITFLOOR
						erase = false
						if not p.cmd.sidemove
						and not p.cmd.forwardmove
							P_TryMove(v, s.x-s.momx, s.y-s.momy, true) --Ride the NPC's head for fun!
							if not (s.eflags & MFE_VERTICALFLIP)
								if v.z < s.z+s.height+s.momz-FRACUNIT
									v.z = s.height+s.momz
								end
							end
						end
						if p.panim == PA_WALK
							s.state = S_PLAY_STND
							p.panim = PA_IDLE
						end					
					end
				end

				if erase and s.moveguy[i]
					s.moveguy[i] = nil continue
				end	
			end	
		end
	end
end
addHook("MobjThinker", rjNPCwalk, MT_RJNPC)
addHook("MobjThinker", rjNPCwalk, MT_PRJNPC)

--Spawn with these parameters, I guess...??
local function rjNPCspawn(mo)
	mo.health = 1
	mo.extravalue1 = 0
	RJNPCsetskin(mo)
	mo.moveguy = {}
	for i = 0,31
		mo.moveguy[i] = mo
	end
end
addHook("MobjSpawn", rjNPCspawn, MT_RJNPC)
addHook("MobjSpawn", rjNPCspawn, MT_PRJNPC)

addHook("MobjThinker", function(mo)
--Swap to a cool supported skin!
local s = mo
	if not mo.skin --Got to have a skin!
		RJNPCsetskin(s)
	end
	if mo.state == S_NPCKO5 then
		if mo.z == mo.floorz 
		A_ForceStop(mo,1,0)
		A_SetObjectFlags(mo,MF_SHOOTABLE,2)
		mo.state = S_NPCKO6
		return
		end
	elseif mo.state == S_NPCSPRING2
	or mo.state == S_NPCSPRING3
	or mo.state == S_NPCJUMP2
	or mo.state == S_NPCJUMP3
	or mo.state == S_NPCJUMP4 then
		if mo.z == mo.floorz 
			A_ForceStop(mo,1,0)
			RJNPCrngstate(s)
		--	mo.state = S_NPCRANDOM
		end
	end
end, MT_RJNPC)

--Fix some stuff.
local function rjNPCdamage(mo, i, so, dmg, dmgtype)
	local s = mo
	if so and so.valid
		if so.player or (so.flags & MF_MISSILE) or (so.type == MT_RJNPC) or (so.type == MT_PRJNPC)
			if dmgtype and dmgtype == DMG_CRUSHED
				return false
			end
			if so.active or so.sonicang or (so.flags2 & (MF2_DONTDRAW|MF2_DEBRIS))
			or so.skin == "xsonic" and so.player
				return false
			end
		end
	end
	if i and i.valid 
		if i.player or (i.flags & MF_MISSILE) or (i.type == MT_RJNPC) or (i.type == MT_PRJNPC)
			if dmgtype and dmgtype == DMG_CRUSHED
				return false
			end
			if i.active or i.sonicang or (i.flags2 & (MF2_DONTDRAW|MF2_DEBRIS))
			or i.skin == "xsonic" and i.player
				return false
			end
		end
	end
end
addHook("ShouldDamage", rjNPCdamage, MT_RJNPC)
addHook("ShouldDamage", rjNPCdamage, MT_PRJNPC)

--No getting crushed by villagers.
local function pNPCdamage(mo, i, so, dmg, dmgtype)
	local s = mo
	if dmgtype and (dmgtype == DMG_CRUSHED)
		if so and so.valid
		and ((so.type == MT_PRJNPC) or (so.type == MT_RJNPC))
			return false
		end
	end
end
addHook("ShouldDamage", pNPCdamage, MT_PLAYER)

--Let's limit this to only the main maps.
addHook("MobjSpawn", function(mo)
	if (gametype == GT_COOP)
	and ((gamemap >= 1000) and (gamemap <= 1007)
	or (gamemap == 1) or (gamemap == 964))
		A_SetFuse(mo,210,(0<<16)+1)
	end
end, MT_FLINGRING)

--Meh.
addHook("MobjThinker", function(s)
	if ((gamemap >= 1000) and (gamemap <= 1007)
	or (gamemap == 1) or (gamemap == 964))
		s.sprite = SPR_SRBM
		s.frame = A
	end
end, MT_BLUEGARGOYLE)

--So many of these MobjThinkers could've been avoided with a simple return. 
--Fuck it, I can NOT be arsed to keep cleaning all this up.

addHook("MobjThinker", function(mo)
if mo.state == S_NPCKO3 then
A_ZThrust(mo,7,(1<<16)+0)
A_Thrust(mo,-5,1)
A_NapalmScatter(mo,(mo.extravalue1<<16) + MT_FLINGRING,(35<<16)+96)
mo.health = 1
mo.extravalue1 = 0
end
end, MT_RJNPC)

addHook("MobjThinker", function(mo)
	if ((mo.state >= S_NPCWALK1) and (mo.state <= S_NPCWALK8)) 
	or mo.state == S_NPCJUMP2
	or mo.state == S_NPCJUMP4 
	or mo.state == S_NPCJUMP3 then 
		if P_CheckDeathPitCollide(mo) then
		P_KillMobj(mo)
		end
	end
end, MT_RJNPC)

addHook("MobjThinker", function(mo)
if mo.state == S_NPCKO5 then
if mo.z == mo.floorz 
A_ForceStop(mo,1,0)
A_SetObjectFlags(mo,MF_SHOOTABLE,2)
mo.state = S_NPCKO6
end
end
end, MT_PRJNPC)

addHook("MobjThinker", function(mo)
--Swap to a cool supported skin!
local s = mo
if not mo.skin
	RJNPCsetskin(s)
elseif mo.state == S_NPCSPRING2
or mo.state == S_NPCSPRING3
or mo.state == S_NPCJUMP2
or mo.state == S_NPCJUMP3
or mo.state == S_NPCJUMP4 then
if mo.z == mo.floorz 
A_ForceStop(mo,1,0)
RJNPCrngstate(s)
--mo.state = S_NPCRANDOM
end
end
end, MT_PRJNPC)

addHook("MobjThinker", function(mo)
if mo.state == S_NPCKO3 then
A_ZThrust(mo,7,(1<<16)+0)
A_Thrust(mo,-5,1)
A_NapalmScatter(mo,(mo.extravalue1<<16) + MT_FLINGRING,(35<<16)+96)
mo.health = 1
mo.extravalue1 = 0
end
end, MT_PRJNPC)

addHook("MobjThinker", function(mo)
if ((mo.state >= S_NPCWALK1) and (mo.state <= S_NPCWALK8)) 
or mo.state == S_NPCJUMP2
or mo.state == S_NPCJUMP4 
or mo.state == S_NPCJUMP3 then 
if P_CheckDeathPitCollide(mo) then
P_KillMobj(mo)
end
end
end, MT_PRJNPC)


// Credits to SSG3 for making this simple script
--Golden Shine: I moved and remade the code a bit. I hope the original credit to SS3 for the original isn't a problem.

local function spawnRJnpc(p, arg1)
	if p != server and not IsPlayerAdmin(p)
		return
	end
	if p.mo and p.mo.valid
		local s = p.mo
		if arg1 and arg1 == "MT_PRJNPC"
			P_SpawnMobj(s.x + 50*FRACUNIT, s.y + 100*FRACUNIT, s.z, MT_PRJNPC)
		elseif arg1 and arg1 == "MT_RJNPC"
			P_SpawnMobj(s.x + 50*FRACUNIT, s.y + 100*FRACUNIT, s.z, MT_RJNPC)
		else
			CONS_Printf(p, "\x80"+"Use\x82 MT_RJNPC\x80 for a regular NPC or\x82 MT_PRJNPC\x80 for a pushable NPC.")
		end
	end
end
COM_AddCommand("AddRJNPC", spawnRJnpc)