Files
wnsrc/lua/entities/lvs_base/sv_damagesystem.lua
lifestorm c6d9b6f580 Upload
2024-08-05 18:40:29 +03:00

412 lines
10 KiB
Lua

--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
ENT._armorParts = {}
ENT._dmgParts = {}
ENT.DSArmorDamageReduction = 0.1
ENT.DSArmorDamageReductionType = DMG_BULLET + DMG_CLUB
ENT.DSArmorIgnoreDamageType = DMG_SONIC
ENT.DSArmorIgnoreForce = 0
ENT.DSArmorBulletPenetrationAdd = 250
function ENT:AddDS( data )
if not data then return end
data.pos = data.pos or Vector(0,0,0)
data.ang = data.ang or Angle(0,0,0)
data.mins = data.mins or Vector(-1,-1,-1)
data.maxs = data.maxs or Vector(1,1,1)
data.Callback = data.Callback or function( tbl, ent, dmginfo ) end
debugoverlay.BoxAngles( self:LocalToWorld( data.pos ), data.mins, data.maxs, self:LocalToWorldAngles( data.ang ), 5, Color( 50, 0, 50, 150 ) )
table.insert( self._dmgParts, data )
end
function ENT:AddDSArmor( data )
if not data then return end
data.pos = data.pos or Vector(0,0,0)
data.ang = data.ang or Angle(0,0,0)
data.mins = data.mins or Vector(-1,-1,-1)
data.maxs = data.maxs or Vector(1,1,1)
data.Callback = data.Callback or function( tbl, ent, dmginfo ) end
debugoverlay.BoxAngles( self:LocalToWorld( data.pos ), data.mins, data.maxs, self:LocalToWorldAngles( data.ang ), 5, Color( 0, 50, 50, 150 ) )
table.insert( self._armorParts, data )
end
function ENT:CalcComponentDamage( dmginfo )
local Len = self:BoundingRadius()
local dmgPos = dmginfo:GetDamagePosition()
local dmgDir = dmginfo:GetDamageForce():GetNormalized()
local dmgPenetration = dmgDir * self.DSArmorBulletPenetrationAdd
debugoverlay.Line( dmgPos - dmgDir * self.DSArmorBulletPenetrationAdd, dmgPos + dmgPenetration, 4, Color( 0, 0, 255 ) )
local closestPart
local closestDist = Len * 2
local HitDistance
for index, part in ipairs( self._armorParts ) do
local mins = part.mins
local maxs = part.maxs
local pos = self:LocalToWorld( part.pos )
local ang = self:LocalToWorldAngles( part.ang )
local HitPos, HitNormal, Fraction = util.IntersectRayWithOBB( dmgPos, dmgPenetration, pos, ang, mins, maxs )
if HitPos then
debugoverlay.Cross( HitPos, 50, 4, Color( 255, 0, 255 ) )
local dist = (HitPos - dmgPos):Length()
if closestDist > dist then
closestPart = part
closestDist = dist
HitDistance = (HitPos - dmgPos):Length()
end
end
end
local lastPartDS
local closestPartDS
local closestDistDS = Len * 2
for index, part in ipairs( self._dmgParts ) do
local mins = part.mins
local maxs = part.maxs
local pos = self:LocalToWorld( part.pos )
local ang = self:LocalToWorldAngles( part.ang )
local HitPos, HitNormal, Fraction = util.IntersectRayWithOBB( dmgPos, dmgPenetration, pos, ang, mins, maxs )
if HitPos and HitDistance then
lastPartDS = part
if HitDistance < (HitPos - dmgPos):Length() then continue end
closestPart = nil
closestDist = Len * 2
end
if not HitPos then continue end
debugoverlay.Cross( HitPos, 50, 4, Color( 255, 0, 255 ) )
local dist = (HitPos - pos):Length()
if closestDistDS > dist then
closestPartDS = part
closestDistDS = dist
end
end
local Hit = false
for index, part in pairs( self._dmgParts ) do
local mins = part.mins
local maxs = part.maxs
local pos = self:LocalToWorld( part.pos )
local ang = self:LocalToWorldAngles( part.ang )
if part == closestPartDS then
Hit = true
part:Callback( self, dmginfo )
debugoverlay.BoxAngles( pos, mins, maxs, ang, 1, Color( 255, 0, 0, 150 ) )
end
end
for index, part in pairs( self._armorParts ) do
local mins = part.mins
local maxs = part.maxs
local pos = self:LocalToWorld( part.pos )
local ang = self:LocalToWorldAngles( part.ang )
if part == closestPart then
if not part:Callback( self, dmginfo ) then
lastPartDS = nil
end
debugoverlay.BoxAngles( pos, mins, maxs, ang, 1, Color( 0, 150, 0, 150 ) )
end
end
if lastPartDS then
lastPartDS:Callback( self, dmginfo )
local mins = lastPartDS.mins
local maxs = lastPartDS.maxs
local pos = self:LocalToWorld( lastPartDS.pos )
local ang = self:LocalToWorldAngles( lastPartDS.ang )
debugoverlay.BoxAngles( pos, mins, maxs, ang, 1, Color( 255, 0, 0, 150 ) )
Hit = false
end
return Hit
end
function ENT:CalcDamage( dmginfo )
if dmginfo:IsDamageType( self.DSArmorIgnoreDamageType ) then return end
if dmginfo:IsDamageType( self.DSArmorDamageReductionType ) then
if dmginfo:GetDamage() ~= 0 then
dmginfo:ScaleDamage( self.DSArmorDamageReduction )
dmginfo:SetDamage( math.max(dmginfo:GetDamage(),1) )
end
end
if dmginfo:IsDamageType( DMG_BLAST ) then
local Inflictor = dmginfo:GetInflictor()
if IsValid( Inflictor ) and isfunction( Inflictor.GetEntityFilter ) then
for ents, _ in pairs( Inflictor:GetEntityFilter() ) do
if ents == self then return end
end
end
end
local IsFireDamage = dmginfo:IsDamageType( DMG_BURN )
local IsCollisionDamage = dmginfo:GetDamageType() == (DMG_CRUSH + DMG_VEHICLE)
local CriticalHit = false
if dmginfo:GetDamageForce():Length() < self.DSArmorIgnoreForce and not IsFireDamage then return end
if not IsCollisionDamage then
CriticalHit = self:CalcComponentDamage( dmginfo )
end
local Damage = dmginfo:GetDamage()
if Damage <= 0 then return end
local CurHealth = self:GetHP()
local NewHealth = math.Clamp( CurHealth - Damage, -self:GetMaxHP(), self:GetMaxHP() )
self:SetHP( NewHealth )
if self:IsDestroyed() then return end
local Attacker = dmginfo:GetAttacker()
if IsValid( Attacker ) and Attacker:IsPlayer() and not IsFireDamage then
net.Start( "lvs_hitmarker" )
net.WriteBool( CriticalHit )
net.Send( Attacker )
end
if Damage > 1 and not IsCollisionDamage and not IsFireDamage then
net.Start( "lvs_hurtmarker" )
net.WriteFloat( math.min( Damage / 50, 1 ) )
net.Send( self:GetEveryone() )
end
if NewHealth <= 0 then
self:SetDestroyed( IsCollisionDamage )
self:ClearPDS()
self.FinalAttacker = dmginfo:GetAttacker()
self.FinalInflictor = dmginfo:GetInflictor()
local Attacker = self.FinalAttacker
if IsValid( Attacker ) and Attacker:IsPlayer() then
net.Start( "lvs_killmarker" )
net.Send( Attacker )
end
local ExplodeTime = self:PreExplode( math.Clamp((self:GetVelocity():Length() - 200) / 200,1.5,16) )
timer.Simple( ExplodeTime, function()
if not IsValid( self ) then return end
self:Explode()
end)
end
end
function ENT:PreExplode( ExplodeTime )
self:OnStartExplosion()
local PhysObj = self:GetPhysicsObject()
if not IsValid( PhysObj ) then return 0 end
self:OnStartFireTrail( PhysObj, ExplodeTime )
return ExplodeTime
end
function ENT:FindDS( PosToCheck, RadiusAdd )
if not isnumber( RadiusAdd ) then
RadiusAdd = 1
end
local closestPart
local closestDist = 50000
local ToCenter = (self:LocalToWorld( self:OBBCenter() ) - PosToCheck):GetNormalized()
debugoverlay.Cross( PosToCheck, 50, 4, Color( 255, 255, 0 ) )
for _, tbl in ipairs( { self._armorParts, self._dmgParts } ) do
for index, part in ipairs( tbl ) do
local mins = part.mins
local maxs = part.maxs
local pos = self:LocalToWorld( part.pos )
local ang = self:LocalToWorldAngles( part.ang )
local HitPos, HitNormal, Fraction = util.IntersectRayWithOBB( PosToCheck, ToCenter * RadiusAdd, pos, ang, mins, maxs )
if HitPos then
local dist = (HitPos - PosToCheck):Length()
if closestDist > dist then
closestPart = part
closestDist = dist
end
end
end
end
if closestPart then
local mins = closestPart.mins
local maxs = closestPart.maxs
local pos = self:LocalToWorld( closestPart.pos )
local ang = self:LocalToWorldAngles( closestPart.ang )
debugoverlay.BoxAngles( pos, mins, maxs, ang, 1, Color( 255, 255, 0, 150 ) )
end
return closestPart
end
function ENT:DamageThink()
if self.MarkForDestruction then
self:Explode()
end
if self:IsDestroyed() then
if self:GetVelocity():Length() < 800 then
self:Explode()
end
end
end
function ENT:HurtPlayer( ply, dmg, attacker, inflictor )
if not IsValid( ply ) then return end
if not IsValid( attacker ) then
attacker = game.GetWorld()
end
if not IsValid( inflictor ) then
inflictor = game.GetWorld()
end
local dmginfo = DamageInfo()
dmginfo:SetDamage( dmg )
dmginfo:SetAttacker( attacker )
dmginfo:SetInflictor( inflictor )
dmginfo:SetDamageType( DMG_DIRECT )
ply:TakeDamageInfo( dmginfo )
end
function ENT:Explode()
if self.ExplodedAlready then return end
self.ExplodedAlready = true
local Driver = self:GetDriver()
if IsValid( Driver ) then
self:HurtPlayer( Driver, Driver:Health() + Driver:Armor(), self.FinalAttacker, self.FinalInflictor )
end
if istable( self.pSeats ) then
for _, pSeat in pairs( self.pSeats ) do
if not IsValid( pSeat ) then continue end
local psgr = pSeat:GetDriver()
if not IsValid( psgr ) then continue end
self:HurtPlayer( psgr, psgr:Health() + psgr:Armor(), self.FinalAttacker, self.FinalInflictor )
end
end
self:OnFinishExplosion()
self:Remove()
end
function ENT:OnStartExplosion()
local effectdata = EffectData()
effectdata:SetOrigin( self:GetPos() )
util.Effect( "lvs_explosion_nodebris", effectdata )
end
function ENT:OnFinishExplosion()
local ent = ents.Create( "lvs_destruction" )
if not IsValid( ent ) then return end
ent:SetModel( self:GetModel() )
ent:SetPos( self:GetPos() )
ent:SetAngles( self:GetAngles() )
ent.GibModels = self.GibModels
ent.Vel = self:GetVelocity()
ent:Spawn()
ent:Activate()
end
function ENT:OnStartFireTrail( PhysObj, ExplodeTime )
local effectdata = EffectData()
effectdata:SetOrigin( self:GetPos() )
effectdata:SetStart( PhysObj:GetMassCenter() )
effectdata:SetEntity( self )
effectdata:SetScale( (self.FireTrailScale or 1) )
effectdata:SetMagnitude( ExplodeTime )
util.Effect( "lvs_firetrail", effectdata )
end
function ENT:IsDestroyed()
return self.Destroyed == true
end
function ENT:OnDestroyed()
end
util.AddNetworkString( "lvs_vehicle_destroy" )
function ENT:SetDestroyed( SuppressOnDestroy )
if self.Destroyed then return end
self.Destroyed = true
hook.Run( "LVS.UpdateRelationship", self )
if SuppressOnDestroy then return end
self:OnDestroyed()
net.Start("lvs_vehicle_destroy")
net.WriteEntity( self )
net.SendPAS( self:GetPos() )
end
include("sv_damagesystem_armor.lua")