--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| 	Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]

if CLIENT then return end
print("Executing Half-Life Resurgence auto replace script...")

-- https://developer.valvesoftware.com/wiki/Half-Life.fgd
-- https://developer.valvesoftware.com/wiki/Half_Life_2.fgd

local replaceTbl_Entities = {
		-- Half-Life 1 --
	["monster_alien_grunt"] = "npc_vj_hlr1_aliengrunt",
	["monster_nihilanth"] = "npc_vj_hlr1_nihilanth",
	["monster_tentacle"] = "npc_vj_hlr1_tentacle",
	["monster_alien_slave"] = "npc_vj_hlr1_alienslave",
	["monster_bigmomma"] = "npc_vj_hlr1_gonarch",
	["monster_bullchicken"] = "npc_vj_hlr1_bullsquid",
	["monster_gargantua"] = "npc_vj_hlr1_garg",
	["monster_human_assassin"] = "npc_vj_hlr1_assassin_female",
	["monster_babycrab"] = "npc_vj_hlr1_headcrab_baby",
	-- ["monster_human_grunt"] = {"npc_vj_hlr1_hgrunt","npc_vj_hlrof_hgrunt","npc_vj_hlrof_hgrunt_med","npc_vj_hlrof_hgrunt_eng"},
	["monster_human_grunt"] = "npc_vj_hlr1_hgrunt",
	["monster_cockroach"] = "npc_vj_hlr1_cockroach",
	["monster_houndeye"] = "npc_vj_hlr1_houndeye",
	["monster_scientist"] = "npc_vj_hlr1_scientist",
	["monster_sitting_scientist"] = "npc_vj_hlr1_scientist",
	["monster_snark"] = "npc_vj_hlr1_snark",
	["monster_zombie"] = {"npc_vj_hlr1_zombie","npc_vj_hlr1_zombie","npc_vj_hlr1_zombie","npc_vj_hlrof_zombie_sec","npc_vj_hlrof_zombie_sec","npc_vj_hlrof_zombie_soldier"},
	["monster_headcrab"] = "npc_vj_hlr1_headcrab",
	["monster_alien_controller"] = "npc_vj_hlr1_aliencontroller",
	["monster_barney"] = "npc_vj_hlr1_securityguard",
	["monster_turret"] = "npc_vj_hlr1_turret",
	["monster_miniturret"] = "npc_vj_hlr1_turret_small",
	["monster_sentry"] = "npc_vj_hlr1_sentry",
	["monster_barnacle"] = "npc_vj_hlr1_barnacle",
	["monster_ichthyosaur"] = "npc_vj_hlr1_ichthyosaur",
	["monster_gman"] = "npc_vj_hlr1_gman",
	["monster_apache"] = "npc_vj_hlr1_apache",
	["monster_osprey"] = "npc_vj_hlr1_osprey",
	["monster_turret"] = "npc_vj_hlr1_cturret",
	["monster_miniturret"] = "npc_vj_hlr1_cturret_mini",
		-- Half-Life 2 --
	["npc_advisor"] = "npc_vj_hlr2_com_advisor",
	["npc_alyx"] = "npc_vj_hlr2_alyx",
	["npc_antlion"] = "npc_vj_hlr2_antlion",
	["npc_antlion_worker"] = "npc_vj_hlr2_antlion_worker",
	["npc_antlionguard"] = "npc_vj_hlr2_antlion_guard",
	["npc_barney"] = "npc_vj_hlr2_barney",
	["npc_citizen"] = "npc_vj_hlr2_citizen",
	["npc_combine_s"] = "npc_vj_hlr2_com_soldier",
	["npc_combinegunship"] = "npc_vj_hlr2_com_gunship",
	["npc_crabsynth"] = "npc_vj_hlr2_com_crab",
	["npc_fastzombie"] = "npc_vj_hlr2_zombie_fast",
	["npc_gman"] = "npc_vj_hlr2_gman",
	["npc_headcrab"] = "npc_vj_hlr2_headcrab",
	["npc_headcrab_black"] = "npc_vj_hlr2_headcrab_poison",
	["npc_headcrab_fast"] = "npc_vj_hlr2_headcrab_fast",
	["npc_helicopter"] = "npc_vj_hlr2_com_chopper",
	["npc_hunter"] = "npc_vj_hlr2_com_hunter",
	["npc_metropolice"] = "npc_vj_hlr2_com_civilp",
	["npc_monk"] = "npc_vj_hlr2_father_grigori",
	["npc_mortarsynth"] = "npc_vj_hlr2_com_mortar",
	["npc_poisonzombie"] = "npc_vj_hlr2_zombie_poison",
	["npc_sniper"] = "npc_vj_hlr2_com_sniper",
	["npc_stalker"] = "npc_vj_hlr2_com_stalker",
	["npc_strider"] = "npc_vj_hlr2_com_strider",
	["npc_turret_ceiling"] = "npc_vj_hlr2_com_ceilingturret",
	["npc_turret_floor"] = "npc_vj_hlr2_com_sentry",
	["npc_zombie"] = "npc_vj_hlr2_zombie",
	["npc_zombine"] = "npc_vj_hlr2_zombine",
	["prop_vehicle_apc"] = "npc_vj_hlr2_com_apc",
}

local replaceTbl_Weapons = {
		-- Half-Life 2 --
	["weapon_357"] = "weapon_vj_357",
	["weapon_alyxgun"] = "weapon_vj_hlr2_alyxgun",
	["weapon_annabelle"] = "weapon_vj_hlr2_annabelle",
	["weapon_ar2"] = "weapon_vj_ar2",
	["weapon_crossbow"] = "weapon_vj_crossbow",
	["weapon_crowbar"] = "weapon_vj_crowbar",
	["weapon_pistol"] = "weapon_vj_9mmpistol",
	["weapon_rpg"] = "weapon_vj_hlr2_rpg",
	["weapon_shotgun"] = "weapon_vj_spas12",
	["weapon_smg1"] = "weapon_vj_smg1",
	["weapon_stunstick"] = "weapon_vj_hlr2_stunstick"
}

local essentialTbl = { -- Will expand upon this later, I recommend we add support for custom HLR packs to add their own data to the auto-replace script (Example: Half-Life 2 HLR pack adding Kleiner or Breen to the auto-script)
		-- Half-Life 2 --
	npc_vj_hlr2_alyx=true,
	npc_vj_hlr2_barney=true,
	npc_vj_hlr2_father_grigori=true,
}

-- Before Create
local replaceOptions = {
	-- If its an antlion guardian, then make sure to spawn that variant!
	["npc_antlionguard"] = function(ent, replaceEnt)
		return (ent:GetSkin() == 0 and "npc_vj_hlr2_antlion_guard") or "npc_vj_hlr2_antlion_guardian"
	end,
	-- Handle citizen / refugee / rebel variants
	["npc_citizen"] = function(ent, replaceEnt)
		for key, val in pairs(ent:GetKeyValues()) do
			if key == "citizentype" then
				if val == 0 or val == 1 then
					return "npc_vj_hlr2_citizen"
				elseif val == 2 then
					return "npc_vj_hlr2_refugee"
				elseif val == 3 or val == 4 then
					return "npc_vj_hlr2_rebel"
				end
			end
		end
	end,
	-- Handle combine soldier variants
	["npc_combine_s"] = function(ent, replaceEnt)
		local mdl = ent:GetModel()
		if mdl == "models/combine_soldier.mdl" then
			return (ent:GetSkin() == 0 and "npc_vj_hlr2_com_soldier") or "npc_vj_hlr2_com_shotgunner"
		elseif mdl == "models/combine_soldier_prisonguard.mdl" then
			return (ent:GetSkin() == 0 and "npc_vj_hlr2_com_prospekt") or "npc_vj_hlr2_com_prospekt_sg"
		elseif mdl == "models/combine_super_soldier.mdl" then
			return "npc_vj_hlr2_com_elite"
		end
	end,
	-- Check for resistance turrets!
	["npc_turret_floor"] = function(ent, replaceEnt)
		return ent:HasSpawnFlags(SF_FLOOR_TURRET_CITIZEN) and "npc_vj_hlr2_res_sentry" or "npc_vj_hlr2_com_sentry"
	end
}

-- Before Spawn
local replacePreSpawn = {
	["npc_citizen"] = function(ent, newEnt)
		if string.find(ent:GetModel(), "female") then
			newEnt.Human_Gender = 1
		else
			newEnt.Human_Gender = 0
		end
	end,
	["npc_metropolice"] = function(ent, newEnt)
		for key, val in pairs(ent:GetKeyValues()) do
			if key == manhacks && val == 0 then
				newEnt.Metrocop_CanHaveManhack = false
			end
		end
	end,
}

-- After Spawn
local afterSpawned = {
	
}

// lua_run PrintTable(Entity(1):GetEyeTrace().Entity:GetTable())
local defPos = Vector(0, 0, 0)

local gStatePrecriminal = false
local gStateAntlionFri = false

-- Prepare the HL1 and HL2 tables (This includes ALL NPCs!)
local allNPCs = list.Get("NPC")
local allHLR = {}
local allHLR1 = {}
local allHLR2 = {}
timer.Simple(0.01, function()
	for k, _ in pairs(allNPCs) do
		-- This should capture all the HL2 NPCs
		if string.StartWith(k, "npc_vj_hlr2") then
			table.insert(allHLR, k)
			table.insert(allHLR2, k)
		-- This should capture all the HL1 NPCs because many of them are "hlrof", "hlr1", "hlrsv", "hlrdc", etc.
		elseif string.StartWith(k, "npc_vj_hlr") then
			table.insert(allHLR, k)
			table.insert(allHLR1, k)
		end
	end
end)


hook.Add("OnEntityCreated", "VJ_HLR_AutoReplace_EntCreate", function(ent)
	local class = ent:GetClass()
	local rEnt = VJ_PICK(replaceTbl_Entities[class])
	if rEnt then
		-- Make sure the game is loaded
		if game && game.GetGlobalState then
			gStatePrecriminal = game.GetGlobalState("gordon_precriminal") == 1
		end
		-- Check if it's HL1 & HL2 and stop if it's not supposed to continue
		local isHL1 = string.StartWith(class, "monster_")
		if isHL1 then
			if GetConVar("vj_hlr_autoreplace_hl1"):GetInt() == 0 then
				return
			end
		elseif GetConVar("vj_hlr_autoreplace_hl2"):GetInt() == 0 then
			return
		end
		timer.Simple(0.01, function()
			if IsValid(ent) then
				local worldName = ent.GetName && ent:GetName() or nil
				//if worldName == "rocketman" then return end -- Makes Odessa work a little bit
				//local spawnAnim = ent:GetSequenceName(ent:GetSequence())
				-- Spawn the correct entity (Ex: different combine or rebels/citizens)
				if replaceOptions[class] then
					rEnt = replaceOptions[class](ent, rEnt) or rEnt
				end
				-- FUN OPTION: Randomized NPC System
				if GetConVar("vj_hlr_autoreplace_random"):GetInt() == 1 then
					if GetConVar("vj_hlr_autoreplace_randommix"):GetInt() == 1 then
						rEnt = VJ_PICK(allHLR) or rEnt
					else
						if isHL1 then
							rEnt = VJ_PICK(allHLR1) or rEnt
						else
							rEnt = VJ_PICK(allHLR2) or rEnt
						end
					end
				end
				-- Start the actual final entity --
				local newClass = VJ_PICK(rEnt)
				local newEnt = ents.Create(newClass)
				if !IsValid(newEnt) then MsgN("Entity [" .. newClass .. "] not valid (missing pack?), keeping original entity") return end
				-- Certain entities need some checks before spawn (Ex: Citizen gender)
				if replacePreSpawn[class] then
					replacePreSpawn[class](ent, newEnt)
				end
				newEnt:SetPos(ent:GetPos() + Vector(0, 0, (class == "monster_barnacle" && -1) or 4))
				newEnt:SetAngles(ent:GetAngles())
				if IsValid(ent:GetParent()) then newEnt:SetParent(ent:GetParent()) end
				if worldName && worldName != "" then newEnt:SetName(worldName) end
				newEnt:Spawn()
				newEnt:Activate()
				-- Handle naming
				if worldName && worldName != "" then -- Scripted NPC
					newEnt.DisableWandering = true
				end
				-- Handle weapon
				local wep = ent.GetActiveWeapon && ent:GetActiveWeapon() or false -- In case GetActiveWeapon is not in the ent's metatable
				//print(worldName, wep)
				if IsValid(wep) then
					local foundWep = replaceTbl_Weapons[wep:GetClass()]
					newEnt:Give(VJ_PICK(foundWep))
				end
				-- Handle enemy
				local ene = ent.GetEnemy && ent:GetEnemy() or false -- In case GetEnemy is not in the ent's metatable
				if IsValid(ene) then
					newEnt:SetEnemy(ene)
				end
				-- Handle key values
				for key, val in pairs(ent:GetSaveTable()) do
					//print(ent, newEnt, key, val)
					key = tostring(key)
					if key == "health" then
						newEnt:SetHealth(val)
					elseif key == "max_health" then
						newEnt:SetMaxHealth(val)
					-- elseif key == "m_bSequenceLoops" && val == true && newEnt:GetInternalVariable("sequence") != -1 then
						-- newEnt.Old_AnimTbl_IdleStand = newEnt.AnimTbl_IdleStand
						-- newEnt.AnimTbl_IdleStand = {VJ_SequenceToActivity(newEnt,newEnt:GetInternalVariable("sequence"))}
						-- newEnt:SetState(VJ_STATE_ONLY_ANIMATION_NOATTACK)
						-- newEnt:VJ_ACT_PLAYACTIVITY(newEnt:GetInternalVariable("sequence"),true,false,false)
					elseif key == "m_vecLastPosition" then
						if val != defPos then
							newEnt:SetLastPosition(val)
							newEnt:VJ_TASK_GOTO_LASTPOS("TASK_WALK_PATH")
						end
					elseif key == "m_bShouldPatrol" && val == false then
						newEnt.DisableWandering = true
					-- Not what I thought it was, actual variable is m_LookDist using the function SetDistLook
					-- Which both of them can't be accessed in Lua...
					//elseif key == "m_flDistTooFar" then
						//newEnt.SightDistance = val
					end
				end
				//newEnt.SightDistance = 2048 -- Default Source engine sight distance...
				-- Handle spawn flags
				//if ent:HasSpawnFlags(SF_NPC_LONG_RANGE) then
					//newEnt.SightDistance = 6000
				//end
				newEnt:SetKeyValue("spawnflags", ent:GetSpawnFlags())
				if ent:HasSpawnFlags(SF_CITIZEN_NOT_COMMANDABLE) then
					newEnt.FollowPlayer = false
				end
				//print(ent:GetInternalVariable("m_bShouldPatrol"))
				-- Handle Gordon precriminal game state
				if gStatePrecriminal == true then -- Toggles friendly-AI for the intro of Half-Life 2
					//newEnt.DisableWandering = true
					newEnt.DisableFindEnemy = true
					newEnt.DisableMakingSelfEnemyToNPCs = true
					newEnt.FriendsWithAllPlayerAllies = true
					newEnt.FollowPlayer = false
					newEnt.Behavior = VJ_BEHAVIOR_PASSIVE
					newEnt.VJ_AutoScript_OldClass = newEnt.VJ_NPC_Class
					newEnt.VJ_NPC_Class = {"CLASS_PLAYER_ALLY", "CLASS_COMBINE"}
					newEnt.VJ_AutoScript_Reset = true
				end
				-- Things to run after it's fully spawned (EX: Turret sight distance)
				if afterSpawned[rEnt] then
					afterSpawned[rEnt](ent, newEnt)
				end
				-- Handle Essential NPCs
				if GetConVar("vj_hlr_autoreplace_essential"):GetInt() == 1 && essentialTbl[newClass] then
					newEnt.GodMode = true
				end
				-- FUN OPTION: Make them all allied together against players
				if GetConVar("vj_hlr_autoreplace_alliedagainstply"):GetInt() == 1 then
					newEnt.VJ_NPC_Class = {"CLASS_HALF_LIFE_AGAINST_PLAYERS"}
				end
				-- print(ent:GetClass(), ent:GetInternalVariable("GameEndAlly"))
				-- Set the starting animation AND velocity
				local vel = ent:GetVelocity()
				timer.Simple(0.01, function()
					if IsValid(newEnt) then
						//newEnt:VJ_ACT_PLAYACTIVITY(spawnAnim, true, false, false)
						if vel:Length() > 0 then
							newEnt:SetGroundEntity(NULL)
							newEnt:SetVelocity(vel)
						end
					end
				end)
				undo.ReplaceEntity(ent, newEnt)
				ent:Remove()
			end
		end)
	end
end)

hook.Add("Think", "VJ_HLR_AutoReplace_Think", function()
	-- Make sure the game is loaded
	if game && game.GetGlobalState then
		gStatePrecriminal = game.GetGlobalState("gordon_precriminal") == 1
		gStateAntlionFri = game.GetGlobalState("antlion_allied") == 1
	end
	for _,v in ipairs(ents.GetAll()) do
		if v:IsNPC() then
			if !gStatePrecriminal && v.VJ_AutoScript_Reset then
				v.VJ_NPC_Class = v.VJ_AutoScript_OldClass
				v.VJ_AutoScript_Reset = false
			end
			if gStateAntlionFri && v.VJ_HLR_Antlion then
				table.insert(v.VJ_NPC_Class,"CLASS_PLAYER_ALLY")
				v.PlayerFriendly = true
				v.FriendsWithAllPlayerAllies = true
			end
		end
	end
end)