zombiesurvival-evolved/gamemodes/zombiesurvival/gamemode/shared.lua

904 lines
25 KiB
Lua
Raw Normal View History

2014-10-02 08:49:54 +08:00
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 models"},
{"Typhon", "lukas-tinel@hotmail.com", "Fear-o-meter textures"},
{"Benjy, The Darker One, Raox, Scott", "", "Code contributions"},
2014-10-02 08:49:54 +08:00
{"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"},
{"ptown, Dr. Broly", "", "Spanish translation"},
{"Anyone else on GitHub or who I've forgotten", "", "Various contributions"},
2014-10-02 08:49:54 +08:00
}
if file.Exists(GM.FolderName.."/gamemode/maps/"..game.GetMap()..".lua", "LUA") then
include("maps/"..game.GetMap()..".lua")
end
2014-10-02 08:49:54 +08:00
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("obj_vector_extend.lua")
include("obj_entity_extend.lua")
include("obj_player_extend.lua")
include("obj_weapon_extend.lua")
2014-10-02 08:49:54 +08:00
include("sh_translate.lua")
include("sh_colors.lua")
include("sh_serialization.lua")
include("sh_util.lua")
include("skillweb/sh_skillweb.lua")
2014-10-02 08:49:54 +08:00
include("sh_options.lua")
include("sh_zombieclasses.lua")
include("sh_animations.lua")
include("sh_sigils.lua")
include("sh_channel.lua")
include("sh_weaponquality.lua")
2014-10-02 08:49:54 +08:00
include("noxapi/noxapi.lua")
include("vault/shared.lua")
2014-10-02 08:49:54 +08:00
include("workshopfix.lua")
include_library("perf")
include_library("player_movement")
include_library("inventory")
include_library("ammoexpand")
2014-10-02 08:49:54 +08:00
----------------------
GM.EndRound = false
GM.StartingWorth = 100
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
}
local SERVER = SERVER
local CLIENT = CLIENT
local HITGROUP_HEAD = HITGROUP_HEAD
local HITGROUP_LEFTARM = HITGROUP_LEFTARM
local HITGROUP_RIGHTARM = HITGROUP_RIGHTARM
local HITGROUP_GEAR = HITGROUP_GEAR
local HITGROUP_STOMACH = HITGROUP_STOMACH
local HITGROUP_LEFTLEG = HITGROUP_LEFTLEG
local HITGROUP_RIGHTLEG = HITGROUP_RIGHTLEG
local PTeam = FindMetaTable("Player").Team
2014-10-02 08:49:54 +08:00
function GM:AddCustomAmmo()
game.AddAmmoType({name = "dummy"})
2014-10-02 08:49:54 +08:00
game.AddAmmoType({name = "pulse"})
game.AddAmmoType({name = "impactmine"})
game.AddAmmoType({name = "chemical"})
game.AddAmmoType({name = "scrap"})
2014-10-02 08:49:54 +08:00
game.AddAmmoType({name = "stone"})
game.AddAmmoType({name = "flashbomb"})
game.AddAmmoType({name = "betty"})
game.AddAmmoType({name = "molotov"})
game.AddAmmoType({name = "corgasgrenade"})
game.AddAmmoType({name = "crygasgrenade"})
game.AddAmmoType({name = "bloodshot"})
2014-10-02 08:49:54 +08:00
game.AddAmmoType({name = "spotlamp"})
game.AddAmmoType({name = "manhack"})
game.AddAmmoType({name = "manhack_saw"})
game.AddAmmoType({name = "drone"})
game.AddAmmoType({name = "pulse_cutter"})
game.AddAmmoType({name = "drone_hauler"})
game.AddAmmoType({name = "rollermine"})
game.AddAmmoType({name = "sigilfragment"})
game.AddAmmoType({name = "corruptedfragment"})
game.AddAmmoType({name = "mediccloudbomb"})
game.AddAmmoType({name = "nanitecloudbomb"})
game.AddAmmoType({name = "repairfield"})
game.AddAmmoType({name = "zapper"})
game.AddAmmoType({name = "zapper_arc"})
game.AddAmmoType({name = "remantler"})
game.AddAmmoType({name = "turret_buckshot"})
game.AddAmmoType({name = "turret_assault"})
game.AddAmmoType({name = "turret_rocket"})
game.AddAmmoType({name = "camera"})
game.AddAmmoType({name = "tv"})
game.AddAmmoType({name = "foodwatermelon"})
game.AddAmmoType({name = "foodorange"})
game.AddAmmoType({name = "foodbanana"})
game.AddAmmoType({name = "foodsoda"})
game.AddAmmoType({name = "foodmilk"})
game.AddAmmoType({name = "foodtakeout"})
game.AddAmmoType({name = "foodwater"})
end
GM.Food = {}
function GM:RegisterFood()
self.Food = {}
for k, v in pairs(weapons.GetList()) do
if v.Base == "weapon_zs_basefood" then
table.insert(self.Food, v.ClassName)
end
end
2014-10-02 08:49:54 +08:00
end
function GM:RefreshMapIsObjective()
local mapname = string.lower(game.GetMap())
if string.find(mapname, "_obj_", 1, true) or string.find(mapname, "objective", 1, true) then
self.ObjectiveMap = true
else
self.ObjectiveMap = false
end
2014-10-02 08:49:54 +08:00
end
function GM:AssignItemProperties()
for _, tab in ipairs(self.Items) do
if tab.SWEP then
local sweptab = self.ZSInventoryItemData[tab.SWEP] or weapons.Get(tab.SWEP)
if sweptab then
if not tab.Description then
tab.Description = sweptab.Description
end
if not tab.Tier then
tab.Tier = sweptab.Tier
end
if not tab.MaxStock then
tab.MaxStock = sweptab.MaxStock
end
if tab.Name == "?" then
tab.Name = sweptab.PrintName or tab.Name
end
end
end
end
2014-10-02 08:49:54 +08:00
end
-- Utility function to setup a weapon's DefaultClip.
function GM:SetupDefaultClip(tab)
tab.DefaultClip = math.ceil(tab.ClipSize * self.SurvivalClips * (tab.ClipMultiplier or 1))
2014-10-02 08:49:54 +08:00
end
-- Some weapons are derived from weapon_base and try to make use of .Owner
function GM:FixWeaponBase()
local base = weapons.GetStored("weapon_base")
2014-10-02 08:49:54 +08:00
base.TranslateActivity = function(me)
if me.ActivityTranslate[act] ~= nil then
return me.ActivityTranslate[act]
end
2014-10-02 08:49:54 +08:00
return -1
2014-10-02 08:49:54 +08:00
end
base.TakePrimaryAmmo = function(me, num)
if me.Weapon:Clip1() <= 0 then
if me:Ammo1() <= 0 then return end
2014-10-02 08:49:54 +08:00
me:GetOwner():RemoveAmmo(num, me.Weapon:GetPrimaryAmmoType())
2014-10-02 08:49:54 +08:00
return
end
2014-10-02 08:49:54 +08:00
me.Weapon:SetClip1(me.Weapon:Clip1() - num)
2014-10-02 08:49:54 +08:00
end
base.Ammo1 = function(me)
return me:GetOwner():GetAmmoCount(me.Weapon:GetPrimaryAmmoType())
2014-10-02 08:49:54 +08:00
end
end
2014-10-02 08:49:54 +08:00
function GM:GetRedeemBrains()
return GetGlobalInt("redeembrains", self.DefaultRedeem)
2014-10-02 08:49:54 +08:00
end
function GM:PlayerIsAdmin(pl)
return pl:IsAdmin()
end
function GM:GetFallDamage(pl, fallspeed)
return 0 -- Handled in OnPlayerHitGround
2014-10-02 08:49:54 +08:00
end
function GM:ValidMenuLockOnTarget(pl, ent)
if ent and ent:IsValidLivingHuman() then
2014-10-02 08:49:54 +08:00
local startpos = pl:EyePos()
local endpos = ent:NearestPoint(startpos)
if startpos:DistToSqr(endpos) <= 2304 and TrueVisible(startpos, endpos) then -- 48^2
2014-10-02 08:49:54 +08:00
return true
end
end
return false
end
function GM:GetHandsModel(pl)
return player_manager.TranslatePlayerHands(player_manager.TranslateToPlayerModelName(pl:GetModel()))
end
function GM:GetBestAvailableZombieClass(baseclass_id)
if self:ShouldUseBetterVersionSystem() then
local baseclass
while true do
baseclass = self.ZombieClasses[baseclass_id]
if baseclass and baseclass.BetterVersion and self:IsClassUnlocked(baseclass.BetterVersion) then
baseclass_id = baseclass.BetterVersion
else
break
end
end
end
return self.ZombieClasses[baseclass_id].Index
end
function GM:ShouldUseBetterVersionSystem()
return not self.ObjectiveMap
2014-10-02 08:49:54 +08:00
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
local hpos, nearest, dist
2014-10-02 08:49:54 +08:00
-- 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
hpos = human:GetPos()
nearest = zombie:NearestPoint(hpos)
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.
2014-10-02 08:49:54 +08:00
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 = 512
GM.DynamicSpawnDistVis = 2048
GM.CreeperNestDist = 150
GM.CreeperNestDistBuild = 420
GM.CreeperNestDistBuildNest = 192
GM.CreeperNestDistBuildZSpawn = 256
local trace_dynspawn = {mins = playermins, maxs = playermaxs, mask = MASK_SOLID}
local trace_dynspawn_skybox = {mins = playermins, maxs = playermaxs, mask = MASK_SOLID_BRUSHONLY}
function GM:DynamicSpawnIsValid(ent, humans, allplayers)
2014-10-02 08:49:54 +08:00
if self:ShouldUseAlternateDynamicSpawn() then
return self:DynamicSpawnIsValidOld(ent, humans, allplayers)
2014-10-02 08:49:54 +08:00
end
-- Optional caching for these.
if not humans then humans = team.GetPlayers(TEAM_HUMAN) end
if not allplayers then allplayers = player.GetAll() end
2014-10-02 08:49:54 +08:00
local pos = ent:GetPos() + Vector(0, 0, 1)
local is_nest = ent:GetClass() == "prop_creepernest"
local required_distance = is_nest and self.CreeperNestDist or self.DynamicSpawnDist -- Nests have a shorter distance and no visibility requirement.
2014-10-02 08:49:54 +08:00
--if ent.GetNestBuilt and ent:GetNestBuilt() and not util.TraceHull({start = pos, endpos = pos + playerheight, mins = playermins, maxs = playermaxs, mask = MASK_SOLID_BRUSHONLY}).Hit then
if is_nest and not ent:GetNestBuilt() then
return false
end
2014-10-02 08:49:54 +08:00
-- Check if not enough room.
trace_dynspawn.start = pos
trace_dynspawn.endpos = pos + playerheight
trace_dynspawn.filter = allplayers
table.insert(trace_dynspawn.filter, ent)
local tr = util.TraceHull(trace_dynspawn)
if tr.Hit then
return false
end
-- No need to check this. You shouldn't be able to build a nest on top of nodraw/skybox
-- -- Check if on top of a nodraw / skybox.
-- trace_dynspawn_skybox.start = pos
-- trace_dynspawn_skybox.endpos = pos - playerheight
-- local vtr = util.TraceHull(trace_dynspawn_skybox)
-- if vtr.HitSky or vtr.HitNoDraw then
-- return false
-- end
-- vtr = util.TraceLine(trace_dynspawn_skybox)
-- if vtr.HitSky or vtr.HitNoDraw then
-- return false
-- end
-- Check if too close to a human.
local nearest, dist
for _, human in pairs(humans) do
nearest = human:NearestPoint(pos)
dist = SkewedDistance(nearest, pos, 2.75)
if dist <= required_distance then -- We make it so that the Z distance between a human and an ent is skewed if the ent is below the human.
return false
end
-- Check if visible by any human.
if not is_nest and dist <= self.DynamicSpawnDistVis and WorldVisible(nearest, pos) then
return false
2014-10-02 08:49:54 +08:00
end
end
return true
2014-10-02 08:49:54 +08:00
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 humans = team.GetPlayers(TEAM_HUMAN)
for _, nest in pairs(ents.FindByClass("prop_creepernest")) do
if self:DynamicSpawnIsValid(nest, humans) then
2014-10-02 08:49:54 +08:00
table.insert(tab, nest)
end
end
return tab
end
function GM:GetDesiredStartingZombies()
local numplayers = #player.GetAllActive()
return math.Clamp(math.ceil(numplayers * self.WaveOneZombies), 1, numplayers - 1)
2014-10-02 08:49:54 +08:00
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
game.AddParticles("particles/vman_explosion.pcf")
PrecacheParticleSystem("dusty_explosion_rockets")
2014-10-02 08:49:54 +08:00
end
function GM:ShouldCollide(enta, entb)
local snca = enta.ShouldNotCollide
if snca and snca(enta, entb) then return false end
local sncb = entb.ShouldNotCollide
if sncb and sncb(entb, enta) then return false end
--[[if enta.ShouldNotCollide and enta:ShouldNotCollide(entb) or entb.ShouldNotCollide and entb:ShouldNotCollide(enta) then
2014-10-02 08:49:54 +08:00
return false
end]]
2014-10-02 08:49:54 +08:00
return true
end
function GM:DoChangeDeploySpeed(wep)
if wep:IsValid() and wep.SetDeploySpeed and not wep.NoDeploySpeedChange then
local owner = wep:GetOwner()
wep:SetDeploySpeed(self.BaseDeploySpeed * (owner:IsValid() and owner.DeploySpeedMultiplier or 1) * (owner:IsValid() and owner:GetStatus("frost") and 0.7 or 1))
2014-10-02 08:49:54 +08:00
end
end
function GM:OnPlayerHitGround(pl, inwater, hitfloater, speed)
if inwater then return true end
if speed > 64 then
pl.LandSlow = true
2014-10-02 08:49:54 +08:00
end
local isundead = PTeam(pl) == TEAM_UNDEAD
if isundead and pl:GetZombieClassTable().NoFallDamage then return true end
local threshold_mul
local slowdown_mul
local recovery_mul
local damage_mul
if isundead then
speed = math.max(0, speed - 200)
threshold_mul = 1
slowdown_mul = 1
recovery_mul = 1
damage_mul = 1
else
threshold_mul = pl.FallDamageThresholdMul or 1
slowdown_mul = pl.FallDamageSlowDownMul or 1
recovery_mul = pl.FallDamageRecoveryMul or 1
damage_mul = pl.FallDamageDamageMul or 1
end
2014-10-02 08:49:54 +08:00
local damage = (0.1 * (speed - 525 * threshold_mul)) ^ 1.45
if hitfloater then damage = damage / 2 end
if SERVER then
local groundent = pl:GetGroundEntity()
if groundent:IsValid() and groundent:IsPlayer() and PTeam(groundent) == TEAM_UNDEAD and pl:HasTrinket("curbstompers") then
if groundent:IsHeadcrab() then
groundent:TakeSpecialDamage(groundent:Health() + 70, DMG_DIRECT, pl, pl, pl:GetPos())
elseif groundent:IsTorso() then
groundent:TakeSpecialDamage(50, DMG_CLUB, pl, pl, pl:GetPos())
end
if math.floor(damage) > 0 then
groundent:TakeSpecialDamage(damage * 5, DMG_CLUB, pl, pl, pl:GetPos())
return true
end
end
end
2014-10-02 08:49:54 +08:00
if math.floor(damage) > 0 then
if SERVER then
local h = pl:Health()
pl:TakeSpecialDamage(damage * damage_mul, DMG_FALL, game.GetWorld(), game.GetWorld(), pl:GetPos())
damage = h - pl:Health()
if not self.ZombieEscape and damage >= 5 and pl:Health() > 0 then
if damage >= 30 then
pl:KnockDown(damage * 0.05 * recovery_mul)
end
if not isundead or not pl:GetZombieClassTable().NoFallSlowdown then
pl:RawCapLegDamage(CurTime() + math.min(2, damage * 0.038 * slowdown_mul))
end
2014-10-02 08:49:54 +08:00
end
2014-10-02 08:49:54 +08:00
pl:EmitSound("player/pl_fallpain"..(math.random(2) == 1 and 3 or 1)..".wav")
elseif not self.ZombieEscape and damage >= 5 and (not isundead or not pl:GetZombieClassTable().NoFallSlowdown) then
pl:RawCapLegDamage(CurTime() + math.min(2, damage * 0.038 * slowdown_mul))
2014-10-02 08:49:54 +08:00
end
end
return true
end
function GM:PlayerCanBeHealed(pl)
local maxhp = pl:IsSkillActive(SKILL_D_FRAIL) and math.floor(pl:GetMaxHealth() * 0.25) or pl:GetMaxHealth()
return pl:Health() < maxhp or pl:GetPoisonDamage() > 0 or pl:GetBleedDamage() > 0
2014-10-02 08:49:54 +08:00
end
function GM:PlayerCanPurchase(pl)
if CLIENT and self.CanPurchaseCacheTime and self.CanPurchaseCacheTime >= CurTime() then
return self.CanPurchaseCache
end
local canpurchase = PTeam(pl) == TEAM_HUMAN and self:GetWave() > 0 and pl:Alive() and pl:NearArsenalCrate()
if CLIENT then
self.CanPurchaseCache = canpurchase
self.CanPurchaseCacheTime = CurTime() + 0.5
end
return canpurchase
2014-10-02 08:49:54 +08:00
end
-- This is actually only called by the engine on the server but it's here in case the client wants to know.
local TEAM_SPECTATOR = TEAM_SPECTATOR
2014-10-02 08:49:54 +08:00
function GM:PlayerCanHearPlayersVoice(listener, talker)
return PTeam(listener) == PTeam(talker) or PTeam(listener) == TEAM_SPECTATOR, false
2014-10-02 08:49:54 +08:00
end
GM.PlayerCanHearPlayersVoiceDefault = GM.PlayerCanHearPlayersVoice
2014-10-02 08:49:54 +08:00
function GM:PlayerCanHearPlayersVoiceAllTalk(listener, talker)
return true, false
2014-10-02 08:49:54 +08:00
end
cvars.AddChangeCallback("sv_alltalk", function(cvar, old, new)
GAMEMODE.PlayerCanHearPlayersVoice = new ~= "1" and GAMEMODE.PlayerCanHearPlayersVoiceDefault or GAMEMODE.PlayerCanHearPlayersVoiceAllTalk
end)
GM.PlayerCanHearPlayersVoice = GetConVar("sv_alltalk"):GetBool() and GM.PlayerCanHearPlayersVoiceAllTalk or GM.PlayerCanHearPlayersVoiceDefault
2014-10-02 08:49:54 +08:00
function GM:PlayerTraceAttack(pl, dmginfo, dir, trace)
2014-10-02 08:49:54 +08:00
end
function GM:GetDamageResistance(fearpower)
if self.MaxSigils > 0 and self:GetUseSigils() then
return fearpower * 0.1 + self:NumSigilsCorrupted() / self.MaxSigils * 0.2
end
2014-10-02 08:49:54 +08:00
return fearpower * 0.15
2014-10-02 08:49:54 +08:00
end
function GM:FindUseEntity(pl, ent)
if not ent:IsValid() then
local e = pl:TraceLine(90, MASK_SOLID, pl:GetDynamicTraceFilter()).Entity
2014-10-02 08:49:54 +08:00
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)
Merge with current nox changes. Random starting loadouts now include things that aren't weapons (ammo, traits, etc.). Added an option to disable the automatic suicide when changing zombie classes. Zombies will no longer receive damage resistance when only one human remains. Players can no longer claim the arsenal crate that gets spawned in the human spawn when nobody has one. Any player can pack it up though. The Ghoul's ghoul touch special ability has been changed. It will no longer slow targets but will debuff them for the next 10 seconds. They will take 40% more damage (the extra damage is attributed to the person who ghoul touched them last) for the next 10 seconds as well as slightly disorienting them. The Ghoul movement speed has been reduced from 185 to 170. Added crafting recipe: 'Waraxe' Handgun. Combine two 'Battleaxe' Handguns to craft this. Slightly better version of the Owens. The Flesh Creeper model has been changed to one that doesn't have severely awkward hitboxes. The Flesh Creeper can no longer jump and attack at the same time. The Lead Pipe special trait has been changed from a disorientation to a severe view punch/snap on a global cooldown of 1.5 seconds. The Regenerative trait is now 1 health every 6 seconds under 50% health instead of 1 health every 5 seconds under 50% health. Fast Zombie Legs have been changed to be a slightly faster but slightly weaker version of Zombie Legs. Zombies that have just spawned or enter zombie gas will now have a temporary buff which gives 25% extra speed and 40% damage resistance. This buff lasts for 3 seconds and is refreshed by entering the gas. Gas will no longer heal. Zombies with this buff on will strobe dark green. Added crafting recipe: Bladehack. Combine a saw blade with a manhack to get this. Slower but has more health, does more damage, and has less of a knockback when hitting something. Resupply Boxes now award the owner a point for every 2 people who use their box instead of every single person (so half as many points). Fixed Fast Zombie Legs spawning much more abundantly than intended. Fixed Flesh Creepers not being able to jump over obstacles due to their insanely low jump height. Fixed zombies taking themselves in to account when calculating horde damage resistance (bosses always had full resistance because of this). Fixed allowing people to use worth menu after redeeming.
2014-11-07 13:03:40 +08:00
if LASTHUMAN then return self.ZombieDamageMultiplier end
2014-10-02 08:49:54 +08:00
return self.ZombieDamageMultiplier * (1 - self:GetDamageResistance(self:GetFearMeterPower(pos, TEAM_UNDEAD, ignore)))
end
local temppos
local function SortByDistance(a, b)
return a:GetPos():DistToSqr(temppos) < b:GetPos():DistToSqr(temppos)
2014-10-02 08:49:54 +08:00
end
local function GetSortedSpawnPoints(teamid, pos)
2014-10-02 08:49:54 +08:00
temppos = pos
local spawnpoints
if type(teamid) == "table" then
spawnpoints = teamid
else
spawnpoints = team.GetValidSpawnPoint(teamid)
end
2014-10-02 08:49:54 +08:00
table.sort(spawnpoints, SortByDistance)
return spawnpoints
2014-10-02 08:49:54 +08:00
end
function GM:GetClosestSpawnPoint(teamid, pos)
return GetSortedSpawnPoints(teamid, pos)[1]
end
function GM:GetFurthestSpawnPoint(teamid, pos)
local spawnpoints = GetSortedSpawnPoints(teamid, pos)
return spawnpoints[#spawnpoints]
end
local FEAR_RANGE = 768^2
2014-10-02 08:49:54 +08:00
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)
local plpos, group
2014-10-02 08:49:54 +08:00
for _, pl in pairs(plys) do
if not ingroup[pl] and pl:Alive() then
plpos = pl:GetPos()
group = {pl}
2014-10-02 08:49:54 +08:00
for __, otherpl in pairs(plys) do
if otherpl ~= pl and not ingroup[otherpl] and otherpl:Alive() and otherpl:GetPos():DistToSqr(plpos) <= FEAR_RANGE then
2014-10-02 08:49:54 +08:00
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)
local considered = 0
2014-10-02 08:49:54 +08:00
for _, pl in pairs(plys) do
if pl:Alive() and pl:GetAuraRange() == 2048 then
2014-10-02 08:49:54 +08:00
vVec = vVec + pl:GetPos()
considered = considered + 1
2014-10-02 08:49:54 +08:00
end
end
local epicentre = vVec / considered
2014-10-02 08:49:54 +08:00
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 dist
2014-10-02 08:49:54 +08:00
local power = 0
for _, pl in pairs(player.GetAll()) do
if pl ~= ignore and PTeam(pl) == teamid and not pl:CallZombieFunction0("DoesntGiveFear") and pl:Alive() then
dist = pl:GetPos():DistToSqr(pos)
2014-10-02 08:49:54 +08:00
if dist <= FEAR_RANGE then
power = power + (1 - dist / FEAR_RANGE) * (pl:GetZombieClassTable().FearPerInstance or FEAR_PERINSTANCE)
2014-10-02 08:49:54 +08:00
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(time)
SetGlobalFloat("waveend", time)
2014-10-02 08:49:54 +08:00
end
function GM:GetWaveStart()
return GetGlobalFloat("wavestart", self.WaveZeroLength)
end
function GM:SetWaveStart(time)
SetGlobalFloat("wavestart", time)
2014-10-02 08:49:54 +08:00
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
function GM:VehicleMove()
end