--[[ | 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._pdsParts = {} ENT.PDSDamageVelocity = 100 ENT.PDSDamageMultiplier = 0.05 function ENT:ClearPDS() if not istable( self._pdsParts ) then return end table.Empty( self._pdsParts ) end function ENT:PDSHealthValueChanged( name, old, new) if new == old then return end if not self:IsInitialized() or not istable( self._pdsParts ) or new ~= self:GetMaxHP() then return end for _, part in pairs( self._pdsParts ) do part:SetStage( 0 ) if not part._group or not part._subgroup then continue end self:SetBodygroup( part._group, part._subgroup ) part._group = nil part._subgroup = nil end end local function DamagePart( ent, part, speed ) if not speed then speed = 0 end local stage = part:GetStage() + 1 part:SetStage( stage ) local data = part:GetStageData() if isfunction( data.Callback ) then data:Callback( ent, part, speed ) end if istable( data.bodygroup ) then for group, subgroup in pairs( data.bodygroup ) do if not part._group or not part._subgroup then part._group = group part._subgroup = ent:GetBodygroup( group ) end ent:SetBodygroup( group, subgroup ) end end if isstring( data.sound ) then ent:EmitSound( data.sound, 75, 100, math.min(0.1 + speed / 700,1) ) end if isstring( data.effect ) then local effectdata = EffectData() effectdata:SetOrigin( ent:LocalToWorld( part.pos ) ) util.Effect( data.effect, effectdata, true, true ) end if not istable( data.gib ) or not data.gib.mdl then return end timer.Simple(0, function() if not IsValid( ent ) then return end local InvAttach = isstring( data.gib.target ) local pos local ang if InvAttach then pos = vector_origin ang = angle_zero else if isvector( data.gib.pos ) and isangle( data.gib.ang ) then pos = ent:LocalToWorld( data.gib.pos ) ang = ent:LocalToWorldAngles( data.gib.ang ) end end local gib = ents.Create( "prop_physics" ) gib:SetModel( data.gib.mdl ) gib:SetPos( pos ) gib:SetAngles( ang ) gib:Spawn() gib:Activate() gib:SetCollisionGroup( COLLISION_GROUP_DEBRIS ) gib:SetSkin( ent:GetSkin() ) if InvAttach then local att = gib:GetAttachment( gib:LookupAttachment( data.gib.target ) ) if att then local newpos = ent:LocalToWorld( -att.Pos ) local newang = ent:LocalToWorldAngles( -att.Ang ) gib:SetPos( newpos ) gib:SetAngles( newang ) end end gib:SetOwner( ent ) gib:SetColor( ent:GetColor() ) gib:SetRenderMode( RENDERMODE_TRANSALPHA ) ent:DeleteOnRemove( gib ) ent:TransferCPPI( gib ) timer.Simple( 59.5, function() if not IsValid( gib ) then return end gib:SetRenderFX( kRenderFxFadeFast ) end) timer.Simple( 60, function() if not IsValid( gib ) then return end gib:Remove() end) local PhysObj = gib:GetPhysicsObject() if not IsValid( PhysObj ) then return end PhysObj:SetVelocityInstantaneous( ent:GetVelocity() + Vector(0,0,250) ) PhysObj:AddAngleVelocity( VectorRand() * 500 ) end) end function ENT:AddPDS( data ) if not data then return end if self._pdsPartsID then self._pdsPartsID = self._pdsPartsID + 1 else self._pdsPartsID = 1 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.stages = data.stages or {} data.GetStage = function( self ) if not self._curstage then self._curstage = 0 end return self._curstage end data.SetStage = function( self, stage ) self._curstage = stage end data.GetStageData = function( self, stage ) return self.stages[ self:GetStage() ] or {} end debugoverlay.BoxAngles( self:LocalToWorld( data.pos ), data.mins, data.maxs, self:LocalToWorldAngles( data.ang ), 8, Color( 50, 50, 0, 150 ) ) self._pdsParts[ self._pdsPartsID ] = data if data.allow_damage then local id = self._pdsPartsID self:AddDS( { pos = data.pos, ang = data.ang, mins = data.mins, maxs = data.maxs, Callback = function( tbl, ent, dmginfo ) if not IsValid( ent ) then return end local part = ent._pdsParts[ id ] if not part then return end DamagePart( ent, part, 1000 ) end } ) end end function ENT:FindPDS( PosToCheck, RadiusAdd ) if not isnumber( RadiusAdd ) then RadiusAdd = 1 end local Parts = {} debugoverlay.Cross( PosToCheck, 50, 4, Color( 255, 255, 0 ) ) for index, part in ipairs( self._pdsParts ) do local mins = part.mins local maxs = part.maxs local pos = self:LocalToWorld( part.pos ) local ang = self:LocalToWorldAngles( part.ang ) local dir = (pos - PosToCheck):GetNormalized() local HitPos, HitNormal, Fraction = util.IntersectRayWithOBB( PosToCheck, dir * RadiusAdd, pos, ang, mins, maxs ) if HitPos then table.insert( Parts, part ) end end for _, closestPart in ipairs( Parts ) do 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 Parts end function ENT:CalcPDS( physdata ) local VelDif = math.abs( physdata.OurOldVelocity:Length() - physdata.OurNewVelocity:Length() ) if VelDif < self.PDSDamageVelocity then return end local parts = self:FindPDS( physdata.HitPos, (VelDif - self.PDSDamageVelocity) * self.PDSDamageMultiplier ) if #parts == 0 then return end local HP = self:GetHP() local MaxHP = self:GetMaxHP() if HP == MaxHP then self:SetHP( math.max( MaxHP - 0.1, 1 ) ) end for _, part in pairs( parts ) do DamagePart( self, part, VelDif ) end end