add sborka
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user