2014-10-02 08:49:54 +08:00
local meta = FindMetaTable ( " Entity " )
2018-05-02 06:32:59 +08:00
--[[local o = meta.SetCollisionGroup
function meta : SetCollisionGroup ( g )
print ( self , g )
debug.Trace ( )
o ( self , g )
end ] ]
function meta : IsDoorLocked ( )
return self : GetSaveTable ( ) . m_bLocked
end
function meta : HealPlayer ( pl , amount , pointmul , nobymsg , poisononly )
local healed , rmv = 0 , 0
local health , maxhealth = pl : Health ( ) , pl : IsSkillActive ( SKILL_D_FRAIL ) and math.floor ( pl : GetMaxHealth ( ) * 0.25 ) or pl : GetMaxHealth ( )
local missing_health = maxhealth - health
local poison = pl : GetPoisonDamage ( )
local bleed = pl : GetBleedDamage ( )
local healrec = ( pl.HealingReceived or 1 ) - ( pl : GetPhantomHealth ( ) > 0.5 and 0.5 or 0 ) - ( pl : GetStatus ( " sickness " ) and 0.5 or 0 )
local healmul = self.MedicHealMul or 1
local multiplier = healmul + healrec - 1
local regamount = healmul * amount
amount = amount * multiplier
-- Heal bleed first.
if not poisononly and bleed > 0 then
rmv = math.min ( amount , bleed )
pl : AddBleedDamage ( - rmv )
healed = healed + rmv
amount = amount - rmv
end
-- Heal poison next.
if poison > 0 and amount > 0 then
rmv = math.min ( amount , poison )
pl : AddPoisonDamage ( - rmv )
healed = healed + rmv
amount = amount - rmv
end
-- Then heal missing health.
if not poisononly and missing_health > 0 and amount > 0 then
rmv = math.min ( amount , missing_health )
pl : SetHealth ( health + rmv )
healed = healed + rmv
amount = amount - rmv
end
pointmul = ( pointmul or 1 ) / ( math.max ( healed , regamount ) / regamount )
if healed > 0 and self : IsPlayer ( ) then
gamemode.Call ( " PlayerHealedTeamMember " , self , pl , healed , self : GetActiveWeapon ( ) , pointmul , nobymsg , healed >= 10 )
pl : SetPhantomHealth ( math.max ( 0 , pl : GetPhantomHealth ( ) - healed ) )
end
return healed
end
local healthpropscalar = {
[ " models/props_c17/door01_left.mdl " ] = 0.7
}
2014-10-02 08:49:54 +08:00
function meta : GetDefaultBarricadeHealth ( )
local mass = 2
2014-11-12 09:25:52 +08:00
if self._OriginalMass then
mass = self._OriginalMass
else
local phys = self : GetPhysicsObject ( )
if phys : IsValid ( ) then
mass = phys : GetMass ( )
end
2014-10-02 08:49:54 +08:00
end
2018-05-02 06:32:59 +08:00
local mdl = string.lower ( self : GetModel ( ) )
local scalar = healthpropscalar [ mdl ] or 1
return math.Clamp ( ( mass * GAMEMODE.BarricadeHealthMassFactor + self : GetVolume ( ) * GAMEMODE.BarricadeHealthVolumeFactor ) * scalar , GAMEMODE.BarricadeHealthMin , GAMEMODE.BarricadeHealthMax )
2014-10-02 08:49:54 +08:00
end
function meta : HitFence ( data , phys )
local pos = phys : GetPos ( )
local vel = data.OurOldVelocity
local endpos = data.HitPos + vel : GetNormalized ( )
if util.TraceLine ( { start = pos , endpos = endpos , mask = MASK_SOLID , filter = self } ) . Hit and not util.TraceLine ( { start = pos , endpos = endpos , mask = MASK_SHOT , filter = self } ) . Hit then -- Essentially hit a fence or passable object.
self : SetPos ( data.HitPos )
phys : SetPos ( data.HitPos )
phys : SetVelocityInstantaneous ( vel )
return true
end
return false
end
2018-05-02 06:32:59 +08:00
function meta : FakePropBreak ( )
local ent = ents.Create ( " prop_physics " )
if ent : IsValid ( ) then
ent : SetModel ( self : GetModel ( ) )
ent : SetMaterial ( self : GetMaterial ( ) )
ent : SetAngles ( self : GetAngles ( ) )
ent : SetPos ( self : GetPos ( ) )
ent : SetSkin ( self : GetSkin ( ) or 0 )
ent : SetColor ( self : GetColor ( ) )
ent : Spawn ( )
ent : Fire ( " break " , " " , 0 )
ent : Fire ( " kill " , " " , 0.1 )
end
end
function meta : SetBarricadeHealth ( m )
self : SetDTFloat ( 1 , m )
end
function meta : SetMaxBarricadeHealth ( m )
self : SetDTFloat ( 2 , m )
end
function meta : SetBarricadeRepairs ( m )
self : SetDTFloat ( 3 , m )
end
2014-10-02 08:49:54 +08:00
function meta : GhostAllPlayersInMe ( timeout , allowrepeat )
if not allowrepeat then
if self.GhostedBefore then return end
self.GhostedBefore = true
end
2018-05-02 06:32:59 +08:00
if self.PreHoldCollisionGroup and self.PreHoldCollisionGroup == COLLISION_GROUP_DEBRIS_TRIGGER then return end -- No need to ghost.
2014-10-02 08:49:54 +08:00
local ent = ents.Create ( " point_propnocollide " )
if ent : IsValid ( ) then
ent : SetPos ( self : GetPos ( ) )
ent : Spawn ( )
if timeout then
ent : SetTimeOut ( CurTime ( ) + timeout )
end
ent : SetTeam ( TEAM_HUMAN )
ent : SetProp ( self )
2018-05-02 06:32:59 +08:00
return ent
end
end
function meta : AddUselessDamage ( damage )
self.UselessDamage = ( self.UselessDamage or 0 ) + damage
end
function meta : RemoveUselessDamage ( damage )
if self.UselessDamage then
damage = math.min ( self.UselessDamage , damage )
self.UselessDamage = self.UselessDamage - damage
return damage
2014-10-02 08:49:54 +08:00
end
2018-05-02 06:32:59 +08:00
return 0
end
function meta : ClearUselessDamage ( )
self.UselessDamage = nil
2014-10-02 08:49:54 +08:00
end
local function SortItems ( a , b )
if a.CleanupPriority == b.CleanupPriority then
return a.Created < b.Created
end
return a.CleanupPriority < b.CleanupPriority
end
local function CheckItemCreated ( self )
if not self : IsValid ( ) or self.PlacedInMap then return end
local tab = { }
for _ , ent in pairs ( ents.FindByClass ( " prop_ammo " ) ) do
if not ent.PlacedInMap then
table.insert ( tab , ent )
end
end
for _ , ent in pairs ( ents.FindByClass ( " prop_weapon " ) ) do
if not ent.PlacedInMap then
table.insert ( tab , ent )
end
end
if # tab > GAMEMODE.MaxDroppedItems then
table.sort ( tab , SortItems )
for i = 1 , GAMEMODE.MaxDroppedItems do
tab [ i ] : Remove ( )
end
end
end
function meta : ItemCreated ( )
self.Created = self.Created or CurTime ( )
timer.Simple ( 0 , function ( ) CheckItemCreated ( self ) end )
end
function meta : FireOutput ( outpt , activator , caller , args )
local intab = self [ outpt ]
if intab then
for key , tab in pairs ( intab ) do
2015-01-31 07:55:12 +08:00
local param = ( ( tab.args == " " ) and args ) or tab.args
2014-10-02 08:49:54 +08:00
for __ , subent in pairs ( self : FindByNameHammer ( tab.entityname , activator , caller ) ) do
local delay = tonumber ( tab.delay )
if delay == nil or delay <= 0 then
2015-01-31 07:55:12 +08:00
subent : Input ( tab.input , activator , caller , param )
2014-10-02 08:49:54 +08:00
else
local inp = tab.input
2015-01-31 07:55:12 +08:00
timer.Simple ( delay , function ( ) if subent : IsValid ( ) then subent : Input ( inp , activator , caller , param ) end end )
2014-10-02 08:49:54 +08:00
end
end
end
end
end
function meta : AddOnOutput ( key , value )
self [ key ] = self [ key ] or { }
local tab = string.Explode ( " , " , value )
table.insert ( self [ key ] , { entityname = tab [ 1 ] , input = tab [ 2 ] , args = tab [ 3 ] , delay = tab [ 4 ] , reps = tab [ 5 ] } )
end
function meta : FindByNameHammer ( name , activator , caller )
if name == " !self " then return { self } end
if name == " !activator " then return { activator } end
if name == " !caller " then return { caller } end
return ents.FindByName ( name )
end
function meta : IsNailed ( )
if self : IsValid ( ) and self.Nails then -- In case we're the world.
for _ , nail in pairs ( self.Nails ) do
if nail and nail : IsValid ( ) and ( nail : GetAttachEntity ( ) == self or nail : GetBaseEntity ( ) == self ) then
return true
end
end
end
return false
end
function meta : IsNailedToWorld ( hierarchy )
if self : IsNailed ( ) then
for _ , nail in pairs ( self.Nails ) do
if nail : GetAttachEntity ( ) : IsWorld ( ) then
return true
end
end
end
if hierarchy then
for _ , ent in pairs ( self : GetAllConstrainedEntities ( ) ) do
if ent ~= self and ent : IsValid ( ) and ent : IsNailedToWorld ( ) then return true end
end
end
return false
end
function meta : IsNailedToWorldHierarchy ( )
return self : IsNailedToWorld ( true )
end
function meta : GetNailFrozen ( )
return self.m_NailFrozen
end
meta.IsNailFrozen = meta.GetNailFrozen
function meta : SetNailFrozen ( frozen )
if frozen then
local phys = self : GetPhysicsObject ( )
if phys : IsValid ( ) and phys : IsMoveable ( ) then
self.m_NailFrozen = true
phys : EnableMotion ( false )
end
elseif self : IsNailFrozen ( ) then
local phys = self : GetPhysicsObject ( )
if phys : IsValid ( ) then
self.m_NailFrozen = false
phys : EnableMotion ( true )
phys : Wake ( )
end
end
end
function constraint . GetAllConstrainedEntitiesOrdered ( ent )
local allcons = constraint.GetAllConstrainedEntities ( ent )
local tab = { }
if allcons then
for k , v in pairs ( allcons ) do
table.insert ( tab , v )
end
end
return tab
end
function meta : GetAllConstrainedEntities ( )
local allcons = constraint.GetAllConstrainedEntitiesOrdered ( self )
if not allcons or # allcons == 0 then
return { self }
end
return allcons
end
function meta : PackUp ( pl )
if not self.CanPackUp then return end
local cur = pl : GetStatus ( " packup " )
if cur and cur : IsValid ( ) then return end
local status = pl : GiveStatus ( " packup " )
if status : IsValid ( ) then
status : SetPackUpEntity ( self )
2018-05-02 06:32:59 +08:00
status : SetEndTime ( CurTime ( ) + ( self.PackUpTime or 3 ) * ( not self.IgnorePackTimeMul and pl.DeployablePackTimeMul or 1 ) )
2014-10-02 08:49:54 +08:00
if self.GetObjectOwner then
local owner = self : GetObjectOwner ( )
if owner : IsValid ( ) and owner : Team ( ) == TEAM_HUMAN and owner ~= pl and not gamemode.Call ( " PlayerIsAdmin " , pl ) then
status : SetNotOwner ( true )
end
end
end
end
function meta : GetPropsInContraption ( )
local allcons = constraint.GetAllConstrainedEntities ( self )
if not allcons or # allcons == 0 then
return 1
end
return # allcons
end
function meta : HumanNearby ( )
for _ , pl in pairs ( team.GetPlayers ( TEAM_HUMAN ) ) do
2018-05-02 06:32:59 +08:00
if pl : Alive ( ) and self : GetPos ( ) : DistToSqr ( pl : GetPos ( ) ) <= 262144 then -- 512^2
2014-10-02 08:49:54 +08:00
return true
end
end
end
function meta : ResetLastBarricadeAttacker ( attacker , dmginfo )
if attacker : IsPlayer ( ) and attacker : Team ( ) == TEAM_UNDEAD then
self.m_LastDamagedByZombie = CurTime ( )
if self : HumanNearby ( ) then
local dmg = math.ceil ( dmginfo : GetDamage ( ) )
attacker.BarricadeDamage = attacker.BarricadeDamage + dmg
if attacker.LifeBarricadeDamage ~= nil then
attacker : AddLifeBarricadeDamage ( dmg )
2018-05-02 06:32:59 +08:00
GAMEMODE.StatTracking : IncreaseElementKV ( STATTRACK_TYPE_ZOMBIECLASS , attacker : GetZombieClassTable ( ) . Name , " BarricadeDamage " , dmg )
2014-10-02 08:49:54 +08:00
end
end
end
end
2022-07-12 18:33:47 +08:00
meta.OldSetPhysicsAttacker = meta.OldSetPhysicsAttacker or meta.SetPhysicsAttacker
2014-10-02 08:49:54 +08:00
function meta : SetPhysicsAttacker ( ent )
2014-11-23 02:41:04 +08:00
if string.sub ( self : GetClass ( ) , 1 , 12 ) == " func_physbox " and ent : IsValid ( ) then
2014-10-02 08:49:54 +08:00
self.PBAttacker = ent
2018-05-02 06:32:59 +08:00
self.NPBAttacker = CurTime ( ) + 5
2014-10-02 08:49:54 +08:00
end
self : OldSetPhysicsAttacker ( ent )
end
-- Return true to override default behavior.
function meta : DamageNails ( attacker , inflictor , damage , dmginfo )
if not self : IsNailed ( ) or self.m_NailsDontAbsorb then return end
2018-05-02 06:32:59 +08:00
-- Props that don't have barricade health yet might still be nailed to something.
local nails = self : GetLivingNails ( )
local isattach = false
for i , nail in ipairs ( nails ) do
isattach = self == nail : GetAttachEntity ( ) or isattach
end
if self : GetBarricadeHealth ( ) <= 0 and not isattach then return end
2014-10-02 08:49:54 +08:00
if not gamemode.Call ( " CanDamageNail " , self , attacker , inflictor , damage , dmginfo ) then
if dmginfo then
dmginfo : SetDamage ( 0 )
2018-05-02 06:32:59 +08:00
dmginfo : SetDamageType ( DMG_GENERIC )
2014-10-02 08:49:54 +08:00
end
return true
end
2018-05-02 06:32:59 +08:00
-- No physics damage to props. Stops ramming/thrown weapons damaging them.
if damage < 0 or dmginfo : GetDamageType ( ) == DMG_CRUSH then
2014-10-02 08:49:54 +08:00
if dmginfo then
dmginfo : SetDamage ( 0 )
end
return true
end
2018-05-02 06:32:59 +08:00
if self.ReinforceEnd and CurTime ( ) < self.ReinforceEnd and self.ReinforceApplier and self.ReinforceApplier : IsValidLivingHuman ( ) then
local applier = self.ReinforceApplier
local multi = 0.92
local dmgbefore = damage * 0.08
local points = dmgbefore / 8
dmginfo : SetDamage ( dmginfo : GetDamage ( ) * multi )
damage = damage * multi
applier.PropDef = ( applier.PropDef or 0 ) + dmgbefore
applier : AddPoints ( points )
end
if gamemode.Call ( " IsEscapeDoorOpen " ) then
local multi = gamemode.Call ( " GetEscapeStage " ) * 1.5
dmginfo : SetDamage ( dmginfo : GetDamage ( ) * multi )
damage = damage * multi
end
2014-10-02 08:49:54 +08:00
self : ResetLastBarricadeAttacker ( attacker , dmginfo )
if # nails <= 0 then return end
2018-05-02 06:32:59 +08:00
if attacker : IsPlayer ( ) then
-- :O)
if attacker.SpawnProtection then
damage = damage * 5
dmginfo : SetDamage ( damage )
self : AddUselessDamage ( damage )
end
GAMEMODE : DamageFloater ( attacker , self , dmginfo : GetDamagePosition ( ) , dmginfo : GetDamage ( ) )
end
2014-10-02 08:49:54 +08:00
self : SetBarricadeHealth ( self : GetBarricadeHealth ( ) - damage )
for i , nail in ipairs ( nails ) do
nail : OnDamaged ( damage , attacker , inflictor , dmginfo )
end
2018-05-02 06:32:59 +08:00
-- No points for repairing damage from fire, trigger_hurt, etc.
if not attacker : IsZombie ( ) then
self : AddUselessDamage ( damage )
2014-10-02 08:49:54 +08:00
end
2018-05-02 06:32:59 +08:00
attacker.LastBarricadeHit = CurTime ( )
2014-10-02 08:49:54 +08:00
if dmginfo then dmginfo : SetDamage ( 0 ) end
if self : GetBarricadeHealth ( ) <= 0 then
if self : GetModel ( ) ~= " " and self : GetModel ( ) ~= " models/error.mdl " then
if self : GetName ( ) == " " and self : GetVolume ( ) < 100 then
self : Fire ( " break " , " " , 0.01 )
self : Fire ( " kill " , " " , 0.05 )
else
local ent = ents.Create ( " env_propbroken " )
if ent : IsValid ( ) then
ent : Spawn ( )
ent : AttachTo ( self )
end
end
end
for _ , nail in pairs ( nails ) do
2018-05-02 06:32:59 +08:00
self : RemoveNail ( nail , nil , nil , true )
2014-10-02 08:49:54 +08:00
end
end
return true
end
function meta : GetNails ( )
local tab = { }
if self.Nails then
for _ , nail in pairs ( self.Nails ) do
if nail and nail : IsValid ( ) then
table.insert ( tab , nail )
end
end
end
return tab
end
function meta : GetLivingNails ( )
local tab = { }
if self.Nails then
for _ , nail in pairs ( self.Nails ) do
if nail and nail : IsValid ( ) and nail : GetNailHealth ( ) > 0 then
table.insert ( tab , nail )
end
end
end
return tab
end
2018-05-02 06:32:59 +08:00
function meta : NumLivingNails ( )
local amount = 0
if self.Nails then
for _ , nail in pairs ( self.Nails ) do
if nail and nail : IsValid ( ) and nail : GetNailHealth ( ) > 0 then
amount = amount + 1
end
end
end
return amount
end
2014-10-02 08:49:54 +08:00
function meta : GetFirstNail ( )
if self.Nails then
for i , nail in ipairs ( self.Nails ) do
if nail and nail : IsValid ( ) and not nail : GetAttachEntity ( ) : IsValid ( ) then return nail end
end
for i , nail in ipairs ( self.Nails ) do
if nail and nail : IsValid ( ) then return nail end
end
end
end
local function GetNailOwner ( nail , filter )
for _ , ent in pairs ( ents.GetAll ( ) ) do
2014-11-21 15:24:24 +08:00
if ent and ent ~= filter and ent.Nails and ent : IsValid ( ) then
2014-10-02 08:49:54 +08:00
for __ , n in pairs ( ent.Nails ) do
if n == nail then
return ent
end
end
end
end
return game.GetWorld ( )
end
2018-05-02 06:32:59 +08:00
function meta : RemoveNail ( nail , dontremoveentity , removedby , forceremoveconstraint )
2014-10-02 08:49:54 +08:00
if not self : IsNailed ( ) then return end
if not nail then
nail = self : GetFirstNail ( )
end
if not nail or not nail : IsValid ( ) then return end
local cons = nail : GetNailConstraint ( )
local othernails = 0
2018-05-02 06:32:59 +08:00
if not forceremoveconstraint then
for _ , othernail in pairs ( ents.FindByClass ( " prop_nail " ) ) do
if othernail ~= nail and not nail.m_IsRemoving and othernail : GetNailConstraint ( ) : IsValid ( ) and othernail : GetNailConstraint ( ) == cons then
othernails = othernails + 1
end
2014-10-02 08:49:54 +08:00
end
end
-- Only remove the constraint if it's the last nail.
if othernails == 0 and cons : IsValid ( ) then
2018-05-02 06:32:59 +08:00
if self.PropHealth and self : GetBarricadeHealth ( ) > 0 then
local repairs_frac = self : GetBarricadeRepairs ( ) / self : GetMaxBarricadeRepairs ( )
if repairs_frac < 0.5 then
self.PropHealth = math.min ( self.PropHealth , self : GetBarricadeHealth ( ) )
local brit = math.Clamp ( self.PropHealth / self.TotalHealth , 0 , 1 )
local col = self : GetColor ( )
col.r = 255
col.g = 255 * brit
col.b = 255 * brit
self : SetColor ( col )
end
end
2014-10-02 08:49:54 +08:00
cons : Remove ( )
end
local ent2 = GetNailOwner ( nail , self )
for i , n in ipairs ( self.Nails ) do
if n == nail then
table.remove ( self.Nails , i )
break
end
end
if ent2 and ent2.Nails then
for i , n in ipairs ( ent2.Nails ) do
if n == nail then
table.remove ( ent2.Nails , i )
ent2 : TemporaryBarricadeObject ( )
break
end
end
end
self : TemporaryBarricadeObject ( )
gamemode.Call ( " OnNailRemoved " , nail , self , ent2 , removedby )
if not dontremoveentity then
nail : Remove ( )
nail.m_IsRemoving = true
end
2018-05-02 06:32:59 +08:00
self : RecalculateNailBonuses ( )
self : CollisionRulesChanged ( )
if ent2 and ent2 : IsValid ( ) then
ent2 : CollisionRulesChanged ( )
end
2014-10-02 08:49:54 +08:00
return true
end
2018-05-02 06:32:59 +08:00
function meta : RemoveNextFrame ( time )
self.Removing = true
self : Fire ( " kill " , " " , time or 0.01 )
end
local function barricadetimer ( self , timername )
if self : IsValid ( ) then
for _ , e in pairs ( ents.FindInBox ( self : WorldSpaceAABB ( ) ) ) do
if e and e : IsValid ( ) and e : IsPlayer ( ) and e : Alive ( ) then
return
end
end
self.IsBarricadeObject = nil
self : CollisionRulesChanged ( )
end
timer.Remove ( timername )
end
function meta : TemporaryBarricadeObject ( )
if self.IsBarricadeObject then return end
for _ , e in pairs ( ents.FindInBox ( self : WorldSpaceAABB ( ) ) ) do
if e and e : IsValid ( ) and e : IsPlayer ( ) and e : Alive ( ) then
self.IsBarricadeObject = true
self : CollisionRulesChanged ( )
local timername = " TemporaryBarricadeObject " .. self : GetCreationID ( )
timer.Create ( timername , 0 , 0 , function ( ) barricadetimer ( self , timername ) end )
return
end
end
end
function meta : RecalculateNailBonuses ( )
local max_health = self : GetMaxBarricadeHealth ( )
if max_health == 0 then return end
local num_extra_nails = math.Clamp ( self : NumLivingNails ( ) - 1 , 0 , 3 )
local repairs_frac = self : GetBarricadeRepairs ( ) / self : GetMaxBarricadeRepairs ( )
self.OriginalMaxHealth = self.OriginalMaxHealth or max_health
self.OriginalMaxBarricadeRepairs = self.OriginalMaxBarricadeRepairs or max_repairs
local health = self : GetBarricadeHealth ( )
local new_max_health = self.OriginalMaxHealth + num_extra_nails * GAMEMODE.ExtraHealthPerExtraNail
self : SetMaxBarricadeHealth ( new_max_health )
self : SetBarricadeHealth ( health / max_health * new_max_health )
self : SetBarricadeRepairs ( repairs_frac * self : GetMaxBarricadeRepairs ( ) )
end
function meta : SetupDeployableSkillHealth ( extramodifier )
local owner = self : GetObjectOwner ( )
local newmaxhealth = self.MaxHealth or self : GetMaxObjectHealth ( )
local currentmaxhealth = self : GetMaxObjectHealth ( )
if owner : IsValid ( ) then
newmaxhealth = newmaxhealth * owner : GetTotalAdditiveModifier ( " DeployableHealthMul " , extramodifier )
end
newmaxhealth = math.ceil ( newmaxhealth )
self : SetMaxObjectHealth ( newmaxhealth )
self : SetObjectHealth ( self : GetObjectHealth ( ) / currentmaxhealth * newmaxhealth )
end
function meta : DealProjectileTraceDamage ( damage , tr , owner )
local ent = tr.Entity
local damageinfo = DamageInfo ( )
damageinfo : SetDamageType ( DMG_BULLET )
damageinfo : SetDamage ( damage )
damageinfo : SetDamagePosition ( tr.HitPos )
damageinfo : SetAttacker ( owner )
damageinfo : SetInflictor ( self : ProjectileDamageSource ( ) )
local vel
if ent : IsPlayer ( ) then
ent : SetLastHitGroup ( tr.HitGroup )
if tr.HitGroup == HITGROUP_HEAD then
ent : SetWasHitInHead ( )
end
vel = ent : GetVelocity ( )
end
ent : DispatchTraceAttack ( damageinfo , tr , tr.Normal )
if vel and ent : IsPlayer ( ) and owner ~= ent then
ent : SetLocalVelocity ( vel )
end
end
GM.ProjectileThickness = 3
function meta : ProjectileTraceAhead ( phys )
if not self.Touched then
local vel = self.PreVel or phys : GetVelocity ( )
if self.PreVel then self.PreVel = nil end
local velnorm = vel : GetNormalized ( )
local ahead = ( vel : LengthSqr ( ) * FrameTime ( ) ) / 1200
local fwd = velnorm * ahead
local start = self : GetPos ( ) - fwd
local side = vel : Angle ( ) : Right ( ) * GAMEMODE.ProjectileThickness
local proj_trace = { mask = MASK_SHOT , filter = { self , team.GetPlayers ( TEAM_HUMAN ) } }
proj_trace.start = start - side
proj_trace.endpos = start - side + fwd
local tr = util.TraceLine ( proj_trace )
proj_trace.start = start + side
proj_trace.endpos = start + side + fwd
local tr2 = util.TraceLine ( proj_trace )
local trs = { tr , tr2 }
for _ , trace in pairs ( trs ) do
if trace.Entity then
local ent = trace.Entity
if ent : IsValidLivingZombie ( ) or ent.ZombieConstruction then
self.Touched = trace
end
break
end
end
end
end
-- Cache invisible entities every so often, for TrueVisible functions. Fixes a few issues and improves performance.
GM.CachedInvisibleEntities = { }
timer.Create ( " CachedInvisibleEntities " , 1 , 0 , function ( )
if not GAMEMODE then return end
GAMEMODE.CachedInvisibleEntities = { }
local invisents = player.GetAll ( )
for _ , ent in pairs ( ents.GetAll ( ) ) do
if ent.IgnoreTraces or ent.NoBlockExplosions then
invisents [ # invisents + 1 ] = ent
end
end
GAMEMODE.CachedInvisibleEntities = invisents
end )