add sborka

This commit is contained in:
2026-03-31 10:27:04 +03:00
commit f5e5f56c84
2345 changed files with 382127 additions and 0 deletions

View File

@@ -0,0 +1,243 @@
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( "lvs_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( "lvs_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( "lvs_physics_wheeldust", effectdata, true, true )
end
end
function ENT:DoWaterEffects( Base, traceWater, Pos )
local effectdata = EffectData()
effectdata:SetOrigin( traceWater.Fraction > 0.5 and traceWater.HitPos or Pos )
effectdata:SetEntity( Base )
effectdata:SetMagnitude( self:GetRadius() )
effectdata:SetFlags( 0 )
util.Effect( "lvs_physics_wheelwatersplash", effectdata )
end
function ENT:DoWheelChainEffects( Base, trace )
if not LVS.ShowEffects then return end
if not self.DustEffectSurfaces[ util.GetSurfacePropName( trace.SurfaceProps ) ] then return end
local effectdata = EffectData()
effectdata:SetOrigin( trace.HitPos )
effectdata:SetEntity( Base )
effectdata:SetMagnitude( self:GetRadius() )
effectdata:SetNormal( trace.HitNormal )
util.Effect( "lvs_physics_trackdust", effectdata, true, true )
end
function ENT:CalcWheelEffects()
local Base = self:GetBase()
if not IsValid( Base ) then return end
local T = CurTime()
local EntTable = self:GetTable()
if (EntTable._NextWheelSound or 0) < T then
EntTable._NextWheelSound = T + 0.05
if EntTable._fxDelay ~= 1 and EntTable.TraceResult and EntTable.TraceResultWater then
self:CalcWheelSounds( Base, EntTable.TraceResult, EntTable.TraceResultWater )
end
end
if (EntTable._NextFx or 0) > T then return end
local ply = LocalPlayer()
if not IsValid( ply ) then return end
local ViewEnt = ply:GetViewEntity()
if IsValid( ViewEnt ) then
ply = ViewEnt
end
local Delay = 0.05
if self:GetWidth() <= 0 then
EntTable._fxDelay = math.min( Delay + (self:GetPos() - ply:GetPos()):LengthSqr() * 0.00000005, 1 )
else
EntTable._fxDelay = math.min( Delay + (self:GetPos() - ply:GetPos()):LengthSqr() * 0.000000001, 1 )
end
EntTable._NextFx = T + EntTable._fxDelay
local Radius = Base:GetWheelUp() * (self:GetRadius() + 1)
local Vel = self:GetVelocity()
local Pos = self:GetPos() + Vel * 0.025
local StartPos = Pos + Radius
local EndPos = Pos - Radius
local traceData = {
start = StartPos,
endpos = EndPos,
filter = Base:GetCrosshairFilterEnts(),
}
local trace = util.TraceLine( traceData )
traceData.mask = MASK_WATER
local traceWater = util.TraceLine( traceData )
EntTable.TraceResult = trace
EntTable.TraceResultWater = traceWater
if traceWater.Hit and trace.HitPos.z < traceWater.HitPos.z then
if math.abs( self:GetRPM() ) > 25 then
self:DoWaterEffects( Base, traceWater, Pos )
end
else
if self:GetWheelChainMode() and trace.Hit and math.abs( self:GetRPM() ) > 25 and Vel:LengthSqr() > 1500 then
self:DoWheelChainEffects( Base, trace )
end
end
if self:GetSlip() < 500 or EntTable._fxDelay > 0.1 then self:StopWheelEffects() return end
self:StartWheelEffects( Base, trace, traceWater )
end
function ENT:CalcWheelSounds( Base, trace, traceWater )
if not trace.Hit then return end
local RPM = math.abs( self:GetRPM() )
if self:GetDamaged() and RPM > 30 then
if self:GetWheelChainMode() then
local effectdata = EffectData()
effectdata:SetOrigin( self:GetPos() )
effectdata:SetNormal( self:GetForward() )
util.Effect( "lvs_physics_trackscraping", effectdata, true, true )
Base:DoTireSound( "tracks_damage_layer" )
else
local Ang = self:GetForward():Angle() + Angle(10,0,0)
Ang:RotateAroundAxis( Base:GetUp(), -90 )
local effectdata = EffectData()
effectdata:SetOrigin( trace.HitPos + trace.HitNormal )
effectdata:SetNormal( Ang:Forward() * math.min( self:GetSlip() / 10000, 3 ) * (self:GetRPM() > 0 and 1 or -1) )
effectdata:SetMagnitude( 1 )
util.Effect( "manhacksparks", effectdata, true, true )
Base:DoTireSound( "tire_damage_layer" )
return
end
end
if not Base:GetEngineActive() and RPM < 50 then return end
if traceWater.Hit then
Base:DoTireSound( "roll_wet" )
return
end
local surface = self.DustEffectSurfaces[ util.GetSurfacePropName( trace.SurfaceProps ) ] and "_dirt" or ""
local snd_type = (self:GetSlip() > 500) and "skid" or "roll"
if Base:GetRacingTires() and surface == "" then surface = "_racing" end
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
if snd_type == "roll" and not self:GetWheelChainMode() and self:GetHP() ~= self:GetMaxHP() then
surface = "_damaged"
end
Base:DoTireSound( snd_type..surface )
end

View File

@@ -0,0 +1,117 @@
include("shared.lua")
include("cl_effects.lua")
include("cl_skidmarks.lua")
function ENT:Initialize()
local Mins, Maxs = self:GetRenderBounds()
self:SetRenderBounds( Mins, Maxs, Vector( 50, 50, 50 ) )
self:DrawShadow( false )
end
if GravHull then
function ENT:DrawWheel( flags )
self:SetAngles( self:LocalToWorldAngles( self:GetAlignmentAngle() ) ) -- GravHull overwrites SetRenderAngles, but SetAngles works too...
self:DrawModel( flags )
end
else
function ENT:DrawWheel( flags )
self:SetRenderAngles( self:LocalToWorldAngles( self:GetAlignmentAngle() ) )
self:DrawModel( flags )
end
end
function ENT:DrawWheelBroken( flags )
local base = self:GetBase()
if not IsValid( base ) or not LVS.MapDoneLoading then
self:DrawModel( flags )
return
end
-- Alternative method, tuning wheels... Workaround for diggers wheel pack. Flickers for some people... it is what it is
if self:GetBoneCount() > 1 then
local pos = self:GetPos()
self:SetRenderOrigin( pos - base:GetUp() * base.WheelPhysicsTireHeight )
self:DrawWheel( flags )
self:SetRenderOrigin()
return
end
-- bone position method... more reliable and works on infmap, but doesnt work on diggers wheel pack
self:SetupBones()
local pos, ang = self:GetBonePosition( 0 )
if not pos then self:DrawModel( flags ) return end
self:SetBonePosition( 0, pos - base:GetUp() * base.WheelPhysicsTireHeight, ang )
self:DrawWheel( flags )
self:SetBonePosition( 0, pos , ang )
end
function ENT:Draw( flags )
if self:GetHideModel() then return end
if self:GetNWDamaged() then
self:DrawWheelBroken( flags )
return
end
self:DrawWheel( flags )
end
function ENT:DrawTranslucent()
self:CalcWheelEffects()
end
function ENT:Think()
self:CalcWheelSlip()
self:SetNextClientThink( CurTime() + 0.1 )
return true
end
function ENT:OnRemove()
self:StopWheelEffects()
end
function ENT:CalcWheelSlip()
local Base = self:GetBase()
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() )
if rpm == 0 then
self._WheelSlip = rpmTheoretical + VelLength * 4
self._WheelSkid = self._WheelSlip
else
self._WheelSlip = math.max( rpm - rpmTheoretical - 10, 0 ) ^ 2 + math.max( math.abs( Base:VectorSplitNormal( self:GetForward(), Vel * 4 ) ) - VelLength, 0 )
self._WheelSkid = VelLength + self._WheelSlip
end
end
function ENT:GetSlip()
return (self._WheelSlip or 0)
end
function ENT:GetSkid()
return (self._WheelSkid or 0)
end

View File

@@ -0,0 +1,182 @@
ENT.SkidmarkMaterial = Material("sprites/lvs/skidmark")
ENT.SkidmarkMaterialDamaged = Material("sprites/lvs/skidmark_damaged")
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 = {
[""] = true,
["concrete"] = true,
["plastic_barrel_buoyant"] = true,
["phx_rubbertire2"] = true,
["tile"] = true,
["metal"] = true,
["boulder"] = true,
["default"] = true,
}
ENT.DustEffectSurfaces = {
["sand"] = true,
["dirt"] = true,
["grass"] = true,
["antlionsand"] = true,
["gravel"] = true,
}
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,
damaged = self:GetNWDamaged(),
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
if skidmark.damaged then
render.SetMaterial( self.SkidmarkMaterialDamaged )
else
render.SetMaterial( self.SkidmarkMaterial )
end
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_skidmarks", function( bDepth, bSkybox )
if bSkybox then return end
for _, wheel in ipairs( ents.FindByClass("lvs_wheeldrive_wheel") ) do
wheel:RenderSkidMarks()
end
end)

View File

@@ -0,0 +1,150 @@
AddCSLuaFile( "shared.lua" )
AddCSLuaFile( "cl_init.lua" )
AddCSLuaFile( "cl_effects.lua" )
AddCSLuaFile( "cl_skidmarks.lua" )
include("shared.lua")
include("sv_axle.lua")
include("sv_brakes.lua")
include("sv_damage.lua")
function ENT:GetWheelType()
return self._WheelType or LVS.WHEELTYPE_NONE
end
function ENT:SetWheelType( wheel_type )
self._WheelType = wheel_type
end
function ENT:SetSuspensionHeight( newheight )
newheight = newheight and math.Clamp( newheight, -1, 1 ) or 0
self._SuspensionHeightMultiplier = newheight
if not IsValid( self.SuspensionConstraintElastic ) then return end
local Length = self.SuspensionConstraintElastic:GetTable().length or 25
self.SuspensionConstraintElastic:Fire( "SetSpringLength", Length + Length * newheight )
end
function ENT:GetSuspensionHeight()
return self._SuspensionHeightMultiplier or 0
end
function ENT:SetSuspensionStiffness( new )
new = new and math.Clamp( new, -1, 1 ) or 0
self._SuspensionStiffnessMultiplier = new
if not IsValid( self.SuspensionConstraintElastic ) then return end
local data = self.SuspensionConstraintElastic:GetTable()
local damping = data.damping or 2000
local constant = data.constant or 20000
self.SuspensionConstraintElastic:Fire( "SetSpringConstant", constant + constant * new, 0 )
self.SuspensionConstraintElastic:Fire( "SetSpringDamping", damping + damping * new, 0 )
end
function ENT:GetSuspensionStiffness()
return self._SuspensionStiffnessMultiplier or 0
end
function ENT:Initialize()
self:SetCollisionGroup( COLLISION_GROUP_PASSABLE_DOOR )
-- this is so vj npcs can still see us
self:AddEFlags( EFL_DONTBLOCKLOS )
end
function ENT:GravGunPickupAllowed( ply )
return false
end
function ENT:StartThink()
if self.AutomaticFrameAdvance then return end
self.AutomaticFrameAdvance = true
self.Think = function( self )
self:NextThink( CurTime() )
return true
end
self:Think()
end
function ENT:Think()
return false
end
function ENT:OnRemove()
self:StopLeakAir()
end
function ENT:lvsMakeSpherical( radius )
if not radius or radius <= 0 then
radius = (self:OBBMaxs() - self:OBBMins()) * 0.5
radius = math.max( radius.x, radius.y, radius.z )
end
self:PhysicsInitSphere( radius, "jeeptire" )
self:SetRadius( radius )
self:DrawShadow( not self:GetHideModel() )
end
function ENT:PhysicsMaterialUpdate( TargetValue )
local base = self:GetBase()
local PhysObj = self:GetPhysicsObject()
if not IsValid( base ) or not IsValid( PhysObj ) then return end
local ListID = math.Clamp( math.Round( (TargetValue or 1) * 10, 0 ), 0, 12 )
PhysObj:SetMaterial( base.WheelPhysicsMaterials[ ListID ] )
end
function ENT:PhysicsOnGround()
local PhysObj = self:GetPhysicsObject()
if not IsValid( PhysObj ) then return false end
local EntLoad,_ = PhysObj:GetStress()
return EntLoad > 0
end
function ENT:PhysicsCollide( data, physobj )
if data.Speed > 150 and data.DeltaTime > 0.2 then
local VelDif = data.OurOldVelocity:Length() - data.OurNewVelocity:Length()
local Volume = math.min( math.abs( VelDif ) / 300 , 1 )
self:EmitSound( "lvs/vehicles/generic/suspension_hit_".. math.random(1,17) ..".ogg", 70, 100, Volume ^ 2 )
end
local HitEntity = data.HitEntity
local HitObject = data.HitObject
if IsValid( HitEntity ) and IsValid( HitObject ) then
physobj:SetVelocityInstantaneous( data.OurOldVelocity )
if HitObject:IsMotionEnabled() and HitObject:GetMass() < physobj:GetMass() then
HitObject:SetVelocityInstantaneous( data.OurOldVelocity * 2 )
end
end
if math.abs(data.OurNewVelocity.z - data.OurOldVelocity.z) > 100 then
physobj:SetVelocityInstantaneous( data.OurOldVelocity )
end
local base = self:GetBase()
if IsValid( base ) then
base:OnWheelCollision( data, physobj )
end
end

View File

@@ -0,0 +1,84 @@
ENT.PrintName = "Wheel"
ENT.Type = "anim"
ENT.DoNotDuplicate = true
ENT.RenderGroup = RENDERGROUP_BOTH
ENT._lvsRepairToolLabel = "Wheel"
ENT._lvsNoPhysgunInteraction = true
function ENT:SetupDataTables()
self:NetworkVar( "Float", 0, "Radius")
self:NetworkVar( "Float", 1, "Width")
self:NetworkVar( "Float", 2, "Camber" )
self:NetworkVar( "Float", 3, "Caster" )
self:NetworkVar( "Float", 4, "Toe" )
self:NetworkVar( "Float", 5, "RPM" )
self:NetworkVar( "Float", 6, "HP" )
self:NetworkVar( "Float", 7, "MaxHP" )
self:NetworkVar( "Angle", 0, "AlignmentAngle" )
self:NetworkVar( "Entity", 0, "Base" )
self:NetworkVar( "Bool", 0, "HideModel" )
self:NetworkVar( "Bool", 1, "Destroyed" )
self:NetworkVar( "Bool", 2, "NWDamaged" )
self:NetworkVar( "Bool", 3, "WheelChainMode" )
if SERVER then
self:SetMaxHP( 100 )
self:SetHP( 100 )
self:SetWidth( 3 )
self:NetworkVarNotify( "HP", self.HealthValueChanged )
end
end
function ENT:GetDamaged()
return self:GetNWDamaged()
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
function ENT:CheckAlignment()
self.CamberCasterToe = (math.abs( self:GetToe() ) + math.abs( self:GetCaster() ) + math.abs( self:GetCamber() )) ~= 0
if CLIENT then return end
local SteerType = self:GetSteerType()
local Caster = self:GetCaster()
local Camber = math.abs( self:GetCamber() )
local CamberValue1 = (math.min( Camber, 15 ) / 15) * 0.3
local CamberValue2 = (math.Clamp( Camber - 15, 0, 65 ) / 65) * 0.7
local CasterValue = (math.min( math.abs( Caster ), 15 ) / 15) * math.max( 1 - Camber / 10, 0 )
if SteerType == LVS.WHEEL_STEER_NONE then CasterValue = 0 end
if SteerType == LVS.WHEEL_STEER_FRONT and Caster < 0 then CasterValue = 0 end
if SteerType == LVS.WHEEL_STEER_REAR and Caster > 0 then CasterValue = 0 end
local TractionValue = 1 - CamberValue1 - CamberValue2 + CasterValue
self:PhysicsMaterialUpdate( TractionValue )
return TractionValue
end

View File

@@ -0,0 +1,58 @@
AccessorFunc(ENT, "axle", "Axle", FORCE_NUMBER)
function ENT:SetMaster( master )
self._Master = master
end
function ENT:GetMaster()
return self._Master
end
function ENT:GetDirectionAngle()
if not IsValid( self._Master ) then return angle_zero end
return self._Master:GetAngles()
end
function ENT:GetRotationAxis()
local WorldAngleDirection = -self:WorldToLocalAngles( self:GetDirectionAngle() )
return WorldAngleDirection:Right()
end
function ENT:GetSteerType()
if self._steerType then return self._steerType end
local base = self:GetBase()
if not IsValid( base ) then return 0 end
self._steerType = base:GetAxleData( self:GetAxle() ).SteerType
return self._steerType
end
function ENT:GetTorqueFactor()
if self._torqueFactor then return self._torqueFactor end
local base = self:GetBase()
if not IsValid( base ) then return 0 end
self._torqueFactor = base:GetAxleData( self:GetAxle() ).TorqueFactor or 0
return self._torqueFactor
end
function ENT:GetBrakeFactor()
if self._brakeFactor then return self._brakeFactor end
local base = self:GetBase()
if not IsValid( base ) then return 0 end
self._brakeFactor = base:GetAxleData( self:GetAxle() ).BrakeFactor or 0
return self._brakeFactor
end

View File

@@ -0,0 +1,81 @@
function ENT:SetHandbrake( enable )
if enable then
self:EnableHandbrake()
return
end
self:ReleaseHandbrake()
end
function ENT:IsHandbrakeActive()
return self._handbrakeActive == true
end
function ENT:EnableHandbrake()
if self._handbrakeActive then return end
self._handbrakeActive = true
self:LockRotation()
end
function ENT:ReleaseHandbrake()
if not self._handbrakeActive then return end
self._handbrakeActive = nil
self:ReleaseRotation()
end
function ENT:LockRotation( TimedLock )
if TimedLock then
self._RotationLockTime = CurTime() + 0.15
end
if self:IsRotationLocked() then return end
local Master = self:GetMaster()
if not IsValid( Master ) then return end
self.bsLock = constraint.AdvBallsocket(self,Master,0,0,vector_origin,vector_origin,0,0,-0.1,-0.1,-0.1,0.1,0.1,0.1,0,0,0,1,1)
self.bsLock.DoNotDuplicate = true
local PhysObj = self:GetPhysicsObject()
if self._OriginalMass or not IsValid( PhysObj ) then return end
local Mass = PhysObj:GetMass()
self._OriginalMass = Mass
PhysObj:SetMass( Mass * 2 )
end
function ENT:ReleaseRotation()
if self._RotationLockTime then
if self._RotationLockTime > CurTime() then
return
end
end
if not self:IsRotationLocked() then return end
self.bsLock:Remove()
local PhysObj = self:GetPhysicsObject()
if not IsValid( PhysObj ) or not self._OriginalMass then return end
PhysObj:SetMass( self._OriginalMass )
self._OriginalMass = nil
end
function ENT:IsRotationLocked()
return IsValid( self.bsLock )
end

View File

@@ -0,0 +1,216 @@
ENT.DSDamageAllowedType = DMG_SLASH + DMG_AIRBOAT + DMG_BULLET + DMG_SNIPER + DMG_BUCKSHOT + DMG_PREVENT_PHYSICS_FORCE
ENT.DSArmorIgnoreForce = 0
function ENT:OnTakeDamage( dmginfo )
local base = self:GetBase()
if not IsValid( base ) then return end
if self:GetWheelChainMode() then
if dmginfo:IsDamageType( DMG_PREVENT_PHYSICS_FORCE ) then return end
base:OnTakeDamage( dmginfo )
return
end
local MaxArmor = self:GetMaxHP()
local Damage = dmginfo:GetDamage()
if not dmginfo:IsDamageType( DMG_PREVENT_PHYSICS_FORCE ) then
local IsFireDamage = dmginfo:IsDamageType( DMG_BURN )
if dmginfo:GetDamageForce():Length() < self.DSArmorIgnoreForce and not IsFireDamage then return end
local MaxHealth = base:GetMaxHP()
local ArmoredHealth = MaxHealth + MaxArmor
local NumShotsToKill = ArmoredHealth / Damage
local ScaleDamage = math.Clamp( MaxHealth / (NumShotsToKill * Damage),0,1)
dmginfo:ScaleDamage( ScaleDamage )
base:OnTakeDamage( dmginfo )
end
if not dmginfo:IsDamageType( self.DSDamageAllowedType ) then return end
if not isnumber( base.WheelPhysicsTireHeight ) or base.WheelPhysicsTireHeight <= 0 then return end
local CurHealth = self:GetHP()
local NewHealth = math.Clamp( CurHealth - Damage, 0, MaxArmor )
self:SetHP( NewHealth )
if NewHealth > 0 then
self:StartLeakAir()
return
end
self:DestroyTire()
end
function ENT:StartLeakAir()
if self._IsLeakingAir then return end
self._IsLeakingAir = true
local ID = "lvsLeakAir"..self:EntIndex()
timer.Create( ID, 0.2, 0, function()
if not IsValid( self ) then timer.Remove( ID ) return end
local dmg = DamageInfo()
dmg:SetDamage( 1 )
dmg:SetAttacker( self )
dmg:SetInflictor( self )
dmg:SetDamageType( DMG_PREVENT_PHYSICS_FORCE )
self:TakeDamageInfo( dmg )
end)
end
function ENT:StopLeakAir()
if not self._IsLeakingAir then return end
local ID = "lvsLeakAir"..self:EntIndex()
timer.Remove( ID )
self._IsLeakingAir = nil
end
function ENT:HealthValueChanged( name, old, new)
if new == old or old > new or new ~= self:GetMaxHP() then return end
self:RepairTire()
end
function ENT:DestroyTire()
if self:GetNWDamaged() then return end
self:SetNWDamaged( true )
self:StopLeakAir()
local base = self:GetBase()
local PhysObj = self:GetPhysicsObject()
if not IsValid( base ) or not IsValid( PhysObj ) then return end
self._OldTirePhysProp = PhysObj:GetMaterial()
PhysObj:SetMaterial( "glass" )
self:EmitSound("lvs/wheel_pop"..math.random(1,4)..".ogg")
local effectdata = EffectData()
effectdata:SetOrigin( self:GetPos() )
effectdata:SetEntity( self )
util.Effect( "lvs_tire_blow", effectdata, true, true )
self._RestoreBodyGroups = {}
for id, group in pairs( self:GetBodyGroups() ) do
for subid, subgroup in pairs( group.submodels ) do
if subgroup == "" or string.lower( subgroup ) == "empty" then
local bodyGroupId = id - 1
self._RestoreBodyGroups[ bodyGroupId ] = self:GetBodygroup( bodyGroupId )
self:SetBodygroup( bodyGroupId, subid )
end
end
end
if not IsValid( self.SuspensionConstraintElastic ) then return end
local Length = (self.SuspensionConstraintElastic:GetTable().length or 25) - base.WheelPhysicsTireHeight
self.SuspensionConstraintElastic:Fire( "SetSpringLength", math.max( Length - base.WheelPhysicsTireHeight , 1 ) )
end
function ENT:RepairTire()
self:StopLeakAir()
if not self._OldTirePhysProp then return end
if istable( self._RestoreBodyGroups ) then
for bodyGroupId, subid in pairs( self._RestoreBodyGroups ) do
self:SetBodygroup( bodyGroupId, subid )
end
self._RestoreBodyGroups = nil
end
self:SetNWDamaged( false )
self:SetSuspensionHeight( self._SuspensionHeightMultiplier )
local PhysObj = self:GetPhysicsObject()
if not IsValid( PhysObj ) or PhysObj:GetMaterial() ~= "glass" then
goto FinishRepairTire
end
PhysObj:SetMaterial( self._OldTirePhysProp )
-- coders from the industry hate this, so we use it intentionally to assert dominance
:: FinishRepairTire ::
self._OldTirePhysProp = nil
end
function ENT:IsTireDestroyed()
return isstring( self._OldTirePhysProp )
end
function ENT:SetDamaged( new )
if new == self:GetNWDamaged() then return end
self:SetNWDamaged( new )
if new then
if not self._torqueFactor or self.old_torqueFactor then return end
self.old_torqueFactor = self._torqueFactor
self._torqueFactor = self._torqueFactor * 0.25
return
end
if not self.old_torqueFactor then return end
self._torqueFactor = self.old_torqueFactor
self.old_torqueFactor = nil
end
function ENT:Destroy()
if self:GetDestroyed() then return end
self:SetDestroyed( true )
self:SetDamaged( true )
local Master = self:GetMaster()
if not IsValid( Master ) or IsValid( self.bsLockDMG ) then return end
local Fric = 10
self.bsLockDMG = constraint.AdvBallsocket(self,Master,0,0,vector_origin,vector_origin,0,0,-180,-180,-180,180,180,180,Fric,Fric,Fric,1,1)
self.bsLockDMG.DoNotDuplicate = true
end
function ENT:Repair()
self:SetHP( self:GetMaxHP() )
self:SetDestroyed( false )
self:SetDamaged( false )
if IsValid( self.bsLockDMG ) then
self.bsLockDMG:Remove()
end
end