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

1002 lines
25 KiB
Lua

local meta = FindMetaTable("Player")
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
function meta:LogID()
return "<"..self:SteamID().."> "..self:Name()
end
function meta:GetMaxHealthEx()
if P_Team(self) == 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: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)
end
end
function meta:DoRandomFlinchEvent()
self:DoRandomEvent(PLAYERANIMEVENT_FLINCH_HEAD, 12)
end
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)
end
end
end
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)
end
end
end
function meta:IsSpectator()
return P_Team(self) == TEAM_SPECTATOR
end
function meta:GetAuraRange()
if GAMEMODE.ZombieEscape then
return 8192
end
local wep = self:GetActiveWeapon()
return wep:IsValid() and wep.GetAuraRange and wep:GetAuraRange() or 2048
end
function meta:GetAuraRangeSqr()
local r = self:GetAuraRange()
return r * r
end
function meta:GetPoisonDamage()
return self.Poison and self.Poison:IsValid() and self.Poison: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: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
function meta:DispatchAltUse()
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)
local ent = tr.Entity
if ent and ent:IsValid() and ent.AltUse then
return ent:AltUse(self, tr)
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()
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
local nearest = ent:NearestPoint(pos)
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
meta.IsNearArsenalCrate = meta.NearArsenalCrate
function meta:NearRemantler()
local pos = self:EyePos()
local remantlers = ents.FindByClass("prop_remantler")
for _, ent in pairs(remantlers) do
local nearest = ent:NearestPoint(pos)
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()
end
end
ammotype = ammotype and ammotype:lower() or self.ResupplyChoice
if not ammotype or not GAMEMODE.AmmoResupply[ammotype] then
return "scrap"
end
return ammotype
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
function meta:GetBloodArmor()
return self:GetDTInt(DT_PLAYER_INT_BLOODARMOR)
end
function meta:AddLegDamage(damage)
if self.SpawnProtection then return end
local legdmg = self:GetLegDamage() + damage
if self:GetFlatLegDamage() - damage * 0.25 > damage then
legdmg = self:GetFlatLegDamage()
end
self:SetLegDamage(legdmg)
end
function meta:AddLegDamageExt(damage, attacker, inflictor, type)
inflictor = inflictor or attacker
if type == SLOWTYPE_PULSE then
local legdmg = damage * (attacker.PulseWeaponSlowMul or 1)
local startleg = self:GetFlatLegDamage()
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
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()
return math.max(0, (self.LegDamage or 0) - CurTime())
end
function meta:GetFlatLegDamage()
return math.max(0, ((self.LegDamage or 0) - CurTime()) * 8)
end
function meta:AddArmDamage(damage)
if self.SpawnProtection then return end
local armdmg = self:GetArmDamage() + damage
if self:GetFlatArmDamage() - damage * 0.25 > damage then
armdmg = self:GetFlatArmDamage()
end
self:SetArmDamage(armdmg)
end
function meta:SetArmDamage(damage)
self.ArmDamage = CurTime() + math.min(GAMEMODE.MaxArmDamage, damage * 0.125)
if SERVER then
self:UpdateArmDamage()
end
end
function meta:RawSetArmDamage(time)
self.ArmDamage = math.min(CurTime() + GAMEMODE.MaxArmDamage, time)
if SERVER then
self:UpdateArmDamage()
end
end
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
end
end
function meta:GetZombieClass()
return self.Class or GAMEMODE.DefaultZombieClass
end
local ZombieClasses = {}
if GAMEMODE then
ZombieClasses = GAMEMODE.ZombieClasses
end
hook.Add("Initialize", "LocalizeZombieClasses", function() ZombieClasses = GAMEMODE.ZombieClasses end)
function meta:GetZombieClassTable()
return ZombieClasses[self:GetZombieClass()]
end
-- 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)
end
end
end
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
end
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
end
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
end
end
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
end
end
meta.CallZombieFunction = meta.CallZombieFunction4 -- 4 should be enough for legacy.
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)})
end
function meta:SetSpeed(speed)
if not speed then speed = 200 end
local runspeed = self:GetBloodArmor() > 0 and self:IsSkillActive(SKILL_CARDIOTONIC) and speed + 40 or speed
self:SetWalkSpeed(speed)
self:SetRunSpeed(runspeed)
self:SetMaxSpeed(runspeed)
end
function meta:SetHumanSpeed(speed)
if P_Team(self) == TEAM_HUMAN then self:SetSpeed(speed) end
end
function meta:ResetSpeed(noset, health)
if not self:IsValid() then return end
if P_Team(self) == TEAM_UNDEAD then
local speed = math.max(140, self:GetZombieClassTable().Speed * GAMEMODE.ZombieSpeedMultiplier - (GAMEMODE.ObjectiveMap and 20 or 0))
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 speed < SPEED_NORMAL then
speed = SPEED_NORMAL - (SPEED_NORMAL - speed) * (self.WeaponWeightSlowMul or 1)
end
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)
if 32 < speed and not GAMEMODE.ZombieEscape then
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))
end
end
if not noset then
self:SetSpeed(speed)
end
return speed
end
function meta:ResetJumpPower(noset)
local power = DEFAULT_JUMP_POWER
if P_Team(self) == TEAM_UNDEAD then
power = self:CallZombieFunction0("GetJumpPower") or power
local classtab = self:GetZombieClassTable()
if classtab and classtab.JumpPower then
power = classtab.JumpPower
end
else
power = power * (self.JumpPowerMul or 1)
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, 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
self:SetDTBool(0, b)
self:SetDTBool(1, b and fullspeed)
--self:SetCustomCollisionCheck(b)
self:CollisionRulesChanged()
self:ResetJumpPower()
end
function meta:GetBarricadeGhosting()
return E_GetDTBool(self, 0)
end
meta.IsBarricadeGhosting = meta.GetBarricadeGhosting
function meta:ShouldBarricadeGhostWith(ent)
return ent:IsBarricadeProp()
end
function meta:BarricadeGhostingThink()
if E_GetDTBool(self, 1) then
if not self:ActiveBarricadeGhosting() then
self:SetBarricadeGhosting(false)
end
else
if self:KeyDown(IN_ZOOM) or self:ActiveBarricadeGhosting() then
if self.FirstGhostThink then
self:SetLocalVelocity(vector_origin)
self.FirstGhostThink = false
end
return
end
self.FirstGhostThink = true
self:SetBarricadeGhosting(false)
end
end
-- Needs to be as optimized as possible.
function meta:ShouldNotCollide(ent)
if E_IsValid(ent) then
if getmetatable(ent) == meta then
return P_Team(self) == P_Team(ent) or E_GetTable(self).NoCollideAll or E_GetTable(ent).NoCollideAll
end
return E_GetDTBool(self, 0) and ent:IsBarricadeProp()
end
return false
end
meta.OldSetHealth = meta.OldSetHealth or FindMetaTable("Entity").SetHealth
function meta:SetHealth(health)
self:OldSetHealth(health)
if P_Team(self) == TEAM_HUMAN and 1 <= health then
self:ResetSpeed(nil, health)
end
end
function meta:IsHeadcrab()
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
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
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
end
local function DynamicTraceFilter(ent)
if ent.IgnoreTraces or ent:IsPlayer() then
return false
end
return true
end
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)
start = start or self:GetShootPos()
dir = dir or self:GetAimVector()
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
local t = {}
local onlyhitworld
for i=1, 50 do
tr = util_TraceLine(melee_trace)
if not tr.Hit then
tr = util_TraceHull(melee_trace)
end
if not tr.Hit then break end
if tr.HitWorld then
table.insert(t, tr)
break
end
if onlyhitworld then break end
CheckFHB(tr)
ent = tr.Entity
if ent:IsValid() then
if not ent:IsPlayer() then
melee_trace.mask = MASK_SOLID_BRUSHONLY
onlyhitworld = true
end
table.insert(t, tr)
temp_pen_ents[ent] = true
end
end
temp_pen_ents = {}
return t, onlyhitworld
end
function meta:ActiveBarricadeGhosting(override)
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
max.x = max.x - 1
max.y = max.y - 1
for _, ent in pairs(ents.FindInBox(min, max)) do
if ent and ent:IsValid() and 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: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
function meta:GetMaxZombieHealth()
return self:GetZombieClassTable().Health
end
local oldmaxhealth = FindMetaTable("Entity").GetMaxHealth
function meta:GetMaxHealth()
if P_Team(self) == 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
-- 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
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