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,202 @@
include("shared.lua")
ENT.TrackSystemEnable = false
ENT.TrackHull = Vector(1,1,1)
ENT.TrackData = {}
function ENT:CalcTrackScrollTexture()
local EntTable = self:GetTable()
if not EntTable.TrackSystemEnable then return end
local DriveWheelFL = self:GetTrackDriveWheelLeft()
if IsValid( DriveWheelFL ) then
local rotation = self:WorldToLocalAngles( DriveWheelFL:GetAngles() ).r
local scroll = self:CalcScroll( "scroll_left", rotation )
self:SetPoseParameter(EntTable.TrackPoseParameterLeft, scroll * EntTable.TrackPoseParameterLeftMul )
self:SetSubMaterial( EntTable.TrackLeftSubMaterialID, self:ScrollTexture( "left", EntTable.TrackScrollTexture, EntTable.TrackLeftSubMaterialMul * scroll ) )
end
local DriveWheelFR = self:GetTrackDriveWheelRight()
if IsValid( DriveWheelFR ) then
local rotation = self:WorldToLocalAngles( DriveWheelFR:GetAngles() ).r
local scroll = self:CalcScroll( "scroll_right", rotation )
self:SetPoseParameter(EntTable.TrackPoseParameterRight, scroll * EntTable.TrackPoseParameterRightMul )
self:SetSubMaterial( EntTable.TrackRightSubMaterialID, self:ScrollTexture( "right", EntTable.TrackScrollTexture, EntTable.TrackRightSubMaterialMul * scroll ) )
end
end
local WorldUp = Vector(0,0,1)
function ENT:CalcTracks()
local EntTable = self:GetTable()
if self:GetHP() <= 0 then
if EntTable._ResetSubMaterials then
EntTable._ResetSubMaterials = nil
for i = 0, 128 do
self:SetSubMaterial( i )
end
end
return
end
EntTable._ResetSubMaterials = true
local T = CurTime()
if (EntTable._NextCalcTracks or 0) < T then
local ply = LocalPlayer()
if not IsValid( ply ) then return end
local ViewEnt = ply:GetViewEntity()
if IsValid( ViewEnt ) then
ply = ViewEnt
end
local Delay = math.min( (self:GetPos() - ply:GetPos()):LengthSqr() * 0.00000005, 1 )
EntTable._NextCalcTracks = T + Delay
local Mul = math.max( Delay / RealFrameTime(), 1 )
local TrackHull = EntTable.TrackHull * (math.max( WorldUp:Dot( self:GetUp() ), 0 ) ^ 2)
EntTable._TrackPoseParameters = {}
for _, data in pairs( EntTable.TrackData ) do
if not istable( data.Attachment ) or not istable( data.PoseParameter ) then continue end
if not isstring( data.PoseParameter.name ) then continue end
local att = self:GetAttachment( self:LookupAttachment( data.Attachment.name ) )
if not att then continue end
local traceLength = data.Attachment.traceLength or 100
local toGroundDistance = data.Attachment.toGroundDistance or 20
local trace = util.TraceHull( {
start = att.Pos,
endpos = att.Pos - self:GetUp() * traceLength,
filter = self:GetCrosshairFilterEnts(),
mins = -TrackHull,
maxs = TrackHull,
} )
local Rate = data.PoseParameter.lerpSpeed or 25
local Dist = (att.Pos - trace.HitPos):Length() + EntTable.TrackHull.z - toGroundDistance
local RangeMul = data.PoseParameter.rangeMultiplier or 1
if data.IsBonePP == nil then
data.IsBonePP = string.StartsWith( data.PoseParameter.name, "!" )
continue
end
EntTable._TrackPoseParameters[ data.PoseParameter.name ] = {}
EntTable._TrackPoseParameters[ data.PoseParameter.name ].IsBonePP = data.IsBonePP
if data.IsBonePP then
EntTable._TrackPoseParameters[ data.PoseParameter.name ].Pose = math.Clamp( self:QuickLerp( data.PoseParameter.name, Dist * RangeMul, Rate * Mul ) / (data.PoseParameter.range or 10), 0 , 1 )
else
EntTable._TrackPoseParameters[ data.PoseParameter.name ].Pose = self:QuickLerp( data.PoseParameter.name, Dist * RangeMul, Rate * Mul )
end
end
end
if not EntTable._TrackPoseParameters then return end
for name, data in pairs( EntTable._TrackPoseParameters ) do
if data.IsBonePP then
self:SetBonePoseParameter( name, data.Pose )
continue
end
self:SetPoseParameter( name, data.Pose )
end
self:CalcTrackScrollTexture()
end
DEFINE_BASECLASS( "lvs_base_wheeldrive" )
function ENT:Think()
if not self:IsInitialized() then return end
self:CalcTracks()
BaseClass.Think( self )
end
ENT.TrackSounds = "lvs/vehicles/sherman/tracks_loop.wav"
ENT.TireSoundTypes = {
["skid"] = "common/null.wav",
["skid_dirt"] = "lvs/vehicles/generic/wheel_skid_dirt.wav",
["skid_wet"] = "common/null.wav",
["tracks_damage_layer"] = "lvs/tracks_damaged_loop.wav",
["tire_damage_layer"] = "lvs/wheel_destroyed_loop.wav",
}
function ENT:TireSoundThink()
for snd, _ in pairs( self.TireSoundTypes ) do
local T = self:GetTireSoundTime( snd )
if T > 0 then
local speed = self:GetVelocity():Length()
local sound = self:StartTireSound( snd )
if string.StartsWith( snd, "skid" ) or snd == "tire_damage_layer" then
local vel = speed
speed = math.max( math.abs( self:GetWheelVelocity() ) - vel, 0 ) * 5 + vel
end
local volume = math.min(speed / math.max( self.MaxVelocity, self.MaxVelocityReverse ),1) ^ 2 * T
local pitch = 100 + math.Clamp((speed - 400) / 200,0,155)
if snd == "tracks_damage_layer" then
volume = math.min( speed / 100, 1 ) * T
end
sound:ChangeVolume( volume, 0 )
sound:ChangePitch( pitch, 0.5 )
else
self:StopTireSound( snd )
end
end
end
function ENT:DoTireSound( snd )
if not istable( self._TireSounds ) then
self._TireSounds = {}
end
if string.StartsWith( snd, "roll" ) then
snd = "roll"
end
self._TireSounds[ snd ] = CurTime() + self.TireSoundFade
end
function ENT:StartTireSound( snd )
if not self.TireSoundTypes[ snd ] or not istable( self._ActiveTireSounds ) then
self._ActiveTireSounds = {}
end
if self._ActiveTireSounds[ snd ] then return self._ActiveTireSounds[ snd ] end
local sound = CreateSound( self, (snd == "roll") and self.TrackSounds or self.TireSoundTypes[ snd ] )
sound:SetSoundLevel( string.StartsWith( snd, "skid" ) and self.TireSoundLevelSkid or self.TireSoundLevelRoll )
sound:PlayEx(0,100)
self._ActiveTireSounds[ snd ] = sound
return sound
end

View File

@@ -0,0 +1,13 @@
AddCSLuaFile( "shared.lua" )
AddCSLuaFile( "cl_init.lua" )
include("shared.lua")
include("sv_tracksystem.lua")
AddCSLuaFile( "modules/cl_tankview.lua" )
AddCSLuaFile( "modules/cl_attachable_playermodels.lua" )
AddCSLuaFile( "modules/sh_turret.lua" )
AddCSLuaFile( "modules/sh_turret_ballistics.lua" )
AddCSLuaFile( "modules/sh_turret_splitsound.lua" )
ENT.DSArmorDamageReductionType = DMG_CLUB
ENT.DSArmorIgnoreDamageType = DMG_BULLET + DMG_SONIC + DMG_ENERGYBEAM

View File

@@ -0,0 +1,43 @@
if SERVER then return end
function ENT:GetPlayerModel( name )
if not istable( self._PlayerModels ) then return end
return self._PlayerModels[ name ]
end
function ENT:RemovePlayerModel( name )
if not istable( self._PlayerModels ) then return end
for id, model in pairs( self._PlayerModels ) do
if name and id ~= name then continue end
if not IsValid( model ) then continue end
model:Remove()
end
end
function ENT:CreatePlayerModel( ply, name )
if not isstring( name ) then return end
if not istable( self._PlayerModels ) then
self._PlayerModels = {}
end
if IsValid( self._PlayerModels[ name ] ) then return self._PlayerModels[ name ] end
local model = ClientsideModel( ply:GetModel() )
model:SetNoDraw( true )
model.GetPlayerColor = function() return ply:GetPlayerColor() end
model:SetSkin( ply:GetSkin() )
self._PlayerModels[ name ] = model
return model
end
function ENT:OnRemoved()
self:RemovePlayerModel()
end

View File

@@ -0,0 +1,74 @@
if SERVER then return end
function ENT:TankViewOverride( ply, pos, angles, fov, pod )
return pos, angles, fov
end
function ENT:CalcTankView( ply, original_pos, original_ang, original_fov, pod )
local pos, angles, fov = self:TankViewOverride( ply, original_pos, original_ang, original_fov, pod )
local view = {}
view.origin = pos
view.angles = angles
view.fov = fov
view.drawviewer = false
if not pod:GetThirdPersonMode() then return view end
local mn = self:OBBMins()
local mx = self:OBBMaxs()
local radius = ( mn - mx ):Length()
local radius = radius + radius * pod:GetCameraDistance()
local clamped_angles = pod:WorldToLocalAngles( angles )
clamped_angles.p = math.max( clamped_angles.p, -20 )
clamped_angles = pod:LocalToWorldAngles( clamped_angles )
local StartPos = pos
local EndPos = StartPos - clamped_angles:Forward() * radius + clamped_angles:Up() * (radius * pod:GetCameraHeight())
local WallOffset = 4
local tr = util.TraceHull( {
start = StartPos,
endpos = EndPos,
filter = function( e )
local c = e:GetClass()
local collide = not c:StartWith( "prop_physics" ) and not c:StartWith( "prop_dynamic" ) and not c:StartWith( "prop_ragdoll" ) and not e:IsVehicle() and not c:StartWith( "gmod_" ) and not c:StartWith( "lvs_" ) and not c:StartWith( "player" ) and not e.LVS
return collide
end,
mins = Vector( -WallOffset, -WallOffset, -WallOffset ),
maxs = Vector( WallOffset, WallOffset, WallOffset ),
} )
view.angles = angles + Angle(5,0,0)
view.origin = tr.HitPos + pod:GetUp() * 65
view.drawviewer = true
if tr.Hit and not tr.StartSolid then
view.origin = view.origin + tr.HitNormal * WallOffset
end
return view
end
function ENT:CalcViewDirectInput( ply, pos, angles, fov, pod )
if not pod:GetThirdPersonMode() then
angles = pod:LocalToWorldAngles( ply:EyeAngles() )
end
return self:CalcTankView( ply, pos, angles, fov, pod )
end
function ENT:CalcViewMouseAim( ply, pos, angles, fov, pod )
return self:CalcTankView( ply, pos, angles, fov, pod )
end
function ENT:CalcViewPassenger( ply, pos, angles, fov, pod )
if not pod:GetThirdPersonMode() then
angles = pod:LocalToWorldAngles( ply:EyeAngles() )
end
return self:CalcTankView( ply, pos, angles, fov, pod )
end

View File

@@ -0,0 +1,354 @@
ENT.TurretPodIndex = 1 -- 1 = driver
ENT.TurretAimRate = 25
ENT.TurretRotationSound = "vehicles/tank_turret_loop1.wav"
ENT.TurretRotationSoundDamaged = "lvs/turret_damaged_loop.wav"
ENT.TurretFakeBarrel = false
ENT.TurretFakeBarrelRotationCenter = vector_origin
ENT.TurretPitchPoseParameterName = "turret_pitch"
ENT.TurretPitchMin = -15
ENT.TurretPitchMax = 15
ENT.TurretPitchMul = 1
ENT.TurretPitchOffset = 0
ENT.TurretPitchEnableCentering = true
ENT.TurretYawPoseParameterName = "turret_yaw"
ENT.TurretYawMul = 1
ENT.TurretYawOffset = 0
ENT.TurretYawEnableCentering = true
ENT.TurretRateDestroyedMul = 0.25
function ENT:TurretSystemDT()
self:AddDT( "Bool", "TurretForceCenter" )
self:AddDT( "Bool", "NWTurretEnabled" )
self:AddDT( "Bool", "NWTurretDestroyed" )
self:AddDT( "Bool", "TurretDamaged" )
self:AddDT( "Entity", "NWTurretArmor" )
if SERVER then
self:SetTurretEnabled( true )
self:SetTurretPitch( self.TurretPitchOffset )
self:SetTurretYaw( self.TurretYawOffset )
end
end
function ENT:SetTurretDestroyed( new )
self:SetNWTurretDestroyed( new )
self:SetTurretDamaged( new )
end
function ENT:GetTurretDestroyed( new )
return self:GetNWTurretDestroyed()
end
function ENT:SetTurretEnabled( new )
self:SetNWTurretEnabled( new )
end
function ENT:SetTurretArmor( TurretArmor )
self:SetNWTurretArmor( TurretArmor )
if CLIENT then return end
TurretArmor.OnDestroyed = function( ent, dmginfo )
if not IsValid( self ) then return end
self:SetTurretDestroyed( true )
end
TurretArmor.OnRepaired = function( ent )
if not IsValid( self ) then return end
self:SetTurretDestroyed( false )
end
TurretArmor.OnHealthChanged = function( ent, dmginfo, old, new )
if new >= old then return end
self:SetTurretDamaged( true )
end
end
function ENT:GetTurretArmor()
return self:GetNWTurretArmor()
end
function ENT:GetTurretEnabled()
if self:GetTurretDestroyed() then return false end
return self:GetNWTurretEnabled()
end
function ENT:SetTurretPitch( num )
self._turretPitch = num
end
function ENT:SetTurretYaw( num )
self._turretYaw = num
end
function ENT:GetTurretPitch()
return (self._turretPitch or self.TurretPitchOffset)
end
function ENT:GetTurretYaw()
return (self._turretYaw or self.TurretYawOffset)
end
if CLIENT then
function ENT:UpdatePoseParameters( steer, speed_kmh, engine_rpm, throttle, brake, handbrake, clutch, gear, temperature, fuel, oil, ammeter )
self:CalcTurret()
end
function ENT:CalcTurret()
local pod = self:GetPassengerSeat( self.TurretPodIndex )
if not IsValid( pod ) then return end
local plyL = LocalPlayer()
local ply = pod:GetDriver()
if ply ~= plyL then return end
self:AimTurret()
end
net.Receive( "lvs_turret_sync_other", function( len )
local veh = net.ReadEntity()
if not IsValid( veh ) then return end
local Pitch = net.ReadFloat()
local Yaw = net.ReadFloat()
if isfunction( veh.SetTurretPitch ) then
veh:SetTurretPitch( Pitch )
end
if isfunction( veh.SetTurretYaw ) then
veh:SetTurretYaw( Yaw )
end
end )
else
util.AddNetworkString( "lvs_turret_sync_other" )
function ENT:OnPassengerChanged( Old, New, PodIndex )
if PodIndex ~= self.TurretPodIndex then return end
if IsValid( New ) then return end
net.Start( "lvs_turret_sync_other" )
net.WriteEntity( self )
net.WriteFloat( self:GetTurretPitch() )
net.WriteFloat( self:GetTurretYaw() )
net.Broadcast()
end
function ENT:CalcTurretSound( Pitch, Yaw, AimRate )
local DeltaPitch = Pitch - self:GetTurretPitch()
local DeltaYaw = Yaw - self:GetTurretYaw()
local PitchVolume = math.abs( DeltaPitch ) / AimRate
local YawVolume = math.abs( DeltaYaw ) / AimRate
local PlayPitch = PitchVolume > 0.95
local PlayYaw = YawVolume > 0.95
local TurretArmor = self:GetTurretArmor()
local Destroyed = self:GetTurretDamaged()
if Destroyed and (PlayPitch or PlayYaw) and IsValid( TurretArmor ) then
local T = CurTime()
if (self._NextTurDMGfx or 0) < T then
self._NextTurDMGfx = T + 0.1
local effectdata = EffectData()
effectdata:SetOrigin( TurretArmor:LocalToWorld( Vector(0,0,TurretArmor:GetMins().z) ) )
effectdata:SetNormal( self:GetUp() )
util.Effect( "lvs_physics_turretscraping", effectdata, true, true )
end
end
if PlayPitch or PlayYaw then
self:DoTurretSound()
end
local T = self:GetTurretSoundTime()
if T > 0 then
local volume = math.max( PitchVolume, YawVolume )
local pitch = 90 + 10 * (1 - volume)
if Destroyed then
local sound = self:StartTurretSoundDMG()
pitch = pitch * self.TurretRateDestroyedMul
sound:ChangeVolume( volume * 0.25, 0.25 )
end
local sound = self:StartTurretSound()
sound:ChangeVolume( volume * 0.25, 0.25 )
sound:ChangePitch( pitch, 0.25 )
else
self:StopTurretSound()
self:StopTurretSoundDMG()
end
end
function ENT:DoTurretSound()
if not self._TurretSound then self._TurretSound = 0 end
self._TurretSound = CurTime() + 1.1
end
function ENT:GetTurretSoundTime()
if not self._TurretSound then return 0 end
return math.max(self._TurretSound - CurTime(),0) / 1
end
function ENT:StopTurretSound()
if not self._turretSND then return end
self._turretSND:Stop()
self._turretSND = nil
end
function ENT:StartTurretSoundDMG()
if self._turretSNDdmg then return self._turretSNDdmg end
self._turretSNDdmg = CreateSound( self, self.TurretRotationSoundDamaged )
self._turretSNDdmg:PlayEx(0.5, 100)
return self._turretSNDdmg
end
function ENT:StopTurretSoundDMG()
if not self._turretSNDdmg then return end
self._turretSNDdmg:Stop()
self._turretSNDdmg = nil
end
function ENT:StartTurretSound()
if self._turretSND then return self._turretSND end
self._turretSND = CreateSound( self, self.TurretRotationSound )
self._turretSND:PlayEx(0,100)
return self._turretSND
end
function ENT:OnRemoved()
self:StopTurretSound()
self:StopTurretSoundDMG()
end
function ENT:OnTick()
self:AimTurret()
end
function ENT:CreateTurretPhysics( data )
if not isstring( data.follow ) or not isstring( data.mdl ) then return NULL end
local idFollow = self:LookupAttachment( data.follow )
local attFollow = self:GetAttachment( idFollow )
if not attFollow then return NULL end
local Follower = ents.Create( "lvs_wheeldrive_attachment_follower" )
if not IsValid( Follower ) then return NULL end
local Master = ents.Create( "lvs_wheeldrive_steerhandler" )
if not IsValid( Master ) then Follower:Remove() return NULL end
Master:SetPos( attFollow.Pos )
Master:SetAngles( attFollow.Ang )
Master:Spawn()
Master:Activate()
self:DeleteOnRemove( Master )
self:TransferCPPI( Master )
Follower:SetModel( data.mdl )
Follower:SetPos( attFollow.Pos )
Follower:SetAngles( self:GetAngles() )
Follower:Spawn()
Follower:Activate()
Follower:SetBase( self )
Follower:SetFollowAttachment( idFollow )
Follower:SetMaster( Master )
self:TransferCPPI( Follower )
self:DeleteOnRemove( Follower )
local B1 = constraint.Ballsocket( Follower, self, 0, 0, self:WorldToLocal( attFollow.Pos ), 0, 0, 1 )
B1.DoNotDuplicate = true
local Lock = 0.0001
local B2 = constraint.AdvBallsocket( Follower,Master,0,0,vector_origin,vector_origin,0,0,-Lock,-Lock,-Lock,Lock,Lock,Lock,0,0,0,1,1)
B2.DoNotDuplicate = true
return Follower
end
end
function ENT:IsTurretEnabled()
if self:GetHP() <= 0 then return false end
if not self:GetTurretEnabled() then return false end
return IsValid( self:GetPassenger( self.TurretPodIndex ) ) or self:GetAI()
end
function ENT:AimTurret()
if not self:IsTurretEnabled() then if SERVER then self:StopTurretSound() self:StopTurretSoundDMG() end return end
local EntTable = self:GetTable()
local weapon = self:GetWeaponHandler( EntTable.TurretPodIndex )
if not IsValid( weapon ) then return end
local AimAngles = self:WorldToLocalAngles( weapon:GetAimVector():Angle() )
if EntTable.TurretFakeBarrel then
AimAngles = self:WorldToLocalAngles( (self:LocalToWorld( EntTable.TurretFakeBarrelRotationCenter ) - weapon:GetEyeTrace().HitPos):Angle() )
end
local AimRate = EntTable.TurretAimRate * FrameTime()
if self:GetTurretDamaged() then
AimRate = AimRate * EntTable.TurretRateDestroyedMul
end
if self:GetTurretForceCenter() then
if EntTable.TurretPitchEnableCentering then AimAngles.p = EntTable.TurretPitchOffset end
if EntTable.TurretYawEnableCentering then AimAngles.y = EntTable.TurretYawOffset end
end
local Pitch = math.Clamp( math.ApproachAngle( self:GetTurretPitch(), AimAngles.p, AimRate ), EntTable.TurretPitchMin, EntTable.TurretPitchMax )
local Yaw = math.ApproachAngle( self:GetTurretYaw(), AimAngles.y, AimRate )
if EntTable.TurretYawMin and EntTable.TurretYawMax then
Yaw = math.Clamp( Yaw, EntTable.TurretYawMin, EntTable.TurretYawMax )
end
if SERVER then
self:CalcTurretSound( Pitch, Yaw, AimRate )
end
self:SetTurretPitch( Pitch )
self:SetTurretYaw( Yaw )
self:SetPoseParameter(EntTable.TurretPitchPoseParameterName, EntTable.TurretPitchOffset + self:GetTurretPitch() * EntTable.TurretPitchMul )
self:SetPoseParameter(EntTable.TurretYawPoseParameterName, EntTable.TurretYawOffset + self:GetTurretYaw() * EntTable.TurretYawMul )
end

View File

@@ -0,0 +1,371 @@
ENT.TurretBallisticsPredicted = true
ENT.TurretBallisticsUpright = 0.6
ENT.TurretBallisticsProjectileVelocity = 10000
ENT.TurretBallisticsMuzzleAttachment = "muzzle"
ENT.TurretBallisticsViewAttachment = "sight"
ENT.OpticsZoomOnly = true
ENT.OpticsScreenCentered = true
function ENT:TurretSystemDT()
self:AddDT( "Bool", "TurretForceCenter" )
self:AddDT( "Bool", "NWTurretEnabled" )
self:AddDT( "Bool", "NWTurretDestroyed" )
self:AddDT( "Bool", "TurretDamaged" )
self:AddDT( "Entity", "NWTurretArmor" )
self:AddDT( "Float", "TurretCompensation" )
self:AddDT( "Float", "NWTurretPitch" )
self:AddDT( "Float", "NWTurretYaw" )
if SERVER then
self:SetTurretEnabled( true )
self:SetTurretPitch( self.TurretPitchOffset )
self:SetTurretYaw( self.TurretYawOffset )
end
end
function ENT:SetTurretPitch( num )
self:SetNWTurretPitch( num )
end
function ENT:SetTurretYaw( num )
self:SetNWTurretYaw( num )
end
function ENT:GetTurretPitch()
return self:GetNWTurretPitch()
end
function ENT:GetTurretYaw()
return self:GetNWTurretYaw()
end
function ENT:GetTurretViewOrigin()
local ID = self:LookupAttachment( self.TurretBallisticsViewAttachment )
local Att = self:GetAttachment( ID )
if not Att then return self:GetPos(), false end
return Att.Pos, true
end
if SERVER then
util.AddNetworkString( "lvs_turret_ballistics_synchronous" )
local function GetTurretEyeTrace( base, weapon )
local startpos, found = base:GetTurretViewOrigin()
if not found then return weapon:GetEyeTrace() end
local pod = weapon:GetDriverSeat()
if IsValid( pod ) and pod:GetThirdPersonMode() then
if weapon == base then
startpos = pod:LocalToWorld( pod:OBBCenter() )
else
startpos = weapon:GetPos()
end
end
local data = {
start = startpos,
endpos = (startpos + weapon:GetAimVector() * 50000),
filter = base:GetCrosshairFilterEnts(),
}
local trace = util.TraceLine( data )
return trace
end
function ENT:CalcTurretAngles( EntTable )
local weapon = self:GetWeaponHandler( EntTable.TurretPodIndex )
if not IsValid( weapon ) then return angle_zero end
local UpZ = self:GetUp().z
if UpZ < EntTable.TurretBallisticsUpright then return self:WorldToLocalAngles( weapon:GetAimVector():Angle() ) end
local pod = weapon:GetDriverSeat()
if IsValid( pod ) then
local ply = weapon:GetDriver()
local ForceNoCompensation = false
if IsValid( ply ) then
if self.OpticsZoomOnly and not ply:lvsKeyDown( "ZOOM" ) then
ForceNoCompensation = true
end
if ply ~= weapon._LastBallisticsSendTo then
weapon._LastBallisticsSendTo = ply
local velocity = EntTable.TurretBallisticsProjectileVelocity
local muzzle = EntTable.TurretBallisticsMuzzleAttachment
local sight = EntTable.TurretBallisticsViewAttachment
self:TurretUpdateBallistics( velocity, muzzle, sight )
end
if pod:GetThirdPersonMode() or ForceNoCompensation then
return self:WorldToLocalAngles( weapon:GetAimVector():Angle() )
end
else
if not weapon:GetAI() then
return self:WorldToLocalAngles( weapon:GetAimVector():Angle() )
end
end
end
local ID = self:LookupAttachment( EntTable.TurretBallisticsMuzzleAttachment )
local Muzzle = self:GetAttachment( ID )
if not Muzzle then return self:WorldToLocalAngles( weapon:GetAimVector():Angle() ) end
local MuzzlePos = Muzzle.Pos
local MuzzleDir = Muzzle.Ang:Forward()
local MuzzleAng = MuzzleDir:Angle()
local AimPos = GetTurretEyeTrace( self, weapon ).HitPos
local StartPos = MuzzlePos
local ProjectileVelocity = EntTable.TurretBallisticsProjectileVelocity
local Dist = (AimPos - MuzzlePos):Length()
local OffsetPredicted = vector_origin
if EntTable.TurretBallisticsPredicted then
OffsetPredicted = physenv.GetGravity() * ((Dist / ProjectileVelocity) ^ 2)
end
local EndPos = AimPos - OffsetPredicted
local Dir = (EndPos - StartPos):GetNormalized()
local Pos, Ang = WorldToLocal( Muzzle.Pos, Dir:Angle(), Muzzle.Pos, MuzzleAng )
-- more body pitch/roll = more inaccurate. If Z up get smaller, turret must align more conservative to not overshoot
local TurretSmoothing = math.abs( UpZ )
self:SetTurretCompensation( OffsetPredicted.z )
return Angle( self:GetTurretPitch() + Ang.p * TurretSmoothing, self:GetTurretYaw() + Ang.y * TurretSmoothing, 0 )
end
else
ENT.IconTurret = Material( "lvs/turret.png" )
ENT.IconTurretBody = Material( "lvs/turret_body.png" )
ENT.IconTurretBarrel = Material( "lvs/turret_barrel.png" )
ENT.IconTurretRing = Material( "lvs/turret_ring.png" )
ENT.TurretColorMain = color_white
ENT.TurretColorShadow = Color(0,0,0,200)
ENT.TurretColorDamaged = Color(255,0,0,255)
LVS:AddHudEditor( "Turret Info", ScrW() * 0.5 - 75, ScrH() - 110, 150, 100, 150, 100, "TURRETINFO",
function( self, vehicle, X, Y, W, H, ScrX, ScrY, ply )
if not vehicle.LVSHudPaintTurretInfo then return end
vehicle:LVSHudPaintTurretInfo( X + W * 0.5 - H * 0.5, Y, H, H, ScrX, ScrY, ply )
end
)
function ENT:LVSHudPaintTurretInfo( X, Y, W, H, ScrX, ScrY, ply )
local pod = ply:GetVehicle()
if not IsValid( pod ) or pod:lvsGetPodIndex() ~= self.TurretPodIndex then return end
local EntTable = self:GetTable()
local _, viewangles = ply:lvsGetView()
local turret_yaw = self:GetTurretYaw()
local yaw_body = - ply:GetVehicle():WorldToLocalAngles( viewangles ).y + 90
local yaw_turret = turret_yaw + yaw_body
local IconSize = W * 0.75
surface.SetDrawColor( EntTable.TurretColorShadow )
surface.SetMaterial( EntTable.IconTurretBody )
surface.DrawTexturedRectRotated( X + W * 0.5 + 2, Y + H * 0.5 + 2, IconSize, IconSize, yaw_body )
local BodyColor = EntTable.TurretColorMain
if self.GetWheels then
for _, wheel in pairs( self:GetWheels() ) do
if not wheel:GetDamaged() then continue end
BodyColor = EntTable.TurretColorDamaged
break
end
end
surface.SetDrawColor( BodyColor )
surface.SetMaterial( EntTable.IconTurretBody )
surface.DrawTexturedRectRotated( X + W * 0.5, Y + H * 0.5, IconSize, IconSize, yaw_body )
surface.SetDrawColor( EntTable.TurretColorShadow )
surface.SetMaterial( EntTable.IconTurret )
surface.DrawTexturedRectRotated( X + W * 0.5 + 2, Y + H * 0.5 + 2, IconSize, IconSize, yaw_turret )
surface.SetDrawColor( EntTable.TurretColorMain )
surface.SetMaterial( EntTable.IconTurretBarrel )
surface.DrawTexturedRectRotated( X + W * 0.5, Y + H * 0.5, IconSize, IconSize, yaw_turret )
if self:GetTurretDamaged() then surface.SetDrawColor( EntTable.TurretColorDamaged ) end
surface.SetMaterial( EntTable.IconTurretRing )
surface.DrawTexturedRectRotated( X + W * 0.5, Y + H * 0.5, IconSize, IconSize, yaw_turret )
end
net.Receive( "lvs_turret_ballistics_synchronous", function( len )
local vehicle = net.ReadEntity()
local velocity = net.ReadFloat()
local muzzle = net.ReadString()
local sight = net.ReadString()
if not IsValid( vehicle ) then return end
if velocity == 0 then velocity = nil end
if muzzle == "" then muzzle = nil end
if sight == "" then sight = nil end
vehicle:TurretUpdateBallistics( velocity, muzzle, sight )
end )
function ENT:CalcOpticsCrosshairDot( Pos2D )
local ID = self:LookupAttachment( self.TurretBallisticsMuzzleAttachment )
local Muzzle = self:GetAttachment( ID )
if not Muzzle then return end
local MuzzlePos = Muzzle.Pos
local Pos = MuzzlePos
local LastPos = MuzzlePos
local StartPos = MuzzlePos
local StartDirection = Muzzle.Ang:Forward()
local Velocity = self.TurretBallisticsProjectileVelocity
local Gravity = vector_origin
if self.TurretBallisticsPredicted then
Gravity = physenv.GetGravity()
end
cam.Start3D()
local Iteration = 0
while Iteration < 1000 do
Iteration = Iteration + 1
local TimeAlive = Iteration / 200
local EndPos = StartPos + StartDirection * TimeAlive * Velocity + Gravity * (TimeAlive ^ 2)
Pos = EndPos
local trace = util.TraceLine( {
start = LastPos,
endpos = EndPos,
mask = MASK_SOLID,
} )
LastPos = EndPos
if trace.Hit then
Pos = trace.HitPos
break
end
end
cam.End3D()
self:PaintOpticsCrosshair( Pos:ToScreen() )
end
end
function ENT:TurretUpdateBallistics( newvelocity, newmuzzle, newsight )
if newvelocity then
self.TurretBallisticsProjectileVelocity = newvelocity
end
if newmuzzle then
self.TurretBallisticsMuzzleAttachment = newmuzzle
end
if newsight then
self.TurretBallisticsViewAttachment = newsight
end
if CLIENT then return end
local ply = self:GetPassenger( self.TurretPodIndex )
if not IsValid( ply ) then return end
net.Start( "lvs_turret_ballistics_synchronous" )
net.WriteEntity( self )
net.WriteFloat( newvelocity or 0 )
net.WriteString( newmuzzle or "" )
net.WriteString( newsight or "" )
net.Send( ply )
end
function ENT:AimTurret()
if not self:IsTurretEnabled() then if SERVER then self:StopTurretSound() self:StopTurretSoundDMG() end return end
local EntTable = self:GetTable()
if SERVER then
local AimAngles = self:CalcTurretAngles( EntTable )
local AimRate = EntTable.TurretAimRate * FrameTime()
if self:GetTurretDamaged() then
AimRate = AimRate * EntTable.TurretRateDestroyedMul
end
if self:GetTurretForceCenter() then
if EntTable.TurretPitchEnableCentering then AimAngles.p = EntTable.TurretPitchOffset end
if EntTable.TurretYawEnableCentering then AimAngles.y = EntTable.TurretYawOffset end
end
local Pitch = math.Clamp( math.ApproachAngle( self:GetTurretPitch(), AimAngles.p, AimRate ), EntTable.TurretPitchMin, EntTable.TurretPitchMax )
local Yaw = math.ApproachAngle( self:GetTurretYaw(), AimAngles.y, AimRate )
if EntTable.TurretYawMin and EntTable.TurretYawMax then
Yaw = math.Clamp( Yaw, EntTable.TurretYawMin, EntTable.TurretYawMax )
end
self:CalcTurretSound( Pitch, Yaw, AimRate )
self:SetTurretPitch( Pitch )
self:SetTurretYaw( Yaw )
self:SetPoseParameter(EntTable.TurretPitchPoseParameterName, EntTable.TurretPitchOffset + self:GetTurretPitch() * EntTable.TurretPitchMul )
self:SetPoseParameter(EntTable.TurretYawPoseParameterName, EntTable.TurretYawOffset + self:GetTurretYaw() * EntTable.TurretYawMul )
return
end
local Rate = math.min( FrameTime() * EntTable.TurretAimRate, 1 )
local TargetPitch = EntTable.TurretPitchOffset + self:GetTurretPitch() * EntTable.TurretPitchMul
local TargetYaw = EntTable.TurretYawOffset + self:GetTurretYaw() * EntTable.TurretYawMul
EntTable._turretPitch = EntTable._turretPitch and EntTable._turretPitch + (TargetPitch - EntTable._turretPitch) * Rate or EntTable.TurretPitchOffset
EntTable._turretYaw = EntTable._turretYaw and EntTable._turretYaw + (TargetYaw - EntTable._turretYaw) * Rate or EntTable.TurretYawOffset
self:SetPoseParameter(EntTable.TurretPitchPoseParameterName, EntTable._turretPitch )
self:SetPoseParameter(EntTable.TurretYawPoseParameterName, EntTable._turretYaw )
end

View File

@@ -0,0 +1,155 @@
include("sh_turret.lua")
ENT.TurretRotationSound = "vehicles/tank_turret_loop1.wav"
ENT.TurretElevationSound = "vehicles/tank_turret_loop2.wav"
if CLIENT then return end
function ENT:CalcTurretSound( Pitch, Yaw, AimRate )
local DeltaPitch = Pitch - self:GetTurretPitch()
local DeltaYaw = Yaw - self:GetTurretYaw()
local PitchVolume = math.abs( DeltaPitch ) / AimRate
local YawVolume = math.abs( DeltaYaw ) / AimRate
local PlayPitch = PitchVolume > 0.95
local PlayYaw = YawVolume > 0.95
local TurretArmor = self:GetTurretArmor()
local Destroyed = self:GetTurretDestroyed()
if Destroyed and (PlayPitch or PlayYaw) and IsValid( TurretArmor ) then
local T = CurTime()
if (self._NextTurDMGfx or 0) < T then
self._NextTurDMGfx = T + 0.1
local effectdata = EffectData()
effectdata:SetOrigin( TurretArmor:GetPos() )
effectdata:SetNormal( self:GetUp() )
effectdata:SetRadius( 0 )
util.Effect( "cball_bounce", effectdata, true, true )
end
end
if Destroyed and (PlayPitch or PlayYaw) then
self:StartTurretSoundDMG()
else
self:StopTurretSoundDMG()
end
if PlayPitch then
self:DoElevationSound()
end
if PlayYaw then
self:DoRotationSound()
end
if self:GetRotationSoundTime() > 0 then
local sound = self:StartRotationSound()
local volume = YawVolume
local pitch = 90 + 10 * (1 - volume)
sound:ChangeVolume( volume * 0.25, 0.25 )
sound:ChangePitch( pitch, 0.25 )
else
self:StopRotationSound()
end
if self:GetElevationSoundTime() > 0 then
local sound = self:StartElevationSound()
local volume = PitchVolume
local pitch = 90 + 10 * (1 - volume)
sound:ChangeVolume( volume * 0.25, 0.25 )
sound:ChangePitch( pitch, 0.25 )
else
self:StopElevationSound()
end
end
function ENT:DoRotationSound()
if not self._RotationSound then self._RotationSound = 0 end
self._RotationSound = CurTime() + 1.1
end
function ENT:DoElevationSound()
if not self._ElevationSound then self._ElevationSound = 0 end
self._ElevationSound = CurTime() + 1.1
end
function ENT:GetRotationSoundTime()
if not self._RotationSound then return 0 end
return math.max(self._RotationSound - CurTime(),0) / 1
end
function ENT:GetElevationSoundTime()
if not self._ElevationSound then return 0 end
return math.max(self._ElevationSound - CurTime(),0) / 1
end
function ENT:StopRotationSound()
if not self._turretRotSND then return end
self._turretRotSND:Stop()
self._turretRotSND = nil
end
function ENT:StopElevationSound()
if not self._turretElevSND then return end
self._turretElevSND:Stop()
self._turretElevSND = nil
end
function ENT:StartTurretSoundDMG()
if self._turretSNDdmg then return self._turretSNDdmg end
self._turretSNDdmg = CreateSound( self, self.TurretRotationSoundDamaged )
self._turretSNDdmg:PlayEx(0.5, 100)
return self._turretSNDdmg
end
function ENT:StopTurretSoundDMG()
if not self._turretSNDdmg then return end
self._turretSNDdmg:Stop()
self._turretSNDdmg = nil
end
function ENT:StartRotationSound()
if self._turretRotSND then return self._turretRotSND end
self._turretRotSND = CreateSound( self, self.TurretRotationSound )
self._turretRotSND:PlayEx(0,100)
return self._turretRotSND
end
function ENT:StartElevationSound()
if self._turretElevSND then return self._turretElevSND end
self._turretElevSND = CreateSound( self, self.TurretElevationSound )
self._turretElevSND:PlayEx(0,100)
return self._turretElevSND
end
function ENT:StopTurretSound()
self:StopElevationSound()
self:StopRotationSound()
self:StopTurretSoundDMG()
end
function ENT:OnRemoved()
self:StopTurretSound()
end

View File

@@ -0,0 +1,22 @@
ENT.Base = "lvs_base_wheeldrive"
ENT.PrintName = "[LVS] Wheeldrive Tank"
ENT.Author = "Luna"
ENT.Information = "Luna's Vehicle Script"
ENT.Category = "[LVS] - Cars"
ENT.Spawnable = false
ENT.AdminSpawnable = false
ENT.MaxHealthEngine = 400
ENT.MaxHealthFuelTank = 100
function ENT:TrackSystemDT()
self:AddDT( "Entity", "TrackDriveWheelLeft" )
self:AddDT( "Entity", "TrackDriveWheelRight" )
end
function ENT:GetVehicleType()
return "tank"
end

View File

@@ -0,0 +1,171 @@
function ENT:OnLeftTrackRepaired()
end
function ENT:OnRightTrackRepaired()
end
function ENT:OnLeftTrackDestroyed()
end
function ENT:OnRightTrackDestroyed()
end
function ENT:GetTrackPhysics()
return self._TrackPhysics
end
function ENT:CreateTrackPhysics( mdl )
if IsValid( self._TrackPhysics ) then return self._TrackPhysics end
if not isstring( mdl ) then return NULL end
local TrackPhysics = ents.Create( "lvs_wheeldrive_trackphysics" )
if not IsValid( TrackPhysics ) then return NULL end
TrackPhysics:SetModel( mdl )
TrackPhysics:SetPos( self:GetPos() )
TrackPhysics:SetAngles( self:GetAngles() )
TrackPhysics:Spawn()
TrackPhysics:Activate()
TrackPhysics:SetBase( self )
self:TransferCPPI( TrackPhysics )
self:DeleteOnRemove( TrackPhysics )
self._TrackPhysics = TrackPhysics
local weld_constraint = constraint.Weld( TrackPhysics, self, 0, 0 )
weld_constraint.DoNotDuplicate = true
return TrackPhysics
end
function ENT:CreateWheelChain( wheels )
if not istable( wheels ) then return end
local Lock = 0.0001
local VectorNull = Vector(0,0,0)
for _, wheel in pairs( wheels ) do
if not IsValid( wheel ) then continue end
wheel:SetWheelChainMode( true )
wheel:SetWidth( 0 )
wheel:SetCollisionBounds( VectorNull, VectorNull )
end
for i = 2, #wheels do
local prev = wheels[ i - 1 ]
local cur = wheels[ i ]
if not IsValid( cur ) or not IsValid( prev ) then continue end
local B = constraint.AdvBallsocket(prev,cur,0,0,vector_origin,vector_origin,0,0,-Lock,-180,-180,Lock,180,180,0,0,0,1,1)
B.DoNotDuplicate = true
local Rope = constraint.Rope(prev,cur,0,0,vector_origin,vector_origin,(prev:GetPos() - cur:GetPos()):Length(), 0, 0, 0,"cable/cable2", false)
Rope.DoNotDuplicate = true
end
local WheelChain = {}
WheelChain.OnDestroyed = function( ent )
if not IsValid( ent ) or ent._tracksDestroyed then return end
ent._tracksDestroyed = true
self:OnTrackDestroyed( ent.wheeltype )
self:OnHandleTrackGib( ent.wheeltype, true )
for _, wheel in pairs( wheels ) do
if not IsValid( wheel ) then continue end
wheel:Destroy()
end
end
WheelChain.OnRepaired = function( ent )
for _, wheel in pairs( wheels ) do
if not IsValid( wheel ) then continue end
wheel:Repair()
end
if not IsValid( ent ) or not ent._tracksDestroyed then return end
ent._tracksDestroyed = nil
self:OnTrackRepaired( ent.wheeltype )
self:OnHandleTrackGib( ent.wheeltype, false )
end
WheelChain.OnHealthChanged = function( ent, dmginfo, old, new )
if new >= old then return end
for _, wheel in pairs( wheels ) do
if not IsValid( wheel ) then continue end
wheel:SetDamaged( true )
end
end
return WheelChain
end
function ENT:SetTrackArmor( Armor, WheelChain )
if not IsValid( Armor ) then return end
Armor.OnDestroyed = WheelChain.OnDestroyed
Armor.OnRepaired = WheelChain.OnRepaired
Armor.OnHealthChanged = WheelChain.OnHealthChanged
Armor:SetLabel( "Tracks" )
end
function ENT:OnTrackRepaired( wheeltype )
if wheeltype == LVS.WHEELTYPE_LEFT then
self:OnLeftTrackRepaired()
end
if wheeltype == LVS.WHEELTYPE_RIGHT then
self:OnRightTrackRepaired()
end
end
function ENT:OnTrackDestroyed( wheeltype )
if wheeltype == LVS.WHEELTYPE_LEFT then
self:OnLeftTrackDestroyed()
end
if wheeltype == LVS.WHEELTYPE_RIGHT then
self:OnRightTrackDestroyed()
end
end
function ENT:SetTrackArmorLeft( Armor, WheelChain )
Armor.wheeltype = LVS.WHEELTYPE_LEFT
self:SetTrackArmor( Armor, WheelChain )
end
function ENT:SetTrackArmorRight( Armor, WheelChain )
Armor.wheeltype = LVS.WHEELTYPE_RIGHT
self:SetTrackArmor( Armor, WheelChain )
end
function ENT:OnHandleTrackGib( wheeltype, destroy )
local TrackPhys = self:GetTrackPhysics()
if not IsValid( TrackPhys ) then return end
if destroy then
TrackPhys:SpawnGib( wheeltype )
return
end
TrackPhys:ClearGib()
end