-- Sometimes persistent ones don't get created.
local dummy = CreateClientConVar("_zs_dummyconvar", 1, false, false)
local oldCreateClientConVar = CreateClientConVar
function CreateClientConVar(...)
	return oldCreateClientConVar(...) or dummy
end

include("sh_globals.lua")

include("obj_entity_extend_cl.lua")
include("obj_player_extend_cl.lua")
include("obj_weapon_extend_cl.lua")

include("loader.lua")

include("shared.lua")
include("cl_draw.lua")
include("cl_util.lua")
include("cl_options.lua")
include("cl_scoreboard.lua")
include("cl_targetid.lua")
include("cl_postprocess.lua")
include("cl_voicesets.lua")
include("cl_net.lua")
include("skillweb/cl_skillweb.lua")

include("vgui/dteamcounter.lua")
include("vgui/dmodelpanelex.lua")
include("vgui/dammocounter.lua")
include("vgui/dteamheading.lua")
include("vgui/dmodelkillicon.lua")

include("vgui/dexroundedpanel.lua")
include("vgui/dexroundedframe.lua")
include("vgui/dexrotatedimage.lua")
include("vgui/dexnotificationslist.lua")
include("vgui/dexchanginglabel.lua")

include("vgui/mainmenu.lua")
include("vgui/pmainmenu.lua")
include("vgui/poptions.lua")
include("vgui/phelp.lua")
include("vgui/pclassselect.lua")
include("vgui/pweapons.lua")
include("vgui/pendboard.lua")
include("vgui/pworth.lua")
include("vgui/parsenal.lua")
include("vgui/premantle.lua")
include("vgui/dpingmeter.lua")
include("vgui/dsidemenu.lua")
include("vgui/dspawnmenu.lua")
include("vgui/zsgamestate.lua")
include("vgui/zshealtharea.lua")
include("vgui/zsstatusarea.lua")

include("cl_dermaskin.lua")
include("cl_deathnotice.lua")
include("cl_floatingscore.lua")
include("cl_hint.lua")
include("cl_thirdperson.lua")

include("itemstocks/cl_stock.lua")

include("cl_zombieescape.lua")

w, h = ScrW(), ScrH()

MySelf = MySelf or NULL
hook.Add("InitPostEntity", "GetLocal", function()
	MySelf = LocalPlayer()

	GAMEMODE.HookGetLocal = GAMEMODE.HookGetLocal or function(g) end
	gamemode.Call("HookGetLocal", MySelf)
	RunConsoleCommand("initpostentity")

	MySelf:ApplySkills()
end)

-- Remove when model decal crash is fixed.
--[[function util.Decal()
end]]

-- Save on global lookup time.
local collectgarbage = collectgarbage
local render = render
local surface = surface
local draw = draw
local cam = cam
local player = player
local ents = ents
local util = util
local math = math
local string = string
local bit = bit
local gamemode = gamemode
local hook = hook
local Vector = Vector
local VectorRand = VectorRand
local Angle = Angle
local AngleRand = AngleRand
local Entity = Entity
local Color = Color
local FrameTime = FrameTime
local RealTime = RealTime
local CurTime = CurTime
local SysTime = SysTime
local EyePos = EyePos
local EyeAngles = EyeAngles
local pairs = pairs
local ipairs = ipairs
local tostring = tostring
local tonumber = tonumber
local type = type
local ScrW = ScrW
local ScrH = ScrH
local Lerp = Lerp
local EF_DIMLIGHT = EF_DIMLIGHT
local TEXT_ALIGN_CENTER = TEXT_ALIGN_CENTER
local TEXT_ALIGN_LEFT = TEXT_ALIGN_LEFT
local TEXT_ALIGN_RIGHT = TEXT_ALIGN_RIGHT
local TEXT_ALIGN_TOP = TEXT_ALIGN_TOP
local TEXT_ALIGN_BOTTOM = TEXT_ALIGN_BOTTOM
local TEXT_ALIGN_TOP_REAL = TEXT_ALIGN_TOP_REAL
local TEXT_ALIGN_BOTTOM_REAL = TEXT_ALIGN_BOTTOM_REAL

local TEAM_HUMAN = TEAM_HUMAN
local TEAM_UNDEAD = TEAM_UNDEAD
local translate = translate

local COLOR_PURPLE = COLOR_PURPLE
local COLOR_GRAY = COLOR_GRAY
local COLOR_RED = COLOR_RED
local COLOR_DARKRED = COLOR_DARKRED
local COLOR_DARKGREEN = COLOR_DARKGREEN
local COLOR_GREEN = COLOR_GREEN
local COLOR_WHITE = COLOR_WHITE

local vector_up = Vector(0, 0, 1)
local vector_down = Vector(0, 0, 1)

--local surface_SetFont = surface.SetFont
--local surface_SetTexture = surface.SetTexture
local surface_SetMaterial = surface.SetMaterial
local surface_SetDrawColor = surface.SetDrawColor
--local surface_DrawLine = surface.DrawLine
local surface_DrawRect = surface.DrawRect
local surface_DrawOutlinedRect = surface.DrawOutlinedRect
local surface_DrawTexturedRect = surface.DrawTexturedRect
local surface_DrawTexturedRectRotated = surface.DrawTexturedRectRotated
local surface_DrawTexturedRectUV = surface.DrawTexturedRectUV
local surface_PlaySound = surface.PlaySound

local render_SetBlend = render.SetBlend
local render_ModelMaterialOverride = render.ModelMaterialOverride
local render_SetColorModulation = render.SetColorModulation
local render_SuppressEngineLighting = render.SuppressEngineLighting
local cam_IgnoreZ = cam.IgnoreZ
local render_SetMaterial = render.SetMaterial
local render_DrawQuadEasy = render.DrawQuadEasy
local cam_Start3D = cam.Start3D
local cam_End3D = cam.End3D
local cam_Start3D2D = cam.Start3D2D
local cam_End3D2D = cam.End3D2D
local render_FogMode = render.FogMode
local render_FogStart = render.FogStart
local render_FogEnd = render.FogEnd
local render_FogColor = render.FogColor
local render_FogMaxDensity = render.FogMaxDensity
local render_GetFogDistances = render.GetFogDistances
local render_GetFogColor = render.GetFogColor
local render_GetFogMode = render.GetFogMode

local draw_SimpleText = draw.SimpleText
local draw_SimpleTextBlurry = draw.SimpleTextBlurry
local draw_SimpleTextBlur = draw.SimpleTextBlur
local draw_GetFontHeight = draw.GetFontHeight

local MedicalAuraDistance = 800 ^ 2

local M_Player = FindMetaTable("Player")
local P_Team = M_Player.Team

GM.LifeStatsBrainsEaten = 0
GM.LifeStatsHumanDamage = 0
GM.LifeStatsBarricadeDamage = 0
GM.InputMouseX = 0
GM.InputMouseY = 0
GM.LastTimeDead = 0
GM.LastTimeAlive = 0
GM.HeartBeatTime = 0
GM.FOVLerp = 1
GM.HurtEffect = 0
GM.PrevHealth = 0
GM.SuppressArsenalTime = 0
GM.ZombieThirdPerson = false
GM.Beats = {}
GM.CurrentRound = 1

GM.DeathFog = 0
GM.FogStart = 0
GM.FogEnd = 8000
GM.FogRed = 30
GM.FogGreen = 30
GM.FogBlue = 30

function GM:ClickedPlayerButton(pl, button)
end

function GM:ClickedEndBoardPlayerButton(pl, button)
end

function GM:CenterNotify(...)
	if self.CenterNotificationHUD and self.CenterNotificationHUD:IsValid() then
		return self.CenterNotificationHUD:AddNotification(...)
	end
end

function GM:TopNotify(...)
	if self.TopNotificationHUD and self.TopNotificationHUD:IsValid() then
		return self.TopNotificationHUD:AddNotification(...)
	end
end

function GM:_InputMouseApply(cmd, x, y, ang)
	if MySelf:KeyDown(IN_WALK) and MySelf:IsHolding() then
		self.InputMouseX = math.NormalizeAngle(self.InputMouseX - x * 0.02 * GAMEMODE.PropRotationSensitivity)
		self.InputMouseY = math.NormalizeAngle(self.InputMouseY - y * 0.02 * GAMEMODE.PropRotationSensitivity)

		local snap = GAMEMODE.PropRotationSnap
		local snapanglex, snapangley = self.InputMouseX, self.InputMouseY
		if snap > 0 then
			snapanglex = Angle(self.InputMouseX, 0, 0):SnapTo("p", snap).p
			snapangley = Angle(self.InputMouseY, 0, 0):SnapTo("p", snap).p
		end

		RunConsoleCommand("_zs_rotateang", snapanglex, snapangley)
		return true
	end

	if self:UseOverTheShoulder() and P_Team(MySelf) == TEAM_HUMAN then
		self:InputMouseApplyOTS(cmd, x, y, ang)
	end
end

function GM:_GUIMousePressed(mc)
end

function GM:TryHumanPickup(pl, entity)
end

function GM:AddExtraOptions(list, window)
end

function GM:SpawnMenuEnabled()
	return false
end

function GM:SpawnMenuOpen()
	return false
end

function GM:ContextMenuOpen()
	return false
end

function GM:_HUDWeaponPickedUp(wep)
	if P_Team(MySelf) == TEAM_HUMAN and not wep.NoPickupNotification then
		self:Rewarded(wep:GetClass())
	end
end

function GM:HUDItemPickedUp(itemname)
end

function GM:HUDAmmoPickedUp(itemname, amount)
end

function GM:InitPostEntity()
	self:CreateLateVGUI()

	self:AssignItemProperties()
	self:FixWeaponBase()

	self:LocalPlayerFound()

	gamemode.Call("EvaluateFilmMode")

	timer.Simple(2, function() GAMEMODE:GetFogData() end)

	RunConsoleCommand("pp_bloom", "0")
end

local fogstart = 0
local fogend = 0
local fogr = 0
local fogg = 0
local fogb = 0

function GM:SetupFog()
	local power = self.DeathFog
	local rpower = 1 - self.DeathFog

	fogstart = self.FogStart * rpower
	fogend = self.FogEnd * rpower + 150 * power
	fogr = self.FogRed * rpower
	fogg = self.FogGreen * rpower + 40 * power
	fogb = self.FogBlue * rpower

	local dimvision = MySelf.DimVision
	if dimvision and dimvision:IsValid() then
		power = dimvision:GetDim()

		fogstart = Lerp(power, fogstart, 1)
		fogend = Lerp(power, fogend, math.min(148 / math.max(0.01, MySelf.DimVisionEffMul), fogend))
		fogr = Lerp(power, fogr, 0)
		fogg = Lerp(power, fogg, 0)
		fogb = Lerp(power, fogb, 0)
	end
end

function GM:_SetupWorldFog()
	if self.DeathFog == 0 and not MySelf.DimVision then return end

	self:SetupFog()

	render_FogMode(1)

	render_FogStart(fogstart)
	render_FogEnd(fogend)
	render_FogColor(fogr, fogg, fogb)
	render_FogMaxDensity(1)

	return true
end

function GM:_SetupSkyboxFog(skyboxscale)
	if self.DeathFog == 0 and not MySelf.DimVision then return end

	self:SetupFog()

	render_FogMode(1)

	render_FogStart(fogstart * skyboxscale)
	render_FogEnd(fogend * skyboxscale)
	render_FogColor(fogr, fogg, fogb)
	render_FogMaxDensity(1)

	return true
end

function GM:PreDrawSkyBox()
	self.DrawingInSky = true
end

local matSky = CreateMaterial("SkyOverride", "UnlitGeneric", {["$basetexture"] = "color/white", ["$vertexcolor"] = 1, ["$vertexalpha"] = 1, ["$model"] = 1})
local colSky = Color(0, 30, 0)
function GM:PostDrawSkyBox()
	self.DrawingInSky = false

	local dimvision = MySelf.DimVision
	dimvision = dimvision and dimvision:IsValid() and dimvision:GetDim()
	if self.DeathFog > 0 or dimvision then
		colSky.a = math.max(self.DeathFog, dimvision or 0) * 230
		colSky.g = self.DeathFog * 30

		cam_Start3D(EyePos(), EyeAngles())
			render_SuppressEngineLighting(true)

			render_SetMaterial(matSky)

			render_DrawQuadEasy(Vector(0, 0, 10240), Vector(0, 0, -1), 20480, 20480, colSky, 0)
			render_DrawQuadEasy(Vector(0, 10240, 0), Vector(0, -1, 0), 20480, 20480, colSky, 0)
			render_DrawQuadEasy(Vector(0, -10240, 0), Vector(0, 1, 0), 20480, 20480, colSky, 0)
			render_DrawQuadEasy(Vector(10240, 0, 0), Vector(-1, 0, 0), 20480, 20480, colSky, 0)
			render_DrawQuadEasy(Vector(-10240, 0, 0), Vector(1, 0, 0), 20480, 20480, colSky, 0)

			render_SuppressEngineLighting(false)
		cam_End3D()
	end
end

function GM:GetFogData()
	local _fogstart, _fogend = render_GetFogDistances()
	local _fogr, _fogg, _fogb = render_GetFogColor()

	self.FogStart = _fogstart
	self.FogEnd = _fogend
	self.FogRed = _fogr
	self.FogGreen = _fogg
	self.FogBlue = _fogb
end

function GM:ShouldDraw3DWeaponHUD()
	return GAMEMODE.WeaponHUDMode ~= 1
end

function GM:ShouldDraw2DWeaponHUD()
	return GAMEMODE.WeaponHUDMode >= 1 or self:UseOverTheShoulder()
end

local matAura = Material("models/debug/debugwhite")
local skip = false
function GM.PostPlayerDrawMedical(pl)
	if not skip and P_Team(pl) == TEAM_HUMAN and pl ~= MySelf then
		local eyepos = EyePos()
		local dist = pl:NearestPoint(eyepos):DistToSqr(eyepos)
		if dist < MedicalAuraDistance then
			local green = pl:Health() / pl:GetMaxHealth()

			pl.SkipDrawHooks = true
			skip = true

			render_SuppressEngineLighting(true)
			render_ModelMaterialOverride(matAura)
			render_SetBlend((1 - dist / MedicalAuraDistance) * 0.1 * (1 + math.abs(math.sin((CurTime() + pl:EntIndex()) * 4)) * 0.05))
			render_SetColorModulation(1 - green, green, pl:GetDTBool(DT_PLAYER_BOOL_FRAIL) and 1 or 0)
				pl:DrawModel()
			render_SetColorModulation(1, 1, 1)
			render_SetBlend(1)
			render_ModelMaterialOverride()
			render_SuppressEngineLighting(false)

			skip = false
			pl.SkipDrawHooks = false
		end
	end
end

function GM:OnReloaded()
	self.BaseClass.OnReloaded(self)

	self:LocalPlayerFound()
end

-- The whole point of this is so we don't need to check if the local player is valid 1000 times a second.
-- Empty functions get filled when the local player is found.
function GM:Think() end
GM.HUDWeaponPickedUp = GM.Think
GM.Think = GM._Think
GM.HUDShouldDraw = GM.Think
GM.CachedFearPower = GM.Think
GM.CalcView = GM.Think
GM.ShouldDrawLocalPlayer = GM.Think
GM.PostDrawOpaqueRenderables = GM.Think
GM.PostDrawTranslucentRenderables = GM.Think
GM.HUDPaint = GM.Think
GM.HUDPaintBackground = GM.Think
GM.CreateMove = GM.Think
GM.PrePlayerDraw = GM.Think
GM.PostPlayerDraw = GM.Think
GM.InputMouseApply = GM.Think
GM.GUIMousePressed = GM.Think
GM.HUDWeaponPickedUp = GM.Think
function GM:LocalPlayerFound()
	self.Think = self._Think
	self.HUDShouldDraw = self._HUDShouldDraw
	self.CachedFearPower = self._CachedFearPower
	self.CalcView = self._CalcView
	self.ShouldDrawLocalPlayer = self._ShouldDrawLocalPlayer
	self.PostDrawTranslucentRenderables = self._PostDrawTranslucentRenderables
	self.HUDPaint = self._HUDPaint
	self.HUDPaintBackground = self._HUDPaintBackground
	self.CreateMove = self._CreateMove
	self.PrePlayerDraw = self._PrePlayerDraw
	self.PostPlayerDraw = self._PostPlayerDraw
	self.InputMouseApply = self._InputMouseApply
	self.GUIMousePressed = self._GUIMousePressed
	self.HUDWeaponPickedUp = self._HUDWeaponPickedUp
	self.RenderScene = self._RenderScene
	self.SetupSkyboxFog = self._SetupSkyboxFog
	self.SetupWorldFog = self._SetupWorldFog

	LocalPlayer().LegDamage = 0
	LocalPlayer().ArmDamage = 0

	if render.GetDXLevel() >= 80 then
		self.RenderScreenspaceEffects = self._RenderScreenspaceEffects
	end
end

local LastSigilCorrupted = -math.huge
local LastSigilUncorrupted = -math.huge
local function DrawEyeFlash(x, y, size, islast)
	local curtime = CurTime()
	local bsize = size * (1 + curtime * 2 % 1)
	surface_SetDrawColor(220, 0, 0, 240)
	surface_DrawTexturedRectRotated(x, y, bsize, bsize, 0)

	if islast then
		local dt = (curtime - LastSigilCorrupted) / 3
		if dt < 1 then
			local idt = 1 - dt
			surface_SetDrawColor(idt ^ 0.5 * 255, 0, 0, 255)
			surface_DrawTexturedRectRotated(x, y, size * dt * 150, size, dt * 10)
			surface_DrawTexturedRectRotated(x, y, size, size * dt * 150, dt * 10)
		else
			dt = (curtime - LastSigilUncorrupted) / 3
			if dt < 1 then
				local idt = 1 - dt
				surface_SetDrawColor(0, dt ^ 0.5 * 255, 0, 255)
				surface_DrawTexturedRectRotated(x, y, size * idt * 150, size, idt * 10)
				surface_DrawTexturedRectRotated(x, y, size, size * idt * 150, idt * 10)
			end
		end
	end
end

local currentpower = 0
local spawngreen = 0
local matFearMeter = Material("zombiesurvival/fearometer")
local matNeedle = Material("zombiesurvival/fearometerneedle")
local matEyeGlow = Material("Sprites/light_glow02_add_noz")
local matSigil = Material("zombiesurvival/sigil.png")
local matArsenal = Material("zombiesurvival/arsenalcrate.png")
local matResupply = Material("zombiesurvival/resupply.png")
local matRemantler = Material("zombiesurvival/remantler.png")
local matCrossout = Material("zombiesurvival/crossout.png")
local matNest = Material("zombiesurvival/nest.png")
--local matGradientRight = Material("vgui/gradient-r")
--local matGradientLeft = CreateMaterial("gradient-l", "UnlitGeneric", {["$basetexture"] = "vgui/gradient-l", ["$vertexalpha"] = "1", ["$vertexcolor"] = "1", ["$ignorez"] = "1", ["$nomip"] = "1"})
function GM:DrawFearMeter(power, screenscale)
	if currentpower < power then
		currentpower = math.min(power, currentpower + FrameTime() * (math.tan(currentpower) * 2 + 0.05))
	elseif power < currentpower then
		currentpower = math.max(power, currentpower - FrameTime() * (math.tan(currentpower) * 2 + 0.05))
	end

	local w, h = ScrW(), ScrH()
	local size = 192 * screenscale
	local half_size = size / 2
	local mx, my = w / 2 - half_size, h - size

	surface_SetMaterial(matFearMeter)
	surface_SetDrawColor(140, 140, 140, 240)
	surface_DrawTexturedRect(mx, my, size, size)
	if currentpower >= 0.75 then
		local pulse = CurTime() % 3 - 1
		if pulse > 0 then
			pulse = pulse ^ 2
			local pulsesize = pulse * screenscale * 28
			surface_SetDrawColor(140, 140, 140, 120 - pulse * 120)
			surface_DrawTexturedRect(mx - pulsesize, my - pulsesize, size + pulsesize * 2, size + pulsesize * 2)
		end
	end

	surface_SetMaterial(matNeedle)
	surface_SetDrawColor(160, 160, 160, 225)
	local rot = math.Clamp((0.5 - currentpower) + math.sin(RealTime() * 10) * 0.01, -0.5, 0.5) * 300
	surface_DrawTexturedRectRotated(w * 0.5 - math.max(0, rot * size * -0.0001), h - half_size - math.abs(rot) * size * 0.00015, size, size, rot)

	if P_Team(MySelf) == TEAM_UNDEAD then
		if self:GetDynamicSpawning() and self:ShouldUseAlternateDynamicSpawn() then
			local obs = MySelf:GetObserverTarget()
			spawngreen = math.Approach(spawngreen, self:DynamicSpawnIsValid(obs and obs:IsValid() and obs:IsPlayer() and obs:Team() == TEAM_UNDEAD and obs or MySelf) and 1 or 0, FrameTime() * 4)

			local sy = my + size * 0.6953
			local gsize = size * 0.085

			surface_SetMaterial(matEyeGlow)
			surface_SetDrawColor(220 * (1 - spawngreen), 220 * spawngreen, 0, 240)
			surface_DrawTexturedRectRotated(mx + size * 0.459, sy, gsize, gsize, 0)
			surface_DrawTexturedRectRotated(mx + size * 0.525, sy, gsize, gsize, 0)
		end

		if currentpower > 0 and not self.ZombieEscape then
			draw_SimpleTextBlurry(translate.Format("resist_x", math.ceil(self:GetDamageResistance(currentpower) * 100)), "ZSDamageResistance", w * 0.5, my + size * 0.75, Color(currentpower * 200, 200 - currentpower * 200, 0, 255), TEXT_ALIGN_CENTER)
		end
	end

	if self:GetUseSigils() and self.MaxSigils > 0 then
		local sigwid, sighei = screenscale * 18, screenscale * 36
		local extrude = size * 0.25 + sighei / 2
		local angle_current = -180
		local angle_step = 180 / (self.MaxSigils - 1)
		local rad, sigil, health, maxhealth, corrupt, damageflash, sigx, sigy, healthfrac

		local sigils = GAMEMODE.CachedSigils
		local corruptsigils = 0
		for i=1, self.MaxSigils do
			sigil = sigils[i]
			health = 0
			maxhealth = 0
			corrupt = false
			if sigil and sigil:IsValid() then
				health = sigil:GetSigilHealth()
				maxhealth = sigil:GetSigilMaxHealth()
				corrupt = sigil:GetSigilCorrupted()
				corruptsigils = corruptsigils + (corrupt and 1 or 0)
			end

			if health >= 0 then
				rad = math.rad(angle_current)
				sigx = mx + half_size + math.cos(rad) * extrude
				sigy = my + half_size + math.sin(rad) * extrude

				if sigil and sigil:IsValid() then
					damageflash = math.min((CurTime() - sigil:GetSigilLastDamaged()) * 2, 1) * 255
				else
					damageflash = 255
				end
				healthfrac = health / maxhealth
				if corrupt then
					surface_SetDrawColor((255 - damageflash) * healthfrac, damageflash * healthfrac, 0, 220)
				else
					surface_SetDrawColor((255 - damageflash) * healthfrac, damageflash * healthfrac, 220, 220)
				end

				surface_SetMaterial(matSigil)
				surface_DrawTexturedRectRotated(sigx, sigy, sigwid, sighei, angle_current + 90)

				if corrupt then
					surface_SetMaterial(matCrossout)
					surface_SetDrawColor(220, 0, 0, 220)
					surface_DrawTexturedRect(sigx - sigwid / 2, sigy - sighei / 2, sigwid, sighei)
				end

				angle_current = angle_current + angle_step
			end
		end

		local des = corruptsigils / self.MaxSigils --self:GetSigilsDestroyed() / self.MaxSigils
		if des >= 0.3333 then
			surface_SetMaterial(matEyeGlow)

			local eye_size = size * 0.125
			local sy = my + size * 0.6953

			if des >= 0.6666 then
				DrawEyeFlash(mx + size * 0.459, sy, eye_size)
				DrawEyeFlash(mx + size * 0.525, sy, eye_size, true)
			else
				DrawEyeFlash(mx + size * 0.459, sy, eye_size, true)
			end
		end
	end
end

function GM:GetDynamicSpawning()
	return not GetGlobalBool("DynamicSpawningDisabled", false)
end

function GM:TrackLastDeath()
	if MySelf:Alive() then
		self.LastTimeAlive = CurTime()
	else
		self.LastTimeDead = CurTime()
	end
end

function GM:IsClassicMode()
	return GetGlobalBool("classicmode", false)
end

function GM:IsBabyMode()
	return GetGlobalBool("babymode", false)
end

function GM:PostRender()
end

local lastwarntim = -1
--local NextGas = 0
function GM:_Think()
	local time = CurTime()

	if self:GetEscapeStage() == ESCAPESTAGE_DEATH then
		self.DeathFog = math.min(self.DeathFog + FrameTime() / 5, 1)

		--[[if time >= NextGas then
			NextGas = time + 0.01

			local randdir, emitpos, particle
			local eyepos = EyePos()

			local emitter = ParticleEmitter(eyepos)

			for i=1, 3 do
				randdir = VectorRand()
				randdir.z = math.abs(randdir.z)
				randdir:Normalize()
				emitpos = eyepos + randdir * math.Rand(0, 1200)

				particle = emitter:Add("particles/smokey", emitpos)
				particle:SetVelocity(randdir * math.Rand(8, 256))
				particle:SetAirResistance(16)
				particle:SetDieTime(math.Rand(2.2, 3.5))
				particle:SetStartAlpha(math.Rand(70, 90))
				particle:SetEndAlpha(0)
				particle:SetStartSize(1)
				particle:SetEndSize(math.Rand(150, 325))
				particle:SetRoll(math.Rand(0, 360))
				particle:SetRollDelta(math.Rand(-1, 1))
				particle:SetColor(0, math.Rand(20, 45), 0)
			end

			emitter:Finish() emitter = nil collectgarbage("step", 64)
		end]]
	elseif self.DeathFog > 0 then
		self.DeathFog = math.max(self.DeathFog - FrameTime() / 5, 0)
	end

	local health = MySelf:Health()
	if self.PrevHealth and health < self.PrevHealth then
		self.HurtEffect = math.min(self.HurtEffect + (self.PrevHealth - health) * 0.02, 1.5)
	elseif self.HurtEffect > 0 then
		self.HurtEffect = math.max(0, self.HurtEffect - FrameTime() * 0.65)
	end
	self.PrevHealth = health

	self:TrackLastDeath()

	local endtime = self:GetWaveActive() and self:GetWaveEnd() or self:GetWaveStart()
	if endtime ~= -1 then
		local timleft = math.max(0, endtime - time)
		if timleft <= 10 and lastwarntim ~= math.ceil(timleft) then
			lastwarntim = math.ceil(timleft)
			if 0 < lastwarntim then
				LocalPlayer():EmitSound("buttons/lightswitch2.wav", 100, 110 - lastwarntim * 2)
			end
		end
	end

	local myteam = P_Team(MySelf)

	self:PlayBeats(myteam, self:CachedFearPower())

	local thirdperson
	if myteam == TEAM_HUMAN then
		local wep = MySelf:GetActiveWeapon()
		if wep:IsValid() and wep.GetIronsights and wep:GetIronsights() then
			self.FOVLerp = math.Approach(self.FOVLerp, wep.IsScoped and not self.DisableScopes and wep.IronsightsMultiplier or not wep.IsScoped and wep.IronsightsMultiplier or 0.6, FrameTime() * 4)
		elseif self.FOVLerp ~= 1 then
			self.FOVLerp = math.Approach(self.FOVLerp, 1, FrameTime() * 5)
		end

		if MySelf:GetBarricadeGhosting() then
			MySelf:BarricadeGhostingThink()
		end
		thirdperson = self.OverTheShoulder
	else
		self.HeartBeatTime = self.HeartBeatTime + (6 + self:CachedFearPower() * 5) * FrameTime()
		thirdperson = self.ZombieThirdPerson

		--[[if not MySelf:Alive() then
			self:ToggleZombieVision(false)
		end]]
	end
	self.TransparencyRadius = thirdperson and self.TransparencyRadius3p or self.TransparencyRadius1p

	local tab

	for _, pl in pairs(player.GetAll()) do
		if P_Team(pl) == TEAM_UNDEAD then
			tab = pl:GetZombieClassTable()
			if tab.BuildBonePositions then
				if not pl.WasBuildingBonePositions then
					pl.BuildingBones = pl:GetBoneCount() - 1
					pl.WasBuildingBonePositions = true
				end
				pl:ResetBones()
				tab.BuildBonePositions(tab, pl)
			elseif pl.WasBuildingBonePositions then
				pl.WasBuildingBonePositions = nil
				pl:ResetBones()
			end
		elseif pl.WasBuildingBonePositions then
			pl.WasBuildingBonePositions = nil
			pl:ResetBones()
		end
	end
end

function GM:ShouldPlayBeats(teamid, fear)
	return not self.RoundEnded and not self.ZombieEscape and not GetGlobalBool("beatsdisabled", false)
end

local cv_ShouldPlayMusic = CreateClientConVar("zs_playmusic", 1, true, false)
local NextBeat = 0
local LastBeatLevel = 0
function GM:PlayBeats(teamid, fear)
	if RealTime() <= NextBeat or not gamemode.Call("ShouldPlayBeats", teamid, fear) then return end

	--if (LASTHUMAN or self:GetAllSigilsDestroyed()) and cv_ShouldPlayMusic:GetBool() then
	if LASTHUMAN and cv_ShouldPlayMusic:GetBool() then
		MySelf:EmitSound(self.LastHumanSound, 0, 100, self.BeatsVolume)
		NextBeat = RealTime() + SoundDuration(self.LastHumanSound) - 0.025
		return
	end

	if fear <= 0 or not self.BeatsEnabled then return end

	local beats = self.Beats[teamid == TEAM_HUMAN and self.BeatSetHuman or self.BeatSetZombie]
	if not beats then return end

	LastBeatLevel = math.Approach(LastBeatLevel, math.ceil(fear * 10), 3)

	local snd = beats[LastBeatLevel]
	if snd then
		MySelf:EmitSound(snd, 0, 100, self.BeatsVolume)
		NextBeat = RealTime() + (self.SoundDuration[snd] or SoundDuration(snd)) - 0.025
	end
end

local colPackUp = Color(20, 255, 20, 220)
local colPackUpNotOwner = Color(255, 240, 10, 220)
function GM:DrawPackUpBar(x, y, fraction, notowner, screenscale)
	local col = notowner and colPackUpNotOwner or colPackUp

	local maxbarwidth = 270 * screenscale
	local barheight = 11 * screenscale
	local barwidth = maxbarwidth * math.Clamp(fraction, 0, 1)
	local startx = x - maxbarwidth * 0.5

	surface_SetDrawColor(0, 0, 0, 220)
	surface_DrawRect(startx, y, maxbarwidth, barheight)
	surface_SetDrawColor(col)
	surface_DrawRect(startx + 3, y + 3, barwidth - 6, barheight - 6)
	surface_DrawOutlinedRect(startx, y, maxbarwidth, barheight)

	draw_SimpleText(notowner and CurTime() % 2 < 1 and translate.Format("requires_x_people", 4) or notowner and translate.Get("packing_others_object") or translate.Get("packing"), "ZSHUDFontSmall", x, y - draw_GetFontHeight("ZSHUDFontSmall") - 2, col, TEXT_ALIGN_CENTER)
end

local colSigilTeleport = Color(125, 215, 255, 220)
function GM:DrawSigilTeleportBar(x, y, fraction, target, screenscale)
	local maxbarwidth = 270 * screenscale
	local barheight = 11 * screenscale
	local barwidth = maxbarwidth * math.Clamp(fraction, 0, 1)
	local startx = x - maxbarwidth * 0.5

	local letter = "?"
	for i, sigil in pairs(ents.FindByClass("prop_obj_sigil")) do
		if target == sigil then
			letter = string.char(64 + i)
			break
		end
	end

	surface_SetDrawColor(0, 0, 0, 220)
	surface_DrawRect(startx, y, maxbarwidth, barheight)
	surface_SetDrawColor(colSigilTeleport)
	surface_DrawRect(startx + 3, y + 3, barwidth - 6, barheight - 6)
	surface_DrawOutlinedRect(startx, y, maxbarwidth, barheight)

	draw_SimpleText(translate.Format("teleporting_to_sigil", letter), "ZSHUDFontSmall", x, y - draw_GetFontHeight("ZSHUDFontSmall") - 2, colSigilTeleport, TEXT_ALIGN_CENTER)
	draw_SimpleText(translate.Get("press_shift_to_cancel"), "ZSHUDFontSmaller", x, y + draw_GetFontHeight("ZSHUDFontSmaller") - 16, colSigilTeleport, TEXT_ALIGN_CENTER)
	draw_SimpleText(translate.Get("point_at_a_sigil_to_choose_destination"), "ZSHUDFontSmaller", x, y + draw_GetFontHeight("ZSHUDFontSmaller") * 2 - 16, colSigilTeleport, TEXT_ALIGN_CENTER)
end

function GM:HumanHUD(screenscale)
	local curtime = CurTime()
	local w, h = ScrW(), ScrH()

	local packup = MySelf.PackUp
	local sigiltp = MySelf.SigilTeleport
	if packup and packup:IsValid() then
		self:DrawPackUpBar(w * 0.5, h * 0.55, 1 - packup:GetTimeRemaining() / packup:GetMaxTime(), packup:GetNotOwner(), screenscale)
	elseif sigiltp and sigiltp:IsValid() then
		self:DrawSigilTeleportBar(w * 0.5, h * 0.55, 1 - sigiltp:GetTimeRemaining() / sigiltp:GetMaxTime(), sigiltp:GetTargetSigil(), screenscale)
	end

	if not self.RoundEnded then
		if self:GetWave() == 0 and not self:GetWaveActive() then
			local txth = draw_GetFontHeight("ZSHUDFontSmall")
			local desiredzombies = self:GetDesiredStartingZombies()

			draw_SimpleTextBlurry(translate.Get("waiting_for_players").." "..util.ToMinutesSecondsCD(math.max(0, self:GetWaveStart() - curtime)), "ZSHUDFontSmall", w * 0.5, h * 0.25, COLOR_GRAY, TEXT_ALIGN_CENTER)

			if desiredzombies > 0 then
				draw_SimpleTextBlurry(translate.Get(self:HasSigils() and "humans_furthest_from_sigils_are_zombies" or "humans_closest_to_spawns_are_zombies"), "ZSHUDFontSmall", w * 0.5, h * 0.25 + txth, COLOR_GRAY, TEXT_ALIGN_CENTER)
				draw_SimpleTextBlurry(translate.Format("number_of_initial_zombies_this_game", self.WaveOneZombies * 100, desiredzombies), "ZSHUDFontSmall", w * 0.5, h * 0.7, COLOR_GRAY, TEXT_ALIGN_CENTER)

				for i, pl in ipairs(self.ZombieVolunteers) do
					if pl:IsValid() then
						draw_SimpleTextBlurry(translate.Get("zombie_volunteers"), "ZSHUDFontSmall", w * 0.5, h * 0.7 + txth, COLOR_GRAY, TEXT_ALIGN_CENTER)
						break
					end
				end

				local y = h * 0.7 + txth * 1.9
				txth = draw_GetFontHeight("ZSHUDFontTiny")
				for i, pl in ipairs(self.ZombieVolunteers) do
					if pl:IsValid() then
						draw_SimpleTextBlurry(pl:Name(), "ZSHUDFontTiny", w * 0.5, y, pl == MySelf and COLOR_SOFTRED or COLOR_GRAY, TEXT_ALIGN_CENTER)
						y = y + txth * 0.8
					end
				end
			end
		end

		local drown = MySelf.status_drown
		if drown and drown:IsValid() then
			surface_SetDrawColor(0, 0, 0, 60)
			surface_DrawRect(w * 0.4, h * 0.35, w * 0.2, 12)
			surface_SetDrawColor(30, 30, 230, 180)
			surface_DrawOutlinedRect(w * 0.4, h * 0.35, w * 0.2, 12)
			surface_DrawRect(w * 0.4, h * 0.35, w * 0.2 * (1 - drown:GetDrown()), 12)
			draw_SimpleTextBlurry(translate.Get("breath").." ", "ZSHUDFontSmall", w * 0.4, h * 0.35 + 6, COLOR_LBLUE, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER)
		end
	end

	local lockon = self.HumanMenuLockOn
	if lockon and self:ValidMenuLockOnTarget(MySelf, lockon) then
		local txth = draw_GetFontHeight("ZSHUDFontSmall")
		draw_SimpleTextBlurry(translate.Format("giving_items_to", lockon:Name()), "ZSHUDFontSmall", w * 0.5, h * 0.55 + txth, COLOR_GRAY, TEXT_ALIGN_CENTER)
	end

	if gamemode.Call("PlayerCanPurchase", MySelf) then
		draw_SimpleTextBlurry(translate.Get("press_f2_for_the_points_shop"), "ZSHUDFontSmall", w * 0.5, screenscale * 135, COLOR_GRAY, TEXT_ALIGN_CENTER)
	end
end

function GM:_HUDPaint()
	if self.FilmMode then return end

	local screenscale = BetterScreenScale()
	local myteam = P_Team(MySelf)

	self:HUDDrawTargetID(myteam, screenscale)

	if self:GetWave() > 0 then
		self:DrawFearMeter(self:CachedFearPower(), screenscale)
	end

	if myteam == TEAM_UNDEAD then
		self:ZombieHUD()
	elseif myteam == TEAM_HUMAN then
		self:HumanHUD(screenscale)
	end

	if GetGlobalBool("classicmode") then
		draw_SimpleTextBlurry(translate.Get("classic_mode"), "ZSHUDFontSmaller", 4, ScrH() - 4, COLOR_GRAY, TEXT_ALIGN_LEFT, TEXT_ALIGN_BOTTOM_REAL)
	end
end

function GM:ZombieObserverHUD(obsmode)
	local w, h = ScrW(), ScrH()
	local texh = draw_GetFontHeight("ZSHUDFontSmall")

	if obsmode == OBS_MODE_CHASE then
		local target = MySelf:GetObserverTarget()
		if target and target:IsValid() then
			if target:IsPlayer() and P_Team(target) == TEAM_UNDEAD then
				draw_SimpleTextBlur(translate.Format("observing_x", target:Name(), math.max(0, target:Health())), "ZSHUDFontSmall", w * 0.5, h * 0.75 - texh - 32, COLOR_DARKRED, TEXT_ALIGN_CENTER)
			end

			if target.IsCreeperNest or target.MinionSpawn then
				local txt = target.IsCreeperNest and "Nest" or "Gore Child"

				draw_SimpleTextBlur(translate.Format("observing_x_simple", txt), "ZSHUDFontSmall", w * 0.5, h * 0.75 - texh - 32, COLOR_DARKRED, TEXT_ALIGN_CENTER)
			end

			dyn = self:GetDynamicSpawning() and self:DynamicSpawnIsValid(target)
		end
	end

	local space = texh + 2
	local x, y = w / 2, h * 0.68

	if self:GetWaveActive() then
		draw_SimpleTextBlurry(translate.Get("press_lmb_to_spawn"), "ZSHUDFontSmall", x, y, COLOR_GRAY, TEXT_ALIGN_CENTER)
		draw_SimpleTextBlurry(translate.Get("press_rmb_to_spawn_close"), "ZSHUDFontSmall", x, y + space, COLOR_GRAY, TEXT_ALIGN_CENTER)
		draw_SimpleTextBlurry(translate.Get("press_reload_to_spawn_far"), "ZSHUDFontSmall", x, y + space * 2, COLOR_GRAY, TEXT_ALIGN_CENTER)
		draw_SimpleTextBlurry(translate.Get("press_alt_nest_menu"), "ZSHUDFontSmaller", x, y + space * 4, COLOR_GRAY, TEXT_ALIGN_CENTER)
	end

	draw_SimpleTextBlurry(translate.Get("press_jump_to_free_roam"), "ZSHUDFontSmall", x, y + space * 3, COLOR_GRAY, TEXT_ALIGN_CENTER)
end

local colLifeStats = Color(255, 50, 50, 255)
function GM:ZombieHUD()
	if self.LifeStatsEndTime and CurTime() < self.LifeStatsEndTime and (self.LifeStatsBarricadeDamage > 0 or self.LifeStatsHumanDamage > 0 or self.LifeStatsBrainsEaten > 0) then
		colLifeStats.a = math.Clamp((self.LifeStatsEndTime - CurTime()) / (self.LifeStatsLifeTime * 0.33), 0, 1) * 255

		local th = draw_GetFontHeight("ZSHUDFontSmall")
		local x = ScrW() * 0.75
		local y = ScrH() * 0.75

		draw_SimpleTextBlur(translate.Get("that_life"), "ZSHUDFontSmall", x, y, colLifeStats, TEXT_ALIGN_LEFT)
		y = y + th

		if self.LifeStatsBarricadeDamage > 0 then
			draw_SimpleTextBlur(translate.Format("x_damage_to_barricades", self.LifeStatsBarricadeDamage), "ZSHUDFontSmall", x, y, colLifeStats, TEXT_ALIGN_LEFT)
			y = y + th
		end
		if self.LifeStatsHumanDamage > 0 then
			draw_SimpleTextBlur(translate.Format("x_damage_to_humans", self.LifeStatsHumanDamage), "ZSHUDFontSmall", x, y, colLifeStats, TEXT_ALIGN_LEFT)
			y = y + th
		end
		if self.LifeStatsBrainsEaten > 0 then
			draw_SimpleTextBlur(translate.Format("x_brains_eaten", self.LifeStatsBrainsEaten), "ZSHUDFontSmall", x, y, colLifeStats, TEXT_ALIGN_LEFT)
			y = y + th
		end
	end

	local obsmode = MySelf:GetObserverMode()
	if obsmode ~= OBS_MODE_NONE then
		self:ZombieObserverHUD(obsmode)
	elseif not MySelf:Alive() then
		local x = ScrW() * 0.5
		local y = ScrH() * 0.3

		if not self:GetWaveActive() then
			draw_SimpleTextBlur(translate.Get("waiting_for_next_wave"), "ZSHUDFont", x, y, COLOR_DARKRED, TEXT_ALIGN_CENTER)
		end
	end

	if not self:GetWaveActive() and self:GetWave() ~= 0 then
		local pl = GAMEMODE.NextBossZombie
		if pl and pl:IsValid() then
			local x, y = ScrW() / 2, ScrH() * 0.3 + draw_GetFontHeight("ZSHUDFont")
			if pl == MySelf then
				draw_SimpleTextBlur(translate.Format("you_will_be_x_soon", translate.Get(GAMEMODE.ZombieClasses[GAMEMODE.NextBossZombieClass].TranslationName)), "ZSHUDFont", x, y, Color(255, 50, 50), TEXT_ALIGN_CENTER)
			else
				draw_SimpleTextBlur(translate.Format("x_will_be_y_soon", pl:Name(), translate.Get(GAMEMODE.ZombieClasses[GAMEMODE.NextBossZombieClass].TranslationName)), "ZSHUDFont", x, y, COLOR_GRAY, TEXT_ALIGN_CENTER)
			end
		end
	end
end

function GM:RequestedDefaultCart()
	local defaultcart = GetConVar("zs_defaultcart"):GetString()
	if #defaultcart > 0 then
		defaultcart = string.lower(defaultcart)

		for i, carttab in ipairs(self.SavedCarts) do
			if carttab[1] and string.lower(carttab[1]) == defaultcart then
				gamemode.Call("SuppressArsenalUpgrades", 1)
				RunConsoleCommand("worthcheckout", unpack(carttab[2]))

				return
			end
		end

		RunConsoleCommand("worthrandom")
	end
end

function GM:_PostDrawTranslucentRenderables()
	if not self.DrawingInSky then
		self:DrawPointWorldHints()
		self:DrawWorldHints()
		self:DrawSigilIndicators()
		self:DrawCrateIndicators()
		self:DrawResupplyIndicators()
		self:DrawRemantlerIndicators()
		self:DrawHumanIndicators()
		self:DrawNestIndicators()
	end
end

function GM:DrawCrateIndicators()
	if P_Team(MySelf) ~= TEAM_HUMAN or not MySelf:IsSkillActive(SKILL_INSIGHT) then return end

	local pos, distance, ang, deployable, alpha
	local eyepos = EyePos()

	surface_SetMaterial(matArsenal)

	for i, arsenal in pairs(GAMEMODE.CachedArsenalEntities) do
		if not arsenal:IsValid() then continue end
		deployable = arsenal.GetObjectOwner

		pos = arsenal:GetPos()
		pos.z = pos.z + (arsenal:IsPlayer() and 32 or (deployable and 12 or -8))
		distance = eyepos:DistToSqr(pos)

		if (distance >= 6400 and distance <= 1048576) and (not deployable or not WorldVisible(eyepos, pos)) then -- Limited to Scavenger's Eyes distance.
			ang = (eyepos - pos):Angle()
			ang:RotateAroundAxis(ang:Right(), 270)
			ang:RotateAroundAxis(ang:Up(), 90)
			alpha = math.min(220, math.sqrt(distance / 4))

			cam_IgnoreZ(true)
			cam_Start3D2D(pos, ang, math.max(250, math.sqrt(distance)) / 5000)

			surface_SetDrawColor(255, 255, 255, alpha)
			surface_DrawTexturedRect(-123, -113, 248, 228)

			draw_SimpleTextBlurry("Arsenal Crate", "ZS3D2DFont2Big", 0, 128, COLOR_GRAY, TEXT_ALIGN_CENTER)

			cam_End3D2D()
			cam_IgnoreZ(false)
		end
	end
end

function GM:DrawResupplyIndicators()
	if P_Team(MySelf) ~= TEAM_HUMAN or not MySelf:IsSkillActive(SKILL_ACUITY) then return end

	local pos, distance, ang, deployable, alpha
	local eyepos = EyePos()

	surface_SetMaterial(matResupply)

	for i, resupply in pairs(GAMEMODE.CachedResupplyEntities) do
		if not resupply:IsValid() then continue end
		deployable = resupply.GetObjectOwner

		pos = resupply:GetPos()
		pos.z = pos.z + (resupply:IsPlayer() and 32 or (deployable and 12 or -8))
		distance = eyepos:DistToSqr(pos)

		if (distance >= 6400 and distance <= 1048576) and (not deployable or not WorldVisible(eyepos, pos)) then -- Limited to Scavenger's Eyes distance.
			ang = (eyepos - pos):Angle()
			ang:RotateAroundAxis(ang:Right(), 270)
			ang:RotateAroundAxis(ang:Up(), 90)
			alpha = math.min(220, math.sqrt(distance / 4))

			cam_IgnoreZ(true)
			cam_Start3D2D(pos, ang, math.max(250, math.sqrt(distance)) / 5000)

			surface_SetDrawColor(255, 255, 255, alpha)
			surface_DrawTexturedRect(-128, -128, 256, 256)

			local timeremain = math.ceil(math.max(0, (MySelf.NextUse or 0) - CurTime()))
			local txt = not MySelf.NextUse and translate.Get("ready") or timeremain > 0 and timeremain or translate.Get("ready")
			draw_SimpleTextBlurry(txt, "ZS3D2DFont2Big", 0, 128, COLOR_GRAY, TEXT_ALIGN_CENTER)

			cam_End3D2D()
			cam_IgnoreZ(false)
		end
	end
end

function GM:DrawRemantlerIndicators()
	if P_Team(MySelf) ~= TEAM_HUMAN or not MySelf:IsSkillActive(SKILL_VISION) then return end

	local pos, distance, ang, deployable, alpha
	local eyepos = EyePos()

	surface_SetMaterial(matRemantler)

	for i, remantler in pairs(GAMEMODE.CachedRemantlerEntities) do
		if not remantler:IsValid() then continue end
		deployable = remantler.GetObjectOwner

		pos = remantler:GetPos()
		pos.z = pos.z + (remantler:IsPlayer() and 32 or (deployable and 12 or -8))
		distance = eyepos:DistToSqr(pos)

		if (distance >= 6400 and distance <= 1048576) and (not deployable or not WorldVisible(eyepos, pos)) then -- Limited to Scavenger's Eyes distance.
			ang = (eyepos - pos):Angle()
			ang:RotateAroundAxis(ang:Right(), 270)
			ang:RotateAroundAxis(ang:Up(), 90)
			alpha = math.min(220, math.sqrt(distance / 4))

			cam_IgnoreZ(true)
			cam_Start3D2D(pos, ang, math.max(250, math.sqrt(distance)) / 5000)

			surface_SetDrawColor(255, 255, 255, alpha)
			surface_DrawTexturedRect(-128, -128, 256, 256)

			draw_SimpleTextBlurry("Weapon Remantler", "ZS3D2DFont2Big", 0, 128, COLOR_GRAY, TEXT_ALIGN_CENTER)

			cam_End3D2D()
			cam_IgnoreZ(false)
		end
	end
end

function GM:DrawNestIndicators()
	if P_Team(MySelf) ~= TEAM_ZOMBIE then return end

	local pos, distance, ang, alpha
	local eyepos = EyePos()

	surface_SetMaterial(matNest)

	for i, nest in pairs(GAMEMODE.CachedNests) do
		if not nest:IsValid() then continue end

		pos = nest:GetPos()
		pos.z = pos.z + 32
		distance = eyepos:DistToSqr(pos)

		ang = (eyepos - pos):Angle()
		ang:RotateAroundAxis(ang:Right(), 270)
		ang:RotateAroundAxis(ang:Up(), 90)
		alpha = math.min(220, math.sqrt(distance / 4))

		cam_IgnoreZ(true)
		cam_Start3D2D(pos, ang, math.max(250, math.sqrt(distance)) / 5000)

		surface_SetDrawColor(255, 255, 255, alpha)
		surface_DrawTexturedRect(-128, -128, 256, 256)

		draw_SimpleTextBlurry("Nest", "ZS3D2DFont2Big", 0, 128, COLOR_GRAY, TEXT_ALIGN_CENTER)

		if distance < 80000 then
			local nown = nest:GetNestOwner()
			local ownname = nown:IsValidZombie() and nown:ClippedName() or ""

			draw_SimpleTextBlurry(ownname, "ZS3D2DFont2", 0, 256, COLOR_GRAY, TEXT_ALIGN_CENTER)
		end

		cam_End3D2D()
		cam_IgnoreZ(false)
	end
end

function GM:DrawSigilIndicators()
	if not self:GetUseSigils() then return end

	local health, pos, distance, maxhealth, corrupted, damageflash, missinghealthfrac, ang, alpha
	local eyepos = EyePos()

	surface_SetMaterial(matSigil)

	for i, sigil in pairs(GAMEMODE.CachedSigils) do
		if not sigil:IsValid() then continue end

		health = sigil:GetSigilHealth()
		if health > 0 then
			pos = sigil:GetPos()
			pos.z = pos.z + 48
			distance = eyepos:DistToSqr(pos)

			maxhealth = sigil:GetSigilMaxHealth()
			corrupted = sigil:GetSigilCorrupted()
			damageflash = math.min((CurTime() - sigil:GetSigilLastDamaged()) * 2, 1) * 255
			missinghealthfrac = 1 - health / maxhealth
			alpha = math.min(220, math.sqrt(distance / 4))

			ang = (eyepos - pos):Angle()
			ang:RotateAroundAxis(ang:Right(), 270)
			ang:RotateAroundAxis(ang:Up(), 90)

			cam_IgnoreZ(true)
			cam_Start3D2D(pos, ang, math.max(250, math.sqrt(distance)) / 5000)
			local oldfogmode = render_GetFogMode()
			render_FogMode(0)

			if corrupted then
				surface_SetDrawColor(255 - damageflash, damageflash, 0, alpha)
			else
				surface_SetDrawColor(damageflash, 255, damageflash, alpha)
			end
			surface_DrawTexturedRect(-64, -128, 128, 256)
			if missinghealthfrac > 0 then
				surface_SetDrawColor(40, 40, 40, 255)
				surface_DrawTexturedRectUV(-64, -128, 128, 256 * missinghealthfrac, 0, 0, 1, missinghealthfrac)
			end

			draw_SimpleTextBlurry(string.char(64 + i), "ZS3D2DFont2Big", 0, 128, COLOR_GRAY, TEXT_ALIGN_CENTER)

			render_FogMode(oldfogmode)
			cam_End3D2D()
			cam_IgnoreZ(false)
		end
	end
end

function GM:RestartRound()
	self.TheLastHuman = nil
	self.RoundEnded = nil
	LASTHUMAN = nil

	if pEndBoard and pEndBoard:IsValid() then
		pEndBoard:Remove()
		pEndBoard = nil
	end

	self:ClearItemStocks()

	self:InitPostEntity()

	self:RevertZombieClasses()
end

function GM:_HUDShouldDraw(name)
	if self.FilmMode and name ~= "CHudWeaponSelection" then return false end

	return name ~= "CHudHealth" and name ~= "CHudBattery"
	and name ~= "CHudAmmo" and name ~= "CHudSecondaryAmmo"
	and name ~= "CHudDamageIndicator"
end

local Current = 0
local NextCalculate = 0
function GM:_CachedFearPower()
	if CurTime() >= NextCalculate then
		NextCalculate = CurTime() + 0.15
		Current = self:GetFearMeterPower(EyePos(), TEAM_UNDEAD, MySelf)
	end

	return Current
end

function surface.CreateLegacyFont(font, size, weight, antialias, additive, name, shadow, outline, blursize)
	surface.CreateFont(name, {font = font, size = size, weight = weight, antialias = antialias, additive = additive, shadow = shadow, outline = outline, blursize = blursize})
end

local fontfamily = "Ghoulish Fright AOE"
local fontfamilysm = "Remington Noiseless"
local fontfamily3d = "hidden"
local fontsizeadd = 8
local fontweight = 0

function GM:Create3DFonts()
	local fontsizeadd3D = 0
	local fontweight3D = 0

	surface.CreateLegacyFont(fontfamily3d, 28 + fontsizeadd3D, fontweight3D, false, false,  "ZS3D2DFontSmaller", false, true)
	surface.CreateLegacyFont(fontfamily3d, 48 + fontsizeadd3D, fontweight3D, false, false,  "ZS3D2DFontSmall", false, true)
	surface.CreateLegacyFont(fontfamily3d, 72 + fontsizeadd3D, fontweight3D, false, false, "ZS3D2DFont", false, true)
	surface.CreateLegacyFont(fontfamily3d, 128 + fontsizeadd3D, fontweight3D, false, false, "ZS3D2DFontBig", false, true)
	surface.CreateLegacyFont(fontfamily3d, 28 + fontsizeadd3D, fontweight3D, false, false,  "ZS3D2DFontSmallerBlur", false, false, 16)
	surface.CreateLegacyFont(fontfamily3d, 48 + fontsizeadd3D, fontweight3D, false, false,  "ZS3D2DFontSmallBlur", false, false, 16)
	surface.CreateLegacyFont(fontfamily3d, 72 + fontsizeadd3D, fontweight3D, false, false, "ZS3D2DFontBlur", false, false, 16)
	surface.CreateLegacyFont(fontfamily3d, 128 + fontsizeadd3D, fontweight3D, false, false, "ZS3D2DFontBigBlur", false, false, 16)
	surface.CreateLegacyFont(fontfamily, 40 + fontsizeadd3D, fontweight3D, false, false,  "ZS3D2DFont2Smaller", false, true)
	surface.CreateLegacyFont(fontfamily, 48 + fontsizeadd3D, fontweight3D, false, false,  "ZS3D2DFont2Small", false, true)
	surface.CreateLegacyFont(fontfamily, 72 + fontsizeadd3D, fontweight3D, false, false, "ZS3D2DFont2", false, true)
	surface.CreateLegacyFont(fontfamily, 128 + fontsizeadd3D, fontweight3D, false, false, "ZS3D2DFont2Big", false, true)
	surface.CreateLegacyFont(fontfamily, 40 + fontsizeadd3D, fontweight3D, false, false,  "ZS3D2DFont2SmallerBlur", false, false, 16)
	surface.CreateLegacyFont(fontfamily, 48 + fontsizeadd3D, fontweight3D, false, false,  "ZS3D2DFont2SmallBlur", false, false, 16)
	surface.CreateLegacyFont(fontfamily, 72 + fontsizeadd3D, fontweight3D, false, false, "ZS3D2DFont2Blur", false, false, 16)
	surface.CreateLegacyFont(fontfamily, 128 + fontsizeadd3D, fontweight3D, false, false, "ZS3D2DFont2BigBlur", false, false, 16)

	surface.CreateLegacyFont(fontfamilysm, 14 + fontsizeadd3D, fontweight3D, false, false,  "ZS3D2DUnstyleTiny", false, true)
	surface.CreateLegacyFont(fontfamilysm, 24 + fontsizeadd3D, fontweight3D, false, false,  "ZS3D2DUnstyleSmallest", false, true)
	surface.CreateLegacyFont(fontfamilysm, 36 + fontsizeadd3D, fontweight3D, false, false,  "ZS3D2DUnstyleSmaller", false, true)
end

function GM:CreateNonScaleFonts()
	surface.CreateLegacyFont("tahoma", 96, 1000, true, false, "zshintfont", false, true)

	-- Default, DefaultBold, DefaultSmall, etc. were changed when gmod13 hit. These are renamed fonts that have the old values.
	surface.CreateFont("DefaultFontVerySmall", {font = "tahoma", size = 10, weight = 0, antialias = false})
	surface.CreateFont("DefaultFontSmall", {font = "tahoma", size = 11, weight = 0, antialias = false})
	surface.CreateFont("DefaultFontSmallDropShadow", {font = "tahoma", size = 11, weight = 0, shadow = true, antialias = false})
	surface.CreateFont("DefaultFont", {font = "tahoma", size = 13, weight = 500, antialias = false})
	surface.CreateFont("DefaultFontAA", {font = "tahoma", size = 13, weight = 500, antialias = true})
	surface.CreateFont("DefaultFontBold", {font = "tahoma", size = 13, weight = 1000, antialias = false})
	surface.CreateFont("DefaultFontLarge", {font = "tahoma", size = 16, weight = 0, antialias = false})
	surface.CreateFont("DefaultFontLargeAA", {font = "tahoma", size = 16, weight = 0, antialias = true})
	surface.CreateFont("DefaultFontLargest", {font = "tahoma", size = 22, weight = 0, antialias = false})
	surface.CreateFont("DefaultFontLargestAA", {font = "tahoma", size = 22, weight = 0, antialias = true})
end

function GM:CreateScalingFonts()
	local fontaa = true
	local fontshadow = false
	local fontoutline = true

	local screenscale = BetterScreenScale()

	surface.CreateLegacyFont("csd", screenscale * 42, 100, true, false, "zsdeathnoticecs", false, false)
	surface.CreateLegacyFont("HL2MP", screenscale * 42, 100, true, false, "zsdeathnotice", false, false)

	surface.CreateLegacyFont("csd", screenscale * 96, 100, true, false, "zsdeathnoticecsws", false, false)
	surface.CreateLegacyFont("HL2MP", screenscale * 96, 100, true, false, "zsdeathnoticews", false, false)

	surface.CreateLegacyFont("csd", screenscale * 72, 100, true, false, "zsdeathnoticecspa", false, false)
	surface.CreateLegacyFont("HL2MP", screenscale * 72, 100, true, false, "zsdeathnoticepa", false, false)

	surface.CreateLegacyFont(fontfamily, screenscale * (16 + fontsizeadd), fontweight, fontaa, false, "ZSHUDFontTiny", fontshadow, fontoutline)
	surface.CreateLegacyFont(fontfamily, screenscale * (20 + fontsizeadd), fontweight, fontaa, false, "ZSHUDFontSmallest", fontshadow, fontoutline)
	surface.CreateLegacyFont(fontfamily, screenscale * (22 + fontsizeadd), fontweight, fontaa, false, "ZSHUDFontSmaller", fontshadow, fontoutline)
	surface.CreateLegacyFont(fontfamily, screenscale * (28 + fontsizeadd), fontweight, fontaa, false, "ZSHUDFontSmall", fontshadow, fontoutline)
	surface.CreateLegacyFont(fontfamily, screenscale * (42 + fontsizeadd), fontweight, fontaa, false, "ZSHUDFont", fontshadow, fontoutline)
	surface.CreateLegacyFont(fontfamily, screenscale * (72 + fontsizeadd), fontweight, fontaa, false, "ZSHUDFontBig", fontshadow, fontoutline)
	surface.CreateLegacyFont(fontfamily, screenscale * (16 + fontsizeadd), fontweight, fontaa, false, "ZSHUDFontTinyBlur", false, false, 8)
	surface.CreateLegacyFont(fontfamily, screenscale * (22 + fontsizeadd), fontweight, fontaa, false, "ZSHUDFontSmallerBlur", false, false, 8)
	surface.CreateLegacyFont(fontfamily, screenscale * (28 + fontsizeadd), fontweight, fontaa, false, "ZSHUDFontSmallBlur", false, false, 8)
	surface.CreateLegacyFont(fontfamily, screenscale * (42 + fontsizeadd), fontweight, fontaa, false, "ZSHUDFontBlur", false, false, 8)
	surface.CreateLegacyFont(fontfamily, screenscale * (72 + fontsizeadd), fontweight, fontaa, false, "ZSHUDFontBigBlur", false, false, 8)

	surface.CreateLegacyFont(fontfamily, screenscale * (20 + fontsizeadd/2), 0, fontaa, false, "ZSAmmoName", false, false)

	local liscreenscale = math.max(0.95, BetterScreenScale())

	surface.CreateLegacyFont(fontfamily, liscreenscale * (32 + fontsizeadd), fontweight, true, false, "ZSScoreBoardTitle", false, true)
	surface.CreateLegacyFont(fontfamily, liscreenscale * (22 + fontsizeadd), fontweight, true, false, "ZSScoreBoardSubTitle", false, true)
	surface.CreateLegacyFont(fontfamily, liscreenscale * (16 + fontsizeadd), fontweight, true, false, "ZSScoreBoardPlayer", false, true)
	surface.CreateLegacyFont(fontfamily, liscreenscale * (24 + fontsizeadd), fontweight, true, false, "ZSScoreBoardHeading", false, false)
	surface.CreateLegacyFont("arial", 18 * liscreenscale, 0, true, false, "ZSScoreBoardPlayerSmall", false, true)
	surface.CreateLegacyFont("arial", 15 * liscreenscale, 0, true, false, "ZSScoreBoardPlayerSmaller", false, true)
	surface.CreateLegacyFont("tahoma", 11 * liscreenscale, 0, true, false, "ZSScoreBoardPing")

	surface.CreateLegacyFont(fontfamily, screenscale * (16 + fontsizeadd), fontweight, fontaa, false, "ZSHUDFontTinyNS", false, false)
	surface.CreateLegacyFont(fontfamily, screenscale * (20 + fontsizeadd), fontweight, fontaa, false, "ZSHUDFontSmallestNS", false, false)
	surface.CreateLegacyFont(fontfamily, screenscale * (22 + fontsizeadd), fontweight, fontaa, false, "ZSHUDFontSmallerNS", false, false)
	surface.CreateLegacyFont(fontfamily, screenscale * (28 + fontsizeadd), fontweight, fontaa, false, "ZSHUDFontSmallNS", false, false)
	surface.CreateLegacyFont(fontfamily, screenscale * (42 + fontsizeadd), fontweight, fontaa, false, "ZSHUDFontNS", false, false)
	surface.CreateLegacyFont(fontfamily, screenscale * (72 + fontsizeadd), fontweight, fontaa, false, "ZSHUDFontBigNS", false, false)

	surface.CreateLegacyFont(fontfamilysm, screenscale * 13, fontweight, fontaa, false, "ZSBodyTextFontSmall", fontshadow)

	surface.CreateLegacyFont(fontfamilysm, screenscale * 15, fontweight, fontaa, false, "ZSBodyTextFont", fontshadow, fontoutline)
	surface.CreateLegacyFont(fontfamilysm, screenscale * 20, fontweight, fontaa, false, "ZSBodyTextFontBig", fontshadow, fontoutline)

	surface.CreateLegacyFont(fontfamily, screenscale * (20 + fontsizeadd), 0, true, false, "ZSDamageResistance", false, true)
	surface.CreateLegacyFont(fontfamily, screenscale * (20 + fontsizeadd), 0, true, false, "ZSDamageResistanceBlur", false, true)

	surface.CreateFont("ZSXPBar", {font = "tahoma", size = screenscale * 14, weight = 500, antialias = false, shadow = true})
end

function GM:CreateFonts()
	self:Create3DFonts()
	self:CreateNonScaleFonts()
	self:CreateScalingFonts()
end

function GM:EvaluateFilmMode()
	local visible = not self.FilmMode

	if self.GameStatePanel and self.GameStatePanel:IsValid() then
		self.GameStatePanel:SetVisible(visible)
	end

	if self.TopNotificationHUD and self.TopNotificationHUD:IsValid() then
		self.TopNotificationHUD:SetVisible(visible)
	end

	if self.CenterNotificationHUD and self.CenterNotificationHUD:IsValid() then
		self.CenterNotificationHUD:SetVisible(visible)
	end

	if self.XPHUD and self.XPHUD:IsValid() then
		self.XPHUD:SetVisible(visible and self.DisplayXPHUD)
	end

	if self.HealthHUD and self.HealthHUD:IsValid() then
		self.HealthHUD:SetVisible(visible)
	end

	if self.StatusHUD and self.StatusHUD:IsValid() then
		self.StatusHUD:SetVisible(visible)
	end
end

function GM:CreateVGUI()
	local screenscale = BetterScreenScale()
	self.GameStatePanel = vgui.Create("ZSGameState")
	self.GameStatePanel:SetTextFont("ZSHUDFontSmaller")
	self.GameStatePanel:SetAlpha(220)
	self.GameStatePanel:SetSize(screenscale * 420, screenscale * 80)
	self.GameStatePanel:ParentToHUD()

	self.TopNotificationHUD = vgui.Create("DEXNotificationsList")
	self.TopNotificationHUD:SetAlign(RIGHT)
	self.TopNotificationHUD.PerformLayout = function(pan)
		pan:SetSize(ScrW() * 0.4, ScrH() * 0.6)
		pan:AlignTop(16 * BetterScreenScale())
		pan:AlignRight()
	end
	self.TopNotificationHUD:InvalidateLayout()
	self.TopNotificationHUD:ParentToHUD()

	self.CenterNotificationHUD = vgui.Create("DEXNotificationsList")
	self.CenterNotificationHUD:SetAlign(CENTER)
	self.CenterNotificationHUD:SetMessageHeight(36)
	self.CenterNotificationHUD.PerformLayout = function(pan)
		pan:SetSize(ScrW() / 2, ScrH() * 0.35)
		pan:CenterHorizontal()
		pan:AlignBottom(16 * BetterScreenScale())
	end
	self.CenterNotificationHUD:InvalidateLayout()
	self.CenterNotificationHUD:ParentToHUD()
end

function GM:CreateLateVGUI()
	if not self.HealthHUD then
		self.HealthHUD = vgui.Create("ZSHealthArea")
	end

	if not self.StatusHUD then
		self.StatusHUD = vgui.Create("ZSStatusArea")
	end

	if not self.XPHUD then
		self.XPHUD = vgui.Create("ZSExperienceHUD")
		self.XPHUD:ParentToHUD()
		self.XPHUD:InvalidateLayout()
	end
end

function GM:Initialize()
	self:FixSkillConnections()
	self:CreateFonts()
	self:PrecacheResources()
	self:CreateVGUI()
	self:InitializeBeats()
	self:AddCustomAmmo()
	self:RegisterFood()
	self:CreateWeaponQualities()
	self:CreateSpriteMaterials()

	-- Not sure if this still crashes, but whatever.
	RunConsoleCommand("r_drawmodeldecals", "0")
	-- Flashlight dynamic lights of other players.
	RunConsoleCommand("r_dynamic", "0")

	self:RefreshMapIsObjective()
end

-- These can be accessed without pointing to the IMaterial by using ! before the material string.
function GM:CreateSpriteMaterials()
	local params = {["$translucent"] = "1", ["$vertexcolor"] = "1", ["$vertexalpha"] = "1"}
	for i=1, 8 do
		params["$basetexture"] = "Decals/blood"..i
		CreateMaterial("sprite_bloodspray"..i, "UnlitGeneric", params)
	end
end

function GM:ShutDown()
	RunConsoleCommand("r_drawmodeldecals", "1")
	RunConsoleCommand("r_dynamic", "1")
end

local function FirstOfGoodType(a)
	local ext

	for _, v in pairs(a) do
		ext = string.sub(v, -4)
		if ext == ".ogg" or ext == ".wav" or ext == ".mp3" then
			return v
		end
	end
end

function GM:InitializeBeats()
	local _, dirs = file.Find("sound/zombiesurvival/beats/*", "GAME")
	for _, dirname in pairs(dirs) do
		if dirname == "none" or dirname == "default" then continue end

		self.Beats[dirname] = {}
		local highestexist
		for i=1, 10 do
			local a, __ = file.Find("sound/zombiesurvival/beats/"..dirname.."/"..i..".*", "GAME")
			local a1 = FirstOfGoodType(a)
			if a1 then
				local filename = "zombiesurvival/beats/"..dirname.."/"..a1
				if file.Exists("sound/"..filename, "GAME") then
					self.Beats[dirname][i] = Sound(filename)
					highestexist = filename

					continue
				end
			end

			if highestexist then
				self.Beats[dirname][i] = highestexist
			end
		end
	end
end

function GM:PlayerDeath(pl, attacker)
end

function GM:ScalePlayerDamage(pl, hitgroup, dmginfo)
end

function GM:LastHuman(pl)
	if not IsValid(pl) then pl = nil end

	self.TheLastHuman = pl

	if not LASTHUMAN then
		LASTHUMAN = true
		timer.Simple(0.5, function() GAMEMODE:LastHumanMessage() end)
	end
end

function GM:LastHumanMessage()
	if self.RoundEnded or not MySelf:IsValid() then return end

	local icon = self.PantsMode and "weapon_zs_legs" or "default"
	if P_Team(MySelf) == TEAM_UNDEAD or not MySelf:Alive() then
		self:CenterNotify({killicon = icon}, {font = "ZSHUDFont"}, " ", COLOR_RED, translate.Get(self.PantsMode and "kick_the_last_human" or "kill_the_last_human"), {killicon = icon})
	else
		self:CenterNotify({font = "ZSHUDFont"}, " ", COLOR_RED, translate.Get("you_are_the_last_human"))
		self:CenterNotify({killicon = icon}, " ", COLOR_RED, translate.Format(self.PantsMode and "x_pants_out_to_get_you" or "x_zombies_out_to_get_you", team.NumPlayers(TEAM_UNDEAD)), {killicon = icon})
	end
end

function GM:PlayerShouldTakeDamage(pl, attacker)
	return pl == attacker or not attacker:IsPlayer() or P_Team(pl) ~= P_Team(attacker) or pl.AllowTeamDamage or attacker.AllowTeamDamage
end

function GM:SetWave(wave)
	SetGlobalInt("wave", wave)
end

local matRing = Material("effects/select_ring")
function GM:DrawCircle(x, y, radius, color)
	surface.SetMaterial(matRing)
	surface.SetDrawColor(color)
	surface.DrawTexturedRect(x - radius, y - radius, radius * 2, radius * 2)
end

local matFilmGrain = Material("zombiesurvival/filmgrain/filmgrain")
function GM:_HUDPaintBackground()
	if self.FilmGrainEnabled and P_Team(MySelf) ~= TEAM_UNDEAD then
		surface_SetMaterial(matFilmGrain)
		surface_SetDrawColor(0, 0, 0, (0.25 + 0.75 * self:CachedFearPower()) * self.FilmGrainOpacity)
		surface_DrawTexturedRectUV(0, 0, ScrW(), ScrH(), 2, 2, 0, 0)
	end

	local wep = MySelf:GetActiveWeapon()
	if wep:IsValid() and wep.DrawHUDBackground then
		wep:DrawHUDBackground()
	end
end

local function GiveWeapon()
	if GAMEMODE.HumanMenuLockOn then
		RunConsoleCommand("zsgiveweapon", GAMEMODE.HumanMenuLockOn:EntIndex(), GAMEMODE.InventoryMenu.SelInv)
	end
end
local function GiveWeaponClip()
	if GAMEMODE.HumanMenuLockOn then
		RunConsoleCommand("zsgiveweaponclip", GAMEMODE.HumanMenuLockOn:EntIndex(), GAMEMODE.InventoryMenu.SelInv)
	end
end
local function DropWeapon()
	RunConsoleCommand("zsdropweapon", GAMEMODE.InventoryMenu.SelInv)
end
local function EmptyClip()
	RunConsoleCommand("zsemptyclip")
end
local function DismantleWeapon()
	RunConsoleCommand("zs_dismantle", GAMEMODE.InventoryMenu.SelInv)
end

local function AltSelItemUpd()
	local activeweapon = MySelf:GetActiveWeapon()
	if not activeweapon or not activeweapon:IsValid() then return end

	local actwclass = activeweapon:GetClass()
	GAMEMODE.HumanMenuPanel.SelectedItemLabel:SetText(weapons.Get(actwclass).PrintName)
end

function GM:DoAltSelectedItemUpdate()
	if self.InventoryMenu.SelInv then
		self.HumanMenuPanel.SelectedItemLabel:SetText(self.ZSInventoryItemData[self.InventoryMenu.SelInv].PrintName)
	else
		timer.Simple(0.25, AltSelItemUpd)
	end
end

function GM:HumanMenu()
	if self.ZombieEscape then return end

	local ent = MySelf:MeleeTrace(48, 2, nil, nil, true).Entity
	if self:ValidMenuLockOnTarget(MySelf, ent) then
		self.HumanMenuLockOn = ent
	else
		self.HumanMenuLockOn = nil
	end

	self:OpenInventory()
	if self.HumanMenuPanel and self.HumanMenuPanel:IsValid() then
		self.HumanMenuPanel:SetVisible(true)
		self.HumanMenuPanel:OpenMenu()

		self:DoAltSelectedItemUpdate()
		return
	end

	local panel = vgui.Create("DSideMenu")
	self.HumanMenuPanel = panel

	local screenscale = BetterScreenScale()
	for k, v in pairs(self.AmmoNames) do
		local b = vgui.Create("DAmmoCounter", panel)
		b:SetAmmoType(k)
		b:SetTall(math.max(32, screenscale * 36))
		panel:AddItem(b)
	end

	local hei = draw_GetFontHeight("ZSHUDFontSmall")

	local selecteditemtitle = EasyLabel(panel, "Selected Item", "ZSHUDFontSmall", color_white)
	selecteditemtitle:SetContentAlignment(5)
	panel:AddItem(selecteditemtitle)

	local selecteditemlabel = EasyLabel(panel, "Fists", "ZSHUDFontSmaller", color_white)
	selecteditemlabel:SetContentAlignment(5)
	panel:AddItem(selecteditemlabel)
	panel.SelectedItemLabel = selecteditemlabel

	local gwbtn = vgui.Create("DButton")
	gwbtn:SetFont("ZSHUDFontSmaller")
	gwbtn:SetText("Give Item")
	gwbtn:SetSize(panel:GetWide() - 8 * screenscale, hei - 4 * screenscale)
	gwbtn:CenterHorizontal()
	gwbtn.DoClick = GiveWeapon
	panel:AddItem(gwbtn)

	gwbtn = vgui.Create("DButton")
	gwbtn:SetFont("ZSHUDFontSmaller")
	gwbtn:SetText("Give Item and 5 clips")
	gwbtn:SetSize(panel:GetWide() - 8 * screenscale, hei - 4 * screenscale)
	gwbtn:CenterHorizontal()
	gwbtn.DoClick = GiveWeaponClip
	panel:AddItem(gwbtn)

	gwbtn = vgui.Create("DButton")
	gwbtn:SetFont("ZSHUDFontSmaller")
	gwbtn:SetText("Drop Item")
	gwbtn:SetSize(panel:GetWide() - 8 * screenscale, hei - 4 * screenscale)
	gwbtn:CenterHorizontal()
	gwbtn.DoClick = DropWeapon
	panel:AddItem(gwbtn)

	gwbtn = vgui.Create("DButton")
	gwbtn:SetFont("ZSHUDFontSmaller")
	gwbtn:SetText("Empty Weapon Clip")
	gwbtn:SetSize(panel:GetWide() - 8 * screenscale, hei - 4 * screenscale)
	gwbtn:CenterHorizontal()
	gwbtn.DoClick = EmptyClip
	panel:AddItem(gwbtn)

	gwbtn = vgui.Create("DButton")
	gwbtn:SetFont("ZSHUDFontSmaller")
	gwbtn:SetText("Dismantle Item")
	gwbtn:SetSize(panel:GetWide() - 8 * screenscale, hei - 4 * screenscale)
	gwbtn:CenterHorizontal()
	gwbtn.DoClick = DismantleWeapon
	panel:AddItem(gwbtn)

	panel:AddItem(EasyLabel(panel, "Resupply Ammo Selection", "DefaultFont", color_white))
	local dropdown = vgui.Create("DComboBox", panel)
	dropdown:SetMouseInputEnabled(true)
	dropdown:AddChoice("Resupply Held Weapon")
	for k,v in pairs(self.AmmoResupply) do
		dropdown:AddChoice(self.AmmoNames[k])
	end
	dropdown.OnSelect = function(me, index, value, data)
		if value == "Resupply Held Weapon" then
			MySelf.ResupplyChoice = nil
			RunConsoleCommand("zs_resupplyammotype", "default")
			return
		end

		for k,v in pairs(self.AmmoNames) do
			if value == v then
				MySelf.ResupplyChoice = k
				RunConsoleCommand("zs_resupplyammotype", k)
				break
			end
		end
	end
	dropdown:SetText("Resupply Held Weapon")
	dropdown:SetTextColor(color_black)
	panel:AddItem(dropdown)

	self.HumanMenuSupplyChoice = dropdown

	panel:OpenMenu()
end

function GM:ZombieSpawnMenu()
	if self.ZombieEscape then return end

	if self.ZSpawnMenu and self.ZSpawnMenu:IsValid() then
		self.ZSpawnMenu:SetVisible(true)
		self.ZSpawnMenu:OpenMenu()
		self.ZSpawnMenu:RefreshContents()

		return
	end

	local panel = vgui.Create("DZombieSpawnMenu")
	self.ZSpawnMenu = panel

	panel:OpenMenu()
end

function GM:PlayerBindPress(pl, bind, wasin)
	if bind == "gmod_undo" or bind == "undo" then
		RunConsoleCommand("+zoom")
		timer.Create("ReleaseZoom", 1, 1, function() RunConsoleCommand("-zoom") end)
	elseif bind == "+menu_context" then
		if P_Team(pl) == TEAM_UNDEAD then
			self.ZombieThirdPerson = not self.ZombieThirdPerson
		elseif P_Team(pl) == TEAM_HUMAN then
			self:ToggleOTSCamera()
		end
	elseif bind == "impulse 100" then
		if P_Team(pl) == TEAM_UNDEAD and pl:Alive() then
			self:ToggleZombieVision()
		end
	end
end

function GM:_ShouldDrawLocalPlayer(pl)
	return FROM_CAMERA or P_Team(pl) == TEAM_UNDEAD and (self.ZombieThirdPerson or pl:CallZombieFunction0("ShouldDrawLocalPlayer"))
	or P_Team(pl) == TEAM_HUMAN and self:UseOverTheShoulder()
	or pl:IsPlayingTaunt()
	or pl.Revive and pl.Revive:IsValid()
	or pl.KnockedDown and pl.KnockedDown:IsValid()
end

local roll = 0
function GM:_CalcView(pl, origin, angles, fov, znear, zfar)
	if pl.Confusion and pl.Confusion:IsValid() then
		pl.Confusion:CalcView(pl, origin, angles, fov, znear, zfar)
	end

	if pl.Revive and pl.Revive:IsValid() and pl.Revive.GetRagdollEyes then
		if self.ThirdPersonKnockdown or self.ZombieThirdPerson then
			origin = pl:GetThirdPersonCameraPos(origin, angles)
		else
			local rpos, rang = pl.Revive:GetRagdollEyes(pl)
			if rpos then
				origin = rpos
				angles = rang
			end
		end
	elseif pl.KnockedDown and pl.KnockedDown:IsValid() then
		if self.ThirdPersonKnockdown or self:UseOverTheShoulder() then
			origin = pl:GetThirdPersonCameraPos(origin, angles)
		else
			local rpos, rang = self:GetRagdollEyes(pl)
			if rpos then
				origin = rpos
				angles = rang
			end
		end
	elseif pl:ShouldDrawLocalPlayer() and pl:OldAlive() and not pl:HasWon() then
		if P_Team(pl) == TEAM_UNDEAD then
			origin = pl:GetThirdPersonCameraPos(origin, angles)
		elseif self:UseOverTheShoulder() then
			self:CalcViewOTS(pl, origin, angles, fov, znear, zfar)
		end
	end

	local targetroll = 0
	if self.MovementViewRoll then
		local dir = pl:GetVelocity()
		local speed = dir:Length()
		dir:Normalize()

		targetroll = targetroll + dir:Dot(angles:Right()) * math.min(30, speed / 100)
	end

	if pl:WaterLevel() >= 3 then
		targetroll = targetroll + math.sin(CurTime()) * 7
	end

	roll = math.Approach(roll, targetroll, math.max(0.25, math.sqrt(math.abs(roll))) * 30 * FrameTime())
	angles.roll = angles.roll + roll

	if pl:IsPlayingTaunt() then
		self:CalcViewTaunt(pl, origin, angles, fov, znear, zfar)
	end

	local target = pl:GetObserverTarget()
	if target and target:IsValid() then
		local lasttarget = self.LastObserverTarget
		if lasttarget and lasttarget:IsValid() and target ~= lasttarget then
			if self.LastObserverTargetLerp then
				if CurTime() >= self.LastObserverTargetLerp then
					self.LastObserverTarget = nil
					self.LastObserverTargetLerp = nil
				else
					local delta = math.Clamp((self.LastObserverTargetLerp - CurTime()) / 0.3333, 0, 1) ^ 0.5
					origin:Set(self.LastObserverTargetPos * delta + origin * (1 - delta))
				end
			else
				self.LastObserverTargetLerp = CurTime() + 0.3333
			end
		else
			self.LastObserverTarget = target
			self.LastObserverTargetPos = origin
		end
	end

	if pl:GetObserverMode() ~= OBS_MODE_NONE then
		angles.roll = 0 --Fixes babies tilting the screen
	end

	pl:CallZombieFunction2("CalcView", origin, angles)

	return self.BaseClass.CalcView(self, pl, origin, angles, fov, znear, zfar)
end

function GM:CalcViewTaunt(pl, origin, angles, fov, znear, zfar)
	local tr = util.TraceHull({start = origin, endpos = origin - angles:Forward() * 72, mins = Vector(-2, -2, -2), maxs = Vector(2, 2, 2), mask = MASK_OPAQUE, filter = pl})
	origin:Set(tr.HitPos + tr.HitNormal * 2)
end

function GM:CreateMoveTaunt(cmd)
	cmd:ClearButtons(0)
	cmd:ClearMovement()
end

function GM:PostProcessPermitted(str)
	return false
end

function GM:HUDPaintEndRound()
end

function GM:PreDrawViewModel(vm, pl, wep)
	if pl and pl:IsValid() and (pl:IsHolding() or GAMEMODE.HideViewModels) then return true end

	if wep and wep:IsValid() and wep.PreDrawViewModel then
		return wep:PreDrawViewModel(vm)
	end
end

function GM:PostDrawViewModel(vm, pl, wep)
	if wep and wep:IsValid() then
		if wep.UseHands or not wep:IsScripted() then
			local hands = pl:GetHands()
			if hands and hands:IsValid() then
				hands:DrawModel()
			end
		end

		if wep.PostDrawViewModel then
			wep:PostDrawViewModel(vm)
		end
	end
end

local undo = false
local matWhite = Material("models/debug/debugwhite")
local lowhealthcolor = GM.AuraColorEmpty
local fullhealthcolor = GM.AuraColorFull
function GM:_PrePlayerDraw(pl)
	local shadowman = false

	if pl ~= MySelf and pl:IsEffectActive(EF_DIMLIGHT) then
		pl:RemoveEffects(EF_DIMLIGHT)
	end

	local myteam = P_Team(MySelf)
	local theirteam = P_Team(pl)

	local radius = self.TransparencyRadius
	if radius > 0 and myteam == theirteam and pl ~= MySelf and not (GAMEMODE.AlwaysDrawFriend and pl:IsFriend()) and not self.MedicalAura then
		local dist = pl:GetPos():DistToSqr(EyePos())
		if dist < radius then
			local blend = (dist / radius) ^ 1.4
			if blend <= 0.1 then
				pl.ShadowMan = true return true
			end
			render_SetBlend(blend)
			if myteam == TEAM_HUMAN and blend < 0.5 then
				render_ModelMaterialOverride(matWhite)
				render_SetColorModulation(0.2, 0.2, 0.2)
				shadowman = true
			end
			undo = true
		end
	end

	pl.ShadowMan = shadowman

	if pl:CallZombieFunction0("PrePlayerDraw") then return true end

	if pl.SpawnProtection and (not (pl.status_overridemodel and pl.status_overridemodel:IsValid()) or pl:GetZombieClassTable().NoHideMainModel) then
		undo = true
		render_ModelMaterialOverride(matWhite)
		render_SetBlend(0.02 + (CurTime() + pl:EntIndex() * 0.2) % 0.05)
		render_SetColorModulation(0, 0.3, 0)
		render_SuppressEngineLighting(true)
	end

	if self.m_ZombieVision and myteam == TEAM_UNDEAD and theirteam == TEAM_HUMAN then
		local dist = pl:GetPos():DistToSqr(EyePos())
		if dist <= pl:GetAuraRangeSqr() and (not pl:GetDTBool(DT_PLAYER_BOOL_NECRO) or dist >= 27500) then
			undo = true
			local healthfrac = pl:Health() / pl:GetMaxHealth()

			render_SetBlend(1)
			render_ModelMaterialOverride(matWhite)
			render_SetColorModulation(
				Lerp(healthfrac, lowhealthcolor.r, fullhealthcolor.r) / 255,
				Lerp(healthfrac, lowhealthcolor.g, fullhealthcolor.g) / 255,
				Lerp(healthfrac, lowhealthcolor.b, fullhealthcolor.b) / 255
			)
			render_SuppressEngineLighting(true)
			cam_IgnoreZ(true)
		end
	end
end

local colFriend = Color(10, 255, 10, 60)
local matFriendRing = Material("SGM/playercircle")
local matTargetTri = Material("gui/point.png")
function GM:_PostPlayerDraw(pl)
	pl:CallZombieFunction0("PostPlayerDraw")

	if undo then
		render_SetBlend(1)
		render_ModelMaterialOverride()
		render_SetColorModulation(1, 1, 1)
		render_SuppressEngineLighting(false)
		cam_IgnoreZ(false)

		undo = false
	end

	local eyepos, ang, tpos, distance, hpf
	if MySelf.TargetLocus and self.TraceTargetTeam == pl and pl:IsValidLivingZombie() and not pl:GetZombieClassTable().IgnoreTargetAssist then
		tpos = pl:GetPos()
		tpos.z = tpos.z + 80

		eyepos = MySelf:EyePos()
		distance = eyepos:DistToSqr(tpos)

		ang = (eyepos - tpos):Angle()
		ang:RotateAroundAxis(ang:Right(), 270)
		ang:RotateAroundAxis(ang:Up(), 90)

		cam_IgnoreZ(true)
		cam_Start3D2D(tpos, ang, math.max(750, math.sqrt(distance)) / 6500)
			surface_SetMaterial(matTargetTri)

			hpf = pl:Health() / pl:GetMaxZombieHealth()

			surface_SetDrawColor(255 - (255 * hpf), 255 * hpf, 0, 230)
			surface_DrawTexturedRect(-96, -96, 96, 96)
		cam_End3D2D()
		cam_IgnoreZ(false)
	end

	if pl ~= MySelf and P_Team(MySelf) == P_Team(pl) and pl:IsFriend() then
		local pos = pl:GetPos()
		pos.z = pos.z + 2
		render_SetMaterial(matFriendRing)
		render_DrawQuadEasy(pos, vector_up, 32, 32, colFriend)
		render_DrawQuadEasy(pos, vector_down, 32, 32, colFriend)
	end
end

function GM:HUDPaintBackgroundEndRound()
	local x, y = ScrW() / 2, ScrH() * 0.8
	local timleft = math.max(0, self.EndTime + self.EndGameTime - CurTime())

	if timleft <= 0 then
		draw_SimpleTextBlur(translate.Get("loading"), "ZSHUDFont", x, y, COLOR_WHITE, TEXT_ALIGN_CENTER)
	else
		draw_SimpleTextBlur(translate.Format("next_round_in_x", util.ToMinutesSecondsCD(timleft)), "ZSHUDFontSmall", x, y, COLOR_WHITE, TEXT_ALIGN_CENTER)
	end
end

local function EndRoundCalcView(pl, origin, angles, fov, znear, zfar)
	if GAMEMODE.EndTime and CurTime() < GAMEMODE.EndTime + 5 then
		local endposition = GAMEMODE.LastHumanPosition
		local override = GetGlobalVector("endcamerapos", vector_origin)
		if override ~= vector_origin then
			endposition = override
		end
		if endposition then
			local delta = math.Clamp((CurTime() - GAMEMODE.EndTime) * 2, 0, 1)

			local start = endposition * delta + origin * (1 - delta)
			local tr = util.TraceHull({start = start, endpos = start + delta * 64 * Angle(0, CurTime() * 30, 0):Forward(), mins = Vector(-2, -2, -2), maxs = Vector(2, 2, 2), filter = player.GetAll(), mask = MASK_SOLID})
			return {origin = tr.HitPos + tr.HitNormal, angles = (start - tr.HitPos):Angle()}
		end

		return
	end

	hook.Remove("CalcView", "EndRoundCalcView")
end

local function EndRoundShouldDrawLocalPlayer(pl)
	if GAMEMODE.EndTime and CurTime() < GAMEMODE.EndTime + 5 then
		return true
	end

	hook.Remove("ShouldDrawLocalPlayer", "EndRoundShouldDrawLocalPlayer")
end

function GM:EndRound(winner, nextmap)
	if self.RoundEnded then return end
	self.RoundEnded = true

	ROUNDWINNER = winner

	self.EndTime = CurTime()

	RunConsoleCommand("stopsound")

	self.HUDPaint = self.HUDPaintEndRound
	self.HUDPaintBackground = self.HUDPaintBackgroundEndRound

	if winner == TEAM_UNDEAD and GetGlobalBool("endcamera", true) then
		hook.Add("CalcView", "EndRoundCalcView", EndRoundCalcView)
		hook.Add("ShouldDrawLocalPlayer", "EndRoundShouldDrawLocalPlayer", EndRoundShouldDrawLocalPlayer)
	end

	local dvar = winner == TEAM_UNDEAD and self.AllLoseSound or self.HumanWinSound
	local snd = GetGlobalString(winner == TEAM_UNDEAD and "losemusic" or "winmusic", dvar)
	if snd == "default" then
		snd = dvar
	elseif snd == "none" then
		snd = nil
	end
	if snd then
		timer.Simple(0.5, function() surface_PlaySound(snd) end)
	end

	timer.Simple(5, function()
		if not (pEndBoard and pEndBoard:IsValid()) then
			MakepEndBoard(winner)
		end
	end)
end

function GM:WeaponDeployed(pl, wep)
	self:DoChangeDeploySpeed(wep)
end

function GM:LocalPlayerDied(attackername)
	LASTDEATH = RealTime()

	surface_PlaySound(self.DeathSound)
	if attackername then
		self:CenterNotify(COLOR_RED, {font = "ZSHUDFont"}, translate.Get("you_have_died"))
		self:CenterNotify(COLOR_RED, translate.Format(self.PantsMode and "you_were_kicked_by_x" or "you_were_killed_by_x", tostring(attackername)))
	else
		self:CenterNotify(COLOR_RED, {font = "ZSHUDFont"}, translate.Get("you_have_died"))
	end
end

function GM:KeyPress(pl, key)
	if key == self.MenuKey then
		local team = P_Team(pl)
		if team == TEAM_HUMAN and pl:Alive() and not pl:IsHolding() then
			gamemode.Call("HumanMenu")
		elseif team == TEAM_ZOMBIE and not pl:Alive() then
			gamemode.Call("ZombieSpawnMenu")
		end
	elseif key == IN_SPEED then
		if pl:Alive() then
			if P_Team(pl) == TEAM_HUMAN then
				pl:DispatchAltUse()
			elseif P_Team(pl) == TEAM_UNDEAD then
				pl:CallZombieFunction0("AltUse")
			end
		end
	end
end

function GM:KeyRelease(pl, key)
	if key == self.MenuKey then
		if self.HumanMenuPanel and self.HumanMenuPanel:IsValid() then
			if self.InventoryMenu and self.InventoryMenu:IsValid() then
				self.InventoryMenu:SetVisible(false)

				if self.m_InvViewer and self.m_InvViewer:IsValid() then
					self.m_InvViewer:SetVisible(false)
				end
			end

			if self.HumanMenuSupplyChoice then
				self.HumanMenuSupplyChoice:CloseMenu()
			end

			if self.InventoryMenu.SelInv then
				self.InventoryMenu.SelInv = nil
				self:DoAltSelectedItemUpdate()

				local grid = self.InventoryMenu.Grid
				for k, v in pairs(grid:GetChildren()) do
					v.On = false
				end
			end
		end
	end
end

function GM:PlayerStepSoundTime(pl, iType, bWalking)
	local time = pl:CallZombieFunction2("PlayerStepSoundTime", iType, bWalking)
	if time then
		return time
	end

	if iType == STEPSOUNDTIME_NORMAL or iType == STEPSOUNDTIME_WATER_FOOT then
		return 520 - pl:GetVelocity():Length()
	end

	if iType == STEPSOUNDTIME_ON_LADDER then
		return 500
	end

	if iType == STEPSOUNDTIME_WATER_KNEE then
		return 650
	end

	return 350
end

function GM:PlayerFootstep(pl, vFootPos, iFoot, strSoundName, fVolume)
	return pl:CallZombieFunction4("PlayerFootstep", vFootPos, iFoot, strSoundName, fVolume)
end

function GM:PlayerCanCheckout(pl)
	return pl:IsValid() and P_Team(pl) == TEAM_HUMAN and pl:Alive() and self:GetWave() <= 0
end

function GM:OpenWorth()
	if gamemode.Call("PlayerCanCheckout", MySelf) then
		MakepWorth()
	end
end

function GM:CloseWorth()
	if pWorth and pWorth:IsValid() then
		pWorth:Remove()
		pWorth = nil
	end
end

function GM:SuppressArsenalUpgrades(suppresstime)
	self.SuppressArsenalTime = math.max(CurTime() + suppresstime, self.SuppressArsenalTime)
end

function GM:Rewarded(class, amount)
	if CurTime() < self.SuppressArsenalTime then return end

	class = class or "0"

	local toptext = translate.Get("arsenal_upgraded")

	local wep = weapons.Get(class)
	if wep and wep.PrintName and #wep.PrintName > 0 then
		if killicon.Get(class) == killicon.Get("default") then
			self:CenterNotify(COLOR_PURPLE, toptext..": ", color_white, wep.PrintName)
		else
			self:CenterNotify({killicon = class}, " ", COLOR_PURPLE, toptext..": ", color_white, wep.PrintName)
		end
	elseif amount then
		self:CenterNotify(COLOR_PURPLE, toptext..": ", color_white, amount.." "..class)
	else
		self:CenterNotify(COLOR_PURPLE, toptext)
	end
end

function PlayMenuOpenSound()
	MySelf:EmitSound("buttons/lightswitch2.wav", 100, 30)
end

function PlayMenuCloseSound()
	MySelf:EmitSound("buttons/lightswitch2.wav", 100, 20)
end
