mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-16 21:33:46 +03:00
664 lines
16 KiB
Lua
664 lines
16 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/
|
||
|
|
--]]
|
||
|
|
|
||
|
|
AddCSLuaFile()
|
||
|
|
|
||
|
|
ENT.Type = "anim"
|
||
|
|
|
||
|
|
ENT.Spawnable = false
|
||
|
|
ENT.AdminSpawnable = false
|
||
|
|
|
||
|
|
ENT.DoNotDuplicate = true
|
||
|
|
|
||
|
|
ENT.RenderGroup = RENDERGROUP_BOTH
|
||
|
|
|
||
|
|
function ENT:SetupDataTables()
|
||
|
|
self:NetworkVar( "Float", 0, "Radius")
|
||
|
|
self:NetworkVar( "Float", 1, "OnGround" )
|
||
|
|
self:NetworkVar( "Float", 2, "RPM" )
|
||
|
|
self:NetworkVar( "Float", 3, "GripLoss" )
|
||
|
|
self:NetworkVar( "Float", 4, "Speed" )
|
||
|
|
self:NetworkVar( "Float", 5, "SkidSound" )
|
||
|
|
self:NetworkVar( "String", 2, "SurfaceMaterial" )
|
||
|
|
self:NetworkVar( "Bool", 1, "Damaged" )
|
||
|
|
self:NetworkVar( "Entity", 1, "BaseEnt" )
|
||
|
|
|
||
|
|
if SERVER then
|
||
|
|
self:NetworkVarNotify( "Damaged", self.OnDamaged )
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:GetWidth()
|
||
|
|
return 3
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:VelToRPM( speed )
|
||
|
|
if not speed then return 0 end
|
||
|
|
|
||
|
|
return speed * 60 / math.pi / (self:GetRadius() * 2)
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:RPMToVel( rpm )
|
||
|
|
if not rpm then return 0 end
|
||
|
|
|
||
|
|
return (math.pi * rpm * self:GetRadius() * 2) / 60
|
||
|
|
end
|
||
|
|
|
||
|
|
if SERVER then
|
||
|
|
function ENT:Initialize()
|
||
|
|
self:SetModel( "models/props_vehicles/tire001c_car.mdl" )
|
||
|
|
self:PhysicsInit( SOLID_VPHYSICS )
|
||
|
|
self:SetMoveType( MOVETYPE_VPHYSICS )
|
||
|
|
self:SetSolid( SOLID_VPHYSICS )
|
||
|
|
self:SetCollisionGroup( COLLISION_GROUP_WEAPON )
|
||
|
|
self:SetUseType( SIMPLE_USE )
|
||
|
|
self:AddFlags( FL_OBJECT )
|
||
|
|
self:AddEFlags( EFL_NO_PHYSCANNON_INTERACTION )
|
||
|
|
|
||
|
|
self:DrawShadow( false )
|
||
|
|
|
||
|
|
self.OldMaterial = ""
|
||
|
|
self.OldMaterial2 = ""
|
||
|
|
self.OldVar = 0
|
||
|
|
self.OldVar2 = 0
|
||
|
|
|
||
|
|
local Color = self:GetColor()
|
||
|
|
local dot = Color.r * Color.g * Color.b * Color.a
|
||
|
|
self.OldColor = dot
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:Use( ply )
|
||
|
|
local base = self:GetBaseEnt()
|
||
|
|
|
||
|
|
if not IsValid( base ) then return end
|
||
|
|
|
||
|
|
base:Use( ply )
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:Think()
|
||
|
|
if self.GhostEnt then
|
||
|
|
local Color = self:GetColor()
|
||
|
|
local dot = Color.r * Color.g * Color.b * Color.a
|
||
|
|
if dot ~= self.OldColor then
|
||
|
|
if IsValid( self.GhostEnt ) then
|
||
|
|
self.GhostEnt:SetColor( Color )
|
||
|
|
self.GhostEnt:SetRenderMode( self:GetRenderMode() )
|
||
|
|
end
|
||
|
|
self.OldColor = dot
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
self:NextThink( CurTime() + 0.15 )
|
||
|
|
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:FixTire()
|
||
|
|
self:SetDamaged( false )
|
||
|
|
|
||
|
|
if not self.PreBreak then return end
|
||
|
|
|
||
|
|
self.PreBreak:Stop()
|
||
|
|
self.PreBreak = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:OnRemove()
|
||
|
|
if self.PreBreak then
|
||
|
|
self.PreBreak:Stop()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:PhysicsCollide( data, physobj )
|
||
|
|
if data.Speed > 100 and data.DeltaTime > 0.2 then
|
||
|
|
if data.Speed > 400 then
|
||
|
|
self:EmitSound( "Rubber_Tire.ImpactHard" )
|
||
|
|
self:EmitSound( "simulated_vehicles/suspension_creak_".. math.random(1,6) ..".ogg" )
|
||
|
|
else
|
||
|
|
self:EmitSound( "Rubber.ImpactSoft" )
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:OnTakeDamage( dmginfo )
|
||
|
|
local BaseEnt = self:GetBaseEnt()
|
||
|
|
|
||
|
|
if IsValid( BaseEnt ) and dmginfo:IsDamageType( DMG_AIRBOAT + DMG_BULLET ) then
|
||
|
|
BaseEnt:OnTakeDamage( dmginfo )
|
||
|
|
end
|
||
|
|
|
||
|
|
if self:GetDamaged() or not simfphys.DamageEnabled then return end
|
||
|
|
|
||
|
|
local Damage = dmginfo:GetDamage()
|
||
|
|
local DamagePos = dmginfo:GetDamagePosition()
|
||
|
|
|
||
|
|
if not IsValid( BaseEnt ) or BaseEnt:GetBulletProofTires() or Damage <= 1 then return end
|
||
|
|
|
||
|
|
if not self.PreBreak then
|
||
|
|
self.PreBreak = CreateSound(self, "ambient/gas/cannister_loop.wav")
|
||
|
|
self.PreBreak:PlayEx(0.5,100)
|
||
|
|
|
||
|
|
timer.Simple(math.Rand(0.5,5), function()
|
||
|
|
if IsValid(self) and not self:GetDamaged() then
|
||
|
|
self:SetDamaged( true )
|
||
|
|
if self.PreBreak then
|
||
|
|
self.PreBreak:Stop()
|
||
|
|
self.PreBreak = nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
else
|
||
|
|
self:SetDamaged( true )
|
||
|
|
self.PreBreak:Stop()
|
||
|
|
self.PreBreak = nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:OnDamaged( name, old, new)
|
||
|
|
if new == old then return end
|
||
|
|
|
||
|
|
if new == true then
|
||
|
|
self.dRadius = self:BoundingRadius() * 0.28
|
||
|
|
|
||
|
|
self:EmitSound( "simulated_vehicles/sfx/tire_break.ogg" )
|
||
|
|
|
||
|
|
if IsValid( self.GhostEnt ) then
|
||
|
|
self.GhostEnt:SetParent( nil )
|
||
|
|
self.GhostEnt:GetPhysicsObject():EnableMotion( false )
|
||
|
|
self.GhostEnt:SetPos( self:LocalToWorld( Vector(0,0,-self.dRadius) ) )
|
||
|
|
self.GhostEnt:SetParent( self )
|
||
|
|
end
|
||
|
|
else
|
||
|
|
if IsValid( self.GhostEnt ) then
|
||
|
|
self.GhostEnt:SetParent( nil )
|
||
|
|
self.GhostEnt:GetPhysicsObject():EnableMotion( false )
|
||
|
|
self.GhostEnt:SetPos( self:LocalToWorld( Vector(0,0,0) ) )
|
||
|
|
self.GhostEnt:SetParent( self )
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local BaseEnt = self:GetBaseEnt()
|
||
|
|
|
||
|
|
if not IsValid( BaseEnt ) then return end
|
||
|
|
|
||
|
|
BaseEnt:SetSuspension( self.Index , new )
|
||
|
|
end
|
||
|
|
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
ENT.SkidmarkTraceAdd = Vector(0,0,10)
|
||
|
|
ENT.SkidmarkDelay = 0.05
|
||
|
|
ENT.SkidmarkLifetime = 10
|
||
|
|
|
||
|
|
ENT.SkidmarkRed = 0
|
||
|
|
ENT.SkidmarkGreen = 0
|
||
|
|
ENT.SkidmarkBlue = 0
|
||
|
|
ENT.SkidmarkAlpha = 150
|
||
|
|
|
||
|
|
ENT.SkidmarkSurfaces = {
|
||
|
|
["concrete"] = true,
|
||
|
|
["tile"] = true,
|
||
|
|
["metal"] = true,
|
||
|
|
["boulder"] = true,
|
||
|
|
["default"] = true,
|
||
|
|
}
|
||
|
|
|
||
|
|
ENT.DustEffectSurfaces = {
|
||
|
|
["sand"] = true,
|
||
|
|
["dirt"] = true,
|
||
|
|
["grass"] = true,
|
||
|
|
["antlionsand"] = true,
|
||
|
|
}
|
||
|
|
|
||
|
|
function ENT:Initialize()
|
||
|
|
self.FadeHeat = 0
|
||
|
|
|
||
|
|
timer.Simple( 0.01, function()
|
||
|
|
if not IsValid( self ) then return end
|
||
|
|
self.Radius = self:BoundingRadius()
|
||
|
|
end)
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:Think()
|
||
|
|
self:ManageSmoke()
|
||
|
|
|
||
|
|
local T = CurTime()
|
||
|
|
|
||
|
|
if (self.fxTimer or 0) < T then
|
||
|
|
self:CalcWheelSlip()
|
||
|
|
|
||
|
|
self.fxTimer = T + 0.1
|
||
|
|
end
|
||
|
|
|
||
|
|
self:SetNextClientThink( CurTime() + 0.005 )
|
||
|
|
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:ManageSmoke()
|
||
|
|
local BaseEnt = self:GetBaseEnt()
|
||
|
|
|
||
|
|
if not IsValid( BaseEnt ) then return end
|
||
|
|
|
||
|
|
if LocalPlayer():GetPos():DistToSqr(self:GetPos()) > 6000 * 6000 or not BaseEnt:GetActive() then return end
|
||
|
|
|
||
|
|
local WheelOnGround = self:GetOnGround()
|
||
|
|
local GripLoss = self:GetGripLoss()
|
||
|
|
local Material = self:GetSurfaceMaterial()
|
||
|
|
|
||
|
|
if WheelOnGround > 0 and (Material == "concrete" or Material == "rock" or Material == "tile") and GripLoss > 0 then
|
||
|
|
self.FadeHeat = math.Clamp( self.FadeHeat + GripLoss * 0.06,0,10)
|
||
|
|
else
|
||
|
|
self.FadeHeat = self.FadeHeat * 0.995
|
||
|
|
end
|
||
|
|
|
||
|
|
local Scale = self.FadeHeat ^ 3 * 0.001
|
||
|
|
local SmokeOn = (self.FadeHeat >= 7)
|
||
|
|
local DirtOn = GripLoss > 0.05
|
||
|
|
local lcolor = BaseEnt:GetTireSmokeColor() * 255
|
||
|
|
local Speed = self:GetVelocity():Length()
|
||
|
|
local OnRim = self:GetDamaged()
|
||
|
|
|
||
|
|
local Forward = self:GetForward()
|
||
|
|
local Dir = (BaseEnt:GetGear() < 2) and Forward or -Forward
|
||
|
|
|
||
|
|
local WheelSize = self.Radius or 0
|
||
|
|
local Pos = self:GetPos()
|
||
|
|
|
||
|
|
if SmokeOn and not OnRim then
|
||
|
|
local effectdata = EffectData()
|
||
|
|
effectdata:SetOrigin( Pos )
|
||
|
|
effectdata:SetNormal( Dir )
|
||
|
|
effectdata:SetMagnitude( Scale )
|
||
|
|
effectdata:SetRadius( WheelSize )
|
||
|
|
effectdata:SetStart( Vector( lcolor.r, lcolor.g, lcolor.b ) )
|
||
|
|
util.Effect( "simfphys_tiresmoke", effectdata )
|
||
|
|
end
|
||
|
|
|
||
|
|
if WheelOnGround == 0 then return end
|
||
|
|
|
||
|
|
if (Speed > 150 or DirtOn) and OnRim then
|
||
|
|
self:MakeSparks( GripLoss, Dir, Pos, WheelSize )
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:MakeSparks( Scale, Dir, Pos, WheelSize )
|
||
|
|
self.NextSpark = self.NextSpark or 0
|
||
|
|
|
||
|
|
if self.NextSpark < CurTime() then
|
||
|
|
|
||
|
|
self.NextSpark = CurTime() + 0.1
|
||
|
|
local effectdata = EffectData()
|
||
|
|
effectdata:SetOrigin( Pos - Vector(0,0,WheelSize * 0.5) )
|
||
|
|
effectdata:SetNormal( (Dir + Vector(0,0,0.5)) * Scale * 0.5)
|
||
|
|
util.Effect( "manhacksparks", effectdata, true, true )
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:Draw()
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:DrawTranslucent()
|
||
|
|
self:CalcWheelEffects()
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:OnRemove()
|
||
|
|
self:StopWheelEffects()
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:CalcWheelSlip()
|
||
|
|
local Base = self:GetBaseEnt()
|
||
|
|
|
||
|
|
if not IsValid( Base ) then return end
|
||
|
|
|
||
|
|
local Vel = self:GetVelocity()
|
||
|
|
local VelLength = Vel:Length()
|
||
|
|
|
||
|
|
local rpmTheoretical = self:VelToRPM( VelLength )
|
||
|
|
local rpm = math.abs( self:GetRPM() )
|
||
|
|
|
||
|
|
self._WheelSlip = math.max( rpm - rpmTheoretical - 250, 0 ) ^ 2 + math.max( math.abs( Base:VectorSplitNormal( self:GetRight(), Vel * 4 ) ) - VelLength, 0 )
|
||
|
|
self._WheelSkid = VelLength + self._WheelSlip
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:GetSlip()
|
||
|
|
return (self._WheelSlip or 0)
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:GetSkid()
|
||
|
|
return (self._WheelSkid or 0)
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
function ENT:StopWheelEffects()
|
||
|
|
if not self._DoingWheelFx then return end
|
||
|
|
|
||
|
|
self._DoingWheelFx = nil
|
||
|
|
|
||
|
|
self:FinishSkidmark()
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:StartWheelEffects( Base, trace, traceWater )
|
||
|
|
self:DoWheelEffects( Base, trace, traceWater )
|
||
|
|
|
||
|
|
if self._DoingWheelFx then return end
|
||
|
|
|
||
|
|
self._DoingWheelFx = true
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:DoWheelEffects( Base, trace, traceWater )
|
||
|
|
if not trace.Hit then self:FinishSkidmark() return end
|
||
|
|
|
||
|
|
local SurfacePropName = util.GetSurfacePropName( trace.SurfaceProps )
|
||
|
|
local SkidValue = self:GetSkid()
|
||
|
|
|
||
|
|
if traceWater.Hit then
|
||
|
|
local Scale = math.min( 0.3 + (SkidValue - 100) / 4000, 1 ) ^ 2
|
||
|
|
|
||
|
|
local effectdata = EffectData()
|
||
|
|
effectdata:SetOrigin( trace.HitPos )
|
||
|
|
effectdata:SetEntity( Base )
|
||
|
|
effectdata:SetNormal( trace.HitNormal )
|
||
|
|
effectdata:SetMagnitude( Scale )
|
||
|
|
effectdata:SetFlags( 1 )
|
||
|
|
util.Effect( "simfphys_physics_wheeldust", effectdata, true, true )
|
||
|
|
|
||
|
|
self:FinishSkidmark()
|
||
|
|
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
if self.SkidmarkSurfaces[ SurfacePropName ] then
|
||
|
|
local Scale = math.min( 0.3 + SkidValue / 4000, 1 ) ^ 2
|
||
|
|
|
||
|
|
if Scale > 0.2 then
|
||
|
|
self:StartSkidmark( trace.HitPos )
|
||
|
|
self:CalcSkidmark( trace, Base:GetCrosshairFilterEnts() )
|
||
|
|
else
|
||
|
|
self:FinishSkidmark()
|
||
|
|
end
|
||
|
|
|
||
|
|
local effectdata = EffectData()
|
||
|
|
effectdata:SetOrigin( trace.HitPos )
|
||
|
|
effectdata:SetEntity( Base )
|
||
|
|
effectdata:SetNormal( trace.HitNormal )
|
||
|
|
util.Effect( "simfphys_physics_wheelsmoke", effectdata, true, true )
|
||
|
|
else
|
||
|
|
self:FinishSkidmark()
|
||
|
|
end
|
||
|
|
|
||
|
|
if not LVS.ShowEffects then return end
|
||
|
|
|
||
|
|
if self.DustEffectSurfaces[ SurfacePropName ] then
|
||
|
|
local Scale = math.min( 0.3 + (SkidValue - 100) / 4000, 1 ) ^ 2
|
||
|
|
|
||
|
|
local effectdata = EffectData()
|
||
|
|
effectdata:SetOrigin( trace.HitPos )
|
||
|
|
effectdata:SetEntity( Base )
|
||
|
|
effectdata:SetNormal( trace.HitNormal )
|
||
|
|
effectdata:SetMagnitude( Scale )
|
||
|
|
effectdata:SetFlags( 0 )
|
||
|
|
util.Effect( "simfphys_physics_wheeldust", effectdata, true, true )
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:CalcWheelEffects()
|
||
|
|
local T = CurTime()
|
||
|
|
|
||
|
|
if (self._NextFx or 0) > T then return end
|
||
|
|
|
||
|
|
self._NextFx = T + 0.05
|
||
|
|
|
||
|
|
local Base = self:GetBaseEnt()
|
||
|
|
|
||
|
|
if not IsValid( Base ) then return end
|
||
|
|
|
||
|
|
local Radius = Base:GetUp() * (self:GetRadius() + 1)
|
||
|
|
|
||
|
|
local Pos = self:GetPos() + self:GetVelocity() * 0.025
|
||
|
|
local StartPos = Pos + Radius
|
||
|
|
local EndPos = Pos - Radius
|
||
|
|
|
||
|
|
local trace = util.TraceLine( {
|
||
|
|
start = StartPos,
|
||
|
|
endpos = EndPos,
|
||
|
|
filter = Base:GetCrosshairFilterEnts(),
|
||
|
|
} )
|
||
|
|
|
||
|
|
local traceWater = util.TraceLine( {
|
||
|
|
start = StartPos,
|
||
|
|
endpos = EndPos,
|
||
|
|
filter = Base:GetCrosshairFilterEnts(),
|
||
|
|
mask = MASK_WATER,
|
||
|
|
} )
|
||
|
|
|
||
|
|
self:CalcWheelSounds( Base, trace, traceWater )
|
||
|
|
|
||
|
|
if traceWater.Hit and trace.HitPos.z < traceWater.HitPos.z then
|
||
|
|
if math.abs( self:GetRPM() ) > 25 then
|
||
|
|
local effectdata = EffectData()
|
||
|
|
effectdata:SetOrigin( traceWater.Fraction > 0.5 and traceWater.HitPos or Pos )
|
||
|
|
effectdata:SetEntity( Base )
|
||
|
|
effectdata:SetMagnitude( self:BoundingRadius() )
|
||
|
|
effectdata:SetFlags( 0 )
|
||
|
|
util.Effect( "simfphys_physics_wheelwatersplash", effectdata )
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if self:GetSlip() < 500 or self:GetDamaged() then self:StopWheelEffects() return end
|
||
|
|
|
||
|
|
self:StartWheelEffects( Base, trace, traceWater )
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:CalcWheelSounds( Base, trace, traceWater )
|
||
|
|
if not trace.Hit then return end
|
||
|
|
|
||
|
|
-- TODO: fix reason for this workaround
|
||
|
|
if trace.Entity == self then
|
||
|
|
if istable( Base.CrosshairFilterEnts ) and #Base.CrosshairFilterEnts > 1 then
|
||
|
|
Base.CrosshairFilterEnts = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
local RPM = math.abs( self:GetRPM() )
|
||
|
|
|
||
|
|
if RPM > 50 then
|
||
|
|
if traceWater.Hit then
|
||
|
|
Base:DoTireSound( "roll_wet" )
|
||
|
|
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
if self:GetDamaged() then
|
||
|
|
Base:DoTireSound( "roll_damaged" )
|
||
|
|
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
local surface = self.DustEffectSurfaces[ util.GetSurfacePropName( trace.SurfaceProps ) ] and "_dirt" or ""
|
||
|
|
local snd_type = (self:GetSlip() > 500) and "skid" or "roll"
|
||
|
|
|
||
|
|
if (istable( StormFox ) or istable( StormFox2 )) and surface ~= "_dirt" then
|
||
|
|
local Rain = false
|
||
|
|
|
||
|
|
if StormFox then
|
||
|
|
Rain = StormFox.IsRaining()
|
||
|
|
end
|
||
|
|
|
||
|
|
if StormFox2 then
|
||
|
|
Rain = StormFox2.Weather:IsRaining()
|
||
|
|
end
|
||
|
|
|
||
|
|
if Rain then
|
||
|
|
local effectdata = EffectData()
|
||
|
|
effectdata:SetOrigin( trace.HitPos )
|
||
|
|
effectdata:SetEntity( Base )
|
||
|
|
effectdata:SetMagnitude( self:BoundingRadius() )
|
||
|
|
effectdata:SetFlags( 1 )
|
||
|
|
util.Effect( "lvs_physics_wheelwatersplash", effectdata )
|
||
|
|
|
||
|
|
Base:DoTireSound( snd_type.."_wet" )
|
||
|
|
|
||
|
|
return
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
Base:DoTireSound( snd_type..surface )
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:GetSkidMarks()
|
||
|
|
if not istable( self._activeSkidMarks ) then
|
||
|
|
self._activeSkidMarks = {}
|
||
|
|
end
|
||
|
|
|
||
|
|
return self._activeSkidMarks
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:StartSkidmark( pos )
|
||
|
|
if self:GetWidth() <= 0 or self._SkidMarkID or not LVS.ShowTraileffects then return end
|
||
|
|
|
||
|
|
local ID = 1
|
||
|
|
for _,_ in ipairs( self:GetSkidMarks() ) do
|
||
|
|
ID = ID + 1
|
||
|
|
end
|
||
|
|
|
||
|
|
self._activeSkidMarks[ ID ] = {
|
||
|
|
active = true,
|
||
|
|
startpos = pos + self.SkidmarkTraceAdd,
|
||
|
|
delay = CurTime() + self.SkidmarkDelay,
|
||
|
|
positions = {},
|
||
|
|
}
|
||
|
|
|
||
|
|
self._SkidMarkID = ID
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:FinishSkidmark()
|
||
|
|
if not self._SkidMarkID then return end
|
||
|
|
|
||
|
|
self._activeSkidMarks[ self._SkidMarkID ].active = false
|
||
|
|
|
||
|
|
self._SkidMarkID = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:RemoveSkidmark( id )
|
||
|
|
if not id then return end
|
||
|
|
|
||
|
|
self._activeSkidMarks[ id ] = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:CalcSkidmark( trace, Filter )
|
||
|
|
local T = CurTime()
|
||
|
|
local CurActive = self:GetSkidMarks()[ self._SkidMarkID ]
|
||
|
|
|
||
|
|
if not CurActive or not CurActive.active or CurActive.delay >= T then return end
|
||
|
|
|
||
|
|
CurActive.delay = T + self.SkidmarkDelay
|
||
|
|
|
||
|
|
local W = self:GetWidth()
|
||
|
|
|
||
|
|
local cur = trace.HitPos + self.SkidmarkTraceAdd * 0.5
|
||
|
|
|
||
|
|
local prev = CurActive.positions[ #CurActive.positions ]
|
||
|
|
|
||
|
|
if not prev then
|
||
|
|
local sub = cur - CurActive.startpos
|
||
|
|
|
||
|
|
local L = sub:Length() * 0.5
|
||
|
|
local C = (cur + CurActive.startpos) * 0.5
|
||
|
|
|
||
|
|
local Ang = sub:Angle()
|
||
|
|
local Forward = Ang:Right()
|
||
|
|
local Right = Ang:Forward()
|
||
|
|
|
||
|
|
local p1 = C + Forward * W + Right * L
|
||
|
|
local p2 = C - Forward * W + Right * L
|
||
|
|
|
||
|
|
local t1 = util.TraceLine( { start = p1, endpos = p1 - self.SkidmarkTraceAdd } )
|
||
|
|
local t2 = util.TraceLine( { start = p2, endpos = p2 - self.SkidmarkTraceAdd } )
|
||
|
|
|
||
|
|
prev = {
|
||
|
|
px = CurActive.startpos,
|
||
|
|
p1 = t1.HitPos + t1.HitNormal,
|
||
|
|
p2 = t2.HitPos + t2.HitNormal,
|
||
|
|
lifetime = T + self.SkidmarkLifetime - self.SkidmarkDelay,
|
||
|
|
alpha = 0,
|
||
|
|
}
|
||
|
|
end
|
||
|
|
|
||
|
|
local sub = cur - prev.px
|
||
|
|
|
||
|
|
local L = sub:Length() * 0.5
|
||
|
|
local C = (cur + prev.px) * 0.5
|
||
|
|
|
||
|
|
local Ang = sub:Angle()
|
||
|
|
local Forward = Ang:Right()
|
||
|
|
local Right = Ang:Forward()
|
||
|
|
|
||
|
|
local p1 = C + Forward * W + Right * L
|
||
|
|
local p2 = C - Forward * W + Right * L
|
||
|
|
|
||
|
|
local t1 = util.TraceLine( { start = p1, endpos = p1 - self.SkidmarkTraceAdd, filter = Filter, } )
|
||
|
|
local t2 = util.TraceLine( { start = p2, endpos = p2 - self.SkidmarkTraceAdd, filter = Filter, } )
|
||
|
|
|
||
|
|
local nextID = #CurActive.positions + 1
|
||
|
|
|
||
|
|
CurActive.positions[ nextID ] = {
|
||
|
|
px = cur,
|
||
|
|
p1 = t1.HitPos + t1.HitNormal,
|
||
|
|
p2 = t2.HitPos + t2.HitNormal,
|
||
|
|
lifetime = T + self.SkidmarkLifetime,
|
||
|
|
alpha = math.min( nextID / 10, 1 ),
|
||
|
|
}
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:RenderSkidMarks()
|
||
|
|
local T = CurTime()
|
||
|
|
|
||
|
|
for id, skidmark in pairs( self:GetSkidMarks() ) do
|
||
|
|
local prev
|
||
|
|
local AmountDrawn = 0
|
||
|
|
|
||
|
|
for markID, data in pairs( skidmark.positions ) do
|
||
|
|
if not prev then
|
||
|
|
|
||
|
|
prev = data
|
||
|
|
|
||
|
|
continue
|
||
|
|
end
|
||
|
|
|
||
|
|
local Mul = math.max( data.lifetime - CurTime(), 0 ) / self.SkidmarkLifetime
|
||
|
|
|
||
|
|
if Mul > 0 then
|
||
|
|
AmountDrawn = AmountDrawn + 1
|
||
|
|
render.DrawQuad( data.p2, data.p1, prev.p1, prev.p2, Color( self.SkidmarkRed, self.SkidmarkGreen, self.SkidmarkBlue, math.min(255 * Mul * data.alpha,self.SkidmarkAlpha) ) )
|
||
|
|
end
|
||
|
|
|
||
|
|
prev = data
|
||
|
|
end
|
||
|
|
|
||
|
|
if not skidmark.active and AmountDrawn == 0 then
|
||
|
|
self:RemoveSkidmark( id )
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
hook.Add( "PreDrawTranslucentRenderables", "!!!!lvs_fakephysics_skidmarks", function( bDepth, bSkybox )
|
||
|
|
if bSkybox then return end
|
||
|
|
|
||
|
|
render.SetColorMaterial()
|
||
|
|
|
||
|
|
for _, wheel in ipairs( ents.FindByClass("gmod_sent_vehicle_fphysics_wheel") ) do
|
||
|
|
wheel:RenderSkidMarks()
|
||
|
|
end
|
||
|
|
end)
|