2014-10-02 08:49:54 +08:00
|
|
|
local meta = FindMetaTable("Player")
|
2018-05-02 06:32:59 +08:00
|
|
|
|
|
|
|
local util_SharedRandom = util.SharedRandom
|
|
|
|
local PLAYERANIMEVENT_FLINCH_HEAD = PLAYERANIMEVENT_FLINCH_HEAD
|
|
|
|
local PLAYERANIMEVENT_ATTACK_PRIMARY = PLAYERANIMEVENT_ATTACK_PRIMARY
|
|
|
|
local GESTURE_SLOT_FLINCH = GESTURE_SLOT_FLINCH
|
|
|
|
local GESTURE_SLOT_ATTACK_AND_RELOAD = GESTURE_SLOT_ATTACK_AND_RELOAD
|
|
|
|
local HITGROUP_HEAD = HITGROUP_HEAD
|
|
|
|
local HITGROUP_CHEST = HITGROUP_CHEST
|
|
|
|
local HITGROUP_STOMACH = HITGROUP_STOMACH
|
|
|
|
local HITGROUP_LEFTLEG = HITGROUP_LEFTLEG
|
|
|
|
local HITGROUP_RIGHTLEG = HITGROUP_RIGHTLEG
|
|
|
|
local HITGROUP_LEFTARM = HITGROUP_LEFTARM
|
|
|
|
local HITGROUP_RIGHTARM = HITGROUP_RIGHTARM
|
|
|
|
local TEAM_UNDEAD = TEAM_UNDEAD
|
|
|
|
local TEAM_SPECTATOR = TEAM_SPECTATOR
|
|
|
|
local TEAM_HUMAN = TEAM_HUMAN
|
|
|
|
local IN_ZOOM = IN_ZOOM
|
|
|
|
local MASK_SOLID = MASK_SOLID
|
|
|
|
local MASK_SOLID_BRUSHONLY = MASK_SOLID_BRUSHONLY
|
|
|
|
local util_TraceLine = util.TraceLine
|
|
|
|
local util_TraceHull = util.TraceHull
|
|
|
|
|
|
|
|
local getmetatable = getmetatable
|
|
|
|
|
|
|
|
local M_Entity = FindMetaTable("Entity")
|
|
|
|
|
|
|
|
local P_Team = meta.Team
|
|
|
|
|
|
|
|
local E_IsValid = M_Entity.IsValid
|
|
|
|
local E_GetDTBool = M_Entity.GetDTBool
|
|
|
|
local E_GetTable = M_Entity.GetTable
|
2014-10-02 08:49:54 +08:00
|
|
|
|
2019-12-01 10:58:52 +08:00
|
|
|
function meta:LogID()
|
|
|
|
return "<"..self:SteamID().."> "..self:Name()
|
|
|
|
end
|
|
|
|
|
2014-10-02 08:49:54 +08:00
|
|
|
function meta:GetMaxHealthEx()
|
2018-05-02 06:32:59 +08:00
|
|
|
if P_Team(self) == TEAM_UNDEAD then
|
2014-10-02 08:49:54 +08:00
|
|
|
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
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:DoRandomEvent(event, maxrandom_s1)
|
|
|
|
self:DoCustomAnimEvent(event, math.ceil(util_SharedRandom("anim", 0, maxrandom_s1, self:EntIndex())))
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:DoZombieEvent()
|
|
|
|
self:DoRandomEvent(PLAYERANIMEVENT_ATTACK_PRIMARY, 7)
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:DoFlinchEvent(hitgroup)
|
|
|
|
local base = util_SharedRandom("flinch", 1, self:EntIndex())
|
|
|
|
if hitgroup == HITGROUP_HEAD then
|
|
|
|
self:DoCustomAnimEvent(PLAYERANIMEVENT_FLINCH_HEAD, base * 2 + 4)
|
|
|
|
elseif hitgroup == HITGROUP_CHEST then
|
|
|
|
self:DoCustomAnimEvent(PLAYERANIMEVENT_FLINCH_HEAD, base * 2 + 1)
|
|
|
|
elseif hitgroup == HITGROUP_STOMACH then
|
|
|
|
self:DoCustomAnimEvent(PLAYERANIMEVENT_FLINCH_HEAD, base * 2 + 10)
|
|
|
|
elseif hitgroup == HITGROUP_LEFTARM then
|
|
|
|
self:DoCustomAnimEvent(PLAYERANIMEVENT_FLINCH_HEAD, base + 8)
|
|
|
|
elseif hitgroup == HITGROUP_RIGHTARM then
|
|
|
|
self:DoCustomAnimEvent(PLAYERANIMEVENT_FLINCH_HEAD, base + 9)
|
|
|
|
elseif hitgroup == HITGROUP_LEFTLEG then
|
|
|
|
self:DoCustomAnimEvent(PLAYERANIMEVENT_FLINCH_HEAD, base + 6)
|
|
|
|
elseif hitgroup == HITGROUP_RIGHTLEG then
|
|
|
|
self:DoCustomAnimEvent(PLAYERANIMEVENT_FLINCH_HEAD, base + 7)
|
|
|
|
elseif hitgroup == HITGROUP_BELT then
|
|
|
|
self:DoCustomAnimEvent(PLAYERANIMEVENT_FLINCH_HEAD, base + 3)
|
|
|
|
else
|
|
|
|
self:DoCustomAnimEvent(PLAYERANIMEVENT_FLINCH_HEAD, base * 2)
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:DoRandomFlinchEvent()
|
|
|
|
self:DoRandomEvent(PLAYERANIMEVENT_FLINCH_HEAD, 12)
|
2015-03-10 01:45:47 +08:00
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
local FlinchSequences = {
|
|
|
|
"flinch_01",
|
|
|
|
"flinch_02",
|
|
|
|
"flinch_back_01",
|
|
|
|
"flinch_head_01",
|
|
|
|
"flinch_head_02",
|
|
|
|
"flinch_phys_01",
|
|
|
|
"flinch_phys_02",
|
|
|
|
"flinch_shoulder_l",
|
|
|
|
"flinch_shoulder_r",
|
|
|
|
"flinch_stomach_01",
|
|
|
|
"flinch_stomach_02",
|
|
|
|
}
|
|
|
|
function meta:DoFlinchAnim(data)
|
|
|
|
local seq = FlinchSequences[data] or FlinchSequences[1]
|
|
|
|
if seq then
|
|
|
|
local seqid = self:LookupSequence(seq)
|
|
|
|
if seqid > 0 then
|
|
|
|
self:AddVCDSequenceToGestureSlot(GESTURE_SLOT_FLINCH, seqid, 0, true)
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
end
|
2018-05-02 06:32:59 +08:00
|
|
|
end
|
2014-10-02 08:49:54 +08:00
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
local ZombieAttackSequences = {
|
|
|
|
"zombie_attack_01",
|
|
|
|
"zombie_attack_02",
|
|
|
|
"zombie_attack_03",
|
|
|
|
"zombie_attack_04",
|
|
|
|
"zombie_attack_05",
|
|
|
|
"zombie_attack_06"
|
|
|
|
}
|
|
|
|
function meta:DoZombieAttackAnim(data)
|
|
|
|
local seq = ZombieAttackSequences[data] or ZombieAttackSequences[1]
|
|
|
|
if seq then
|
|
|
|
local seqid = self:LookupSequence(seq)
|
|
|
|
if seqid > 0 then
|
|
|
|
self:AddVCDSequenceToGestureSlot(GESTURE_SLOT_ATTACK_AND_RELOAD, seqid, 0, true)
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
end
|
2018-05-02 06:32:59 +08:00
|
|
|
end
|
2014-10-02 08:49:54 +08:00
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:IsSpectator()
|
|
|
|
return P_Team(self) == TEAM_SPECTATOR
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetAuraRange()
|
2018-05-02 06:32:59 +08:00
|
|
|
if GAMEMODE.ZombieEscape then
|
|
|
|
return 8192
|
|
|
|
end
|
|
|
|
|
2014-10-02 08:49:54 +08:00
|
|
|
local wep = self:GetActiveWeapon()
|
|
|
|
return wep:IsValid() and wep.GetAuraRange and wep:GetAuraRange() or 2048
|
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:GetAuraRangeSqr()
|
|
|
|
local r = self:GetAuraRange()
|
|
|
|
return r * r
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetPoisonDamage()
|
2018-05-02 06:32:59 +08:00
|
|
|
return self.Poison and self.Poison:IsValid() and self.Poison:GetDamage() or 0
|
2014-10-02 08:49:54 +08:00
|
|
|
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
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:SigilTeleportDestination(not_from_sigil, corrupted)
|
|
|
|
local sigils = corrupted and GAMEMODE:GetCorruptedSigils() or GAMEMODE:GetUncorruptedSigils()
|
|
|
|
|
|
|
|
if not_from_sigil then
|
|
|
|
if #sigils == 0 then return end
|
|
|
|
elseif #sigils <= 1 then return end
|
|
|
|
|
|
|
|
local mypos = self:GetPos()
|
|
|
|
local eyevector = self:GetAimVector()
|
|
|
|
|
|
|
|
local dist = 999999999999
|
|
|
|
local spos, d, icurrent, target, itarget
|
|
|
|
|
|
|
|
if not not_from_sigil then
|
|
|
|
for i, sigil in pairs(sigils) do
|
|
|
|
d = sigil:GetPos():DistToSqr(mypos)
|
|
|
|
if d < dist then
|
|
|
|
dist = d
|
|
|
|
icurrent = i
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
dist = -1
|
|
|
|
for i, sigil in pairs(sigils) do
|
|
|
|
if i == icurrent then continue end
|
|
|
|
|
|
|
|
spos = sigil:GetPos() - mypos
|
|
|
|
spos:Normalize()
|
|
|
|
d = spos:Dot(eyevector)
|
|
|
|
if d > dist then
|
|
|
|
dist = d
|
|
|
|
target = sigil
|
|
|
|
itarget = i
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return target, itarget
|
|
|
|
end
|
|
|
|
|
2014-10-02 08:49:54 +08:00
|
|
|
function meta:DispatchAltUse()
|
2018-05-02 06:32:59 +08:00
|
|
|
local tpexist = self:GetStatus("sigilteleport")
|
|
|
|
if tpexist and tpexist:IsValid() then
|
|
|
|
self:RemoveStatus("sigilteleport", false, true)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
local tr = self:CompensatedMeleeTrace(64, 4, nil, nil, nil, true)
|
2014-10-02 08:49:54 +08:00
|
|
|
local ent = tr.Entity
|
2018-05-02 06:32:59 +08:00
|
|
|
if ent and ent:IsValid() and ent.AltUse then
|
|
|
|
return ent:AltUse(self, tr)
|
2014-10-02 08:49:54 +08:00
|
|
|
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()
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
if self.ArsenalZone and self.ArsenalZone:IsValid() then return true end
|
|
|
|
|
|
|
|
local arseents = {}
|
|
|
|
table.Add(arseents, ents.FindByClass("prop_arsenalcrate"))
|
|
|
|
table.Add(arseents, ents.FindByClass("status_arsenalpack"))
|
|
|
|
|
|
|
|
for _, ent in pairs(arseents) do
|
2014-10-02 08:49:54 +08:00
|
|
|
local nearest = ent:NearestPoint(pos)
|
2018-05-02 06:32:59 +08:00
|
|
|
if pos:DistToSqr(nearest) <= 10000 and (WorldVisible(pos, nearest) or self:TraceLine(100).Entity == ent) then -- 80^2
|
2014-10-02 08:49:54 +08:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
meta.IsNearArsenalCrate = meta.NearArsenalCrate
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:NearRemantler()
|
2014-10-02 08:49:54 +08:00
|
|
|
local pos = self:EyePos()
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
local remantlers = ents.FindByClass("prop_remantler")
|
|
|
|
|
|
|
|
for _, ent in pairs(remantlers) do
|
2014-10-02 08:49:54 +08:00
|
|
|
local nearest = ent:NearestPoint(pos)
|
2018-05-02 06:32:59 +08:00
|
|
|
if pos:DistToSqr(nearest) <= 10000 and (WorldVisible(pos, nearest) or self:TraceLine(100).Entity == ent) then -- 80^2
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetResupplyAmmoType()
|
|
|
|
local ammotype
|
|
|
|
if not self.ResupplyChoice then
|
|
|
|
local wep = self:GetActiveWeapon()
|
|
|
|
if wep:IsValid() then
|
|
|
|
ammotype = wep.GetResupplyAmmoType and wep:GetResupplyAmmoType() or wep.ResupplyAmmoType or wep:GetPrimaryAmmoTypeString()
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
end
|
2018-05-02 06:32:59 +08:00
|
|
|
|
|
|
|
ammotype = ammotype and ammotype:lower() or self.ResupplyChoice
|
|
|
|
|
|
|
|
if not ammotype or not GAMEMODE.AmmoResupply[ammotype] then
|
|
|
|
return "scrap"
|
|
|
|
end
|
|
|
|
|
|
|
|
return ammotype
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
function meta:SetZombieClassName(classname)
|
|
|
|
if GAMEMODE.ZombieClasses[classname] then
|
|
|
|
self:SetZombieClass(GAMEMODE.ZombieClasses[classname].Index)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetPoints()
|
|
|
|
return self:GetDTInt(1)
|
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:GetBloodArmor()
|
|
|
|
return self:GetDTInt(DT_PLAYER_INT_BLOODARMOR)
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:AddLegDamage(damage)
|
|
|
|
if self.SpawnProtection then return end
|
|
|
|
|
|
|
|
local legdmg = self:GetLegDamage() + damage
|
2014-10-02 08:49:54 +08:00
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
if self:GetFlatLegDamage() - damage * 0.25 > damage then
|
|
|
|
legdmg = self:GetFlatLegDamage()
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
self:SetLegDamage(legdmg)
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:AddLegDamageExt(damage, attacker, inflictor, type)
|
|
|
|
inflictor = inflictor or attacker
|
2014-10-02 08:49:54 +08:00
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
if type == SLOWTYPE_PULSE then
|
|
|
|
local legdmg = damage * (attacker.PulseWeaponSlowMul or 1)
|
|
|
|
local startleg = self:GetFlatLegDamage()
|
2014-10-02 08:49:54 +08:00
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
self:AddLegDamage(legdmg)
|
|
|
|
if attacker.PulseImpedance then
|
|
|
|
self:AddArmDamage(legdmg)
|
|
|
|
end
|
|
|
|
|
|
|
|
if SERVER and attacker:HasTrinket("resonance") then
|
|
|
|
attacker.AccuPulse = (attacker.AccuPulse or 0) + (self:GetFlatLegDamage() - startleg)
|
|
|
|
|
|
|
|
if attacker.AccuPulse > 80 then
|
|
|
|
self:PulseResonance(attacker, inflictor)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
elseif type == SLOWTYPE_COLD then
|
|
|
|
if self:IsValidLivingZombie() and self:GetZombieClassTable().ResistFrost then return end
|
|
|
|
|
|
|
|
self:AddLegDamage(damage)
|
|
|
|
self:AddArmDamage(damage)
|
|
|
|
|
|
|
|
if SERVER and attacker:HasTrinket("cryoindu") then
|
|
|
|
self:CryogenicInduction(attacker, inflictor, damage)
|
|
|
|
end
|
|
|
|
end
|
2014-12-19 13:52:24 +08:00
|
|
|
end
|
|
|
|
|
2014-10-02 08:49:54 +08:00
|
|
|
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()
|
2014-12-24 07:24:31 +08:00
|
|
|
return math.max(0, (self.LegDamage or 0) - CurTime())
|
2014-12-19 13:52:24 +08:00
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:GetFlatLegDamage()
|
|
|
|
return math.max(0, ((self.LegDamage or 0) - CurTime()) * 8)
|
2014-11-10 11:51:43 +08:00
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:AddArmDamage(damage)
|
|
|
|
if self.SpawnProtection then return end
|
2014-10-02 08:49:54 +08:00
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
local armdmg = self:GetArmDamage() + damage
|
|
|
|
|
|
|
|
if self:GetFlatArmDamage() - damage * 0.25 > damage then
|
|
|
|
armdmg = self:GetFlatArmDamage()
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
self:SetArmDamage(armdmg)
|
|
|
|
end
|
2014-10-02 08:49:54 +08:00
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:SetArmDamage(damage)
|
|
|
|
self.ArmDamage = CurTime() + math.min(GAMEMODE.MaxArmDamage, damage * 0.125)
|
|
|
|
if SERVER then
|
|
|
|
self:UpdateArmDamage()
|
|
|
|
end
|
|
|
|
end
|
2014-10-02 08:49:54 +08:00
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:RawSetArmDamage(time)
|
|
|
|
self.ArmDamage = math.min(CurTime() + GAMEMODE.MaxArmDamage, time)
|
|
|
|
if SERVER then
|
|
|
|
self:UpdateArmDamage()
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:RawCapArmDamage(time)
|
|
|
|
self:RawSetArmDamage(math.max(self.ArmDamage or 0, time))
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetArmDamage()
|
|
|
|
return math.max(0, (self.ArmDamage or 0) - CurTime())
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetFlatArmDamage()
|
|
|
|
return math.max(0, ((self.ArmDamage or 0) - CurTime()) * 8)
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:Flinch()
|
|
|
|
if CurTime() >= (self.NextFlinch or 0) then
|
|
|
|
self.NextFlinch = CurTime() + 0.75
|
|
|
|
|
|
|
|
if P_Team(self) == TEAM_UNDEAD then
|
|
|
|
self:DoFlinchEvent(self:LastHitGroup())
|
|
|
|
else
|
|
|
|
self:DoRandomFlinchEvent()
|
|
|
|
end
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetZombieClass()
|
|
|
|
return self.Class or GAMEMODE.DefaultZombieClass
|
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
local ZombieClasses = {}
|
|
|
|
if GAMEMODE then
|
|
|
|
ZombieClasses = GAMEMODE.ZombieClasses
|
|
|
|
end
|
|
|
|
hook.Add("Initialize", "LocalizeZombieClasses", function() ZombieClasses = GAMEMODE.ZombieClasses end)
|
2014-10-02 08:49:54 +08:00
|
|
|
function meta:GetZombieClassTable()
|
|
|
|
return ZombieClasses[self:GetZombieClass()]
|
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
-- Called a lot, so optimized
|
|
|
|
-- vararg was culled out because it created tables. Should call the one with appropriate # of args.
|
|
|
|
local zctab
|
|
|
|
local zcfunc
|
|
|
|
function meta:CallZombieFunction0(funcname)
|
|
|
|
if P_Team(self) == TEAM_UNDEAD then
|
|
|
|
zctab = ZombieClasses[E_GetTable(self).Class or GAMEMODE.DefaultZombieClass]
|
|
|
|
zcfunc = zctab[funcname]
|
|
|
|
if zcfunc then
|
|
|
|
return zcfunc(zctab, self)
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:CallZombieFunction1(funcname, a1)
|
|
|
|
if P_Team(self) == TEAM_UNDEAD then
|
|
|
|
zctab = ZombieClasses[E_GetTable(self).Class or GAMEMODE.DefaultZombieClass]
|
|
|
|
zcfunc = zctab[funcname]
|
|
|
|
if zcfunc then
|
|
|
|
return zcfunc(zctab, self, a1)
|
|
|
|
end
|
|
|
|
end
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:CallZombieFunction2(funcname, a1, a2)
|
|
|
|
if P_Team(self) == TEAM_UNDEAD then
|
|
|
|
zctab = ZombieClasses[E_GetTable(self).Class or GAMEMODE.DefaultZombieClass]
|
|
|
|
zcfunc = zctab[funcname]
|
|
|
|
if zcfunc then
|
|
|
|
return zcfunc(zctab, self, a1, a2)
|
|
|
|
end
|
|
|
|
end
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:CallZombieFunction3(funcname, a1, a2, a3)
|
|
|
|
if P_Team(self) == TEAM_UNDEAD then
|
|
|
|
zctab = ZombieClasses[E_GetTable(self).Class or GAMEMODE.DefaultZombieClass]
|
|
|
|
zcfunc = zctab[funcname]
|
|
|
|
if zcfunc then
|
|
|
|
return zcfunc(zctab, self, a1, a2, a3)
|
|
|
|
end
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
2018-05-02 06:32:59 +08:00
|
|
|
end
|
2014-10-02 08:49:54 +08:00
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:CallZombieFunction4(funcname, a1, a2, a3, a4)
|
|
|
|
if P_Team(self) == TEAM_UNDEAD then
|
|
|
|
zctab = ZombieClasses[E_GetTable(self).Class or GAMEMODE.DefaultZombieClass]
|
|
|
|
zcfunc = zctab[funcname]
|
|
|
|
if zcfunc then
|
|
|
|
return zcfunc(zctab, self, a1, a2, a3, a4)
|
|
|
|
end
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
2018-05-02 06:32:59 +08:00
|
|
|
end
|
|
|
|
meta.CallZombieFunction = meta.CallZombieFunction4 -- 4 should be enough for legacy.
|
2014-10-02 08:49:54 +08:00
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:CallZombieFunction5(funcname, a1, a2, a3, a4, a5)
|
|
|
|
if P_Team(self) == TEAM_UNDEAD then
|
|
|
|
zctab = ZombieClasses[E_GetTable(self).Class or GAMEMODE.DefaultZombieClass]
|
|
|
|
zcfunc = zctab[funcname]
|
|
|
|
if zcfunc then
|
|
|
|
return zcfunc(zctab, self, a1, a2, a3, a4, a5)
|
|
|
|
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)})
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
function meta:SetSpeed(speed)
|
|
|
|
if not speed then speed = 200 end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
local runspeed = self:GetBloodArmor() > 0 and self:IsSkillActive(SKILL_CARDIOTONIC) and speed + 40 or speed
|
|
|
|
|
2014-10-02 08:49:54 +08:00
|
|
|
self:SetWalkSpeed(speed)
|
2018-05-02 06:32:59 +08:00
|
|
|
self:SetRunSpeed(runspeed)
|
|
|
|
self:SetMaxSpeed(runspeed)
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
function meta:SetHumanSpeed(speed)
|
2018-05-02 06:32:59 +08:00
|
|
|
if P_Team(self) == TEAM_HUMAN then self:SetSpeed(speed) end
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
function meta:ResetSpeed(noset, health)
|
|
|
|
if not self:IsValid() then return end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
if P_Team(self) == TEAM_UNDEAD then
|
|
|
|
local speed = math.max(140, self:GetZombieClassTable().Speed * GAMEMODE.ZombieSpeedMultiplier - (GAMEMODE.ObjectiveMap and 20 or 0))
|
|
|
|
|
2014-10-02 08:49:54 +08:00
|
|
|
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
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
if speed < SPEED_NORMAL then
|
|
|
|
speed = SPEED_NORMAL - (SPEED_NORMAL - speed) * (self.WeaponWeightSlowMul or 1)
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
if self.SkillSpeedAdd and P_Team(self) == TEAM_HUMAN then
|
|
|
|
speed = speed + self.SkillSpeedAdd
|
|
|
|
end
|
|
|
|
|
|
|
|
if self:IsSkillActive(SKILL_LIGHTWEIGHT) and wep:IsValid() and wep.IsMelee then
|
|
|
|
speed = speed + 6
|
|
|
|
end
|
|
|
|
|
|
|
|
speed = math.max(1, speed)
|
2014-10-02 08:49:54 +08:00
|
|
|
|
|
|
|
if 32 < speed and not GAMEMODE.ZombieEscape then
|
2018-05-02 06:32:59 +08:00
|
|
|
health = health or self:Health()
|
|
|
|
local maxhealth = self:GetMaxHealth() * 0.6666
|
|
|
|
if health < maxhealth then
|
|
|
|
speed = math.max(88, speed - speed * 0.4 * (1 - health / maxhealth) * (self.LowHealthSlowMul or 1))
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if not noset then
|
|
|
|
self:SetSpeed(speed)
|
|
|
|
end
|
|
|
|
|
|
|
|
return speed
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:ResetJumpPower(noset)
|
|
|
|
local power = DEFAULT_JUMP_POWER
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
if P_Team(self) == TEAM_UNDEAD then
|
|
|
|
power = self:CallZombieFunction0("GetJumpPower") or power
|
2014-10-02 08:49:54 +08:00
|
|
|
|
|
|
|
local classtab = self:GetZombieClassTable()
|
|
|
|
if classtab and classtab.JumpPower then
|
|
|
|
power = classtab.JumpPower
|
|
|
|
end
|
|
|
|
else
|
2018-05-02 06:32:59 +08:00
|
|
|
power = power * (self.JumpPowerMul or 1)
|
|
|
|
|
2014-10-02 08:49:54 +08:00
|
|
|
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
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:SetBarricadeGhosting(b, fullspeed)
|
|
|
|
if self == NULL then return end --???
|
|
|
|
|
|
|
|
if b and self.NoGhosting and not self:GetBarricadeGhosting() then
|
|
|
|
self:SetDTFloat(DT_PLAYER_FLOAT_WIDELOAD, CurTime() + 6)
|
|
|
|
end
|
|
|
|
|
|
|
|
if fullspeed == nil then fullspeed = false end
|
2014-10-02 08:49:54 +08:00
|
|
|
|
|
|
|
self:SetDTBool(0, b)
|
2018-05-02 06:32:59 +08:00
|
|
|
self:SetDTBool(1, b and fullspeed)
|
|
|
|
--self:SetCustomCollisionCheck(b)
|
2014-10-02 08:49:54 +08:00
|
|
|
self:CollisionRulesChanged()
|
|
|
|
|
|
|
|
self:ResetJumpPower()
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetBarricadeGhosting()
|
2018-05-02 06:32:59 +08:00
|
|
|
return E_GetDTBool(self, 0)
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
meta.IsBarricadeGhosting = meta.GetBarricadeGhosting
|
|
|
|
|
|
|
|
function meta:ShouldBarricadeGhostWith(ent)
|
|
|
|
return ent:IsBarricadeProp()
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:BarricadeGhostingThink()
|
2018-05-02 06:32:59 +08:00
|
|
|
if E_GetDTBool(self, 1) then
|
|
|
|
if not self:ActiveBarricadeGhosting() then
|
|
|
|
self:SetBarricadeGhosting(false)
|
2015-02-09 05:20:49 +08:00
|
|
|
end
|
2018-05-02 06:32:59 +08:00
|
|
|
else
|
|
|
|
if self:KeyDown(IN_ZOOM) or self:ActiveBarricadeGhosting() then
|
|
|
|
if self.FirstGhostThink then
|
|
|
|
self:SetLocalVelocity(vector_origin)
|
|
|
|
self.FirstGhostThink = false
|
|
|
|
end
|
2014-10-02 08:49:54 +08:00
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
return
|
2014-10-20 19:45:25 +08:00
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
self.FirstGhostThink = true
|
|
|
|
self:SetBarricadeGhosting(false)
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
-- Needs to be as optimized as possible.
|
|
|
|
function meta:ShouldNotCollide(ent)
|
|
|
|
if E_IsValid(ent) then
|
|
|
|
if getmetatable(ent) == meta then
|
2022-07-17 20:46:55 +08:00
|
|
|
return P_Team(self) == P_Team(ent) or E_GetTable(self).NoCollideAll or E_GetTable(ent).NoCollideAll
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
2018-05-02 06:32:59 +08:00
|
|
|
|
|
|
|
return E_GetDTBool(self, 0) and ent:IsBarricadeProp()
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
return false
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
2022-07-12 18:33:47 +08:00
|
|
|
meta.OldSetHealth = meta.OldSetHealth or FindMetaTable("Entity").SetHealth
|
2014-10-02 08:49:54 +08:00
|
|
|
function meta:SetHealth(health)
|
|
|
|
self:OldSetHealth(health)
|
2018-05-02 06:32:59 +08:00
|
|
|
if P_Team(self) == TEAM_HUMAN and 1 <= health then
|
2014-10-02 08:49:54 +08:00
|
|
|
self:ResetSpeed(nil, health)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:IsHeadcrab()
|
2018-05-02 06:32:59 +08:00
|
|
|
return P_Team(self) == TEAM_UNDEAD and GAMEMODE.ZombieClasses[self:GetZombieClass()].IsHeadcrab
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:IsTorso()
|
|
|
|
return P_Team(self) == TEAM_UNDEAD and GAMEMODE.ZombieClasses[self:GetZombieClass()].IsTorso
|
2014-10-02 08:49:54 +08:00
|
|
|
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
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
local temp_attacker = NULL
|
|
|
|
local temp_attacker_team = -1
|
|
|
|
local temp_pen_ents = {}
|
|
|
|
local temp_override_team
|
|
|
|
|
|
|
|
local function MeleeTraceFilter(ent)
|
|
|
|
if ent == temp_attacker
|
|
|
|
or E_GetTable(ent).IgnoreMelee
|
|
|
|
or getmetatable(ent) == meta and P_Team(ent) == temp_attacker_team
|
|
|
|
or not temp_override_team and ent.IgnoreMeleeTeam and ent.IgnoreMeleeTeam == temp_attacker_team
|
|
|
|
or temp_pen_ents[ent] then
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
return true
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
local function DynamicTraceFilter(ent)
|
|
|
|
if ent.IgnoreTraces or ent:IsPlayer() then
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
return true
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
local function MeleeTraceFilterFFA(ent)
|
|
|
|
if temp_pen_ents[ent] then
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
return ent ~= temp_attacker
|
|
|
|
end
|
|
|
|
|
|
|
|
local melee_trace = {filter = MeleeTraceFilter, mask = MASK_SOLID, mins = Vector(), maxs = Vector()}
|
|
|
|
|
|
|
|
function meta:GetDynamicTraceFilter()
|
|
|
|
return DynamicTraceFilter
|
|
|
|
end
|
|
|
|
|
|
|
|
local function CheckFHB(tr)
|
|
|
|
if tr.Entity.FHB and tr.Entity:IsValid() then
|
|
|
|
tr.Entity = tr.Entity:GetParent()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:MeleeTrace(distance, size, start, dir, hit_team_members, override_team, override_mask)
|
2014-10-02 08:49:54 +08:00
|
|
|
start = start or self:GetShootPos()
|
2014-11-16 18:21:55 +08:00
|
|
|
dir = dir or self:GetAimVector()
|
2018-05-02 06:32:59 +08:00
|
|
|
hit_team_members = hit_team_members or GAMEMODE.RoundEnded
|
|
|
|
|
|
|
|
local tr
|
|
|
|
|
|
|
|
temp_attacker = self
|
|
|
|
temp_attacker_team = P_Team(self)
|
|
|
|
temp_override_team = override_team
|
|
|
|
melee_trace.start = start
|
|
|
|
melee_trace.endpos = start + dir * distance
|
|
|
|
melee_trace.mask = override_mask or MASK_SOLID
|
|
|
|
melee_trace.mins.x = -size
|
|
|
|
melee_trace.mins.y = -size
|
|
|
|
melee_trace.mins.z = -size
|
|
|
|
melee_trace.maxs.x = size
|
|
|
|
melee_trace.maxs.y = size
|
|
|
|
melee_trace.maxs.z = size
|
|
|
|
melee_trace.filter = hit_team_members and MeleeTraceFilterFFA or MeleeTraceFilter
|
|
|
|
|
|
|
|
tr = util_TraceLine(melee_trace)
|
|
|
|
|
|
|
|
CheckFHB(tr)
|
|
|
|
|
|
|
|
if tr.Hit then
|
|
|
|
return tr
|
|
|
|
end
|
|
|
|
|
|
|
|
return util_TraceHull(melee_trace)
|
|
|
|
end
|
|
|
|
|
|
|
|
local function InvalidateCompensatedTrace(tr, start, distance)
|
|
|
|
-- Need to do this or people with 300 ping will be hitting people across rooms
|
|
|
|
if tr.Entity:IsValid() and tr.Entity:IsPlayer() and tr.HitPos:DistToSqr(start) > distance * distance + 144 then -- Give just a little bit of leeway
|
|
|
|
tr.Hit = false
|
|
|
|
tr.HitNonWorld = false
|
|
|
|
tr.Entity = NULL
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:CompensatedMeleeTrace(distance, size, start, dir, hit_team_members, override_team)
|
|
|
|
start = start or self:GetShootPos()
|
|
|
|
dir = dir or self:GetAimVector()
|
|
|
|
|
|
|
|
self:LagCompensation(true)
|
|
|
|
local tr = self:MeleeTrace(distance, size, start, dir, hit_team_members, override_team)
|
|
|
|
CheckFHB(tr)
|
|
|
|
self:LagCompensation(false)
|
|
|
|
|
|
|
|
InvalidateCompensatedTrace(tr, start, distance)
|
|
|
|
|
|
|
|
return tr
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:CompensatedPenetratingMeleeTrace(distance, size, start, dir, hit_team_members)
|
|
|
|
start = start or self:GetShootPos()
|
|
|
|
dir = dir or self:GetAimVector()
|
|
|
|
|
|
|
|
self:LagCompensation(true)
|
|
|
|
local t = self:PenetratingMeleeTrace(distance, size, start, dir, hit_team_members)
|
|
|
|
self:LagCompensation(false)
|
|
|
|
|
|
|
|
for _, tr in pairs(t) do
|
|
|
|
InvalidateCompensatedTrace(tr, start, distance)
|
|
|
|
end
|
|
|
|
|
|
|
|
return t
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:CompensatedZombieMeleeTrace(distance, size, start, dir, hit_team_members)
|
|
|
|
start = start or self:GetShootPos()
|
|
|
|
dir = dir or self:GetAimVector()
|
|
|
|
|
|
|
|
self:LagCompensation(true)
|
|
|
|
|
|
|
|
local hit_entities = {}
|
|
|
|
|
|
|
|
local t, hitprop = self:PenetratingMeleeTrace(distance, size, start, dir, hit_team_members)
|
|
|
|
local t_legs = self:PenetratingMeleeTrace(distance, size, self:WorldSpaceCenter(), dir, hit_team_members)
|
|
|
|
|
|
|
|
for _, tr in pairs(t) do
|
|
|
|
hit_entities[tr.Entity] = true
|
|
|
|
end
|
|
|
|
|
|
|
|
if not hitprop then
|
|
|
|
for _, tr in pairs(t_legs) do
|
|
|
|
if not hit_entities[tr.Entity] then
|
|
|
|
t[#t + 1] = tr
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
for _, tr in pairs(t) do
|
|
|
|
InvalidateCompensatedTrace(tr, tr.StartPos, distance)
|
|
|
|
end
|
|
|
|
|
|
|
|
self:LagCompensation(false)
|
|
|
|
|
|
|
|
return t
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:PenetratingMeleeTrace(distance, size, start, dir, hit_team_members)
|
|
|
|
start = start or self:GetShootPos()
|
|
|
|
dir = dir or self:GetAimVector()
|
|
|
|
hit_team_members = hit_team_members or GAMEMODE.RoundEnded
|
|
|
|
|
|
|
|
local tr, ent
|
|
|
|
|
|
|
|
temp_attacker = self
|
|
|
|
temp_attacker_team = P_Team(self)
|
|
|
|
temp_pen_ents = {}
|
|
|
|
melee_trace.start = start
|
|
|
|
melee_trace.endpos = start + dir * distance
|
|
|
|
melee_trace.mask = MASK_SOLID
|
|
|
|
melee_trace.mins.x = -size
|
|
|
|
melee_trace.mins.y = -size
|
|
|
|
melee_trace.mins.z = -size
|
|
|
|
melee_trace.maxs.x = size
|
|
|
|
melee_trace.maxs.y = size
|
|
|
|
melee_trace.maxs.z = size
|
|
|
|
melee_trace.filter = hit_team_members and MeleeTraceFilterFFA or MeleeTraceFilter
|
2014-10-02 08:49:54 +08:00
|
|
|
|
|
|
|
local t = {}
|
|
|
|
local onlyhitworld
|
|
|
|
for i=1, 50 do
|
2018-05-02 06:32:59 +08:00
|
|
|
tr = util_TraceLine(melee_trace)
|
|
|
|
|
|
|
|
if not tr.Hit then
|
|
|
|
tr = util_TraceHull(melee_trace)
|
|
|
|
end
|
2014-10-02 08:49:54 +08:00
|
|
|
|
|
|
|
if not tr.Hit then break end
|
|
|
|
|
|
|
|
if tr.HitWorld then
|
|
|
|
table.insert(t, tr)
|
|
|
|
break
|
|
|
|
end
|
|
|
|
|
|
|
|
if onlyhitworld then break end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
CheckFHB(tr)
|
|
|
|
|
|
|
|
ent = tr.Entity
|
|
|
|
if ent:IsValid() then
|
2014-10-02 08:49:54 +08:00
|
|
|
if not ent:IsPlayer() then
|
2018-05-02 06:32:59 +08:00
|
|
|
melee_trace.mask = MASK_SOLID_BRUSHONLY
|
2014-10-02 08:49:54 +08:00
|
|
|
onlyhitworld = true
|
|
|
|
end
|
|
|
|
|
|
|
|
table.insert(t, tr)
|
2018-05-02 06:32:59 +08:00
|
|
|
temp_pen_ents[ent] = true
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
temp_pen_ents = {}
|
2014-10-02 08:49:54 +08:00
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
return t, onlyhitworld
|
2014-10-02 08:49:54 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
function meta:ActiveBarricadeGhosting(override)
|
2018-05-02 06:32:59 +08:00
|
|
|
if P_Team(self) ~= TEAM_HUMAN and not override or not self:GetBarricadeGhosting() then return false end
|
|
|
|
|
|
|
|
local min, max = self:WorldSpaceAABB()
|
|
|
|
min.x = min.x + 1
|
|
|
|
min.y = min.y + 1
|
2014-10-02 08:49:54 +08:00
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
max.x = max.x - 1
|
|
|
|
max.y = max.y - 1
|
|
|
|
|
|
|
|
for _, ent in pairs(ents.FindInBox(min, max)) do
|
2014-11-19 22:07:50 +08:00
|
|
|
if ent and ent:IsValid() and self:ShouldBarricadeGhostWith(ent) then return true end
|
2014-10-02 08:49:54 +08:00
|
|
|
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
|
|
|
|
|
2018-05-02 06:32:59 +08:00
|
|
|
function meta:NearestRemantler()
|
|
|
|
local pos = self:EyePos()
|
|
|
|
|
|
|
|
local remantlers = ents.FindByClass("prop_remantler")
|
|
|
|
local min, remantler = 99999
|
|
|
|
|
|
|
|
for _, ent in pairs(remantlers) do
|
|
|
|
local nearpoint = ent:NearestPoint(pos)
|
|
|
|
local trmatch = self:TraceLine(100).Entity == ent
|
|
|
|
local dist = trmatch and 0 or pos:DistToSqr(nearpoint)
|
|
|
|
if pos:DistToSqr(nearpoint) <= 10000 and dist < min then
|
|
|
|
remantler = ent
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return remantler
|
|
|
|
end
|
|
|
|
|
2014-10-02 08:49:54 +08:00
|
|
|
function meta:GetMaxZombieHealth()
|
|
|
|
return self:GetZombieClassTable().Health
|
|
|
|
end
|
|
|
|
|
|
|
|
local oldmaxhealth = FindMetaTable("Entity").GetMaxHealth
|
|
|
|
function meta:GetMaxHealth()
|
2018-05-02 06:32:59 +08:00
|
|
|
if P_Team(self) == TEAM_UNDEAD then
|
2014-10-02 08:49:54 +08:00
|
|
|
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
|
|
|
|
|
|
|
|
-- 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
|
2018-05-02 06:32:59 +08:00
|
|
|
|
|
|
|
function meta:GetZombieMeleeSpeedMul()
|
|
|
|
return 1 * (1 + math.Clamp(self:GetArmDamage() / GAMEMODE.MaxArmDamage, 0, 1)) / (self:GetStatus("zombie_battlecry") and 1.2 or 1)
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetMeleeSpeedMul()
|
|
|
|
if P_Team(self) == TEAM_UNDEAD then
|
|
|
|
return self:GetZombieMeleeSpeedMul()
|
|
|
|
end
|
|
|
|
|
|
|
|
return 1 * (1 + math.Clamp(self:GetArmDamage() / GAMEMODE.MaxArmDamage, 0, 1)) / (self:GetStatus("frost") and 0.7 or 1)
|
|
|
|
end
|
|
|
|
|
|
|
|
function meta:GetPhantomHealth()
|
|
|
|
return self:GetDTFloat(DT_PLAYER_FLOAT_PHANTOMHEALTH)
|
|
|
|
end
|