GM.Name		=	"Zombie Survival"

GM.Author	=	"William \"JetBoom\" Moodhe"

GM.Email	=	"williammoodhe@gmail.com"

GM.Website	=	"http://www.noxiousnet.com"



-- No, adding a gun doesn't make your name worth being here.

GM.Credits = {

	{"William \"JetBoom\" Moodhe", "williammoodhe@gmail.com (www.noxiousnet.com)", "Creator / Programmer"},

	{"11k", "tjd113@gmail.com", "Zombie view models"},

	{"Eisiger", "k2deseve@gmail.com", "Zombie kill icons"},

	{"Austin \"Little Nemo\" Killey", "austin_odyssey@yahoo.com", "Ambient music"},

	{"Zombie Panic: Source", "http://www.zombiepanic.org/", "Melee weapon sounds"},

	{"Samuel", "samuel_games@hotmail.com", "Board Kit model"},

	{"Typhon", "lukas-tinel@hotmail.com", "HUD textures"},



	{"Mr. Darkness", "", "Russian translation"},

	{"honsal", "", "Korean translation"},

	{"rui_troia", "", "Portuguese translation"},

	{"Shinyshark", "", "Dutch translation"},

	{"Kradar", "", "Italian translation"},

	{"Raptor", "", "German translation"},

	{"The Special Duckling", "", "Danish translation"},

	{"Box, ptown, Dr. Broly", "", "Spanish translation"}

}



include("nixthelag.lua")

include("buffthefps.lua")



function GM:GetNumberOfWaves()

	local default = GetGlobalBool("classicmode") and 10 or self.NumberOfWaves

	local num = GetGlobalInt("numwaves", default) -- This is controlled by logic_waves.

	return num == -2 and default or num

end



function GM:GetWaveOneLength()

	return GetGlobalBool("classicmode") and self.WaveOneLengthClassic or self.WaveOneLength

end



include("sh_translate.lua")

include("sh_colors.lua")

include("sh_serialization.lua")



include("sh_globals.lua")

include("sh_crafts.lua")

include("sh_util.lua")

include("sh_options.lua")

include("sh_zombieclasses.lua")

include("sh_animations.lua")

include("sh_sigils.lua")



include("noxapi/noxapi.lua")



include("obj_vector_extend.lua")

include("obj_entity_extend.lua")

include("obj_player_extend.lua")

include("obj_weapon_extend.lua")



include("workshopfix.lua")



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



GM.EndRound = false

GM.StartingWorth = 100000

GM.ZombieVolunteers = {}



team.SetUp(TEAM_ZOMBIE, "The Undead", Color(0, 255, 0, 255))

team.SetUp(TEAM_SURVIVORS, "Survivors", Color(0, 160, 255, 255))



local validmodels = player_manager.AllValidModels()

validmodels["tf01"] = nil

validmodels["tf02"] = nil



vector_tiny = Vector(0.001, 0.001, 0.001)



-- ogg/mp3 still doesn't work with SoundDuration() function

GM.SoundDuration = {

	["zombiesurvival/music_win.ogg"] = 33.149,

	["zombiesurvival/music_lose.ogg"] = 45.714,

	["zombiesurvival/lasthuman.ogg"] = 120.503,



	["zombiesurvival/beats/defaulthuman/1.ogg"] = 7.111,

	["zombiesurvival/beats/defaulthuman/2.ogg"] = 7.111,

	["zombiesurvival/beats/defaulthuman/3.ogg"] = 7.111,

	["zombiesurvival/beats/defaulthuman/4.ogg"] = 7.111,

	["zombiesurvival/beats/defaulthuman/5.ogg"] = 7.111,

	["zombiesurvival/beats/defaulthuman/6.ogg"] = 14.222,

	["zombiesurvival/beats/defaulthuman/7.ogg"] = 14.222,

	["zombiesurvival/beats/defaulthuman/8.ogg"] = 7.111,

	["zombiesurvival/beats/defaulthuman/9.ogg"] = 14.222,



	["zombiesurvival/beats/defaultzombiev2/1.ogg"] = 8,

	["zombiesurvival/beats/defaultzombiev2/2.ogg"] = 8,

	["zombiesurvival/beats/defaultzombiev2/3.ogg"] = 8,

	["zombiesurvival/beats/defaultzombiev2/4.ogg"] = 8,

	["zombiesurvival/beats/defaultzombiev2/5.ogg"] = 8,

	["zombiesurvival/beats/defaultzombiev2/6.ogg"] = 6.038,

	["zombiesurvival/beats/defaultzombiev2/7.ogg"] = 6.038,

	["zombiesurvival/beats/defaultzombiev2/8.ogg"] = 6.038,

	["zombiesurvival/beats/defaultzombiev2/9.ogg"] = 6.038,

	["zombiesurvival/beats/defaultzombiev2/10.ogg"] = 6.038

}



function GM:AddCustomAmmo()

	game.AddAmmoType({name = "pulse"})

	game.AddAmmoType({name = "stone"})



	game.AddAmmoType({name = "spotlamp"})

	game.AddAmmoType({name = "manhack"})

	game.AddAmmoType({name = "manhack_saw"})

	game.AddAmmoType({name = "drone"})

end



function GM:CanRemoveOthersNail(pl, nailowner, ent)

	local plpoints = pl:Frags()

	local ownerpoints = nailowner:Frags()

	if plpoints >= 75 or ownerpoints < 75 then return true end



	pl:PrintTranslatedMessage(HUD_PRINTCENTER, "cant_remove_nails_of_superior_player")



	return false

end



function GM:SetRedeemBrains(amount)

	SetGlobalInt("redeembrains", amount)

end



function GM:GetRedeemBrains()

	return GetGlobalInt("redeembrains", self.DefaultRedeem)

end



function GM:PlayerIsAdmin(pl)

	return pl:IsAdmin()

end



function GM:GetFallDamage(pl, fallspeed)

	return 0

end



function GM:ShouldRestartRound()

	if self.TimeLimit == -1 or self.RoundLimit == -1 then return true end



	local roundlimit = self.RoundLimit

	if self.ZombieEscape and roundlimit > 0 then

		roundlimit = math.ceil(roundlimit * 1.5)

	end



	local timelimit = self.TimeLimit

	if self.ZombieEscape and timelimit > 0 then

		timelimit = timelimit * 1.5

	end



	if timelimit > 0 and CurTime() >= timelimit or roundlimit > 0 and self.CurrentRound >= roundlimit then return false end



	return true

end



function GM:ZombieSpawnDistanceSort(other)

	return self._ZombieSpawnDistance < other._ZombieSpawnDistance

end



function GM:SortZombieSpawnDistances(allplayers)

	local curtime = CurTime()



	local zspawns = ents.FindByClass("zombiegasses")

	if #zspawns == 0 then

		zspawns = team.GetValidSpawnPoint(TEAM_UNDEAD)

	end



	for _, pl in pairs(allplayers) do

		if pl:Team() == TEAM_UNDEAD or pl:GetInfo("zs_alwaysvolunteer") == "1" then

			pl._ZombieSpawnDistance = -1

		elseif CLIENT or pl.LastNotAFK and CurTime() <= pl.LastNotAFK + 60 then

			local plpos = pl:GetPos()

			local closest = 9999999

			for _, ent in pairs(zspawns) do

				local dist = ent:GetPos():Distance(plpos)

				if dist < closest then

					closest = dist

				end

			end

			pl._ZombieSpawnDistance = closest

		else

			pl._ZombieSpawnDistance = 9999999

		end

	end



	table.sort(allplayers, self.ZombieSpawnDistanceSort)

end



function GM:SetDynamicSpawning(onoff)

	SetGlobalBool("DynamicSpawningDisabled", not onoff)

	self.DynamicSpawning = onoff

end



function GM:ValidMenuLockOnTarget(pl, ent)

	if ent and ent:IsValid() and ent:IsPlayer() and ent:Team() == TEAM_HUMAN and ent:Alive() then

		local startpos = pl:EyePos()

		local endpos = ent:NearestPoint(startpos)

		if startpos:Distance(endpos) <= 48 and TrueVisible(startpos, endpos) then

			return true

		end

	end



	return false

end



function GM:GetHandsModel(pl)

	return player_manager.TranslatePlayerHands(pl:GetInfo("cl_playermodel"))

end



local playerheight = Vector(0, 0, 72)

local playermins = Vector(-17, -17, 0)

local playermaxs = Vector(17, 17, 4)

local SkewedDistance = util.SkewedDistance



GM.DynamicSpawnDistVisOld = 2048

GM.DynamicSpawnDistOld = 640

function GM:DynamicSpawnIsValidOld(zombie, humans, allplayers)

	-- I didn't make this check where trigger_hurt entities are. Rather I made it check the time since the last time you were hit with a trigger_hurt.

	-- I'm not sure if it's possible to check if a trigger_hurt is enabled or disabled through the Lua bindings.

	if SERVER and zombie.LastHitWithTriggerHurt and CurTime() < zombie.LastHitWithTriggerHurt + 2 then

		return false

	end



	-- Optional caching for these.

	if not humans then humans = team.GetPlayers(TEAM_HUMAN) end

	if not allplayers then allplayers = player.GetAll() end



	local pos = zombie:GetPos() + Vector(0, 0, 1)

	if zombie:Alive() and zombie:GetMoveType() == MOVETYPE_WALK and zombie:OnGround()

	and not util.TraceHull({start = pos, endpos = pos + playerheight, mins = playermins, maxs = playermaxs, mask = MASK_SOLID, filter = allplayers}).Hit then

		local vtr = util.TraceHull({start = pos, endpos = pos - playerheight, mins = playermins, maxs = playermaxs, mask = MASK_SOLID_BRUSHONLY})

		if not vtr.HitSky and not vtr.HitNoDraw then

			local valid = true



			for _, human in pairs(humans) do

				local hpos = human:GetPos()

				local nearest = zombie:NearestPoint(hpos)

				local dist = SkewedDistance(hpos, nearest, 2.75) -- We make it so that the Z distance between a human and a zombie is skewed if the zombie is below the human.

				if dist <= self.DynamicSpawnDistOld or dist <= self.DynamicSpawnDistVisOld and WorldVisible(hpos, nearest) then -- Zombies can't be in radius of any humans. Zombies can't be clearly visible by any humans.

					valid = false

					break

				end

			end



			return valid

		end

	end



	return false

end



function GM:GetBestDynamicSpawnOld(pl, pos)

	local spawns = self:GetDynamicSpawnsOld(pl)

	if #spawns == 0 then return end



	return self:GetClosestSpawnPoint(spawns, pos or self:GetTeamEpicentre(TEAM_HUMAN)) or table.Random(spawns)

end



function GM:GetDynamicSpawnsOld(pl)

	local tab = {}



	local allplayers = player.GetAll()

	local humans = team.GetPlayers(TEAM_HUMAN)

	for _, zombie in pairs(team.GetPlayers(TEAM_UNDEAD)) do

		if zombie ~= pl and self:DynamicSpawnIsValidOld(zombie, humans, allplayers) then

			table.insert(tab, zombie)

		end

	end



	return tab

end



GM.DynamicSpawnDist = 400

GM.DynamicSpawnDistBuild = 650

function GM:DynamicSpawnIsValid(nest, humans, allplayers)

	if self:ShouldUseAlternateDynamicSpawn() then

		return self:DynamicSpawnIsValidOld(nest, humans, allplayers)

	end



	-- Optional caching for these.

	if not humans then humans = team.GetPlayers(TEAM_HUMAN) end

	--if not allplayers then allplayers = player.GetAll() end



	local pos = nest:GetPos() + Vector(0, 0, 1)

	if nest.GetNestBuilt and nest:GetNestBuilt() and not util.TraceHull({start = pos, endpos = pos + playerheight, mins = playermins, maxs = playermaxs, mask = MASK_SOLID_BRUSHONLY}).Hit then

		local vtr = util.TraceHull({start = pos, endpos = pos - playerheight, mins = playermins, maxs = playermaxs, mask = MASK_SOLID_BRUSHONLY})

		if not vtr.HitSky and not vtr.HitNoDraw then

			local valid = true

			local nearest = nest:GetPos()



			for _, human in pairs(humans) do

				local hpos = human:GetPos()

				local dist = SkewedDistance(hpos, nearest, 2.75) -- We make it so that the Z distance between a human and a nest is skewed if the nest is below the human.

				if dist <= self.DynamicSpawnDist then

					valid = false

					break

				end

			end



			return valid

		end

	end



	return false

end



function GM:GetBestDynamicSpawn(pl, pos)

	if self:ShouldUseAlternateDynamicSpawn() then

		return self:GetBestDynamicSpawnOld(pl, pos)

	end



	local spawns = self:GetDynamicSpawns(pl)

	if #spawns == 0 then return end



	return self:GetClosestSpawnPoint(spawns, pos or self:GetTeamEpicentre(TEAM_HUMAN)) or table.Random(spawns)

end



function GM:GetDynamicSpawns(pl)

	if self:ShouldUseAlternateDynamicSpawn() then

		return self:GetDynamicSpawnsOld(pl)

	end



	local tab = {}



	--local allplayers = player.GetAll()

	local humans = team.GetPlayers(TEAM_HUMAN)

	for _, nest in pairs(ents.FindByClass("prop_creepernest")) do

		if self:DynamicSpawnIsValid(nest, humans--[[, allplayers]]) then

			table.insert(tab, nest)

		end

	end



	return tab

end



function GM:GetDesiredStartingZombies()

	local numplayers = #player.GetAll()

	return math.min(math.max(1, math.ceil(numplayers * self.WaveOneZombies)), numplayers - 1)

end



function GM:GetEndRound()

	return self.RoundEnded

end



function GM:PrecacheResources()

	util.PrecacheSound("physics/body/body_medium_break2.wav")

	util.PrecacheSound("physics/body/body_medium_break3.wav")

	util.PrecacheSound("physics/body/body_medium_break4.wav")

	for name, mdl in pairs(player_manager.AllValidModels()) do

		util.PrecacheModel(mdl)

	end

end



function GM:ShouldCollide(enta, entb)

	if enta.ShouldNotCollide and enta:ShouldNotCollide(entb) or entb.ShouldNotCollide and entb:ShouldNotCollide(enta) then

		return false

	end



	return true

end



function GM:Move(pl, move)

	if pl:Team() == TEAM_HUMAN then

		if pl:GetBarricadeGhosting() then

			move:SetMaxSpeed(36)

			move:SetMaxClientSpeed(36)

		elseif move:GetForwardSpeed() < 0 then

			move:SetMaxSpeed(move:GetMaxSpeed() * 0.5)

			move:SetMaxClientSpeed(move:GetMaxClientSpeed() * 0.5)

		elseif move:GetForwardSpeed() == 0 then

			move:SetMaxSpeed(move:GetMaxSpeed() * 0.85)

			move:SetMaxClientSpeed(move:GetMaxClientSpeed() * 0.85)

		end

	elseif pl:CallZombieFunction("Move", move) then

		return

	end



	local legdamage = pl:GetLegDamage()

	if legdamage > 0 then

		local scale = 1 - math.min(1, legdamage * 0.33)

		move:SetMaxSpeed(move:GetMaxSpeed() * scale)

		move:SetMaxClientSpeed(move:GetMaxClientSpeed() * scale)

	end

end



function GM:OnPlayerHitGround(pl, inwater, hitfloater, speed)

	if inwater then return true end



	local isundead = pl:Team() == TEAM_UNDEAD



	if isundead then

		if pl:GetZombieClassTable().NoFallDamage then return true end

	else

		pl:PreventSkyCade()

	end



	if not isundead or not pl:GetZombieClassTable().NoFallSlowdown then

		pl:RawCapLegDamage(CurTime() + math.min(2, speed * 0.0035))

	end



	if SERVER then

		if isundead then

			speed = math.max(0, speed - 200)

		end



		local damage = (0.1 * (speed - 525)) ^ 1.45

		if hitfloater then damage = damage / 2 end



		if math.floor(damage) > 0 then

			if 20 <= damage and damage < pl:Health() then

				pl:KnockDown(damage * 0.05)

			end

			pl:TakeSpecialDamage(damage, DMG_FALL, game.GetWorld(), game.GetWorld(), pl:GetPos())

			pl:EmitSound("player/pl_fallpain"..(math.random(2) == 1 and 3 or 1)..".wav")

		end

	end



	return true

end



function GM:PlayerCanBeHealed(pl)

	return true

end



function GM:PlayerCanPurchase(pl)

	return pl:Team() == TEAM_HUMAN and self:GetWave() > 0 and pl:Alive() and pl:NearArsenalCrate()

end



--function GM:PlayerCanHearPlayersVoice(listener, talker)

	--return listener:Team() == talker:Team()

	--[[if self:GetEndRound() then return true, false end



	if listener:Team() == talker:Team() then

		return true, listener:GetPos():DistanceZSkew(talker:GetPos(), 2) <= 128

	end



	return false]]

--end



function GM:PlayerTraceAttack(pl, dmginfo, dir, trace)

end



function GM:ScalePlayerDamage(pl, hitgroup, dmginfo)

	if hitgroup == HITGROUP_HEAD and dmginfo:IsBulletDamage() then

		pl.m_LastHeadShot = CurTime()

	end



	if not pl:CallZombieFunction("ScalePlayerDamage", hitgroup, dmginfo) then

		if hitgroup == HITGROUP_HEAD then

			dmginfo:SetDamage(dmginfo:GetDamage() * 2)

		elseif hitgroup == HITGROUP_LEFTLEG or hitgroup == HITGROUP_RIGHTLEG or hitgroup == HITGROUP_GEAR then

			dmginfo:SetDamage(dmginfo:GetDamage() * 0.25)

		elseif hitgroup == HITGROUP_STOMACH or hitgroup == HITGROUP_LEFTARM or hitgroup == HITGROUP_RIGHTARM then

			dmginfo:SetDamage(dmginfo:GetDamage() * 0.75)

		end

	end



	if (hitgroup == HITGROUP_LEFTLEG or hitgroup == HITGROUP_RIGHTLEG) and self:PlayerShouldTakeDamage(pl, dmginfo:GetAttacker()) then

		pl:AddLegDamage(dmginfo:GetDamage())

	end

end



function GM:CanDamageNail(ent, attacker, inflictor, damage, dmginfo)

	return not attacker:IsPlayer() or attacker:Team() ~= TEAM_HUMAN

end



function GM:CanPlaceNail(pl, tr)

	return true

end



function GM:CanRemoveNail(pl, nail)

	return true

end



function GM:GetDamageResistance(fearpower)

	return fearpower * 0.35

end



function GM:FindUseEntity(pl, ent)

	if not ent:IsValid() then

		local e = pl:TraceLine(90, MASK_SOLID, pl:GetMeleeFilter()).Entity

		if e:IsValid() then return e end

	end



	return ent

end



function GM:ShouldUseAlternateDynamicSpawn()

	return self.ZombieEscape or self:IsClassicMode() or self.PantsMode or self:IsBabyMode()

end



function GM:GetZombieDamageScale(pos, ignore)

	return self.ZombieDamageMultiplier * (1 - self:GetDamageResistance(self:GetFearMeterPower(pos, TEAM_UNDEAD, ignore)))

end



local temppos

local function SortByDistance(a, b)

	return a:GetPos():Distance(temppos) < b:GetPos():Distance(temppos)

end



function GM:GetClosestSpawnPoint(teamid, pos)

	temppos = pos

	local spawnpoints

	if type(teamid) == "table" then

		spawnpoints = teamid

	else

		spawnpoints = team.GetValidSpawnPoint(teamid)

	end

	table.sort(spawnpoints, SortByDistance)

	return spawnpoints[1]

end



local FEAR_RANGE = 768

local FEAR_PERINSTANCE = 0.075

local RALLYPOINT_THRESHOLD = 0.3



local function GetEpicenter(tab)

	local vec = Vector(0, 0, 0)

	if #tab == 0 then return vec end



	for k, v in pairs(tab) do

		vec = vec + v:GetPos()

	end



	return vec / #tab

end



function GM:GetTeamRallyGroups(teamid)

	local groups = {}

	local ingroup = {}



	local plys = team.GetPlayers(teamid)



	for _, pl in pairs(plys) do

		if not ingroup[pl] and pl:Alive() then

			local plpos = pl:GetPos()

			local group = {pl}



			for __, otherpl in pairs(plys) do

				if otherpl ~= pl and not ingroup[otherpl] and otherpl:Alive() and otherpl:GetPos():Distance(plpos) <= FEAR_RANGE then

					group[#group + 1] = otherpl

				end

			end



			if #group * FEAR_PERINSTANCE >= RALLYPOINT_THRESHOLD then

				for k, v in pairs(group) do

					ingroup[v] = true

				end

				groups[#groups + 1] = group

			end

		end

	end



	return groups

end



function GM:GetTeamRallyPoints(teamid)

	local points = {}



	for _, group in pairs(self:GetTeamRallyGroups(teamid)) do

		points[#points + 1] = {GetEpicenter(group), math.min(1, (#group * FEAR_PERINSTANCE - RALLYPOINT_THRESHOLD) / (1 - RALLYPOINT_THRESHOLD))}

	end



	return points

end



local CachedEpicentreTimes = {}

local CachedEpicentres = {}

function GM:GetTeamEpicentre(teamid, nocache)

	if not nocache and CachedEpicentres[teamid] and CurTime() < CachedEpicentreTimes[teamid] then

		return CachedEpicentres[teamid]

	end



	local plys = team.GetPlayers(teamid)

	local vVec = Vector(0, 0, 0)

	for _, pl in pairs(plys) do

		if pl:Alive() then

			vVec = vVec + pl:GetPos()

		end

	end



	local epicentre = vVec / #plys

	if not nocache then

		CachedEpicentreTimes[teamid] = CurTime() + 0.5

		CachedEpicentres[teamid] = epicentre

	end



	return epicentre

end

GM.GetTeamEpicenter = GM.GetTeamEpicentre



function GM:GetCurrentEquipmentCount(id)

	local count = 0



	local item = self.Items[id]

	if item then

		if item.Countables then

			if type(item.Countables) == "table" then

				for k, v in pairs(item.Countables) do

					count = count + #ents.FindByClass(v)

				end

			else

				count = count + #ents.FindByClass(item.Countables)

			end

		end



		if item.SWEP then

			count = count + #ents.FindByClass(item.SWEP)

		end

	end



	return count

end



function GM:GetFearMeterPower(pos, teamid, ignore)

	if LASTHUMAN then return 1 end



	local power = 0



	for _, pl in pairs(player.GetAll()) do

		if pl ~= ignore and pl:Team() == teamid and not pl:CallZombieFunction("DoesntGiveFear") and pl:Alive() then

			local dist = pl:NearestPoint(pos):Distance(pos)

			if dist <= FEAR_RANGE then

				power = power + ((FEAR_RANGE - dist) / FEAR_RANGE) * (pl:GetZombieClassTable().FearPerInstance or FEAR_PERINSTANCE)

			end

		end

	end



	return math.min(1, power)

end



function GM:GetRagdollEyes(pl)

	local Ragdoll = pl:GetRagdollEntity()

	if not Ragdoll then return end



	local att = Ragdoll:GetAttachment(Ragdoll:LookupAttachment("eyes"))

	if att then

		att.Pos = att.Pos + att.Ang:Forward() * -2

		att.Ang = att.Ang



		return att.Pos, att.Ang

	end

end



function GM:PlayerNoClip(pl, on)

	if pl:IsAdmin() then

		if SERVER then

			PrintMessage(HUD_PRINTCONSOLE, translate.Format(on and "x_turned_on_noclip" or "x_turned_off_noclip", pl:Name()))

		end



		if SERVER then

			pl:MarkAsBadProfile()

		end



		return true

	end



	return false

end



function GM:IsSpecialPerson(pl, image)

	local img, tooltip



	if pl:SteamID() == "STEAM_0:1:3307510" then

		img = "VGUI/steam/games/icon_sourcesdk"

		tooltip = "JetBoom\nCreator of Zombie Survival!"

	elseif pl:IsAdmin() then

		img = "VGUI/servers/icon_robotron"

		tooltip = "Admin"

	elseif pl:IsNoxSupporter() then

		img = "noxiousnet/noxicon.png"

		tooltip = "Nox Supporter"

	end



	if img then

		if CLIENT then

			image:SetImage(img)

			image:SetTooltip(tooltip)

		end



		return true

	end



	return false

end



function GM:GetWaveEnd()

	return GetGlobalFloat("waveend", 0)

end



function GM:SetWaveEnd(wave)

	SetGlobalFloat("waveend", wave)

end



function GM:GetWaveStart()

	return GetGlobalFloat("wavestart", self.WaveZeroLength)

end



function GM:SetWaveStart(wave)

	SetGlobalFloat("wavestart", wave)

end



function GM:GetWave()

	return GetGlobalInt("wave", 0)

end



if GM:GetWave() == 0 then

	GM:SetWaveStart(GM.WaveZeroLength)

	GM:SetWaveEnd(GM.WaveZeroLength + GM:GetWaveOneLength())

end



function GM:GetWaveActive()

	return GetGlobalBool("waveactive", false)

end



function GM:SetWaveActive(active)

	if self.RoundEnded then return end



	if self:GetWaveActive() ~= active then

		SetGlobalBool("waveactive", active)



		if SERVER then

			gamemode.Call("WaveStateChanged", active)

		end

	end

end



if not FixedSoundDuration then

FixedSoundDuration = true

local OldSoundDuration = SoundDuration

function SoundDuration(snd)

	if snd then

		local ft = string.sub(snd, -4)

		if ft == ".mp3" then

			return OldSoundDuration(snd) * 2.25

		end

		if ft == ".ogg" then

			return OldSoundDuration(snd) * 3

		end

	end



	return OldSoundDuration(snd)

end

end

