913 lines
24 KiB
Lua
913 lines
24 KiB
Lua
local meta = FindMetaTable("Player")
|
|
if not meta then return end
|
|
|
|
function meta:FakeDeath(sequenceid, modelscale, length)
|
|
for _, ent in pairs(ents.FindByClass("fakedeath")) do
|
|
if ent:GetOwner() == self then
|
|
ent:Remove()
|
|
end
|
|
end
|
|
|
|
local ent = ents.Create("fakedeath")
|
|
if ent:IsValid() then
|
|
ent:SetOwner(self)
|
|
ent:SetModel(self:GetModel())
|
|
ent:SetSkin(self:GetSkin())
|
|
ent:SetColor(self:GetColor())
|
|
ent:SetMaterial(self:GetMaterial())
|
|
ent:SetPos(self:GetPos() + Vector(0, 0, 64))
|
|
ent:Spawn()
|
|
ent:SetModelScale(modelscale or self:GetModelScale(), 0)
|
|
|
|
ent:SetDeathSequence(sequenceid or 0)
|
|
ent:SetDeathAngles(self:GetAngles())
|
|
ent:SetDeathSequenceLength(length or 1)
|
|
|
|
self:DeleteOnRemove(ent)
|
|
end
|
|
|
|
return ent
|
|
end
|
|
|
|
local MuscularBones = {
|
|
["ValveBiped.Bip01_R_Upperarm"] = Vector(1, 2, 2),
|
|
["ValveBiped.Bip01_R_Forearm"] = Vector(1, 2, 2),
|
|
["ValveBiped.Bip01_L_Upperarm"] = Vector(1, 2, 2),
|
|
["ValveBiped.Bip01_L_Forearm"] = Vector(1, 2, 2)
|
|
}
|
|
function meta:DoMuscularBones()
|
|
if self.BuffMuscular and self:Team() == TEAM_HUMAN then
|
|
self.MuscularBones = {}
|
|
|
|
for bonename, newscale in pairs(MuscularBones) do
|
|
local boneid = self:LookupBone(bonename)
|
|
if boneid and boneid > 0 then
|
|
table.insert(self.MuscularBones, boneid)
|
|
self:ManipulateBoneScale(boneid, newscale)
|
|
end
|
|
end
|
|
elseif self.MuscularBones then
|
|
for _, boneid in pairs(self.MuscularBones) do
|
|
self:ManipulateBoneScale(boneid, Vector(1, 1, 1))
|
|
end
|
|
self.MuscularBones = nil
|
|
end
|
|
end
|
|
|
|
local NoodleArmBones = {
|
|
["ValveBiped.Bip01_R_Upperarm"] = Vector(1, 0.4, 0.4),
|
|
["ValveBiped.Bip01_R_Forearm"] = Vector(1, 0.4, 0.4),
|
|
["ValveBiped.Bip01_L_Upperarm"] = Vector(1, 0.4, 0.4),
|
|
["ValveBiped.Bip01_L_Forearm"] = Vector(1, 0.4, 0.4)
|
|
}
|
|
function meta:DoNoodleArmBones()
|
|
if self.NoObjectPickup and self:Team() == TEAM_HUMAN then
|
|
self.NoodleArmBones = {}
|
|
|
|
for bonename, newscale in pairs(NoodleArmBones) do
|
|
local boneid = self:LookupBone(bonename)
|
|
if boneid and boneid > 0 then
|
|
table.insert(self.NoodleArmBones, boneid)
|
|
self:ManipulateBoneScale(boneid, newscale)
|
|
end
|
|
end
|
|
elseif self.NoodleArmBones then
|
|
for _, boneid in pairs(self.NoodleArmBones) do
|
|
self:ManipulateBoneScale(boneid, Vector(1, 1, 1))
|
|
end
|
|
self.NoodleArmBones = nil
|
|
end
|
|
end
|
|
|
|
function meta:ChangeTeam(teamid)
|
|
local oldteam = self:Team()
|
|
self:SetTeam(teamid)
|
|
if oldteam ~= teamid then
|
|
gamemode.Call("OnPlayerChangedTeam", self, oldteam, teamid)
|
|
end
|
|
|
|
self:CollisionRulesChanged()
|
|
end
|
|
|
|
function meta:TrySpawnAsGoreChild()
|
|
for _, ent in pairs(ents.FindByClass("prop_thrownbaby")) do
|
|
if not ent.SpawnedOn then
|
|
ent.SpawnedOn = true
|
|
|
|
local ang = self:EyeAngles()
|
|
ang.roll = 0
|
|
ang.pitch = 0
|
|
|
|
local deathclass = self.DeathClass
|
|
self:SetZombieClassName("Gore Child")
|
|
self.DeathClass = nil
|
|
self.DidntSpawnOnSpawnPoint = true
|
|
self:UnSpectateAndSpawn()
|
|
self.DeathClass = deathclass
|
|
self:SetPos(ent:GetPos())
|
|
self:SetEyeAngles(ang)
|
|
self:StartFeignDeath(true)
|
|
if IsValid(self.FeignDeath) then
|
|
self.FeignDeath:SetState(1)
|
|
self.FeignDeath:SetDirection(math.random(2) == 1 and DIR_FORWARD or DIR_BACK)
|
|
end
|
|
|
|
ent:Remove()
|
|
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
function meta:AddLifeBarricadeDamage(amount)
|
|
self.LifeBarricadeDamage = self.LifeBarricadeDamage + amount
|
|
|
|
if not self:Alive() and not self:GetZombieClassTable().NeverAlive then
|
|
net.Start("zs_lifestatsbd")
|
|
net.WriteUInt(self.LifeBarricadeDamage, 24)
|
|
net.Send(self)
|
|
end
|
|
end
|
|
|
|
function meta:AddLifeHumanDamage(amount)
|
|
self.LifeHumanDamage = self.LifeHumanDamage + amount
|
|
|
|
if not self:Alive() then
|
|
net.Start("zs_lifestatshd")
|
|
net.WriteUInt(math.ceil(self.LifeHumanDamage), 24)
|
|
net.Send(self)
|
|
end
|
|
end
|
|
|
|
function meta:AddLifeBrainsEaten(amount)
|
|
self.LifeBrainsEaten = self.LifeBrainsEaten + amount
|
|
|
|
if not self:Alive() then
|
|
net.Start("zs_lifestatsbe")
|
|
net.WriteUInt(self.LifeBrainsEaten, 16)
|
|
net.Send(self)
|
|
end
|
|
end
|
|
|
|
function meta:FloatingScore(victimorpos, effectname, frags, flags)
|
|
if type(victimorpos) == "Vector" then
|
|
net.Start("zs_floatscore_vec")
|
|
net.WriteVector(victimorpos)
|
|
net.WriteString(effectname)
|
|
net.WriteInt(frags, 24)
|
|
net.WriteUInt(flags, 8)
|
|
net.Send(self)
|
|
else
|
|
net.Start("zs_floatscore")
|
|
net.WriteEntity(victimorpos)
|
|
net.WriteString(effectname)
|
|
net.WriteInt(frags, 24)
|
|
net.WriteUInt(flags, 8)
|
|
net.Send(self)
|
|
end
|
|
end
|
|
|
|
function meta:MarkAsBadProfile()
|
|
self.NoProfiling = true
|
|
end
|
|
|
|
function meta:CenterNotify(...)
|
|
net.Start("zs_centernotify")
|
|
net.WriteTable({...})
|
|
net.Send(self)
|
|
end
|
|
|
|
function meta:TopNotify(...)
|
|
net.Start("zs_topnotify")
|
|
net.WriteTable({...})
|
|
net.Send(self)
|
|
end
|
|
|
|
function meta:RefreshDynamicSpawnPoint()
|
|
local target = self:GetObserverTarget()
|
|
if (GAMEMODE:GetDynamicSpawning() and self:GetObserverMode() == OBS_MODE_CHASE and target and target:IsValid()) and
|
|
(self.ZombieEscape and target:IsPlayer() and target:Team() == TEAM_UNDEAD
|
|
or not self.ZombieEscape and target:GetClass() == "prop_creepernest" and target:GetNestBuilt()
|
|
or string.sub(target:GetClass(), 1, 12) == "info_player_") then
|
|
self.ForceDynamicSpawn = target
|
|
self.ForceSpawnAngles = self:EyeAngles()
|
|
else
|
|
self.ForceDynamicSpawn = nil
|
|
self.ForceSpawnAngles = nil
|
|
end
|
|
end
|
|
|
|
function meta:PushPackedItem(class, ...)
|
|
if self.PackedItems and ... ~= nil then
|
|
local packed = {...}
|
|
|
|
self.PackedItems[class] = self.PackedItems[class] or {}
|
|
|
|
table.insert(self.PackedItems[class], packed)
|
|
end
|
|
end
|
|
|
|
function meta:PopPackedItem(class)
|
|
if self.PackedItems and self.PackedItems[class] and self.PackedItems[class][1] ~= nil then
|
|
local index = #self.PackedItems[class]
|
|
local data = self.PackedItems[class][index]
|
|
table.remove(self.PackedItems[class], index)
|
|
|
|
return data
|
|
end
|
|
end
|
|
|
|
function meta:ChangeToCrow()
|
|
self.StartCrowing = nil
|
|
|
|
local crowclass = GAMEMODE.ZombieClasses["Crow"]
|
|
if not crowclass then return end
|
|
|
|
local curclass = self.DeathClass or self:GetZombieClass()
|
|
local crowindex = crowclass.Index
|
|
self:SetZombieClass(crowindex)
|
|
self:DoHulls(crowindex, TEAM_UNDEAD)
|
|
|
|
self.DeathClass = nil
|
|
self:UnSpectateAndSpawn()
|
|
self.DeathClass = curclass
|
|
end
|
|
|
|
function meta:SelectRandomPlayerModel()
|
|
self:SetModel(player_manager.TranslatePlayerModel(GAMEMODE.RandomPlayerModels[math.random(#GAMEMODE.RandomPlayerModels)]))
|
|
end
|
|
|
|
function meta:GiveEmptyWeapon(weptype)
|
|
if not self:HasWeapon(weptype) then
|
|
local wep = self:Give(weptype)
|
|
if wep:IsValid() and wep:IsWeapon() then
|
|
wep:EmptyAll()
|
|
end
|
|
|
|
return wep
|
|
end
|
|
end
|
|
|
|
-- Here for when garry makes weapons use 357 ammo like he does every other update.
|
|
--[[local oldgive = meta.Give
|
|
function meta:Give(...)
|
|
local wep = oldgive(self, ...)
|
|
if wep:IsValid() then
|
|
if wep.Primary and wep.Primary.Ammo and wep.Primary.Ammo ~= "none" then
|
|
self:RemoveAmmo(wep.Primary.DefaultClip - wep.Primary.ClipSize, "357")
|
|
wep:SetClip1(0)
|
|
|
|
if wep.Primary.DefaultClip > wep.Primary.ClipSize then
|
|
self:GiveAmmo(wep.Primary.DefaultClip, wep.Primary.Ammo, true)
|
|
end
|
|
wep:SetClip1(wep.Primary.ClipSize)
|
|
self:RemoveAmmo(wep.Primary.ClipSize, wep.Primary.Ammo)
|
|
end
|
|
if wep.Secondary and wep.Secondary.Ammo and wep.Secondary.Ammo ~= "none" then
|
|
self:RemoveAmmo(wep.Secondary.DefaultClip - wep.Secondary.ClipSize, "357")
|
|
wep:SetClip2(0)
|
|
|
|
if wep.Secondary.DefaultClip > wep.Secondary.ClipSize then
|
|
self:GiveAmmo(wep.Secondary.DefaultClip, wep.Secondary.Ammo, true)
|
|
end
|
|
wep:SetClip2(wep.Secondary.ClipSize)
|
|
self:RemoveAmmo(wep.Secondary.ClipSize, wep.Secondary.Ammo)
|
|
end
|
|
end
|
|
return wep
|
|
end]]
|
|
|
|
function meta:StartFeignDeath(force)
|
|
local feigndeath = self.FeignDeath
|
|
if feigndeath and feigndeath:IsValid() then
|
|
if CurTime() >= feigndeath:GetStateEndTime() then
|
|
feigndeath:SetState(1)
|
|
feigndeath:SetStateEndTime(CurTime() + 1.5)
|
|
end
|
|
elseif force or self:IsOnGround() and not self:IsPlayingTaunt() then
|
|
local wep = self:GetActiveWeapon()
|
|
if force or wep:IsValid() and not wep:IsSwinging() and CurTime() > wep:GetNextPrimaryFire() then
|
|
if wep:IsValid() and wep.StopMoaning then
|
|
wep:StopMoaning()
|
|
end
|
|
|
|
local status = self:GiveStatus("feigndeath")
|
|
if status and status:IsValid() then
|
|
status:SetStateEndTime(CurTime() + 1.5)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function meta:UpdateLegDamage()
|
|
net.Start("zs_legdamage")
|
|
net.WriteFloat(self.LegDamage)
|
|
net.Send(self)
|
|
end
|
|
|
|
function meta:SendHint()
|
|
end
|
|
|
|
local function RemoveSkyCade(groundent, timername)
|
|
if not groundent:IsValid() or not (groundent.IsBarricadeObject or groundent:IsNailedToWorldHierarchy()) then
|
|
timer.Destroy(timername)
|
|
return
|
|
end
|
|
|
|
for _, pl in pairs(player.GetAll()) do
|
|
if pl:Alive() and pl:GetGroundEntity() == groundent then
|
|
groundent:TakeDamage(3, groundent, groundent)
|
|
pl:ViewPunch(Angle(math.Rand(-25, 25), math.Rand(-25, 25), math.Rand(-25, 25)))
|
|
if math.random(9) == 1 then
|
|
groundent:EmitSound("npc/strider/creak"..math.random(4)..".wav", 65, math.random(95, 105))
|
|
end
|
|
|
|
return
|
|
end
|
|
end
|
|
|
|
timer.Destroy(timername)
|
|
end
|
|
local checkoffset = Vector(0, 0, -128)
|
|
function meta:PreventSkyCade()
|
|
local groundent = self:GetGroundEntity()
|
|
if groundent:IsValid() then
|
|
if groundent:IsNailedToWorldHierarchy() then
|
|
local phys = groundent:GetPhysicsObject()
|
|
if phys:IsValid() and phys:GetMass() <= CARRY_DRAG_MASS then
|
|
local timername = "RemoveSkyCade"..tostring(groundent)
|
|
local start = groundent:WorldSpaceCenter()
|
|
if not timer.Exists(timername) and not util.TraceHull({start = start,
|
|
endpos = start + checkoffset,
|
|
mins = groundent:OBBMins() * 0.85, maxs = groundent:OBBMaxs() * 0.85,
|
|
mask = MASK_SOLID_BRUSHONLY}).Hit then
|
|
self:MarkAsBadProfile()
|
|
timer.Create(timername, 0.25, 0, function() RemoveSkyCade(groundent, timername) end) -- Oh dear.
|
|
end
|
|
end
|
|
elseif groundent.IsBarricadeObject then
|
|
local timername = "RemoveSkyCade"..tostring(groundent)
|
|
local start = groundent:WorldSpaceCenter()
|
|
if not timer.Exists(timername) and not util.TraceHull({start = start,
|
|
endpos = start + checkoffset,
|
|
mins = groundent:OBBMins() * 0.85, maxs = groundent:OBBMaxs() * 0.85,
|
|
mask = MASK_SOLID_BRUSHONLY}).Hit then
|
|
self:MarkAsBadProfile()
|
|
timer.Create(timername, 0.25, 0, function() RemoveSkyCade(groundent, timername) end) -- Oh dear.
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function meta:CoupleWith(plheadcrab)
|
|
if self:GetZombieClassTable().Headcrab == plheadcrab:GetZombieClassTable().Name then
|
|
local status = self:GiveStatus("headcrabcouple")
|
|
if status:IsValid() then
|
|
status:SetCouple(plheadcrab)
|
|
end
|
|
end
|
|
end
|
|
|
|
function meta:FixModelAngles(velocity)
|
|
local eye = self:EyeAngles()
|
|
self:SetLocalAngles(eye)
|
|
self:SetPoseParameter("move_yaw", math.NormalizeAngle(velocity:Angle().yaw - eye.y))
|
|
end
|
|
|
|
function meta:RemoveAllStatus(bSilent, bInstant)
|
|
if bInstant then
|
|
for _, ent in pairs(ents.FindByClass("status_*")) do
|
|
if not ent.NoRemoveOnDeath and ent:GetOwner() == self then
|
|
ent:Remove()
|
|
end
|
|
end
|
|
else
|
|
for _, ent in pairs(ents.FindByClass("status_*")) do
|
|
if not ent.NoRemoveOnDeath and ent:GetOwner() == self then
|
|
ent.SilentRemove = bSilent
|
|
ent:SetDie()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function meta:RemoveStatus(sType, bSilent, bInstant, sExclude)
|
|
local removed
|
|
|
|
for _, ent in pairs(ents.FindByClass("status_"..sType)) do
|
|
if ent:GetOwner() == self and not (sExclude and ent:GetClass() == "status_"..sExclude) then
|
|
if bInstant then
|
|
ent:Remove()
|
|
else
|
|
ent.SilentRemove = bSilent
|
|
ent:SetDie()
|
|
end
|
|
removed = true
|
|
end
|
|
end
|
|
|
|
return removed
|
|
end
|
|
|
|
function meta:GetStatus(sType)
|
|
local ent = self["status_"..sType]
|
|
if ent and ent:IsValid() and ent.Owner == self then return ent end
|
|
end
|
|
|
|
function meta:GiveStatus(sType, fDie)
|
|
local cur = self:GetStatus(sType)
|
|
if cur then
|
|
if fDie then
|
|
cur:SetDie(fDie)
|
|
end
|
|
cur:SetPlayer(self, true)
|
|
return cur
|
|
else
|
|
local ent = ents.Create("status_"..sType)
|
|
if ent:IsValid() then
|
|
ent:Spawn()
|
|
if fDie then
|
|
ent:SetDie(fDie)
|
|
end
|
|
ent:SetPlayer(self)
|
|
return ent
|
|
end
|
|
end
|
|
end
|
|
|
|
function meta:UnSpectateAndSpawn()
|
|
self:UnSpectate()
|
|
self:Spawn()
|
|
end
|
|
|
|
function meta:SecondWind(pl)
|
|
if self.Gibbed or self:Alive() or self:Team() ~= TEAM_UNDEAD then return end
|
|
|
|
local pos = self:GetPos()
|
|
local angles = self:EyeAngles()
|
|
local lastattacker = self:GetLastAttacker()
|
|
local dclass = self.DeathClass
|
|
self.DeathClass = nil
|
|
self.Revived = true
|
|
self:UnSpectateAndSpawn()
|
|
self.Revived = nil
|
|
self.DeathClass = dclass
|
|
self:SetLastAttacker(lastattacker)
|
|
self:SetPos(pos)
|
|
self:SetHealth(self:Health() * 0.2)
|
|
self:SetEyeAngles(angles)
|
|
|
|
self:CallZombieFunction("OnSecondWind")
|
|
end
|
|
|
|
function meta:DropAll()
|
|
self:DropAllAmmo()
|
|
self:DropAllWeapons()
|
|
end
|
|
|
|
local function CreateRagdoll(pl)
|
|
if pl:IsValid() then pl:OldCreateRagdoll() end
|
|
end
|
|
local function SetModel(pl, mdl)
|
|
if pl:IsValid() then
|
|
pl:SetModel(mdl)
|
|
timer.Simple(0, function() CreateRagdoll(pl) end)
|
|
end
|
|
end
|
|
|
|
meta.OldCreateRagdoll = meta.CreateRagdoll
|
|
function meta:CreateRagdoll()
|
|
local status = self.status_overridemodel
|
|
if status and status:IsValid() then
|
|
local mdl = status:GetModel()
|
|
timer.Simple(0, function() SetModel(self, mdl) end)
|
|
status:SetRenderMode(RENDERMODE_NONE)
|
|
else
|
|
self:OldCreateRagdoll()
|
|
end
|
|
end
|
|
|
|
function meta:DropWeaponByType(class)
|
|
if GAMEMODE.ZombieEscape then return end
|
|
|
|
local wep = self:GetWeapon(class)
|
|
if wep and wep:IsValid() and not wep.Undroppable then
|
|
local ent = ents.Create("prop_weapon")
|
|
if ent:IsValid() then
|
|
ent:SetWeaponType(class)
|
|
ent:Spawn()
|
|
ent:SetClip1(wep:Clip1())
|
|
ent:SetClip2(wep:Clip2())
|
|
|
|
self:StripWeapon(class)
|
|
|
|
return ent
|
|
end
|
|
end
|
|
end
|
|
|
|
function meta:DropAllWeapons()
|
|
local vPos = self:GetPos()
|
|
local vVel = self:GetVelocity()
|
|
local zmax = self:OBBMaxs().z * 0.75
|
|
for _, wep in pairs(self:GetWeapons()) do
|
|
local ent = self:DropWeaponByType(wep:GetClass())
|
|
if ent and ent:IsValid() then
|
|
ent:SetPos(vPos + Vector(math.Rand(-16, 16), math.Rand(-16, 16), math.Rand(2, zmax)))
|
|
ent:SetAngles(VectorRand():Angle())
|
|
local phys = ent:GetPhysicsObject()
|
|
if phys:IsValid() then
|
|
phys:AddAngleVelocity(Vector(math.Rand(-720, 720), math.Rand(-720, 720), math.Rand(-720, 720)))
|
|
phys:ApplyForceCenter(phys:GetMass() * (math.Rand(32, 328) * VectorRand():GetNormalized() + vVel))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function meta:DropAmmoByType(ammotype, amount)
|
|
if GAMEMODE.ZombieEscape then return end
|
|
|
|
local mycount = self:GetAmmoCount(ammotype)
|
|
amount = math.min(mycount, amount or mycount)
|
|
if not amount or amount <= 0 then return end
|
|
|
|
local ent = ents.Create("prop_ammo")
|
|
if ent:IsValid() then
|
|
ent:SetAmmoType(ammotype)
|
|
ent:SetAmmo(amount)
|
|
ent:Spawn()
|
|
|
|
self:RemoveAmmo(amount, ammotype)
|
|
|
|
return ent
|
|
end
|
|
end
|
|
|
|
function meta:DropAllAmmo()
|
|
local vPos = self:GetPos()
|
|
local vVel = self:GetVelocity()
|
|
local zmax = self:OBBMaxs().z * 0.75
|
|
for ammotype in pairs(GAMEMODE.AmmoCache) do
|
|
local ent = self:DropAmmoByType(ammotype)
|
|
if ent and ent:IsValid() then
|
|
ent:SetPos(vPos + Vector(math.Rand(-16, 16), math.Rand(-16, 16), math.Rand(2, zmax)))
|
|
ent:SetAngles(VectorRand():Angle())
|
|
local phys = ent:GetPhysicsObject()
|
|
if phys:IsValid() then
|
|
phys:AddAngleVelocity(Vector(math.Rand(-720, 720), math.Rand(-720, 720), math.Rand(-720, 720)))
|
|
phys:ApplyForceCenter(phys:GetMass() * (math.Rand(32, 328) * VectorRand():GetNormalized() + vVel))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Lets other players know about our maximum health.
|
|
meta.OldSetMaxHealth = FindMetaTable("Entity").SetMaxHealth
|
|
function meta:SetMaxHealth(num)
|
|
num = math.ceil(num)
|
|
self:SetDTInt(0, num)
|
|
self:OldSetMaxHealth(num)
|
|
end
|
|
|
|
function meta:PointCashOut(ent, fmtype)
|
|
if self.m_PointQueue >= 1 and self:Team() == TEAM_HUMAN then
|
|
local points = math.floor(self.m_PointQueue)
|
|
self.m_PointQueue = self.m_PointQueue - points
|
|
|
|
self:AddPoints(points)
|
|
self:FloatingScore(ent or self.m_LastDamageDealtPosition or vector_origin, "floatingscore", points, fmtype or FM_NONE)
|
|
end
|
|
end
|
|
|
|
function meta:AddPoints(points)
|
|
self:AddFrags(points)
|
|
self:SetPoints(self:GetPoints() + points)
|
|
|
|
gamemode.Call("PlayerPointsAdded", self, points)
|
|
end
|
|
|
|
function meta:TakePoints(points)
|
|
self:SetPoints(self:GetPoints() - points)
|
|
end
|
|
|
|
function meta:UpdateAllZombieClasses()
|
|
for _, pl in pairs(player.GetAll()) do
|
|
if pl ~= self and pl:Team() == TEAM_UNDEAD then
|
|
local id = pl:GetZombieClass()
|
|
if id and 0 < id then
|
|
net.Start("zs_zclass")
|
|
net.WriteEntity(pl)
|
|
net.WriteUInt(id, 8)
|
|
net.Send(self)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function meta:CreateAmbience(class)
|
|
class = "status_"..class
|
|
|
|
for _, ent in pairs(ents.FindByClass(class)) do
|
|
if ent:GetOwner() == self then return end
|
|
end
|
|
|
|
local ent = ents.Create(class)
|
|
if ent:IsValid() then
|
|
ent:SetPos(self:LocalToWorld(self:OBBCenter()))
|
|
self[class] = ent
|
|
ent:SetOwner(self)
|
|
ent:SetParent(self)
|
|
ent:Spawn()
|
|
end
|
|
end
|
|
|
|
function meta:SetZombieClass(cl, onlyupdate, filter)
|
|
if onlyupdate then
|
|
net.Start("zs_zclass")
|
|
net.WriteEntity(self)
|
|
net.WriteUInt(cl, 8)
|
|
if filter then
|
|
net.Send(filter)
|
|
else
|
|
net.Broadcast()
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
self:CallZombieFunction("SwitchedAway")
|
|
|
|
local classtab = GAMEMODE.ZombieClasses[cl]
|
|
if classtab then
|
|
self.Class = cl
|
|
if self:Team() == TEAM_UNDEAD then
|
|
self:DoHulls(cl)
|
|
end
|
|
self:CallZombieFunction("SwitchedTo")
|
|
|
|
net.Start("zs_zclass")
|
|
net.WriteEntity(self)
|
|
net.WriteUInt(cl, 8)
|
|
if filter then
|
|
net.Send(filter)
|
|
else
|
|
net.Broadcast()
|
|
end
|
|
end
|
|
end
|
|
|
|
function meta:DoHulls(classid, teamid)
|
|
teamid = teamid or self:Team()
|
|
classid = classid or self:GetZombieClass()
|
|
|
|
if teamid == TEAM_UNDEAD then
|
|
local classtab = GAMEMODE.ZombieClasses[classid]
|
|
if classtab then
|
|
if self:Alive() then
|
|
self:SetMoveType(classtab.MoveType or MOVETYPE_WALK)
|
|
end
|
|
|
|
if classtab.ModelScale then
|
|
self:SetModelScale(classtab.ModelScale, 0)
|
|
elseif self:GetModelScale() ~= DEFAULT_MODELSCALE then
|
|
self:SetModelScale(DEFAULT_MODELSCALE, 0)
|
|
end
|
|
|
|
if not classtab.Hull or not classtab.HullDuck then
|
|
self:ResetHull()
|
|
end
|
|
if classtab.ViewOffset then
|
|
self:SetViewOffset(classtab.ViewOffset)
|
|
elseif self:GetViewOffset() ~= DEFAULT_VIEW_OFFSET then
|
|
self:SetViewOffset(DEFAULT_VIEW_OFFSET)
|
|
end
|
|
if classtab.ViewOffsetDucked then
|
|
self:SetViewOffsetDucked(classtab.ViewOffsetDucked)
|
|
elseif self:GetViewOffsetDucked() ~= DEFAULT_VIEW_OFFSET_DUCKED then
|
|
self:SetViewOffsetDucked(DEFAULT_VIEW_OFFSET_DUCKED)
|
|
end
|
|
if classtab.HullDuck then
|
|
self:SetHullDuck(classtab.HullDuck[1], classtab.HullDuck[2])
|
|
end
|
|
if classtab.Hull then
|
|
self:SetHull(classtab.Hull[1], classtab.Hull[2])
|
|
end
|
|
if classtab.StepSize then
|
|
self:SetStepSize(classtab.StepSize)
|
|
elseif self:GetStepSize() ~= DEFAULT_STEP_SIZE then
|
|
self:SetStepSize(DEFAULT_STEP_SIZE)
|
|
end
|
|
if classtab.JumpPower then
|
|
self:SetJumpPower(classtab.JumpPower)
|
|
elseif self:GetJumpPower() ~= DEFAULT_JUMP_POWER then
|
|
self:SetJumpPower(DEFAULT_JUMP_POWER)
|
|
end
|
|
|
|
self:DrawShadow(not classtab.NoShadow)
|
|
|
|
self.NoCollideAll = classtab.NoCollideAll
|
|
self.AllowTeamDamage = classtab.AllowTeamDamage
|
|
self.NeverAlive = classtab.NeverAlive
|
|
local phys = self:GetPhysicsObject()
|
|
if phys:IsValid() then
|
|
phys:SetMass(classtab.Mass or DEFAULT_MASS)
|
|
end
|
|
end
|
|
else
|
|
self:SetModelScale(DEFAULT_MODELSCALE, 0)
|
|
self:ResetHull()
|
|
self:SetViewOffset(DEFAULT_VIEW_OFFSET)
|
|
self:SetViewOffsetDucked(DEFAULT_VIEW_OFFSET_DUCKED)
|
|
self:SetStepSize(DEFAULT_STEP_SIZE)
|
|
self:SetJumpPower(DEFAULT_JUMP_POWER)
|
|
|
|
self:DrawShadow(true)
|
|
|
|
self.NoCollideAll = nil
|
|
self.AllowTeamDamage = nil
|
|
self.NeverAlive = nil
|
|
local phys = self:GetPhysicsObject()
|
|
if phys:IsValid() then
|
|
phys:SetMass(DEFAULT_MASS)
|
|
end
|
|
end
|
|
|
|
net.Start("zs_dohulls")
|
|
net.WriteEntity(self)
|
|
net.WriteUInt(classid, 16)
|
|
net.WriteUInt(teamid, 16)
|
|
net.Broadcast()
|
|
|
|
self:CollisionRulesChanged()
|
|
end
|
|
|
|
function meta:Redeem()
|
|
gamemode.Call("PlayerRedeemed", self)
|
|
end
|
|
|
|
function meta:RedeemNextFrame()
|
|
timer.Simple(0, function()
|
|
if IsValid(self) then
|
|
self:CheckRedeem(true)
|
|
end
|
|
end)
|
|
end
|
|
|
|
function meta:TakeBrains(amount)
|
|
self:AddFrags(-amount)
|
|
self.BrainsEaten = self.BrainsEaten - 1
|
|
end
|
|
|
|
function meta:AddBrains(amount)
|
|
self:AddFrags(amount)
|
|
self.BrainsEaten = self.BrainsEaten + 1
|
|
self:CheckRedeem()
|
|
end
|
|
|
|
meta.GetBrains = meta.Frags
|
|
|
|
function meta:CheckRedeem(instant)
|
|
if not self:IsValid() or self:Team() ~= TEAM_UNDEAD
|
|
or GAMEMODE:GetRedeemBrains() <= 0 or self:GetBrains() < GAMEMODE:GetRedeemBrains()
|
|
or GAMEMODE.NoRedeeming or self:GetZombieClassTable().Boss then return end
|
|
|
|
if GAMEMODE:GetWave() ~= GAMEMODE:GetNumberOfWaves() or not GAMEMODE.ObjectiveMap and GAMEMODE:GetNumberOfWaves() == 1 and CurTime() < GAMEMODE:GetWaveEnd() - 300 then
|
|
if instant then
|
|
self:Redeem()
|
|
else
|
|
self:RedeemNextFrame()
|
|
end
|
|
end
|
|
end
|
|
|
|
function meta:AntiGrief(dmginfo, overridenostrict)
|
|
if GAMEMODE.GriefStrict and not overridenostrict then
|
|
dmginfo:SetDamage(0)
|
|
dmginfo:ScaleDamage(0)
|
|
return
|
|
end
|
|
|
|
dmginfo:SetDamage(dmginfo:GetDamage() * GAMEMODE.GriefForgiveness)
|
|
|
|
self:GivePenalty(math.ceil(dmginfo:GetDamage() * 0.5))
|
|
self:ReflectDamage(dmginfo:GetDamage())
|
|
end
|
|
|
|
function meta:GivePenalty(amount)
|
|
self.m_PenaltyCarry = (self.m_PenaltyCarry or 0) + amount * 0.1
|
|
local frags = math.floor(self.m_PenaltyCarry)
|
|
if frags > 0 then
|
|
self.m_PenaltyCarry = self.m_PenaltyCarry - frags
|
|
self:GivePointPenalty(frags)
|
|
end
|
|
end
|
|
|
|
function meta:GivePointPenalty(amount)
|
|
self:SetFrags(self:Frags() - amount)
|
|
|
|
net.Start("zs_penalty")
|
|
net.WriteUInt(amount, 16)
|
|
net.Send(self)
|
|
end
|
|
|
|
function meta:ReflectDamage(damage)
|
|
local frags = self:Frags()
|
|
if frags < GAMEMODE.GriefReflectThreshold then
|
|
self:TakeDamage(math.ceil(damage * frags * -0.05 * GAMEMODE.GriefDamageMultiplier))
|
|
end
|
|
end
|
|
|
|
function meta:GiveWeaponByType(weapon, plyr, ammo)
|
|
if ammo then
|
|
local wep = self:GetActiveWeapon()
|
|
if not wep or not wep:IsValid() or not wep.Primary then return end
|
|
|
|
local ammotype = wep:ValidPrimaryAmmo()
|
|
local ammocount = wep:GetPrimaryAmmoCount()
|
|
if ammotype and ammocount then
|
|
local desiredgive = math.min(ammocount, math.ceil((GAMEMODE.AmmoCache[ammotype] or wep.Primary.ClipSize) * 5))
|
|
if desiredgive >= 1 then
|
|
wep:TakeCombinedPrimaryAmmo(desiredgive)
|
|
plyr:GiveAmmo(desiredgive, ammotype)
|
|
|
|
self:PlayGiveAmmoSound()
|
|
self:RestartGesture(ACT_GMOD_GESTURE_ITEM_GIVE)
|
|
end
|
|
end
|
|
end
|
|
|
|
local wep = self:GetActiveWeapon()
|
|
if wep:IsValid() then
|
|
local primary = wep:ValidPrimaryAmmo()
|
|
if primary and 0 < wep:Clip1() then
|
|
self:GiveAmmo(wep:Clip1(), primary, true)
|
|
wep:SetClip1(0)
|
|
end
|
|
local secondary = wep:ValidSecondaryAmmo()
|
|
if secondary and 0 < wep:Clip2() then
|
|
self:GiveAmmo(wep:Clip2(), secondary, true)
|
|
wep:SetClip2(0)
|
|
end
|
|
|
|
self:StripWeapon(weapon:GetClass())
|
|
|
|
local wep2 = plyr:Give(weapon:GetClass())
|
|
if wep2 and wep2:IsValid() then
|
|
if wep2.Primary then
|
|
local primary = wep2:ValidPrimaryAmmo()
|
|
if primary then
|
|
wep2:SetClip1(0)
|
|
plyr:RemoveAmmo(math.max(0, wep2.Primary.DefaultClip - wep2.Primary.ClipSize), primary)
|
|
end
|
|
end
|
|
if wep2.Secondary then
|
|
local secondary = wep2:ValidSecondaryAmmo()
|
|
if secondary then
|
|
wep2:SetClip2(0)
|
|
plyr:RemoveAmmo(math.max(0, wep2.Secondary.DefaultClip - wep2.Secondary.ClipSize), secondary)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function meta:Gib()
|
|
local pos = self:WorldSpaceCenter()
|
|
local headoffset = self:LocalToWorld(self:OBBMaxs()).z - pos.z
|
|
|
|
local effectdata = EffectData()
|
|
effectdata:SetEntity(self)
|
|
effectdata:SetOrigin(pos)
|
|
util.Effect("gib_player", effectdata, true, true)
|
|
|
|
self.Gibbed = CurTime()
|
|
|
|
timer.Simple(0, function() GAMEMODE:CreateGibs(pos, pos2) end)
|
|
end
|
|
|
|
function meta:GetLastAttacker()
|
|
local ent = self.LastAttacker
|
|
if ent and ent:IsValid() and ent:Team() ~= self:Team() and CurTime() <= self.LastAttacked + 10 then
|
|
return ent
|
|
end
|
|
self:SetLastAttacker()
|
|
end
|
|
|
|
function meta:SetLastAttacker(ent)
|
|
if ent then
|
|
if ent ~= self then
|
|
self.LastAttacker = ent
|
|
self.LastAttacked = CurTime()
|
|
end
|
|
else
|
|
self.LastAttacker = nil
|
|
self.LastAttacked = nil
|
|
end
|
|
end
|
|
|
|
meta.OldUnSpectate = meta.UnSpectate
|
|
function meta:UnSpectate()
|
|
if self:GetObserverMode() ~= OBS_MODE_NONE then
|
|
self:OldUnSpectate(obsm)
|
|
end
|
|
end
|