2014-10-02 08:49:54 +08:00
|
|
|
local meta = FindMetaTable("Player")
|
|
|
|
if not meta then return end
|
|
|
|
|
|
|
|
meta.GetTeamID = meta.Team
|
|
|
|
|
|
|
|
function meta:GetMaxHealthEx()
|
|
|
|
if self:Team() == TEAM_UNDEAD then
|
|
|
|
return self:GetMaxZombieHealth()
|
|
|
|
end
|
|
|
|
|
|
|
|
return self:GetMaxHealth()
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:Dismember(dismembermenttype)
|
|
|
|
local effectdata = EffectData()
|
|
|
|
effectdata:SetOrigin(self:EyePos())
|
|
|
|
effectdata:SetEntity(self)
|
|
|
|
effectdata:SetScale(dismembermenttype)
|
|
|
|
util.Effect("dismemberment", effectdata, true, true)
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:HasWon()
|
|
|
|
if self:Team() == TEAM_HUMAN and self:GetObserverMode() == OBS_MODE_ROAMING then
|
|
|
|
if SERVER then
|
|
|
|
local target = self:GetObserverTarget()
|
|
|
|
return target and target:IsValid() and target:GetClass() == "prop_obj_exit"
|
|
|
|
end
|
|
|
|
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetBossZombieIndex()
|
|
|
|
local bossclasses = {}
|
|
|
|
for _, classtable in pairs(GAMEMODE.ZombieClasses) do
|
|
|
|
if classtable.Boss then
|
|
|
|
table.insert(bossclasses, classtable.Index)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if #bossclasses == 0 then return -1 end
|
|
|
|
|
|
|
|
local desired = self:GetInfo("zs_bossclass") or ""
|
|
|
|
if GAMEMODE:IsBabyMode() then
|
|
|
|
desired = "Giga Gore Child"
|
|
|
|
elseif desired == "[RANDOM]" or desired == "" then
|
|
|
|
desired = "Nightmare"
|
|
|
|
end
|
|
|
|
|
|
|
|
local bossindex
|
|
|
|
for _, classindex in pairs(bossclasses) do
|
|
|
|
local classtable = GAMEMODE.ZombieClasses[classindex]
|
|
|
|
if string.lower(classtable.Name) == string.lower(desired) then
|
|
|
|
bossindex = classindex
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return bossindex or bossclasses[math.random(#bossclasses)]
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetAuraRange()
|
|
|
|
local wep = self:GetActiveWeapon()
|
|
|
|
return wep:IsValid() and wep.GetAuraRange and wep:GetAuraRange() or 2048
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetCoupledHeadcrab()
|
|
|
|
local status = self.m_Couple
|
|
|
|
return status and status:IsValid() and status:GetPartner() or NULL
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetPoisonDamage()
|
|
|
|
return self.PoisonRecovery and self.PoisonRecovery:IsValid() and self.PoisonRecovery:GetDamage() or 0
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetBleedDamage()
|
|
|
|
return self.Bleed and self.Bleed:IsValid() and self.Bleed:GetDamage() or 0
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:CallWeaponFunction(funcname, ...)
|
|
|
|
local wep = self:GetActiveWeapon()
|
|
|
|
if wep:IsValid() and wep[funcname] then
|
|
|
|
return wep[funcname](wep, self, ...)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:ClippedName()
|
|
|
|
local name = self:Name()
|
|
|
|
if #name > 16 then
|
|
|
|
name = string.sub(name, 1, 14)..".."
|
|
|
|
end
|
|
|
|
|
|
|
|
return name
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:DispatchAltUse()
|
|
|
|
local tr = self:TraceLine(64, MASK_SOLID, self:GetMeleeFilter())
|
|
|
|
local ent = tr.Entity
|
|
|
|
if ent and ent:IsValid() then
|
|
|
|
if ent.AltUse then
|
|
|
|
return ent:AltUse(self, tr)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:MeleeViewPunch(damage)
|
|
|
|
local maxpunch = (damage + 25) * 0.5
|
|
|
|
local minpunch = -maxpunch
|
|
|
|
self:ViewPunch(Angle(math.Rand(minpunch, maxpunch), math.Rand(minpunch, maxpunch), math.Rand(minpunch, maxpunch)))
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:NearArsenalCrate()
|
|
|
|
local pos = self:EyePos()
|
|
|
|
|
|
|
|
for _, ent in pairs(ents.FindByClass("prop_arsenalcrate")) do
|
|
|
|
local nearest = ent:NearestPoint(pos)
|
|
|
|
if pos:Distance(nearest) <= 80 and (WorldVisible(pos, nearest) or self:TraceLine(80).Entity == ent) then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
meta.IsNearArsenalCrate = meta.NearArsenalCrate
|
|
|
|
|
|
|
|
function meta:NearestArsenalCrateOwnedByOther()
|
|
|
|
local pos = self:EyePos()
|
|
|
|
|
|
|
|
for _, ent in pairs(ents.FindByClass("prop_arsenalcrate")) do
|
|
|
|
local nearest = ent:NearestPoint(pos)
|
|
|
|
local owner = ent:GetObjectOwner()
|
|
|
|
if owner ~= self and owner:IsValid() and owner:IsPlayer() and owner:Team() == TEAM_HUMAN and pos:Distance(nearest) <= 80 and (WorldVisible(pos, nearest) or self:TraceLine(80).Entity == ent) then
|
|
|
|
return ent
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:SetZombieClassName(classname)
|
|
|
|
if GAMEMODE.ZombieClasses[classname] then
|
|
|
|
self:SetZombieClass(GAMEMODE.ZombieClasses[classname].Index)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:AddLegDamage(damage)
|
|
|
|
self:SetLegDamage(self:GetLegDamage() + damage)
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:SetPoints(points)
|
|
|
|
self:SetDTInt(1, points)
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetPoints()
|
|
|
|
return self:GetDTInt(1)
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:SetPalsy(onoff, nosend)
|
|
|
|
self.m_Palsy = onoff
|
|
|
|
if SERVER and not nosend then
|
|
|
|
self:SendLua("LocalPlayer():SetPalsy("..tostring(onoff)..")")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetPalsy()
|
|
|
|
return self.m_Palsy
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:SetHemophilia(onoff, nosend)
|
|
|
|
self.m_Hemophilia = onoff
|
|
|
|
if SERVER and not nosend then
|
|
|
|
self:SendLua("LocalPlayer():SetHemophilia("..tostring(onoff)..")")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetHemophilia()
|
|
|
|
return self.m_Hemophilia
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:SetUnlucky(onoff)
|
|
|
|
self.m_Unlucky = onoff
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetUnlucky()
|
|
|
|
return self.m_Unlucky
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:SetLegDamage(damage)
|
|
|
|
self.LegDamage = CurTime() + math.min(GAMEMODE.MaxLegDamage, damage * 0.125)
|
|
|
|
if SERVER then
|
|
|
|
self:UpdateLegDamage()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:RawSetLegDamage(time)
|
|
|
|
self.LegDamage = math.min(CurTime() + GAMEMODE.MaxLegDamage, time)
|
|
|
|
if SERVER then
|
|
|
|
self:UpdateLegDamage()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:RawCapLegDamage(time)
|
|
|
|
self:RawSetLegDamage(math.max(self.LegDamage or 0, time))
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetLegDamage()
|
|
|
|
local base = self.LegDamage or 0
|
|
|
|
return math.max(0, base - CurTime())
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:ProcessDamage(dmginfo)
|
|
|
|
local attacker, inflictor = dmginfo:GetAttacker(), dmginfo:GetInflictor()
|
|
|
|
|
|
|
|
if self.DamageVulnerability then
|
|
|
|
dmginfo:SetDamage(dmginfo:GetDamage() * self.DamageVulnerability)
|
|
|
|
end
|
|
|
|
|
|
|
|
if self:Team() == TEAM_UNDEAD then
|
|
|
|
if self ~= attacker then
|
2014-11-07 13:03:40 +08:00
|
|
|
dmginfo:SetDamage(dmginfo:GetDamage() * GAMEMODE:GetZombieDamageScale(dmginfo:GetDamagePosition(), self))
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
return self:CallZombieFunction("ProcessDamage", dmginfo)
|
|
|
|
elseif attacker:IsValid() and attacker:IsPlayer() and attacker:Team() == TEAM_UNDEAD and inflictor:IsValid() and inflictor == attacker:GetActiveWeapon() then
|
|
|
|
local damage = dmginfo:GetDamage()
|
|
|
|
|
|
|
|
local scale = inflictor.SlowDownScale or 1
|
|
|
|
if damage >= 40 or scale > 1 then
|
|
|
|
local dolegdamage = true
|
|
|
|
if inflictor.SlowDownImmunityTime then
|
|
|
|
if CurTime() < (self.SlowDownImmunityTime or 0) then
|
|
|
|
dolegdamage = false
|
|
|
|
else
|
|
|
|
self.SlowDownImmunityTime = CurTime() + inflictor.SlowDownImmunityTime
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if dolegdamage then
|
|
|
|
self:RawCapLegDamage(self:GetLegDamage() + CurTime() + damage * 0.04 * (inflictor.SlowDownScale or 1))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if self:GetHemophilia() and damage >= 5 then
|
|
|
|
local dmgtype = dmginfo:GetDamageType()
|
|
|
|
if dmgtype == 0
|
|
|
|
or bit.band(dmgtype, DMG_SLASH) ~= 0
|
|
|
|
or bit.band(dmgtype, DMG_CLUB) ~= 0
|
|
|
|
or bit.band(dmgtype, DMG_BULLET) ~= 0
|
|
|
|
or bit.band(dmgtype, DMG_BUCKSHOT) ~= 0
|
|
|
|
or bit.band(dmgtype, DMG_CRUSH) ~= 0 then
|
|
|
|
local bleed = self:GiveStatus("bleed")
|
|
|
|
if bleed and bleed:IsValid() then
|
|
|
|
bleed:AddDamage(damage * 0.2)
|
|
|
|
if attacker:IsValid() and attacker:IsPlayer() then
|
|
|
|
bleed.Damager = attacker
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:KnockDown(time)
|
|
|
|
if self:Team() ~= TEAM_UNDEAD then
|
|
|
|
self:GiveStatus("knockdown", time or 3)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetZombieClass()
|
|
|
|
return self.Class or GAMEMODE.DefaultZombieClass
|
|
|
|
end
|
|
|
|
|
|
|
|
local ZombieClasses = GM.ZombieClasses
|
|
|
|
function meta:GetZombieClassTable()
|
|
|
|
return ZombieClasses[self:GetZombieClass()]
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:CallZombieFunction(funcname, ...)
|
|
|
|
if self:Team() == TEAM_UNDEAD then
|
|
|
|
local tab = self:GetZombieClassTable()
|
|
|
|
if tab[funcname] then
|
|
|
|
return tab[funcname](tab, self, ...)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:TraceLine(distance, mask, filter, start)
|
|
|
|
start = start or self:GetShootPos()
|
|
|
|
return util.TraceLine({start = start, endpos = start + self:GetAimVector() * distance, filter = filter or self, mask = mask})
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:TraceHull(distance, mask, size, filter, start)
|
|
|
|
start = start or self:GetShootPos()
|
|
|
|
return util.TraceHull({start = start, endpos = start + self:GetAimVector() * distance, filter = filter or self, mask = mask, mins = Vector(-size, -size, -size), maxs = Vector(size, size, size)})
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:DoubleTrace(distance, mask, size, mask2, filter)
|
|
|
|
local tr1 = self:TraceLine(distance, mask, filter)
|
|
|
|
if tr1.Hit then return tr1 end
|
|
|
|
if mask2 then
|
|
|
|
local tr2 = self:TraceLine(distance, mask2, filter)
|
|
|
|
if tr2.Hit then return tr2 end
|
|
|
|
end
|
|
|
|
|
|
|
|
local tr3 = self:TraceHull(distance, mask, size, filter)
|
|
|
|
if tr3.Hit then return tr3 end
|
|
|
|
if mask2 then
|
|
|
|
local tr4 = self:TraceHull(distance, mask2, size, filter)
|
|
|
|
if tr4.Hit then return tr4 end
|
|
|
|
end
|
|
|
|
|
|
|
|
return tr1
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:SetSpeed(speed)
|
|
|
|
if not speed then speed = 200 end
|
|
|
|
|
|
|
|
self:SetWalkSpeed(speed)
|
|
|
|
self:SetRunSpeed(speed)
|
|
|
|
self:SetMaxSpeed(speed)
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:SetHumanSpeed(speed)
|
|
|
|
if self:Team() == TEAM_HUMAN then self:SetSpeed(speed) end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:ResetSpeed(noset, health)
|
|
|
|
if not self:IsValid() then return end
|
|
|
|
|
|
|
|
if self:Team() == TEAM_UNDEAD then
|
|
|
|
local speed = self:GetZombieClassTable().Speed * GAMEMODE.ZombieSpeedMultiplier
|
|
|
|
self:SetSpeed(speed)
|
|
|
|
return speed
|
|
|
|
end
|
|
|
|
|
|
|
|
local wep = self:GetActiveWeapon()
|
|
|
|
local speed
|
|
|
|
|
|
|
|
if wep:IsValid() and wep.GetWalkSpeed then
|
|
|
|
speed = wep:GetWalkSpeed()
|
|
|
|
end
|
|
|
|
|
|
|
|
if not speed then
|
|
|
|
speed = wep.WalkSpeed or SPEED_NORMAL
|
|
|
|
end
|
|
|
|
|
|
|
|
if self.HumanSpeedAdder and self:Team() == TEAM_HUMAN and 32 < speed then
|
|
|
|
speed = speed + self.HumanSpeedAdder
|
|
|
|
end
|
|
|
|
|
|
|
|
--[[if self:IsHolding() then
|
|
|
|
local status = self.status_human_holding
|
|
|
|
if status and status:IsValid() and status:GetObject():IsValid() and status:GetObject():GetPhysicsObject():IsValid() then
|
|
|
|
speed = math.min(speed, math.max(CARRY_SPEEDLOSS_MINSPEED, speed - status:GetObject():GetPhysicsObject():GetMass() * CARRY_SPEEDLOSS_PERKG))
|
|
|
|
end
|
|
|
|
end]]
|
|
|
|
|
|
|
|
if 32 < speed and not GAMEMODE.ZombieEscape then
|
|
|
|
if not health then health = self:Health() end
|
|
|
|
if health < 60 then
|
|
|
|
speed = math.max(88, speed - speed * 0.4 * (1 - health / 60))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if not noset then
|
|
|
|
self:SetSpeed(speed)
|
|
|
|
end
|
|
|
|
|
|
|
|
return speed
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:ResetJumpPower(noset)
|
|
|
|
local power = DEFAULT_JUMP_POWER
|
|
|
|
|
|
|
|
if self:Team() == TEAM_UNDEAD then
|
|
|
|
power = self:CallZombieFunction("GetJumpPower") or power
|
|
|
|
|
|
|
|
local classtab = self:GetZombieClassTable()
|
|
|
|
if classtab and classtab.JumpPower then
|
|
|
|
power = classtab.JumpPower
|
|
|
|
end
|
|
|
|
else
|
|
|
|
if self:GetBarricadeGhosting() then
|
|
|
|
power = power * 0.25
|
|
|
|
if not noset then
|
|
|
|
self:SetJumpPower(power)
|
|
|
|
end
|
|
|
|
|
|
|
|
return power
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local wep = self:GetActiveWeapon()
|
|
|
|
if wep and wep.ResetJumpPower then
|
|
|
|
power = wep:ResetJumpPower(power) or power
|
|
|
|
end
|
|
|
|
|
|
|
|
if not noset then
|
|
|
|
self:SetJumpPower(power)
|
|
|
|
end
|
|
|
|
|
|
|
|
return power
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:SetBarricadeGhosting(b)
|
|
|
|
if b and self.NoGhosting then return end
|
|
|
|
|
|
|
|
self:SetDTBool(0, b)
|
|
|
|
self:CollisionRulesChanged()
|
|
|
|
|
|
|
|
self:ResetJumpPower()
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetBarricadeGhosting()
|
|
|
|
return self:GetDTBool(0)
|
|
|
|
end
|
|
|
|
meta.IsBarricadeGhosting = meta.GetBarricadeGhosting
|
|
|
|
|
|
|
|
function meta:ShouldBarricadeGhostWith(ent)
|
|
|
|
return ent:IsBarricadeProp()
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:BarricadeGhostingThink()
|
|
|
|
if self:KeyDown(IN_ZOOM) or self:ActiveBarricadeGhosting() then return end
|
|
|
|
|
|
|
|
self:SetBarricadeGhosting(false)
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:ShouldNotCollide(ent)
|
2014-10-20 19:45:25 +08:00
|
|
|
if ent:IsValid() then
|
|
|
|
if ent:IsPlayer() then
|
|
|
|
return self:Team() == ent:Team() or self.NoCollideAll or ent.NoCollideAll
|
|
|
|
end
|
|
|
|
|
|
|
|
return self:GetBarricadeGhosting() and ent:IsBarricadeProp() or self:Team() == TEAM_HUMAN and ent:GetPhysicsObject():IsValid() and ent:GetPhysicsObject():HasGameFlag(FVPHYSICS_PLAYER_HELD)
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
2014-10-20 19:45:25 +08:00
|
|
|
return false
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
local function nocollidetimer(self, timername)
|
|
|
|
if self:IsValid() then
|
|
|
|
for _, e in pairs(ents.FindInBox(self:WorldSpaceAABB())) do
|
|
|
|
if e:IsPlayer() and e ~= self and GAMEMODE:ShouldCollide(self, e) then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
self:SetCollisionGroup(COLLISION_GROUP_PLAYER)
|
|
|
|
end
|
|
|
|
|
|
|
|
timer.Destroy(timername)
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:TemporaryNoCollide(force)
|
|
|
|
if self:GetCollisionGroup() ~= COLLISION_GROUP_PLAYER and not force then return end
|
|
|
|
|
|
|
|
for _, e in pairs(ents.FindInBox(self:WorldSpaceAABB())) do
|
|
|
|
if e:IsPlayer() and e ~= self and GAMEMODE:ShouldCollide(self, e) then
|
|
|
|
self:SetCollisionGroup(COLLISION_GROUP_DEBRIS_TRIGGER)
|
|
|
|
|
|
|
|
local timername = "TemporaryNoCollide"..self:UniqueID()
|
|
|
|
timer.CreateEx(timername, 0, 0, nocollidetimer, self, timername)
|
|
|
|
|
|
|
|
return
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
self:SetCollisionGroup(COLLISION_GROUP_PLAYER)
|
|
|
|
end
|
|
|
|
|
|
|
|
meta.OldSetHealth = FindMetaTable("Entity").SetHealth
|
|
|
|
function meta:SetHealth(health)
|
|
|
|
self:OldSetHealth(health)
|
|
|
|
if self:Team() == TEAM_HUMAN and 1 <= health then
|
|
|
|
self:ResetSpeed(nil, health)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:IsHeadcrab()
|
|
|
|
return self:Team() == TEAM_UNDEAD and GAMEMODE.ZombieClasses[self:GetZombieClass()].IsHeadcrab
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:AirBrake()
|
|
|
|
local vel = self:GetVelocity()
|
|
|
|
|
|
|
|
vel.x = vel.x * 0.15
|
|
|
|
vel.y = vel.y * 0.15
|
|
|
|
if vel.z > 0 then
|
|
|
|
vel.z = vel.z * 0.15
|
|
|
|
end
|
|
|
|
|
|
|
|
self:SetLocalVelocity(vel)
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetMeleeFilter()
|
|
|
|
return GAMEMODE.RoundEnded and {self} or team.GetPlayers(self:Team())
|
|
|
|
end
|
|
|
|
meta.GetTraceFilter = meta.GetMeleeFilter
|
|
|
|
|
|
|
|
function meta:MeleeTrace(distance, size, filter, start)
|
|
|
|
return self:TraceHull(distance, MASK_SOLID, size, filter, start)
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:PenetratingMeleeTrace(distance, size, prehit, start)
|
|
|
|
start = start or self:GetShootPos()
|
|
|
|
|
|
|
|
local t = {}
|
|
|
|
local trace = {start = start, endpos = start + self:GetAimVector() * distance, filter = self:GetMeleeFilter(), mask = MASK_SOLID, mins = Vector(-size, -size, -size), maxs = Vector(size, size, size)}
|
|
|
|
local onlyhitworld
|
|
|
|
for i=1, 50 do
|
|
|
|
local tr = util.TraceHull(trace)
|
|
|
|
|
|
|
|
if not tr.Hit then break end
|
|
|
|
|
|
|
|
if tr.HitWorld then
|
|
|
|
table.insert(t, tr)
|
|
|
|
break
|
|
|
|
end
|
|
|
|
|
|
|
|
if onlyhitworld then break end
|
|
|
|
|
|
|
|
local ent = tr.Entity
|
|
|
|
if ent and ent:IsValid() then
|
|
|
|
if not ent:IsPlayer() then
|
|
|
|
trace.mask = MASK_SOLID_BRUSHONLY
|
|
|
|
onlyhitworld = true
|
|
|
|
end
|
|
|
|
|
|
|
|
table.insert(t, tr)
|
|
|
|
table.insert(trace.filter, ent)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if prehit and (#t == 1 and not t[1].HitNonWorld and prehit.HitNonWorld or #t == 0 and prehit.HitNonWorld) then
|
|
|
|
t[1] = prehit
|
|
|
|
end
|
|
|
|
|
|
|
|
return t
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:ActiveBarricadeGhosting(override)
|
|
|
|
if self:Team() ~= TEAM_HUMAN and not override or not self:GetBarricadeGhosting() then return false end
|
|
|
|
|
|
|
|
for _, ent in pairs(ents.FindInBox(self:WorldSpaceAABB())) do
|
|
|
|
if self:ShouldBarricadeGhostWith(ent) then return true end
|
|
|
|
end
|
|
|
|
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:IsHolding()
|
|
|
|
return self:GetHolding():IsValid()
|
|
|
|
end
|
|
|
|
meta.IsCarrying = meta.IsHolding
|
|
|
|
|
|
|
|
function meta:GetHolding()
|
|
|
|
local status = self.status_human_holding
|
|
|
|
if status and status:IsValid() then
|
|
|
|
local obj = status:GetObject()
|
|
|
|
if obj:IsValid() then return obj end
|
|
|
|
end
|
|
|
|
|
|
|
|
return NULL
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetMaxZombieHealth()
|
|
|
|
return self:GetZombieClassTable().Health
|
|
|
|
end
|
|
|
|
|
|
|
|
local oldmaxhealth = FindMetaTable("Entity").GetMaxHealth
|
|
|
|
function meta:GetMaxHealth()
|
|
|
|
if self:Team() == TEAM_UNDEAD then
|
|
|
|
return self:GetMaxZombieHealth()
|
|
|
|
end
|
|
|
|
|
|
|
|
return oldmaxhealth(self)
|
|
|
|
end
|
|
|
|
|
|
|
|
if not meta.OldAlive then
|
|
|
|
meta.OldAlive = meta.Alive
|
|
|
|
function meta:Alive()
|
|
|
|
return self:GetObserverMode() == OBS_MODE_NONE and not self.NeverAlive and self:OldAlive()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local VoiceSets = {}
|
|
|
|
|
|
|
|
VoiceSets["male"] = {
|
|
|
|
["GiveAmmoSounds"] = {
|
|
|
|
Sound("vo/npc/male01/ammo03.wav"),
|
|
|
|
Sound("vo/npc/male01/ammo04.wav"),
|
|
|
|
Sound("vo/npc/male01/ammo05.wav")
|
|
|
|
},
|
|
|
|
["PainSoundsLight"] = {
|
|
|
|
Sound("vo/npc/male01/ow01.wav"),
|
|
|
|
Sound("vo/npc/male01/ow02.wav"),
|
|
|
|
Sound("vo/npc/male01/pain01.wav"),
|
|
|
|
Sound("vo/npc/male01/pain02.wav"),
|
|
|
|
Sound("vo/npc/male01/pain03.wav")
|
|
|
|
},
|
|
|
|
["PainSoundsMed"] = {
|
|
|
|
Sound("vo/npc/male01/pain04.wav"),
|
|
|
|
Sound("vo/npc/male01/pain05.wav"),
|
|
|
|
Sound("vo/npc/male01/pain06.wav")
|
|
|
|
},
|
|
|
|
["PainSoundsHeavy"] = {
|
|
|
|
Sound("vo/npc/male01/pain07.wav"),
|
|
|
|
Sound("vo/npc/male01/pain08.wav"),
|
|
|
|
Sound("vo/npc/male01/pain09.wav")
|
|
|
|
},
|
|
|
|
["DeathSounds"] = {
|
|
|
|
Sound("vo/npc/male01/no02.wav"),
|
|
|
|
Sound("ambient/voices/citizen_beaten1.wav"),
|
|
|
|
Sound("ambient/voices/citizen_beaten3.wav"),
|
|
|
|
Sound("ambient/voices/citizen_beaten4.wav"),
|
|
|
|
Sound("ambient/voices/citizen_beaten5.wav"),
|
|
|
|
Sound("vo/npc/male01/pain07.wav"),
|
|
|
|
Sound("vo/npc/male01/pain08.wav")
|
|
|
|
},
|
|
|
|
["EyePoisonedSounds"] = {
|
|
|
|
Sound("ambient/voices/m_scream1.wav")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VoiceSets["barney"] = {
|
|
|
|
["GiveAmmoSounds"] = {
|
|
|
|
Sound("items/ammo_pickup.wav")
|
|
|
|
},
|
|
|
|
["PainSoundsLight"] = {
|
|
|
|
Sound("vo/npc/Barney/ba_pain02.wav"),
|
|
|
|
Sound("vo/npc/Barney/ba_pain07.wav"),
|
|
|
|
Sound("vo/npc/Barney/ba_pain04.wav")
|
|
|
|
},
|
|
|
|
["PainSoundsMed"] = {
|
|
|
|
Sound("vo/npc/Barney/ba_pain01.wav"),
|
|
|
|
Sound("vo/npc/Barney/ba_pain08.wav"),
|
|
|
|
Sound("vo/npc/Barney/ba_pain10.wav")
|
|
|
|
},
|
|
|
|
["PainSoundsHeavy"] = {
|
|
|
|
Sound("vo/npc/Barney/ba_pain05.wav"),
|
|
|
|
Sound("vo/npc/Barney/ba_pain06.wav"),
|
|
|
|
Sound("vo/npc/Barney/ba_pain09.wav")
|
|
|
|
},
|
|
|
|
["DeathSounds"] = {
|
|
|
|
Sound("vo/npc/Barney/ba_ohshit03.wav"),
|
|
|
|
Sound("vo/npc/Barney/ba_no01.wav"),
|
|
|
|
Sound("vo/npc/Barney/ba_no02.wav"),
|
|
|
|
Sound("vo/npc/Barney/ba_pain03.wav")
|
|
|
|
},
|
|
|
|
["EyePoisonedSounds"] = {
|
|
|
|
Sound("vo/k_lab/ba_thingaway02.wav")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VoiceSets["female"] = {
|
|
|
|
["GiveAmmoSounds"] = {
|
|
|
|
Sound("vo/npc/female01/ammo03.wav"),
|
|
|
|
Sound("vo/npc/female01/ammo04.wav"),
|
|
|
|
Sound("vo/npc/female01/ammo05.wav")
|
|
|
|
},
|
|
|
|
["PainSoundsLight"] = {
|
|
|
|
Sound("vo/npc/female01/pain01.wav"),
|
|
|
|
Sound("vo/npc/female01/pain02.wav"),
|
|
|
|
Sound("vo/npc/female01/pain03.wav")
|
|
|
|
},
|
|
|
|
["PainSoundsMed"] = {
|
|
|
|
Sound("vo/npc/female01/pain04.wav"),
|
|
|
|
Sound("vo/npc/female01/pain05.wav"),
|
|
|
|
Sound("vo/npc/female01/pain06.wav")
|
|
|
|
},
|
|
|
|
["PainSoundsHeavy"] = {
|
|
|
|
Sound("vo/npc/female01/pain07.wav"),
|
|
|
|
Sound("vo/npc/female01/pain08.wav"),
|
|
|
|
Sound("vo/npc/female01/pain09.wav")
|
|
|
|
},
|
|
|
|
["DeathSounds"] = {
|
|
|
|
Sound("vo/npc/female01/no01.wav"),
|
|
|
|
Sound("vo/npc/female01/ow01.wav"),
|
|
|
|
Sound("vo/npc/female01/ow02.wav"),
|
|
|
|
Sound("vo/npc/female01/goodgod.wav"),
|
|
|
|
Sound("ambient/voices/citizen_beaten2.wav")
|
|
|
|
},
|
|
|
|
["EyePoisonedSounds"] = {
|
|
|
|
Sound("ambient/voices/f_scream1.wav")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VoiceSets["alyx"] = {
|
|
|
|
["GiveAmmoSounds"] = {
|
|
|
|
Sound("vo/npc/female01/ammo03.wav"),
|
|
|
|
Sound("vo/npc/female01/ammo04.wav"),
|
|
|
|
Sound("vo/npc/female01/ammo05.wav")
|
|
|
|
},
|
|
|
|
["PainSoundsLight"] = {
|
|
|
|
Sound("vo/npc/Alyx/gasp03.wav"),
|
|
|
|
Sound("vo/npc/Alyx/hurt08.wav")
|
|
|
|
},
|
|
|
|
["PainSoundsMed"] = {
|
|
|
|
Sound("vo/npc/Alyx/hurt04.wav"),
|
|
|
|
Sound("vo/npc/Alyx/hurt06.wav"),
|
|
|
|
Sound("vo/Citadel/al_struggle07.wav"),
|
|
|
|
Sound("vo/Citadel/al_struggle08.wav")
|
|
|
|
},
|
|
|
|
["PainSoundsHeavy"] = {
|
|
|
|
Sound("vo/npc/Alyx/hurt05.wav"),
|
|
|
|
Sound("vo/npc/Alyx/hurt06.wav")
|
|
|
|
},
|
|
|
|
["DeathSounds"] = {
|
|
|
|
Sound("vo/npc/Alyx/no01.wav"),
|
|
|
|
Sound("vo/npc/Alyx/no02.wav"),
|
|
|
|
Sound("vo/npc/Alyx/no03.wav"),
|
|
|
|
Sound("vo/Citadel/al_dadgordonno_c.wav"),
|
|
|
|
Sound("vo/Streetwar/Alyx_gate/al_no.wav")
|
|
|
|
},
|
|
|
|
["EyePoisonedSounds"] = {
|
|
|
|
Sound("vo/npc/Alyx/uggh01.wav"),
|
|
|
|
Sound("vo/npc/Alyx/uggh02.wav")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VoiceSets["combine"] = {
|
|
|
|
["GiveAmmoSounds"] = {
|
|
|
|
Sound("npc/combine_soldier/vo/hardenthatposition.wav"),
|
|
|
|
Sound("npc/combine_soldier/vo/readyweapons.wav"),
|
|
|
|
Sound("npc/combine_soldier/vo/weareinaninfestationzone.wav"),
|
|
|
|
Sound("npc/metropolice/vo/dismountinghardpoint.wav")
|
|
|
|
},
|
|
|
|
["PainSoundsLight"] = {
|
|
|
|
Sound("npc/combine_soldier/pain1.wav"),
|
|
|
|
Sound("npc/combine_soldier/pain2.wav"),
|
|
|
|
Sound("npc/combine_soldier/pain3.wav")
|
|
|
|
},
|
|
|
|
["PainSoundsMed"] = {
|
|
|
|
Sound("npc/metropolice/pain1.wav"),
|
|
|
|
Sound("npc/metropolice/pain2.wav")
|
|
|
|
},
|
|
|
|
["PainSoundsHeavy"] = {
|
|
|
|
Sound("npc/metropolice/pain3.wav"),
|
|
|
|
Sound("npc/metropolice/pain4.wav")
|
|
|
|
},
|
|
|
|
["DeathSounds"] = {
|
|
|
|
Sound("npc/combine_soldier/die1.wav"),
|
|
|
|
Sound("npc/combine_soldier/die2.wav"),
|
|
|
|
Sound("npc/combine_soldier/die3.wav")
|
|
|
|
},
|
|
|
|
["EyePoisonSounds"] = {
|
|
|
|
Sound("npc/combine_soldier/die1.wav"),
|
|
|
|
Sound("npc/combine_soldier/die2.wav"),
|
|
|
|
Sound("npc/metropolice/vo/shit.wav")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VoiceSets["monk"] = {
|
|
|
|
["GiveAmmoSounds"] = {
|
|
|
|
Sound("vo/ravenholm/monk_giveammo01.wav")
|
|
|
|
},
|
|
|
|
["PainSoundsLight"] = {
|
|
|
|
Sound("vo/ravenholm/monk_pain01.wav"),
|
|
|
|
Sound("vo/ravenholm/monk_pain02.wav"),
|
|
|
|
Sound("vo/ravenholm/monk_pain03.wav"),
|
|
|
|
Sound("vo/ravenholm/monk_pain05.wav")
|
|
|
|
},
|
|
|
|
["PainSoundsMed"] = {
|
|
|
|
Sound("vo/ravenholm/monk_pain04.wav"),
|
|
|
|
Sound("vo/ravenholm/monk_pain06.wav"),
|
|
|
|
Sound("vo/ravenholm/monk_pain07.wav"),
|
|
|
|
Sound("vo/ravenholm/monk_pain08.wav")
|
|
|
|
},
|
|
|
|
["PainSoundsHeavy"] = {
|
|
|
|
Sound("vo/ravenholm/monk_pain09.wav"),
|
|
|
|
Sound("vo/ravenholm/monk_pain10.wav"),
|
|
|
|
Sound("vo/ravenholm/monk_pain12.wav")
|
|
|
|
},
|
|
|
|
["DeathSounds"] = {
|
|
|
|
Sound("vo/ravenholm/monk_death07.wav")
|
|
|
|
},
|
|
|
|
["EyePoisonSounds"] = {
|
|
|
|
Sound("vo/ravenholm/monk_death07.wav")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function meta:PlayEyePoisonedSound()
|
|
|
|
local snds = VoiceSets[self.VoiceSet].EyePoisonSounds
|
|
|
|
if snds then
|
|
|
|
self:EmitSound(snds[math.random(1, #snds)])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:PlayGiveAmmoSound()
|
|
|
|
local snds = VoiceSets[self.VoiceSet].GiveAmmoSounds
|
|
|
|
if snds then
|
|
|
|
self:EmitSound(snds[math.random(1, #snds)])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:PlayDeathSound()
|
|
|
|
local snds = VoiceSets[self.VoiceSet].DeathSounds
|
|
|
|
if snds then
|
|
|
|
self:EmitSound(snds[math.random(1, #snds)])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:PlayZombieDeathSound()
|
|
|
|
if not self:CallZombieFunction("PlayDeathSound") then
|
|
|
|
local snds = self:GetZombieClassTable().DeathSounds
|
|
|
|
if snds then
|
|
|
|
self:EmitSound(snds[math.random(#snds)])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:PlayPainSound()
|
|
|
|
if CurTime() < self.NextPainSound then return end
|
|
|
|
|
|
|
|
local snds
|
|
|
|
|
|
|
|
if self:Team() == TEAM_UNDEAD then
|
|
|
|
if self:CallZombieFunction("PlayPainSound") then return end
|
|
|
|
snds = self:GetZombieClassTable().PainSounds
|
|
|
|
else
|
|
|
|
local set = VoiceSets[self.VoiceSet]
|
|
|
|
if set then
|
|
|
|
local health = self:Health()
|
|
|
|
if 70 <= health then
|
|
|
|
snds = set.PainSoundsLight
|
|
|
|
elseif 35 <= health then
|
|
|
|
snds = set.PainSoundsMed
|
|
|
|
else
|
|
|
|
snds = set.PainSoundsHeavy
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if snds then
|
|
|
|
local snd = snds[math.random(#snds)]
|
|
|
|
if snd then
|
|
|
|
self:EmitSound(snd)
|
|
|
|
self.NextPainSound = CurTime() + SoundDuration(snd) - 0.1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local ViewHullMins = Vector(-8, -8, -8)
|
|
|
|
local ViewHullMaxs = Vector(8, 8, 8)
|
|
|
|
function meta:GetThirdPersonCameraPos(origin, angles)
|
|
|
|
local allplayers = player.GetAll()
|
|
|
|
local tr = util.TraceHull({start = origin, endpos = origin + angles:Forward() * -math.max(36, self:Team() == TEAM_UNDEAD and self:GetZombieClassTable().CameraDistance or self:BoundingRadius()), mask = MASK_SHOT, filter = allplayers, mins = ViewHullMins, maxs = ViewHullMaxs})
|
|
|
|
return tr.HitPos + tr.HitNormal * 3
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Override these because they're different in 1st person and on the server.
|
|
|
|
function meta:SyncAngles()
|
|
|
|
local ang = self:EyeAngles()
|
|
|
|
ang.pitch = 0
|
|
|
|
ang.roll = 0
|
|
|
|
return ang
|
|
|
|
end
|
|
|
|
meta.GetAngles = meta.SyncAngles
|
|
|
|
|
|
|
|
function meta:GetForward()
|
|
|
|
return self:SyncAngles():Forward()
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetUp()
|
|
|
|
return self:SyncAngles():Up()
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetRight()
|
|
|
|
return self:SyncAngles():Right()
|
|
|
|
end
|