LootItems = {
	{
		id = 1,
		name = "loot_pistol",
		model = "models/Items/BoxSRounds.mdl",
		chance = 50,
		usefunc = function(self, ply)
			ply:GiveAmmo(40, 3, true)
			ply:GiveAmmo(20, 5, true)
		end
	},
	{
		id = 2,
		name = "loot_bucks",
		model = "models/Items/BoxBuckshot.mdl",
		chance = 25,
		usefunc = function(self, ply)
			ply:GiveAmmo(20, 7, true)
		end
	},
	{
		id = 3,
		name = "loot_smg",
		model = "models/Items/BoxMRounds.mdl",
		chance = 25,
		usefunc = function(self, ply)
			ply:GiveAmmo(90, 4, true)
		end
	},
	{
		id = 4,
		name = "loot_ar",
		model = "models/Items/BoxMRounds.mdl",
		chance = 25,
		usefunc = function(self, ply)
			ply:GiveAmmo(90, 1, true)
		end
	},
	{
		id = 5,
		name = "loot_sr",
		model = "models/Items/BoxMRounds.mdl",
		chance = 20,
		usefunc = function(self, ply)
			ply:GiveAmmo(20, 13, true)
			ply:GiveAmmo(20, 14, true)
		end
	},
	{
		id = 6,
		name = "loot_radio",
		tier = 4,
		model = "models/surrounded/skilllem_radio.mdl",
		chance = 4,
		usefunc = function(self, ply)
			ply:SetNWFloat('CanCallSupport', ply:GetNWFloat('CanCallSupport')+1)
			SuR:PlaySoundOnClient("surrounded/other/radio/radiopick_0"..math.random(1,3)..".mp3", ply)
			SuR:MessageOnClient(15, ply:Nick())
			SuR:ShowTips("radio", ply)
		end
	},
	{
		id = 7,
		name = "loot_antidote",
		model = "models/props_lab/box01a.mdl",
		chance = 15,
		usefunc = function(self, ply)
			ply:SetNWFloat('Sprays', ply:GetNWFloat('Sprays')+1)
		end
	},
	{
		id = 8,
		name = "loot_bandages",
		model = "models/props_junk/cardboard_box004a.mdl",
		chance = 20,
		usefunc = function(self, ply)
			ply:SetNWFloat('Bandages', ply:GetNWFloat('Bandages')+3)
		end
	},
	{
		id = 9,
		name = "loot_fak",
		tier = 3,
		model = "models/Items/HealthKit.mdl",
		chance = 10,
		usefunc = function(self, ply)
			ply:SetNWFloat('Sprays', ply:GetNWFloat('Sprays')+2)
			ply:SetNWFloat('Bandages', ply:GetNWFloat('Bandages')+6)
		end
	},
	{
		id = 10,
		name = "loot_gr",
		tier = 3,
		model = "models/weapons/w_missile_closed.mdl",
		chance = 10,
		usefunc = function(self, ply)
			if ply:GetPlayerClass() == "demoman" and ply:GetLevelState() >= 2 then
				ply:GiveAmmo(6, 8, true)
				ply:GiveAmmo(12, 11, true)
			else
				ply:GiveAmmo(3, 8, true)
				ply:GiveAmmo(6, 11, true)
			end
		end
	},
	{
		id = 11,
		name = "loot_nails",
		model = "models/Items/CrossbowRounds.mdl",
		chance = 20,
		usefunc = function(self, ply)
			ply:SetNWFloat('buildercost', ply:GetNWFloat('buildercost')+3)
		end
	},
	{
		id = 12,
		name = "loot_selfrevive",
		tier = 3,
		model = "models/Items/battery.mdl",
		chance = 10,
		usefunc = function(self, ply)
			ply:SetNWFloat('SelfRevive', ply:GetNWFloat('SelfRevive')+1)
		end
	},
	{
		id = 13,
		name = "loot_ammocrate",
		tier = 3,
		model = "models/Items/item_item_crate.mdl",
		chance = 4,
		usefunc = function(self, ply)
			ply:GiveAmmo(80, 3, true)
			ply:GiveAmmo(40, 5, true)
			ply:GiveAmmo(3, 8, true)
			ply:GiveAmmo(6, 11, true)
			ply:GiveAmmo(40, 13, true)
			ply:GiveAmmo(40, 14, true)
			ply:GiveAmmo(180, 1, true)
			ply:GiveAmmo(180, 4, true)
			ply:GiveAmmo(40, 7, true)
		end
	},
	{
		id = 14,
		name = "loot_capsule",
		tier = 5,
		model = "models/maxofs2d/hover_rings.mdl",
		chance = 1,
		usefunc = function(self, ply)
			ply:AddXP(SuR.Config.XP_Giving["capsule_item"])
			ply:EmitSound("buttons/combine_button_locked.wav")

			local wep = ents.Create("sur_loot")
			wep.LootType = math.random(1,13)
			wep:SetPos(self:WorldSpaceCenter())
			wep:Spawn()
		end
	},
	{
		id = 15,
		name = "loot_grenades",
		tier = 3,
		model = "models/Items/item_item_crate.mdl",
		chance = 4,
		usefunc = function(self, ply)
			ply:SetNWInt('Grenades', ply:GetNWInt('Grenades')+6)
		end
	},
	{
		id = 16,
		name = "loot_money",
		model = "models/surrounded/equipment/small blue cash bundle.mdl",
		chance = 20,
		usefunc = function(self, ply)
			ply:SetNWInt('Money', ply:GetNWInt('Money')+25)
		end
	},
}

local meta = FindMetaTable('Player')

local files = file.Find("surrounded/scripts/*", "LUA")
if istable(files) and #files > 0 then
	for k, f in pairs(files) do
		local scr = file.Read("surrounded/scripts/"..f, "LUA")
		RunString(scr)
		net.Start("SuR.ClientRunString")
		net.WriteString(scr)
		net.Broadcast()
		MsgC(Color(200,200,20), "[Surrounded] ", Color(250,250,250), "surrounded/scripts/"..f.." has been loaded!\n")
	end
end

local config = file.Read("surrounded/"..game.GetMap()..".lua", "LUA")
if config then
	RunString(config)
	net.Start("SuR.ClientRunString")
	net.WriteString(config)
	net.Broadcast()
else
	hook.Add("Think", "SuR_BlockConfig", function()
		SuR:SendDataToClient("GameplayBlockConfig", true)
		SuR.Game_Started = false
		SuR.DeployTime = CurTime()+SuR.Config.Delay_Before_Deploy
	end)
end

function SuR:FindSpawnPositions(pos, dist, attempts)
	attempts = attempts or 5
	local try = 0
    local spawnPositions = {}
	while try < attempts and #spawnPositions <= 0 do
		try = try + 1

		local trace = {}
		trace.start = pos
		trace.mask = MASK_PLAYERSOLID
		trace.filter = function(ent) 
			if ent:IsPlayer() or ent:IsNPC() then 
				return false 
			end 
		end
		
		trace.endpos = pos - Vector(0, 0, 1000)
		local result = util.TraceLine(trace)
		
		if result.Hit then
			local floorPos = result.HitPos
			
			trace.start = floorPos
			trace.endpos = floorPos + Vector(0, 0, 1000)
			result = util.TraceLine(trace)
			
			if result.Hit then
				local ceilingPos = result.HitPos
				
				for i = 0, 360 do
					local offset = Vector(math.random(-dist, dist), math.random(-dist, dist), 0)
					local spawnPos = ceilingPos + offset
					
					trace.start = spawnPos
					trace.endpos = spawnPos - Vector(0, 0, 1000)
					result = util.TraceLine(trace)
					
					if result.Hit then
						debugoverlay.Line(ceilingPos, result.HitPos, 45, Color( 255, 255, 255 ), true)

						if result.HitPos.z-pos.z >= SuR.Config.Loot_HeightLimit then
							local visible = false
							local tab = player.GetAll()
							for i=1,#tab do
								local ply = tab[i]
								if ply:Alive() and ply:VisibleVec(result.HitPos) then
									visible = true
									break
								end
							end

							if visible then continue end

							table.insert(spawnPositions, result.HitPos)
						end
					end
				end
			end
		end
    end
    
    return table.Random(spawnPositions)
end

function SuR:InfectPlayer(ply)
    if ply:GetNWBool("IsInfected") or !ply:Alive() then return end
    ply:SetNWBool("IsInfected", true)
    timer.Simple(math.random(15,45), function()
        if IsValid(ply) and ply:GetNWBool("IsInfected") then
            timer.Create("InfectionProgress" .. ply:EntIndex(), 1, 0, function()
                if IsValid(ply) and ply:GetNWBool("IsInfected") and ply:Alive() then
					if math.random(1,10) == 1 then
						ply:PlayVoiceLine("infected")
						BroadcastLua([[
							local tab = {ACT_FLINCH_HEAD, ACT_FLINCH_STOMACH, ACT_FLINCH_PHYSICS}
							Entity(]]..ply:EntIndex()..[[):AnimRestartGesture(6, tab[math.random(1,#tab)], true)
						]])
					end
                    ply:SetNWFloat('InfectionProgress', ply:GetInfection() + 0.01)
                    if ply:GetInfection() >= 1 then
						ply:SuR_MakeZombie()
                    end
                else
					timer.Remove("InfectionProgress" .. ply:EntIndex())
                    SuR:StopInfection(ply)
                end
            end)
        end
    end)
end

function SuR:StopInfection(ply)
	if !IsValid(ply) then return end
    if ply:GetNWBool("IsInfected") then
		timer.Remove("InfectionProgress" .. ply:EntIndex())
        ply:SetNWBool("IsInfected", false)
        ply:SetNWFloat('InfectionProgress', 0)
    end
end

function SuR:DeleteLogic()
	for _, ent in ipairs(ents.GetAll()) do
		if ent:GetClass() == "func_tracktrain" or ent:GetClass() == "func_train" or ent:GetClass() == "func_tanktrain" or ent:GetClass() == "env_fade" or ent:GetClass() == "env_shake" or string.match(ent:GetClass(), "trigger_") then
			ent:Remove()
		elseif ent:GetClass() == "ambient_generic" then
			ent:Fire("StopSound")
			ent:Remove()
		end
	end
end

function SuR:DeleteItems()
	for _, ent in ipairs(ents.GetAll()) do
		if ent:IsWeapon() or ent:IsNPC() or string.match(ent:GetClass(), "item_") then
			ent:Remove()
		end
	end
end

function SuR:TransferModelData(base, model)
	local ent1Model = base:GetModel()
	local ent1Skin = base:GetSkin()
	local ent1BodyGroups = base:GetNumBodyGroups()
	model:SetModel(ent1Model)
	model:SetSkin(ent1Skin)
	for i = 0, ent1BodyGroups - 1 do
		model:SetBodygroup(i, base:GetBodygroup(i))
	end
end

function SuR:TransferBoneData(base, ragdoll)
	if !IsValid( base ) or !IsValid( ragdoll ) then return end
	for i = 0, ragdoll:GetPhysicsObjectCount() - 1 do
		local bone = ragdoll:GetPhysicsObjectNum( i )
		if ( IsValid( bone ) ) then
			local pos, ang = base:GetBonePosition( ragdoll:TranslatePhysBoneToBone( i ) )
			if ( pos ) then bone:SetPos( pos ) end
			if ( ang ) then bone:SetAngles( ang ) end
		end
	end
end

function SuR:CallSupport(ply, float)
	local tab = SuR.Config.Support_SpawnPositions
	local pos = tab[math.random(1,#tab)]
	if isvector(pos) then
		if float == 1 then
			if IsValid(ply) then
				SuR:SetMarker(pos+Vector(0,0,128), ply, "Supply")
			end

			SuR:PlaySoundOnClient("surrounded/other/radio/radiosupply_0"..math.random(1,3)..".mp3", ply, 3)

			timer.Simple(20, function()
				local ent = ents.Create("prop_dynamic")
				ent:SetModel("models/surrounded/mh53.mdl")
				ent:SetPos(pos)
				ent:SetAngles(Angle(0,math.random(-180,180),0))
				ent:Spawn()
				ent:ResetSequence("yes")

				SuR:PlaySoundOnClient("surrounded/other/support_heli.mp3")
	
				timer.Simple(8, function()
					if !IsValid(ent) then return end

					local tr = util.TraceLine({
						start = pos,
						endpos = pos+Vector(0,0,1300),
						mask = MASK_SOLID_BRUSHONLY
					})

					local ent = ents.Create("sur_resupply")
					ent:SetPos(tr.HitPos-Vector(0,0,32))
					ent:Spawn()
				end)

				timer.Simple(15, function()
					if !IsValid(ent) then return end

					ent:Remove()
				end)
			end)

			SuR:MessageOnClient(9)
		elseif float == 2 then
			ply:Give("sur_artillery_strike")
			ply:SelectWeapon("sur_artillery_strike")
			SuR:MessageOnClient(7)
			SuR:PlaySoundOnClient("surrounded/other/radio/radioshelling_0"..math.random(1,3)..".mp3", ply, 3)
		elseif float == 3 then
			if IsValid(ply) then
				SuR:SetMarker(pos+Vector(0,0,128), ply, "Reinforcement")
			end
			for _, ply in ipairs(player.GetAll()) do
				if !ply:Alive() then
					ply.AllowSpawn = true
					ply:Spawn()
					ply:ScreenFade(SCREENFADE.IN, color_black, 1, 0)
				end
			end
			local ent = ents.Create("sur_npc_infil")
			ent:SetPos(pos)
			ent:SetAngles(Angle(0,math.random(-180,180),0))
			ent:Spawn()
			SuR:MessageOnClient(6)
			SuR:PlaySoundOnClient("surrounded/other/radio/radioreinforce_0"..math.random(1,3)..".mp3", ply, 3)
		elseif float == 4 then
			if IsValid(ply) then
				SuR:SetMarker(pos+Vector(0,0,128), ply, "Bait")
			end
			SuR:PlaySoundOnClient("surrounded/other/radio/radiobait_0"..math.random(1,3)..".mp3", ply, 3)

			timer.Simple(7, function()
				local ent = ents.Create("prop_dynamic")
				ent:SetModel("models/surrounded/mh53.mdl")
				ent:SetPos(pos)
				ent:SetAngles(Angle(0,math.random(-180,180),0))
				ent:Spawn()
				ent:ResetSequence("yes")

				SuR:PlaySoundOnClient("surrounded/other/support_heli.mp3")

				timer.Simple(15, function()
					if !IsValid(ent) then return end

					ent:Remove()
				end)

				timer.Simple(8, function()
					for _, ply in ipairs(player.GetAll()) do
						ply:SetNoTarget(true)
					end
					timer.Create("RemoveNoTarget", 30, 1, function()
						for _, ply in ipairs(player.GetAll()) do
							ply:SetNoTarget(false)
						end
					end)
					timer.Create("RemoveNoTarget"..ply:EntIndex(), 1, 30, function()
						for _, npc in ipairs(ents.FindByClass("npc_vj_*")) do
							npc:SetLastPosition(pos)
							npc:VJ_TASK_GOTO_LASTPOS()
						end
					end)

					if !IsValid(ent) then return end
	
					local tr = util.TraceLine({
						start = pos,
						endpos = pos+Vector(0,0,1300),
						mask = MASK_SOLID_BRUSHONLY
					})
	
					local ent = ents.Create("prop_ragdoll")
					ent:SetCollisionGroup(COLLISION_GROUP_DEBRIS)
					ent:SetModel("models/Humans/Charple04.mdl")
					ent:SetPos(tr.HitPos-Vector(0,0,64))
					ent:Spawn()
					SafeRemoveEntityDelayed(ent, 30)
				end)
			end)

			SuR:MessageOnClient(8)
		elseif float == 5 then
			if IsValid(ply) then
				SuR:SetMarker(pos+Vector(0,0,128), ply, "Gunner")
			end
			timer.Simple(25, function()
				local ent = ents.Create("sur_mi24")
				ent:SetPos(pos)
				ent:SetAngles(Angle(0,math.random(-180,180),0))
				ent:Spawn()
			end)
			SuR:PlaySoundOnClient("surrounded/other/radio/radiogunner_0"..math.random(1,3)..".mp3", ply, 3)
			SuR:MessageOnClient(10)
		elseif float == 6 then
			timer.Simple(2, function()
				if !SuR.Game_Started or !SuR.Wave_Started or SuR.Config.Exfil_Wave > SuR.Current_Wave then return end
				SuR:StartExfil()
			end)
		elseif float == 7 then
			ply:SetNWFloat('CanCallSupport', ply:GetNWFloat('CanCallSupport')+1)
			ply:SendLua([[SuR:OpenBuyMenu()]])
		end
	end
end

function SuR:SendDataToClient(string, value, delay)
	if delay then
		timer.Simple(delay, function()
			net.Start("SuR.SendDataToClient")
			net.WriteString(string)
			net.WriteTable({value})
			net.Broadcast()
		end)
	else
		net.Start("SuR.SendDataToClient")
		net.WriteString(string)
		net.WriteTable({value})
		net.Broadcast()
	end
end

function SuR:SetMarker(pos, ply, text)
	if not text then
		text = ""
	end
    net.Start("SuR.PointMarker")
    net.WriteVector(pos)
	net.WriteString(text)
	if ply then
		net.Send(ply)
	else
		net.Broadcast()
	end
end

function SuR:ShowTips(type, ply)
    net.Start("SuR.ShowTips")
	net.WriteString(type)
	if ply then
		net.Send(ply)
	else
		net.Broadcast()
	end
end

function SuR:MessageOnClient(f, extra, ply)
    net.Start("SuR.MessageOnClient")
    net.WriteFloat(f)
    net.WriteTable({extra})
	if ply then
		net.Send(ply)
	else
		net.Broadcast()
	end
end

function SuR:PlaySoundOnClient(string, ply, delay)
	delay = delay or 0
	timer.Simple(delay, function()
		net.Start("SuR.PlaySoundOnClient")
		net.WriteString(string)
		if ply then
			net.Send(ply)
		else
			net.Broadcast()
		end
	end)
end

function SuR:Countdown(num)
	net.Start("SuR.Countdown")
    net.WriteFloat(num)
    net.Broadcast()
end

function SuR:ShowDeployCutscene(ply, bool)
	if not SuR.Config.Deploy_Cutscene.cam then return end
	net.Start("SuR.DeployCutscene")
    net.WriteBool(bool)
	net.Send(ply)

	if !istable(ply) then
		ply = {ply}
	end
	for k, v in pairs(ply) do
		if v:Alive() then continue end
		v:SetPos(SuR.Config.Deploy_Cutscene.cam[#SuR.Config.Deploy_Cutscene.cam].pos)
	end
end

function SuR:SpawnTierWeapon(tier, pos)
	local weapon = table.Random(SuR.Config.TierWeapons["Melee"])
	if tier == 1 then
		weapon = table.Random(SuR.Config.TierWeapons["Tier1"])
	elseif tier == 2 then
		weapon = table.Random(SuR.Config.TierWeapons["Tier2"])
	elseif tier == 3 then
		weapon = table.Random(SuR.Config.TierWeapons["Tier3"])
	elseif tier == 4 then
		weapon = table.Random(SuR.Config.TierWeapons["Tier4"])
	elseif tier == 5 then
		weapon = table.Random(SuR.Config.TierWeapons["Tier5"])
	end

	if isvector(pos) then
		local wep = ents.Create(weapon)
		wep:SetPos(pos)
		wep:SetAngles(AngleRand())
		wep:Spawn()
		return wep
	elseif IsEntity(pos) and pos:IsPlayer() then
		local wep = pos:Give(weapon)
		return wep
	end
end

function SuR:GiveRandomTableWithChance(tab, extra)
    local totalChance = 0
	local tab2 = table.Copy(tab)
	local t = {}
	if isfunction(extra) then
		for i, subtable in pairs(tab2) do
			if extra(subtable) then
				table.insert(t, subtable)
			end
		end
	else
		t = table.Copy(tab2)
	end

	if #t == 0 then return end

    for _, subtable in pairs(t) do
        totalChance = totalChance + subtable.chance
    end
    
    local randomValue = math.random(totalChance)
    local currentChance = 0
    for i, subtable in pairs(t) do
        currentChance = currentChance + subtable.chance
        if randomValue <= currentChance then
            return subtable, i
        end
    end
end

function SuR:EnablePropDamage()
    for _, ent in ipairs(ents.FindByClass("prop_physics*")) do
		local phys = ent:GetPhysicsObject()
        if ent:GetMoveType() == MOVETYPE_VPHYSICS and IsValid(phys) then
            ent:SetHealth(phys:GetMass()*5)
            ent:SetMaxHealth(phys:GetMass()*5)
			ent.PropDestroyable = true
        end
    end
end

hook.Add("EntityTakeDamage", "EnablePropDamageSuR", function(ent, dmg)
    if ent.PropDestroyable then
        local new_health = ent:Health() - dmg:GetDamage()
        ent:SetHealth(new_health)

        if new_health <= 0 then
			ent:EmitSound("physics/metal/metal_box_break"..math.random(1,2)..".wav")
            ent:Remove()
        end
    end
end)

hook.Add("Think", "EnablePropDamageSuR", function()
    for _, npc in ipairs(ents.FindByClass("*lnr*")) do
		if !npc.IsZombie then continue end

		local trace = util.TraceHull({
            start = npc:GetPos()+ npc:GetForward() * 32,
            endpos = npc:GetPos() + npc:GetForward() * 32,
            filter = function(ent)
                if ent:GetClass():find("prop_physics") then
                    return true
                elseif ent:IsNPC() or ent:IsPlayer() then
                    return false
                else
                    return ent:IsWorld() == false
                end
            end,
			mins = npc:OBBMins(),
			maxs = npc:OBBMaxs(),
        })
		if trace.Hit then
			local phys = trace.Entity:GetPhysicsObject()
			if trace.Entity:GetClass():find("prop_physics") and IsValid(phys) and phys:IsMotionEnabled() then
				if phys:GetMass() <= 400 then
					phys:AddVelocity(npc:GetForward()*16)
				end
				trace.Entity:TakeDamage(1)
			end
		end
    end
end)

function meta:SideToEntity(ent2)
    local vec = (ent2:GetPos() - self:GetPos()):GetNormal():Angle().y
    local targetAngle = self:GetAngles().y
    if targetAngle > 360 then
        targetAngle = targetAngle - 360
    end
    if targetAngle < 0 then
        targetAngle = targetAngle + 360
    end
    
    local angleAround = vec - targetAngle
    if angleAround > 360 then
        angleAround = angleAround - 360
    end
    if angleAround < 0 then
        angleAround = angleAround + 360
    end

    if angleAround <= 45 or angleAround > 315 then
        return "front"
    end
    if angleAround > 45 and angleAround <= 135 then
        return "left"
    end
    if angleAround > 135 and angleAround <= 225 then
        return "back"
    end
    if angleAround > 225 and angleAround <= 315 then
        return "right"
    end
end

function meta:DropButtons()
	self:ConCommand("-forward")
	self:ConCommand("-back")
	self:ConCommand("-moveright")
	self:ConCommand("-moveleft")
end

function meta:SetSVAnimation(anim, autostop, stopmove, cutscene, loop)
    self:SetNWString('SVAnim', anim)
    self:SetNWFloat('SVAnimDelay', select(2, self:LookupSequence(anim)))
    self:SetNWFloat('SVAnimStartTime', CurTime())
    self:SetCycle(0)
	if loop and not autostop then
		self:SetNWBool('SVAnimLoop', true)
	else
		self:SetNWBool('SVAnimLoop', false)
	end
	if not stopmove then
		stopmove = false
	end
	self:SetNWBool('SVAnimStopMoving', stopmove)
	if not cutscene then
		cutscene = false
	else
		self:ConCommand("-attack")
		self:ConCommand("-attack2")
	end
	self:SetNWBool('SVAnimStopCutscene', cutscene)
    if autostop then
        local delay = select(2, self:LookupSequence(anim))
        timer.Simple(delay, function()
            if !IsValid(self) then return end

            local anim2 = self:GetNWString('SVAnim')

            if anim == anim2 then
                self:SetSVAnimation("")
            end
        end)
		timer.Simple(delay, function()
            if !IsValid(self) then return end

            local anim2 = self:GetNWString('SVAnim')

            if anim == anim2 then
                self:SetSVAnimation("")
            end
        end)
    end
end

function meta:PlayFastGesture(seq, isactivity)
	local id = self:LookupSequence(seq)
	if id == -1 and not isactivity then return end

	local ind = self:EntIndex()
	if isactivity then
		BroadcastLua([[
			Entity(]]..ind..[[):AnimRestartGesture(6, ]]..seq..[[, true)
		]])
	else
		BroadcastLua([[
			Entity(]]..ind..[[):AddVCDSequenceToGestureSlot(6, ]]..id..[[, 0, true)
		]])
	end
end

function meta:CheckReachingNPC()
	if !SuR.Config.Enable_Out_Of_Bounds_Check or self:IsZombie() then return true end
	if IsValid(ents.FindByClass("sur_exfil_helo")[1]) then return true end
	local count_z, count_r = 0, 0
	local tab = ents.FindByClass("*vj*")
	for i=1,#tab do
		local ent = tab[i]
		if !ent:IsNPC() then continue end
		count_z = count_z + 1
		if !ent:IsUnreachable(self) then
			count_r = count_r + 1
		end
	end
	return count_r > count_z-count_r or count_z == 0
end

hook.Add("PlayerButtonDown", "SuR_ButtonHook", function(ply, but)
	if but == KEY_F4 then
		ply:SendLua([[SuR:OpenAdminMenu()]])
	end
	if but == KEY_F1 and ply:IsSurvivor() then
		ply:SendLua([[SuR:OpenDropMenu()]])
	elseif but == KEY_F1 and ply:IsZombie() then
		ply:SendLua([[SuR:OpenClassMenu()]])
	end

	if ply:IsZombie() then return end

	if but == KEY_E and ply:IsDowned() then
		ply:SuR_SelfRevive()
	end

	if but == KEY_G then
		ply:AbilityKick()
	elseif but == KEY_E then
		ply:AbilityTakedown()
	elseif but == KEY_L then
		ply:AbilityGrenade()
	elseif but == KEY_V then
		ply:AbilityVault()
	elseif but == KEY_B then
		local tr = util.TraceLine({
			start = ply:EyePos(),
			endpos = ply:EyePos() + ply:GetAimVector() * 72,
			filter = ply,
		})
		if IsValid(tr.Entity) and tr.Entity:IsPlayer() and not tr.Entity:IsDowned() and tr.Entity:Health() < 100 then
			ply:AbilityHealing(tr.Entity)
		else
			ply:AbilityHealing()
		end
	elseif but == KEY_H then
		local tr = util.TraceLine({
			start = ply:EyePos(),
			endpos = ply:EyePos() + ply:GetAimVector() * 72,
			filter = ply,
		})
		if IsValid(tr.Entity) and tr.Entity:IsPlayer() and not tr.Entity:IsDowned() and tr.Entity:GetNWBool("IsInfected") then
			ply:AbilitySpray(tr.Entity)
		else
			ply:AbilitySpray()
		end
	elseif but == KEY_M then
		ply:SendLua([[SuR:OpenRadioMenu()]])
	elseif but == KEY_1 then
		ply:SelectWeapon(ply:GetNWString('MainWeapon'))
	elseif but == KEY_2 then
		ply:SelectWeapon(ply:GetNWString('SecWeapon'))
	elseif but == KEY_3 then
		ply:SelectWeapon(ply:GetNWString('MeleeWeapon'))
	elseif but == KEY_4 then
		ply:SelectWeapon(ply:GetNWString('OtherWeapon'))
	elseif but == KEY_F2 then
		ply:PlaceMark()
	end
end)

function meta:CreateDroppedWeapon(str)
	local hwep = self:GetWeapon(str)
	if str == SuR.Config.EmptyWeapon then return end
	if !IsValid(hwep) then return end

	local tr = util.TraceLine( {
		start = self:EyePos(),
		endpos = self:EyePos() + self:GetAimVector() * 50,
		filter = self,
		mask = MASK_SOLID,
	})

	local clip1 = hwep:Clip1()
	local ent = ents.Create("sur_loot")
	ent.Weapon = {
		name = hwep.PrintName,
		class = str,
		model = hwep.WorldModel,
		usefunc = function(sel, self)
			local weps = self:Give(str)
			if IsValid(weps) then
				self:RemoveAmmo(weps:Clip1()+weps.Primary.DefaultClip, weps:GetPrimaryAmmoType())
				weps:SetClip1(clip1)
			end
		end
	}
	ent:SetPos(tr.HitPos)
	ent:Spawn()
	ent.IsLoot = true

	self:StripWeapon(str)
end

hook.Add("PlayerCanPickupWeapon", "SuR_Weapons", function(ply, weapon)
    if ply:HasWeapon(weapon:GetClass()) then
		return false
	end
end)

hook.Add("WeaponEquip", "SuR_Weapons", function(weapon, ply)
	if table.HasValue(SuR.Config.SecondaryWeapons, weapon:GetClass()) then
		local str = ply:GetNWString('SecWeapon')
		ply:CreateDroppedWeapon(str)
		ply:SetNWString('SecWeapon', weapon:GetClass())
	elseif table.HasValue(SuR.Config.TierWeapons["Melee"], weapon:GetClass()) then
		local str = ply:GetNWString('MeleeWeapon')
		if str == SuR.Config.EmptyWeapon then
			ply:StripWeapon(str)
		else
			ply:CreateDroppedWeapon(str)
		end
		ply:SetNWString('MeleeWeapon', weapon:GetClass())
	elseif table.HasValue(SuR.Config.OtherWeapons, weapon:GetClass()) then
		local str = ply:GetNWString('OtherWeapon')
		ply:CreateDroppedWeapon(str)
		ply:SetNWString('OtherWeapon', weapon:GetClass())
	elseif weapon:GetClass() != SuR.Config.EmptyWeapon then
		local str = ply:GetNWString('MainWeapon')
		ply:CreateDroppedWeapon(str)
		ply:SetNWString('MainWeapon', weapon:GetClass())
	end
end)

hook.Add("OnNPCKilled", "SuR_GameLogic", function(npc, att)
	if npc.IsZombie and att:IsPlayer() then
		net.Start("SuR.KillStatus")
		net.WriteBool(false)
		net.Send(att)
		timer.Simple(0.2, function()
			if !IsValid(att) then return end
			
			att:PlayVoiceLine("enemydown", 30)
		end)
		if (npc:GetClass() == "npc_vj_lnr_riot_shield" or npc:GetClass() == "npc_vj_lnr_riot") and math.random(1,2) == 1 then
			local wep = ents.Create("sur_loot")
			wep.LootType = 6
			wep:SetPos(npc:WorldSpaceCenter())
			wep:SetAngles(AngleRand())
			wep:Spawn()
		end
		if SuR.ActiveMutator == 1 and math.random(1,5) == 1 then
			local gr = ents.Create("obj_vj_grenade")
			gr:SetPos(npc:WorldSpaceCenter())
			gr:SetAngles(AngleRand())
			gr:Spawn()
		elseif SuR.ActiveMutator == 9 and math.random(1,5) == 1 then
			SuR:SpawnLoot(npc:WorldSpaceCenter())
		end

		local tab = SuR.Config.XP_Giving
		if npc.IsBoss then
			att:AddXP(tab["boss_kill"])
		elseif npc.IsSpecial then
			att:AddXP(tab["special_kill"])
		else
			att:AddXP(tab["zombie_kill"])
		end
	end
end)

hook.Add("EntityTakeDamage", "SuR_DamageHook", function(ent, dmg)
	local dc = dmg:GetDamage()
	local att = dmg:GetAttacker()
	local hlt = ent:Health()

	if ent:IsPlayer() and (att:GetClass() == "obj_vj_grenade_sur" or att:GetClass() == "sur_landmine") then
		return true
	end
	if ent:IsPlayer() and att:GetClass() == "sur_support_npc" or att:IsPlayer() and att:IsSurvivor() and ent:GetClass() == "sur_support_npc" then
		return true
	end
	if att:IsPlayer() and att:IsZombie() and ent:IsNPC() and ent.IsZombie then
		return true
	end

	if ent:IsPlayer() and att:IsPlayer() and ent != att and ent:Team() == att:Team() then
		ent:PlayVoiceLine("friendlyfire", 25)
		return true
	end

	if ent.IsZombie and att:IsPlayer() then
		local inf = att:GetActiveWeapon()
		if SuR.ActiveMutator == 5 and IsValid(inf) and inf:IsWeapon() and table.HasValue(SuR.Config.TierWeapons["Melee"], inf:GetClass()) then
			dmg:SetDamage(ent:Health())
		end
		if IsValid(inf) and inf:IsWeapon() and table.HasValue(SuR.Config.TierWeapons["Melee"], inf:GetClass()) then
			if ent.IsVJBaseSNPC and ent:Health() < 400 and not ent.LNR_Crawler then
				ent.Flinching = true
				ent:StopAttacks(true)
				ent.PlayingAttackAnimation = false
				local animTbl = ent.AnimTbl_Flinch
				if HitgroupInfo != nil then animTbl = HitgroupInfo.Animation end
				local anim = istable(animTbl) and VJ_PICK(animTbl) or animTbl
				local animDur = ent.NextMoveAfterFlinchTime
				if animDur == false and anim then
					animDur = ent:DecideAnimationLength(anim, false, ent.FlinchAnimationDecreaseLengthAmount)
				end
				if not isnumber(animDur) or animDur <= 0 then
					animDur = 0.1
				end
				if anim then
					ent:VJ_ACT_PLAYACTIVITY(anim, true, animDur, false, 0, {SequenceDuration=animDur, PlayBackRateCalculated=true})
				end
				timer.Create("timer_act_flinching"..ent:EntIndex(), animDur, 1, function() ent.Flinching = false end)
				if isfunction(ent.CustomOnFlinch_AfterFlinch) then
					ent:CustomOnFlinch_AfterFlinch(dmginfo, hitgroup)
				end
				ent.NextFlinchT = CurTime() + (tonumber(ent.NextFlinchTime) or 0)
			end
		end
		if att:GetPlayerClass() == "survivor" and att:GetLevelState() >= 5 and dmg:IsBulletDamage() then
			dmg:ScaleDamage(1.3)
		elseif att:GetPlayerClass() == "survivor" and att:GetLevelState() >= 4 and dmg:IsBulletDamage() then
			dmg:ScaleDamage(1.2)
		elseif att:GetPlayerClass() == "survivor" and att:GetLevelState() >= 3 and dmg:IsBulletDamage() then
			dmg:ScaleDamage(1.1)
		end
		if dmg:IsBulletDamage() and IsValid(inf) and (inf:GetPrimaryAmmoType() == 13 or inf:GetPrimaryAmmoType() == 14) then
			if att:GetPlayerClass() == "marksman" and att:GetLevelState() >= 5 then
				dmg:ScaleDamage(2)
			elseif att:GetPlayerClass() == "marksman" and att:GetLevelState() >= 3 then
				dmg:ScaleDamage(1.5)
			elseif att:GetPlayerClass() == "marksman" and att:GetLevelState() >= 2 then
				dmg:ScaleDamage(1.25)
			end
		end
		if IsValid(inf) and inf:IsWeapon() and table.HasValue(SuR.Config.TierWeapons["Melee"], inf:GetClass()) then
			if att:GetPlayerClass() == "berserker" and att:GetLevelState() >= 5 then
				dmg:ScaleDamage(3)
			elseif att:GetPlayerClass() == "berserker" and att:GetLevelState() >= 3 then
				dmg:ScaleDamage(2)
			elseif att:GetPlayerClass() == "berserker" and att:GetLevelState() >= 1 then
				dmg:ScaleDamage(1.5)
			end
		end
		if dmg:IsExplosionDamage() then
			if att:GetPlayerClass() == "demoman" and att:GetLevelState() >= 5 then
				dmg:ScaleDamage(2)
			elseif att:GetPlayerClass() == "demoman" and att:GetLevelState() >= 4 then
				dmg:ScaleDamage(1.5)
			end
		end
	end

	if ent:IsPlayer() and ent:IsZombie() and ent:Alive() then
		ent:PlayVoiceLine("zombie_pain", 25)
		return
	end

	if ent:IsPlayer() and ent:Alive() then
		if ent:GetPlayerClass() == "berserker" and ent:GetLevelState() >= 4 then
			dmg:ScaleDamage(0.5)
		end
		if dmg:IsExplosionDamage() and ent:GetPlayerClass() == "demoman" and ent:GetLevelState() >= 1 then
			dmg:ScaleDamage(0.1)
		end
		dc = dmg:GetDamage()
		if dc < hlt and not ent:IsDowned() then
			ent:PlayVoiceLine("pain", 50)
			ent.VoiceLineDelay_LowHP = CurTime()+math.random(15,60)
			if dmg:IsExplosionDamage() or dmg:IsFallDamage() or ((att:GetClass() == "npc_vj_lnr_brute" or att:IsPlayer() and att:IsZombie() and att.ZombieClass == "Brute") and math.random(1,2) == 1 or att:GetClass() == "npc_vj_lnr_tyrant" or att:GetClass() == "npc_vj_lnr_tank" or att:GetClass() == "npc_vj_lnr_titan_mini" or att:GetClass() == "npc_vj_lnr_fatso" and math.random(1,5) == 1) and att:GetPos():DistToSqr(ent:GetPos()) < 20000 then
				ent:Stun(false, math.random(10,20))
			end
			if (att.IsZombie and math.random(1,20) == 1 or SuR.ActiveMutator == 6) or ((att:GetClass() == "npc_vj_lnr_carrier" or att:IsPlayer() and att:IsZombie() and att.ZombieClass == "Carrier") and math.random(1,4) == 1) or (att:GetClass() == "npc_vj_lnr_shambler") then
				SuR:InfectPlayer(ent)
			end
		else
			if not ent:IsDowned() and not ent:HasGodMode() and SuR.ActiveMutator != 10 then
				ent:SuR_Down()
				return true
			end
		end
	end
end)

hook.Add("PlayerDeathSound", "SuR_CustomPlayerDeath", function(ply)
	if ply:IsSurvivor() then
		ply:PlayVoiceLine("death")
	else
		ply:PlayVoiceLine("zombie_death")
	end
	return true
end)

hook.Add("PlayerDeath", "SuR_Think", function(ply, inf, att)
	local rag = ents.Create("prop_ragdoll")
	SuR:TransferModelData(ply, rag)
	rag:SetPos(ply:GetPos())
	rag:Spawn()
	rag:SetCollisionGroup(1)
	SuR:TransferBoneData(ply, rag)
	SafeRemoveEntityDelayed(rag, 120)

	if IsValid(ply:GetRagdollEntity()) then
		ply:GetRagdollEntity():Remove()
	end

	timer.Simple(0.01, function()
		if !IsValid(ply) or !IsValid(rag) or ply:GetInfoNum("sur_viewmod_deathanims", 1) != 0 then return end
		net.Start("SuR.DeathView")
		net.WriteEntity(rag)
		net.Send(ply)
	end)

	if ply:IsDowned() or ply.ExecutingTarget then
		ply:SuR_Revive(true)
		ply:Freeze(false)
		ply:SetSVAnimation("")
		SuR:MessageOnClient(5, ply)

		local ent = SuR:PickRandomPlayer()
		if IsValid(ent) then
			ent:PlayVoiceLine("frienddied")
		end
	end

	if ply:IsZombie() then
		if ply.ZombieClass == "Hazmat" and IsValid(att) and att != ply and math.random(1,4) == 1 then
			ParticleEffect("vj_explosion2",ply:GetPos() + ply:GetUp()*48 + ply:GetForward()*1,Angle(0,0,0),nil) 
			ParticleEffect("vj_explosion1",ply:GetPos() + ply:GetUp()*15,Angle(0,0,0),nil)
			ParticleEffect("vj_explosionfire2",ply:GetPos() + ply:GetUp()*20,Angle(0,0,0),nil)
			ParticleEffect("vj_explosionfire1",ply:GetPos() + ply:GetUp()*20,Angle(0,0,0),nil) 	 
			util.ScreenShake(ply:GetPos(),100,200,1,2500)
			util.VJ_SphereDamage(ply,ply,ply:GetPos(),250,75,DMG_BLAST,false,true,{Force=100})	
			ply:EmitSound("ambient/explosions/explode_"..math.random(1,9)..".wav", 90)	
		end 
		return 
	end
	if SuR.Config.Versus_Mode then
		timer.Simple(0, function() ply:SetTeam(2) end)
	end

	local str = ply:GetNWString('MainWeapon')
	local hwep = ply:GetWeapon(str)
	if IsValid(hwep) then
		local ent = ents.Create("sur_loot")
		ent.Weapon = {
			name = hwep.PrintName,
			class = str,
			model = hwep.WorldModel,
			usefunc = function(self, ply)
				local weps = ply:Give(str)
				if IsValid(weps) then
					ply:RemoveAmmo(weps:Clip1()+weps.Primary.DefaultClip, weps:GetPrimaryAmmoType())
				end
			end
		}
		ent:SetPos(ply:WorldSpaceCenter())
		ent:Spawn()
		ent.IsLoot = true
	end

	local str = ply:GetNWString('SecWeapon')
	local hwep = ply:GetWeapon(str)
	if IsValid(hwep) then
		local ent = ents.Create("sur_loot")
		ent.Weapon = {
			name = hwep.PrintName,
			class = str,
			model = hwep.WorldModel,
			usefunc = function(self, ply)
				local weps = ply:Give(str)
				if IsValid(weps) then
					ply:RemoveAmmo(weps:Clip1()+weps.Primary.DefaultClip, weps:GetPrimaryAmmoType())
				end
			end
		}
		ent:SetPos(ply:WorldSpaceCenter())
		ent:Spawn()
		ent.IsLoot = true
	end

	local str = ply:GetNWString('MeleeWeapon')
	local hwep = ply:GetWeapon(str)
	if IsValid(hwep) and str != SuR.Config.EmptyWeapon then
		local ent = ents.Create("sur_loot")
		ent.Weapon = {
			name = hwep.PrintName,
			class = str,
			model = hwep.WorldModel,
			usefunc = function(self, ply)
				local weps = ply:Give(str)
				if IsValid(weps) then
					ply:RemoveAmmo(weps:Clip1()+weps.Primary.DefaultClip, weps:GetPrimaryAmmoType())
				end
			end
		}
		ent:SetPos(ply:WorldSpaceCenter())
		ent:Spawn()
		ent.IsLoot = true
	end

	local str = ply:GetNWString('OtherWeapon')
	local hwep = ply:GetWeapon(str)
	if IsValid(hwep) then
		local ent = ents.Create("sur_loot")
		ent.Weapon = {
			name = hwep.PrintName,
			class = str,
			model = hwep.WorldModel,
			usefunc = function(self, ply)
				local weps = ply:Give(str)
				if IsValid(weps) then
					ply:RemoveAmmo(weps:Clip1()+weps.Primary.DefaultClip, weps:GetPrimaryAmmoType())
				end
			end
		}
		ent:SetPos(ply:WorldSpaceCenter())
		ent:Spawn()
		ent.IsLoot = true
	end
end)

function meta:MovingDirection(threshold, sidethreshold)
    threshold = threshold or 0.1
	sidethreshold = sidethreshold or 0.2
    local vel = self:GetVelocity()
    local forward = self:GetForward()
    local right = self:GetRight()
    local forwardDot = vel:Dot(forward)
    local rightDot = vel:Dot(right)

	if rightDot > sidethreshold then
        return "right"
    elseif rightDot < -sidethreshold then
        return "left"
    end

    if forwardDot > threshold then
        return "forward"
    elseif forwardDot < -threshold then
        return "backward"
    end

    return "stand"
end

hook.Add("Think", "SuR_Think", function()
	local tab = player.GetAll()
	for i=1, #tab do
		local ent = tab[i]

		if ent:Alive() and ent:GetInfoNum("sur_viewmod", 2) == 0 and !ent:GetNWBool("SVAnimStopCutscene") then
			local num = 64
			local eyes = ent:LookupAttachment("eyes")
			if eyes > 0 then
				local att = ent:GetAttachment(eyes)
				local dif = math.sqrt(att.Pos:DistToSqr(ent:GetPos()))
				num = math.Clamp(dif, 8, 96)
			end
			ent:SetViewOffset(Vector(0, 0, ent.ViewOffsetZ))
			ent:SetViewOffsetDucked(Vector(0, 0, 36))
			ent.ViewOffsetZ = Lerp(FrameTime()/0.1, ent.ViewOffsetZ, num)
		else
			ent:SetViewOffset(Vector(0, 0, 64))
			ent:SetViewOffsetDucked(Vector(0, 0, 36))
			ent.ViewOffsetZ = 64
		end

		ent:SetNWString("MoveDirection", ent:MovingDirection())
		if ent:IsZombie() then 
			ent:SetNoTarget(true)
			continue 
		end

		if ent.ExecutingTarget then
			ent:SetPos(ent.ExecutingPos)
			
			local att = ent.ExecutingEnt
			if !IsValid(att) or IsValid(att) and att:Health() <= 0 then
				ent:Freeze(false)
				ent:GodDisable()
				timer.Simple(5, function()
					if !IsValid(ent) then return end
					ent:SetNoTarget(false)
				end)
				ent:SetNotSolid(false)
				ent:SetSVAnimation("")
				ent.ExecutingTarget = false
				ent.ExecutingEnt = nil
				ent.Grabbed = false
			end

			if ent.Grabbed then
				if ent:Health() <= 1 then
					SuR:StopGrab(ent.ExecutingEnt, ent, true)
				elseif not ent.GrabDamage or ent.GrabDamage < CurTime() then
					ent.GrabDamage = CurTime()+0.1
					ent:SetHealth(ent:Health()-1)
					ent:PlayVoiceLine("pain", 5)
				end
			end
		end

		if ent:IsDowned() and ent:IsStunned(true) then
			ent:SetSVAnimation("")
		end
		
		local wep = ent:GetActiveWeapon()
		if ent:Alive() and IsValid(wep) and ent:KeyDown(IN_RELOAD) and wep:GetMaxClip1() > 0 and wep:Clip1() < wep:GetMaxClip1() then
			ent:PlayVoiceLine("reload", 50)
		end
		if ent:Alive() and IsValid(wep) and ent:KeyDown(IN_ATTACK) and wep:GetMaxClip1() > 0 and wep:Clip1() == 0 and ent:GetAmmoCount(wep:GetPrimaryAmmoType()) == 0 then
			ent:PlayVoiceLine("noammo")
		end
		if IsValid(wep) and wep:GetHoldType() == "shotgun" then
			wep:SetHoldType("rpg")
		end

		if IsValid(wep) and ent:HasWeapon("sur_fuel") then
			ent:SelectWeapon("sur_fuel")
		end

		local tr1 = util.TraceLine({
			start = ent:GetPos(),
			endpos = ent:GetPos() + ent:GetForward() * 32,
			filter = ent,
			mask = MASK_PLAYERSOLID
		})

		local tr2 = util.TraceLine({
			start = ent:EyePos(),
			endpos = ent:EyePos() + ent:GetForward() * 32,
			filter = ent,
			mask = MASK_PLAYERSOLID
		})

		local tr3 = util.TraceHull({
			start = ent:GetPos() + ent:GetForward() * 60,
			endpos = ent:GetPos() + ent:GetForward() * 60,
			filter = ent,
			mins = Vector( -16, -16, 0 ),
			maxs = Vector( 16, 16, 72 ),
			mask = MASK_PLAYERSOLID
		})

		ent:SetNWBool('CanVault', tr1.Hit and not tr2.Hit and not tr3.Hit)
		ent:SetNWBool('CanHeal', ent:Alive() and ent:Health() < ent:GetMaxHealth() and ent:GetNWFloat("Bandages") > 0)

		if ent:Alive() and ent.VoiceLineDelay_EnemyBack < CurTime() then
			local tab = ents.FindInSphere(ent:GetPos()-ent:GetForward()*128, 128)
			for i=1,#tab do
				local npc = tab[i]
				if npc:IsNPC() and npc.IsZombie and ent:IsLineOfSightClear(npc) then
					ent.VoiceLineDelay_EnemyBack = CurTime()+math.random(15,45)
					ent:PlayVoiceLine("enemyback")
					break
				end
			end
		end

		if SuR.Config.Enable_Dangerous_Water then
			local d = ent.WaterDamageDelay or 0
			if ent:Alive() and d < CurTime() and ent:WaterLevel() > 0 then
				ent:TakeDamage(ent:WaterLevel()*5)
				ent.WaterDamageDelay = CurTime()+1
			end
		end

		if ent:Alive() and ent.VoiceLineDelay_EnemySpotted < CurTime() then
			local tab = ents.FindInSphere(ent:GetEyeTrace().HitPos, 128)
			for i=1,#tab do
				local npc = tab[i]
				if npc:IsNPC() and npc.IsZombie and ent:IsLineOfSightClear(npc) then
					ent.VoiceLineDelay_EnemySpotted = CurTime()+math.random(20,60)
					ent:PlayVoiceLine("enemyspotted")
					break
				end
			end
		end

		if !ent:CheckReachingNPC() and SuR:CurrentAliveEnemies()+SuR.Enemy_To_Spawn > 5 then
			ent.ResetBuggedPos = CurTime()+5
			ent:SetNWFloat('BuggedPos', math.Clamp(ent:GetNWFloat('BuggedPos')+FrameTime()/10, 0, 1))
		end

		if ent:GetNWFloat('BuggedPos') >= 1 then
			ent:SetNWFloat('BuggedPos', 0)
			local pos = SuR:GetRandomPos(true, 0, 5000)
			if !isvector(pos) then
				pos = SuR:GetRandomPos(false, 0, 5000)
			end
			if isvector(pos) then
				ent:ScreenFade(SCREENFADE.IN, color_black, 1, 1)
				ent:SetPos(pos)
			end
		elseif !ent:Alive() or ent.ResetBuggedPos < CurTime() then
			ent:SetNWFloat('BuggedPos', 0)
		end

		if ent:Alive() and ent.VoiceLineDelay_LowHP < CurTime() and ent:Health() <= 50 then
			ent.VoiceLineDelay_LowHP = CurTime()+math.random(10,30)
			ent:PlayVoiceLine("lowhp", 75)
		end
	end
end)

hook.Add("CanPlayerSuicide", "SuR_Disable", function(ply)
	return SuR.Config.Enable_Suicide or ply:IsZombie()
end)

hook.Add("PlayerDeathThink", "SuR_DeadSpawn", function(ply)
	if (SuR.Game_Started and SuR.Wave_Started or !SuR.Game_Started) and ply:IsSurvivor() then
		ply:Spectate(6)
		return false
	elseif (SuR.Game_Started and !SuR.Wave_Started or !SuR.Game_Started) and ply:IsZombie() then
		ply:Spectate(6)
		return false
	end
end)

hook.Add("PlayerUse", "SuR_ZombieBlock", function( ply, ent )
	if ply:IsZombie() then
		return false
	end
end)

hook.Add("PlayerInitialSpawn", "SuR_InitialSpawn", function(ply)
	ply:LoadLevelProgress()
	ply:SetNWString("StartWeapon", SuR.Config.TierWeapons["Tier1"][1])
	ply:SetModel("models/player/Group03/male_0"..math.random(1,9)..".mdl")
	ply:SetNWString("VoicePack", "Default [EN]")
	if #SuR.Config.Allowed_PlayerModels > 0 then
		ply:SetModel(SuR.Config.Allowed_PlayerModels[math.random(1,#SuR.Config.Allowed_PlayerModels)])
	end
	if not SuR.Game_Started and SuR.DeployTime > CurTime()+10 and not SuR.DisableLogic then
		timer.Simple(1, function()
			if !IsValid(ply) then return end
			SuR:ShowDeployCutscene(ply, true)
		end)
	end

	------------------------------------

	local config = file.Read("surrounded/"..game.GetMap()..".lua", "LUA")
	if config then
		net.Start("SuR.ClientRunString")
		net.WriteString(config)
		net.Send(ply)
	end

	local files = file.Find("surrounded/scripts/*", "LUA")
	if istable(files) and #files > 0 then
		for k, f in pairs(files) do
			local scr = file.Read("surrounded/scripts/"..f, "LUA")
			net.Start("SuR.ClientRunString")
			net.WriteString(scr)
			net.Send(ply)
		end
	end
end)

hook.Add("PlayerSpawn", "SuR_MainSpawn", function(ply)
	if ply:Team() == 1001 and not SuR.Config.Versus_Mode then
		ply:SetTeam(1)
	elseif ply:Team() == 1001 and SuR.Game_Started and SuR.Config.Versus_Mode then
		ply:SetTeam(2)
	end
	local cant = ply:IsSurvivor() and SuR.Game_Started and SuR.Wave_Started or !SuR.Game_Started or ply:IsZombie() and SuR.Game_Started and !SuR.Wave_Started or !SuR.Game_Started
	if ply.AllowSpawn or SuR.DisableLogic then
		cant = false
		ply.AllowSpawn = false
	end
	if cant then
		ply:KillSilent()
		return
	end
	ply.ViewOffsetZ = 64
	ply:SetModelScale(1, 0)
	ply:SetModel("models/player/Group03/male_0"..math.random(1,9)..".mdl")
	if #SuR.Config.Allowed_PlayerModels > 0 then
		ply:SetModel(SuR.Config.Allowed_PlayerModels[math.random(1,#SuR.Config.Allowed_PlayerModels)])
	end
	if ply:IsSurvivor() then
		SuR:StopInfection(ply)
		ply:AllowFlashlight(true)
		ply:StripWeapons()
		ply:StripAmmo()
		ply:SetSlowWalkSpeed(50)
		ply:SetWalkSpeed(90)
		ply:SetRunSpeed(170)
		ply:SetJumpPower(150)
		ply:SetNoCollideWithTeammates(true)
		ply:AllowFlashlight(true)
		ply.ResetBuggedPos = 0
		ply:SetNWFloat('BuggedPos', 0)
		ply:SetNWFloat('buildercost', 1)
		if ply:GetPlayerClass() == "survivor" and ply:GetLevelState() >= 1 then
			ply:SetNWFloat('SelfRevive', 1)
		else
			ply:SetNWFloat('SelfRevive', 0)
		end
		if ply:GetPlayerClass() == "scout" and ply:GetLevelState() >= 4 then
			ply:SetRunSpeed(ply:GetRunSpeed()*1.4)
		elseif ply:GetPlayerClass() == "scout" and ply:GetLevelState() >= 2 then
			ply:SetRunSpeed(ply:GetRunSpeed()*1.2)
		end

		local str = ply:GetNWString('SuR_PlayerModel', '')
		if str != '' then
			ply:SetModel(str)
		end

		ply:SetupHands()

		ply:SetNWFloat('CanCallSupport', 0)
		ply:SetNWFloat('Bandages', 0)
		ply:SetNWFloat('Sprays', 0)
		ply:SetNWInt('Grenades', 0)
		ply:SetNWInt('Money', 0)

		if #SuR.Config.Player_SpawnPositions > 0 then
			ply:SetPos(table.Random(SuR.Config.Player_SpawnPositions))
		end

		ply.HealingAbilityDelay = 0
		
		ply.VoiceLineDelay_LowHP = CurTime()+15
		ply.VoiceLineDelay_EnemySpotted =  CurTime()+15
		ply.VoiceLineDelay_EnemyBack = CurTime()+15

		if ply.ExfilItems then
			local wep1 = ply:Give(ply.ExfilItems.main)
			local wep2 = ply:Give(ply.ExfilItems.sec)
			local wep3 = ply:Give(ply.ExfilItems.melee)
			local wep4 = ply:Give(ply.ExfilItems.extra)
			ply:SetNWFloat('Bandages', ply.ExfilItems.bandages)
			ply:SetNWFloat('Sprays', ply.ExfilItems.sprays)
			ply:SetNWFloat('CanCallSupport', 1)
			ply.ExfilItems = nil
		else
			if ply:GetPlayerClass() == "berserker" and ply:GetLevelState() >= 3 then
				local wep1 = ply:Give(table.Random(SuR.Config.TierWeapons["Melee"]))
				wep1:SetNWFloat('Tier', 1)
			else
				ply:Give(SuR.Config.EmptyWeapon)
				ply:SetNWString('MeleeWeapon', SuR.Config.EmptyWeapon)
			end
			local wep2 = ply:Give(ply:GetNWString("StartWeapon"))
			ply:StripAmmo()
			ply:GiveAmmo(wep2:GetMaxClip1()*2, wep2:GetPrimaryAmmoType(), true)
		end
		if ply:GetPlayerClass() == "demoman" and ply:GetLevelState() >= 3 then
			ply:Give("tfa_private_m79")
		end
		if ply:GetPlayerClass() == "engineer" and ply:GetLevelState() >= 2 then
			ply:Give("sur_hammer")
			ply:SetNWFloat('buildercost', 10)
		end
		if ply:GetPlayerClass() == "engineer" and ply:GetLevelState() >= 5 then
			ply:Give("sur_mine")
		end
		if ply:GetPlayerClass() == "marksman" and ply:GetLevelState() >= 4 then
			ply:Give(table.Random(SuR.Config.TierWeapons["Tier3"]))
		end
		if ply:GetPlayerClass() == "marksman" and ply:GetLevelState() >= 1 then
			ply:GiveAmmo(160, 3, true)
			ply:GiveAmmo(80, 5, true)
			ply:GiveAmmo(6, 8, true)
			ply:GiveAmmo(12, 11, true)
			ply:GiveAmmo(80, 13, true)
			ply:GiveAmmo(80, 14, true)
			ply:GiveAmmo(300, 1, true)
			ply:GiveAmmo(300, 4, true)
			ply:GiveAmmo(80, 7, true)
		end

		if ply:GetPlayerClass() == "medic" and ply:GetLevelState() >= 5 then
			ply:SetNWFloat('Bandages', 100)
			ply:SetNWFloat('Sprays', 50)
			ply:SetNWFloat('SelfRevive', 5)
		elseif ply:GetPlayerClass() == "medic" and ply:GetLevelState() >= 3 then
			ply:SetNWFloat('Bandages', 18)
			ply:SetNWFloat('Sprays', 6)
			ply:SetNWFloat('SelfRevive', 3)
		elseif ply:GetPlayerClass() == "medic" and ply:GetLevelState() >= 1 then
			ply:SetNWFloat('Bandages', 9)
			ply:SetNWFloat('Sprays', 3)
		end

		if ply:GetPlayerClass() == "scout" and ply:GetLevelState() >= 5 then
			ply:SetNWFloat('CanCallSupport', 5)
		elseif ply:GetPlayerClass() == "scout" and ply:GetLevelState() >= 3 then
			ply:SetNWFloat('CanCallSupport', 2)
		end

		if SuR.Config.Objective_Mode then
			ply:GiveAmmo(160, 3, true)
			ply:GiveAmmo(80, 5, true)
			ply:GiveAmmo(6, 8, true)
			ply:GiveAmmo(12, 11, true)
			ply:GiveAmmo(80, 13, true)
			ply:GiveAmmo(80, 14, true)
			ply:GiveAmmo(300, 1, true)
			ply:GiveAmmo(300, 4, true)
			ply:GiveAmmo(80, 7, true)
		end
	elseif ply:IsZombie() then
		local id = "Walker"
		if isstring(ply.SelectZombieClass) and SuR.Config.Zombie_Unlock_Wave[ply.SelectZombieClass] and SuR.Config.Zombie_Unlock_Wave[ply.SelectZombieClass] <= SuR.Current_Wave then
			id = ply.SelectZombieClass
		end
		local model = table.Random(SuR.Config.Zombie_PlayerModels[id])
		local maxhealth = 100
		if id == "Walker" then
			ply:SetCrouchedWalkSpeed(0.4)
			ply:SetSlowWalkSpeed(25)
			ply:SetWalkSpeed(60)
			ply:SetRunSpeed(60)
			ply:SetJumpPower(240)
			maxhealth = 300
		elseif id == "Runner" then
			if SuR.Current_Wave >= 10 then
				ply:SetCrouchedWalkSpeed(0.2)
				ply:SetSlowWalkSpeed(90)
				ply:SetWalkSpeed(250)
				ply:SetRunSpeed(250)
				ply:SetJumpPower(280)
			elseif SuR.Current_Wave >= 6 then
				ply:SetCrouchedWalkSpeed(0.2)
				ply:SetSlowWalkSpeed(70)
				ply:SetWalkSpeed(200)
				ply:SetRunSpeed(200)
				ply:SetJumpPower(270)
			else
				ply:SetCrouchedWalkSpeed(0.2)
				ply:SetSlowWalkSpeed(50)
				ply:SetWalkSpeed(150)
				ply:SetRunSpeed(150)
				ply:SetJumpPower(260)
			end
			maxhealth = 150
		elseif id == "Carrier" then
			ply:SetCrouchedWalkSpeed(0.2)
			ply:SetSlowWalkSpeed(60)
			ply:SetWalkSpeed(160)
			ply:SetRunSpeed(160)
			ply:SetJumpPower(260)
			maxhealth = 250
		elseif id == "Hazmat" then
			ply:SetCrouchedWalkSpeed(0.2)
			ply:SetSlowWalkSpeed(50)
			ply:SetWalkSpeed(120)
			ply:SetRunSpeed(120)
			ply:SetJumpPower(200)
			maxhealth = 50
		elseif id == "Brute" then
			ply:SetCrouchedWalkSpeed(0.5)
			ply:SetSlowWalkSpeed(25)
			ply:SetWalkSpeed(50)
			ply:SetRunSpeed(50)
			ply:SetJumpPower(200)
			ply:SetModelScale(1.1, 0)
			maxhealth = 1000
		elseif id == "Spitter" then
			ply:SetCrouchedWalkSpeed(0.2)
			ply:SetSlowWalkSpeed(50)
			ply:SetWalkSpeed(100)
			ply:SetRunSpeed(100)
			ply:SetJumpPower(240)
			maxhealth = 100
		elseif id == "Evo" then
			ply:SetCrouchedWalkSpeed(0.2)
			ply:SetSlowWalkSpeed(80)
			ply:SetWalkSpeed(250)
			ply:SetRunSpeed(250)
			ply:SetJumpPower(300)
			maxhealth = 200
		elseif id == "Bomber" then
			ply:SetCrouchedWalkSpeed(0.2)
			ply:SetSlowWalkSpeed(40)
			ply:SetWalkSpeed(80)
			ply:SetRunSpeed(80)
			ply:SetJumpPower(200)
			maxhealth = 300
		end

		ply.ZombieClass = id
		ply:SetModel(model)
		ply:Give("sur_claws")
		ply:AllowFlashlight(false)

		local hnum = math.max(maxhealth*( SuR.Current_Wave/5 + SuR:CurrentFighters(true)/5 ), 50)
		ply:SetHealth(hnum)
		ply:SetMaxHealth(hnum)

		local pos = SuR:GetRandomPos(SuR.Config.Enable_Spawn_In_Building_Zombies, SuR.Config.Zombie_Spawn_Radius_Min, SuR.Config.Zombie_Spawn_Radius_Max)
        if math.random(1,10) == 1 then
            pos = SuR:GetRandomPos(!SuR.Config.Enable_Spawn_In_Building_Zombies, SuR.Config.Zombie_Spawn_Radius_Min, SuR.Config.Zombie_Spawn_Radius_Max)
        end
		
		timer.Simple(0.01, function()
			if !IsValid(ply) then return end
			local vm = ply:GetHands()
			if IsValid(vm) then
				vm:SetSkin(2)
			end
			if isvector(pos) then
				ply:SetPos(pos)
			end
		end)
	else
		ply:KillSilent()
	end
end)

hook.Add("GetFallDamage", "SuR_FallDamage", function(ply, speed)
    if ply:IsZombie() and ply.ZombieClass == "Evo" then
		return 0
	end
end)

hook.Add("CalcMainActivity", "MainAnims", function(ply, vel)
    local str = ply:GetSVAnim()
    local num = ply:GetNWFloat('SVAnimDelay')
    local st = ply:GetNWFloat('SVAnimStartTime')
	local downed = ply:IsDowned()
	local dir = ply:GetNWString("MoveDirection")
    if str != "" then
		if ply:GetNWBool('SVAnimLoop') and (CurTime()-st)/num > 1 then
			ply:SetNWFloat('SVAnimStartTime', CurTime())
		end
        ply:SetCycle((CurTime()-st)/num)
        return -1, ply:LookupSequence(str)
    end
	if downed then
		local speed = ply:GetVelocity():Length()
		if speed > 5 then
            if dir == "forward" then
			    return -1, ply:LookupSequence("sur_downed_trials_forward")
            elseif dir == "backward" then
			    return -1, ply:LookupSequence("sur_downed_trials_backward")
            elseif dir == "left" then
			    return -1, ply:LookupSequence("sur_downed_trials_left")
            elseif dir == "right" then
			    return -1, ply:LookupSequence("sur_downed_trials_right")
            else
                return -1, ply:LookupSequence("sur_downed_trials_idle")
            end
		else
			return -1, ply:LookupSequence("sur_downed_trials_idle")
		end
	end
	if ply:IsZombie() then
		local speed = vel:Length()
		local cr = ply:Crouching()
		if !ply:OnGround() then
			return ACT_JUMP, -1
		elseif speed > 200 and not cr then
			if cr then
				return ACT_WALK_AGITATED, -1
			else
				return ACT_SPRINT, -1
			end
		elseif speed > 75 and not cr or cr and speed > 30 then
			if cr then
				return ACT_WALK_AGITATED, -1
			else
				return ACT_RUN, -1
			end
		elseif speed > 10 then
			if cr then
				return ACT_WALK_STIMULATED, -1
			else
				if ply:GetModel() == "models/vj_lnrspecials/brute.mdl" then
					return ACT_WALK_AIM, -1
				else
					return ACT_WALK, -1
				end
			end
		else
			if cr then
				return ACT_IDLE_STIMULATED, -1
			else
				return ACT_IDLE, -1
			end
		end
	end
end)

concommand.Add("sur_spawn", function(ply, cmd, args)
	if not args[1] or !ply:IsSuperAdmin() then return end

	local ent = ents.Create(args[1])
	if not ent then return end

	if ent:IsWeapon() then
		ent:SetPos(ply:GetPos())
	else
		ent:SetPos(ply:GetEyeTrace().HitPos)
	end
	ent:Spawn()
end)

concommand.Add("sur_zombieclass", function(ply, cmd, args)
	if not args[1] then return end
	ply.SelectZombieClass = args[1]
	if ply:IsZombie() and ply:Alive() then
		ply:Kill()
	end
end)
