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,258 @@
AddCSLuaFile()
ENT.Type = "anim"
ENT.DoNotDuplicate = true
ENT.RenderGroup = RENDERGROUP_BOTH
function ENT:SetupDataTables()
self:NetworkVar( "Entity",0, "Base" )
self:NetworkVar( "Float",0, "HP" )
self:NetworkVar( "Float",1, "MaxHP" )
self:NetworkVar( "Float",2, "IgnoreForce" )
self:NetworkVar( "Vector",0, "Mins" )
self:NetworkVar( "Vector",1, "Maxs" )
self:NetworkVar( "Bool",0, "Destroyed" )
self:NetworkVar( "String",0, "Label" )
if SERVER then
self:SetMaxHP( 100 )
self:SetHP( 100 )
self:SetLabel( "Armor Plate" )
end
end
if SERVER then
function ENT:Initialize()
self:SetMoveType( MOVETYPE_NONE )
self:SetSolid( SOLID_NONE )
self:DrawShadow( false )
end
function ENT:Think()
return false
end
function ENT:OnHealthChanged( dmginfo, old, new )
if old == new then return end
end
function ENT:OnRepaired()
end
function ENT:OnDestroyed( dmginfo )
end
function ENT:OnTakeDamage( dmginfo )
end
function ENT:TakeTransmittedDamage( dmginfo )
local Force = dmginfo:GetDamageForce()
local Damage = dmginfo:GetDamage()
local DamageForce = Force:Length()
local IsBlastDamage = dmginfo:IsDamageType( DMG_BLAST )
local CurHealth = self:GetHP()
local pos = dmginfo:GetDamagePosition()
local dir = Force:GetNormalized()
local base = self:GetBase()
-- translate force value to armor penetration value is Force * 0.1
-- mm to inch is * 0.0393701
-- so correct value is * 0.00393701
local pLength = DamageForce * 0.00393701
local TraceData = {
start = pos - dir * pLength,
endpos = pos + dir * pLength,
}
local trace = util.TraceLine( TraceData )
-- parent stays the same
local parent = trace.Entity
local parentPos = trace.HitPos
local parentDir = trace.HitNormal
-- only one extra iteration should be enough ...
if IsValid( trace.Entity ) and isfunction( trace.Entity.GetBase ) and trace.Entity:GetBase() == base then
TraceData.filter = trace.Entity
local FilteredTrace = util.TraceLine( TraceData )
if FilteredTrace.Hit then
trace = FilteredTrace
end
trace.Entity = base
end
local DotHitNormal = math.Clamp( trace.HitNormal:Dot( dir ) ,-1,1)
local Armor = self:GetIgnoreForce()
local ArmorEffective = Armor / math.abs( DotHitNormal )
if math.abs( DotHitNormal ) > 0.9 then
ArmorEffective = Armor
end
local DisableBounce = false
local Inflictor = dmginfo:GetInflictor()
if IsValid( Inflictor ) then
if Inflictor.DisableBallistics or Inflictor:IsNPC() or Inflictor:IsNextBot() then
DisableBounce = true
end
end
if DamageForce <= ArmorEffective and not IsBlastDamage then
local T = CurTime()
if trace.Entity ~= base then
self._NextBounce = T + 1
return false
end
local Ax = math.acos( DotHitNormal )
local HitAngle = 90 - (180 - math.deg( Ax ))
if HitAngle > 30 then
local effectdata = EffectData()
effectdata:SetOrigin( trace.HitPos )
effectdata:SetNormal( -dir )
util.Effect( "manhacksparks", effectdata, true, true )
self._NextBounce = T + 1
return false
end
local NewDir = dir - trace.HitNormal * math.cos( Ax ) * 2
if (self._NextBounce or 0) > T or DisableBounce then
local effectdata = EffectData()
effectdata:SetOrigin( trace.HitPos )
effectdata:SetNormal( NewDir:GetNormalized() * 0.25 )
util.Effect( "manhacksparks", effectdata, true, true )
return false
end
self._NextBounce = T + 1
local hit_decal = ents.Create( "lvs_armor_bounce" )
hit_decal:SetPos( trace.HitPos )
hit_decal:SetAngles( NewDir:Angle() )
hit_decal:Spawn()
hit_decal:Activate()
hit_decal:EmitSound("lvs/armor_rico"..math.random(1,6)..".wav", 95, 100, math.min( dmginfo:GetDamage() / 1000, 1 ) )
local PhysObj = hit_decal:GetPhysicsObject()
if not IsValid( PhysObj ) then return false end
PhysObj:EnableDrag( false )
PhysObj:SetVelocityInstantaneous( NewDir * 2000 + Vector(0,0,250) )
PhysObj:SetAngleVelocityInstantaneous( VectorRand() * 250 )
return false
end
local NewHealth = math.Clamp( CurHealth - Damage, 0, self:GetMaxHP() )
self:OnHealthChanged( dmginfo, CurHealth, NewHealth )
self:SetHP( NewHealth )
if NewHealth <= 0 and not self:GetDestroyed() then
self:SetDestroyed( true )
self:OnDestroyed( dmginfo )
end
local hit_decal = ents.Create( "lvs_armor_penetrate" )
hit_decal:SetPos( parentPos + parentDir * 0.2 )
hit_decal:SetAngles( parentDir:Angle() + Angle(90,0,0) )
hit_decal:Spawn()
hit_decal:Activate()
hit_decal:SetParent( parent )
return true
end
return
end
function ENT:Initialize()
end
function ENT:OnRemove()
end
function ENT:Think()
end
function ENT:Draw()
end
local function DrawText( pos, text, col )
cam.Start2D()
local data2D = pos:ToScreen()
if not data2D.visible then cam.End2D() return end
local font = "TargetIDSmall"
local x = data2D.x
local y = data2D.y
draw.DrawText( text, font, x + 1, y + 1, Color( 0, 0, 0, 120 ), TEXT_ALIGN_CENTER )
draw.DrawText( text, font, x + 2, y + 2, Color( 0, 0, 0, 50 ), TEXT_ALIGN_CENTER )
draw.DrawText( text, font, x, y, col or color_white, TEXT_ALIGN_CENTER )
cam.End2D()
end
local LVS = LVS
local BoxMat = Material("models/wireframe")
local ColorSelect = Color(0,127,255,150)
local ColorNormal = Color(50,50,50,150)
local ColorTransBlack = Color(0,0,0,150)
local OutlineThickness = Vector(0.5,0.5,0.5)
local ColorText = Color(255,0,0,255)
function ENT:DrawTranslucent()
if not LVS.DeveloperEnabled then return end
local ply = LocalPlayer()
if not IsValid( ply ) or ply:InVehicle() or not ply:KeyDown( IN_SPEED ) then return end
local boxOrigin = self:GetPos()
local boxAngles = self:GetAngles()
local boxMins = self:GetMins()
local boxMaxs = self:GetMaxs()
local HitPos, _, _ = util.IntersectRayWithOBB( ply:GetShootPos(), ply:GetAimVector() * 1000, boxOrigin, boxAngles, boxMins, boxMaxs )
local InRange = isvector( HitPos )
local Col = InRange and ColorSelect or ColorNormal
render.SetColorMaterial()
render.DrawBox( boxOrigin, boxAngles, boxMins, boxMaxs, Col )
render.DrawBox( boxOrigin, boxAngles, boxMaxs + OutlineThickness, boxMins - OutlineThickness, ColorTransBlack )
local boxCenter = (self:LocalToWorld( boxMins ) + self:LocalToWorld( boxMaxs )) * 0.5
if not InRange then return end
DrawText( boxCenter, "Armor: "..(self:GetIgnoreForce() / 100).."mm\nHealth:"..self:GetHP().."/"..self:GetMaxHP(), ColorText )
end

View File

@@ -0,0 +1,124 @@
AddCSLuaFile()
ENT.Type = "anim"
ENT.PrintName = "88mm Round"
ENT.Author = "Luna"
ENT.Information = "Luna's Vehicle Script"
ENT.Category = "[LVS] - Cars - Items"
ENT.Spawnable = false
ENT.AdminOnly = false
ENT.LifeTime = 10
if SERVER then
function ENT:Initialize()
self:SetModel( "models/misc/88mm_projectile.mdl" )
self:SetMoveType( MOVETYPE_VPHYSICS )
self:PhysicsInit( SOLID_VPHYSICS)
self.DieTime = CurTime() + self.LifeTime
self:SetCollisionGroup( COLLISION_GROUP_WORLD )
end
function ENT:Think()
if self.MarkForRemove then self:Remove() return false end
self:NextThink( CurTime() + 0.1 )
if (self.DieTime or 0) > CurTime() then return true end
self:Remove()
return false
end
function ENT:PhysicsCollide( data, physobj )
self.MarkForRemove = true
local effectdata = EffectData()
effectdata:SetOrigin( data.HitPos )
effectdata:SetNormal( -data.HitNormal )
effectdata:SetMagnitude( 0.5 )
util.Effect( "lvs_bullet_impact", effectdata )
end
return
end
ENT.MatSmoke = {
"particle/smokesprites_0001",
"particle/smokesprites_0002",
"particle/smokesprites_0003",
"particle/smokesprites_0004",
"particle/smokesprites_0005",
"particle/smokesprites_0006",
"particle/smokesprites_0007",
"particle/smokesprites_0008",
"particle/smokesprites_0009",
"particle/smokesprites_0010",
"particle/smokesprites_0011",
"particle/smokesprites_0012",
"particle/smokesprites_0013",
"particle/smokesprites_0014",
"particle/smokesprites_0015",
"particle/smokesprites_0016"
}
function ENT:Initialize()
self.DieTime = CurTime() + self.LifeTime
self.emitter = ParticleEmitter( self:GetPos(), false )
end
function ENT:Smoke()
local T = CurTime()
if (self.DieTime or 0) < T then return end
if not IsValid( self.emitter ) then return end
if (self.NextFX or 0) < T then
self.NextFX = T + 0.02
local Timed = 1 - (self.DieTime - T) / self.LifeTime
local Scale = math.max(math.min(2 - Timed * 2,1),0)
local Pos = self:GetPos()
local particle = self.emitter:Add( self.MatSmoke[math.random(1,#self.MatSmoke)], Pos )
local VecCol = (render.GetLightColor( Pos ) * 0.8 + Vector(0.2,0.2,0.2)) * 255
if particle then
particle:SetVelocity( VectorRand() * 10 )
particle:SetDieTime( math.Rand(0.5,1) )
particle:SetAirResistance( 100 )
particle:SetStartAlpha( 100 * Scale )
particle:SetEndAlpha( 0 )
particle:SetStartSize( 10 )
particle:SetEndSize( 20 )
particle:SetRollDelta( 1 )
particle:SetColor( VecCol.r, VecCol.g, VecCol.b )
particle:SetGravity( Vector( 0, 0, 200 ) )
particle:SetCollide( false )
end
end
end
function ENT:Think()
self:Smoke()
end
function ENT:OnRemove()
if not self.emitter then return end
self.emitter:Finish()
end
function ENT:Draw()
self:DrawModel()
end

View File

@@ -0,0 +1,149 @@
AddCSLuaFile()
ENT.Type = "anim"
ENT.RenderGroup = RENDERGROUP_BOTH
ENT.LifeTime = 15
if SERVER then
local CountTotal = {}
function ENT:Initialize()
CountTotal[ self:EntIndex() ] = true
local Num = table.Count( CountTotal )
if (Num > 30 and math.random(1,2) == 1) or Num > 60 then
self:Remove()
return
end
self:SetMoveType( MOVETYPE_NONE )
self:SetSolid( SOLID_NONE )
self:DrawShadow( false )
self.DieTime = CurTime() + self.LifeTime
end
function ENT:OnRemove()
CountTotal[ self:EntIndex() ] = nil
end
function ENT:Think()
self:NextThink( CurTime() + 0.1 )
if not IsValid( self:GetParent() ) then self:Remove() return end
if (self.DieTime or 0) > CurTime() then return true end
self:Remove()
return false
end
return
end
ENT.GlowMat1 = Material( "particle/particle_ring_wave_8" )
ENT.GlowMat2 = Material( "sprites/light_glow02_add" )
ENT.DecalMat = Material( "particle/particle_noisesphere" )
ENT.MatSmoke = {
"particle/smokesprites_0001",
"particle/smokesprites_0002",
"particle/smokesprites_0003",
"particle/smokesprites_0004",
"particle/smokesprites_0005",
"particle/smokesprites_0006",
"particle/smokesprites_0007",
"particle/smokesprites_0008",
"particle/smokesprites_0009",
"particle/smokesprites_0010",
"particle/smokesprites_0011",
"particle/smokesprites_0012",
"particle/smokesprites_0013",
"particle/smokesprites_0014",
"particle/smokesprites_0015",
"particle/smokesprites_0016"
}
local CountTotal = {}
function ENT:Initialize()
CountTotal[ self:EntIndex() ] = true
self.RandomAng = math.random(0,360)
self.DieTime = CurTime() + self.LifeTime
local Pos = self:GetPos()
local Dir = self:GetUp()
self.emitter = ParticleEmitter( Pos, false )
self:EmitSound( "lvs/armor_pen_"..math.random(1,3)..".wav", 95 )
end
function ENT:Smoke()
local T = CurTime()
if (self.DieTime or 0) < T then return end
if not IsValid( self.emitter ) then return end
if (self.NextFX or 0) < T then
self.NextFX = T + 0.2 + table.Count( CountTotal ) / 50
local particle = self.emitter:Add( self.MatSmoke[math.random(1,#self.MatSmoke)], self:GetPos() )
if particle then
particle:SetVelocity( self:GetUp() * 60 + VectorRand() * 30 )
particle:SetDieTime( math.Rand(1.5,2) )
particle:SetAirResistance( 100 )
particle:SetStartAlpha( 30 )
particle:SetEndAlpha( 0 )
particle:SetStartSize( 0 )
particle:SetEndSize( 60 )
particle:SetRollDelta( math.Rand( -1, 1 ) )
particle:SetColor( 50,50,50 )
particle:SetGravity( Vector( 0, 0, 200 ) )
particle:SetCollide( false )
end
end
end
function ENT:Think()
self:Smoke()
end
function ENT:OnRemove()
CountTotal[ self:EntIndex() ] = nil
if not IsValid(self.emitter) then return end
self.emitter:Finish()
end
function ENT:Draw()
local Timed = 1 - (self.DieTime - CurTime()) / self.LifeTime
local Scale = math.max(math.min(2 - Timed * 2,1),0)
local Scale02 = math.max(Scale - 0.8,0) / 0.2
cam.Start3D2D( self:GetPos() + self:GetAngles():Up(), self:GetAngles(), 1 )
surface.SetDrawColor( 255 * Scale02, (93 + 50 * Scale) * Scale02, (50 * Scale) * Scale02, (200 * Scale) * Scale02 )
surface.SetMaterial( self.GlowMat1 )
surface.DrawTexturedRectRotated( 0, 0, 8 , 8 , self.RandomAng )
surface.SetMaterial( self.GlowMat2 )
surface.DrawTexturedRectRotated( 0, 0, 16 , 16 , self.RandomAng )
surface.SetDrawColor( 0, 0, 0, 255 )
surface.SetMaterial( self.DecalMat )
surface.DrawTexturedRectRotated( 0, 0, 16 , 16 , self.RandomAng )
cam.End3D2D()
end
function ENT:DrawTranslucent()
self:Draw()
end

View File

@@ -0,0 +1,30 @@
function ENT:CreateBonePoseParameter( name, bone, ang_min, ang_max, pos_min, pos_max )
if not istable( self._BonePoseParameters ) then self._BonePoseParameters = {} end
self._BonePoseParameters[ name ] = {
bone = (bone or -1),
ang_min = ang_min or angle_zero,
ang_max = ang_max or angle_zero,
pos_min = pos_min or vector_origin,
pos_max = pos_max or vector_origin,
}
end
function ENT:SetBonePoseParameter( name, value )
if name and string.StartsWith( name, "!" ) then
name = string.Replace( name, "!", "" )
end
local EntTable = self:GetTable()
if not istable( EntTable._BonePoseParameters ) or not EntTable._BonePoseParameters[ name ] then return end
local data = EntTable._BonePoseParameters[ name ]
local ang = LerpAngle( value, data.ang_min, data.ang_max )
local pos = LerpVector( value, data.pos_min, data.pos_max )
self:ManipulateBoneAngles( data.bone, ang )
self:ManipulateBonePosition( data.bone, pos )
end

View File

@@ -0,0 +1,142 @@
function ENT:StartWindSounds()
if not LVS.ShowEffects then return end
self:StopWindSounds()
if LocalPlayer():lvsGetVehicle() ~= self then return end
local EntTable = self:GetTable()
EntTable._WindSFX = CreateSound( self, "LVS.Physics.Wind" )
EntTable._WindSFX:PlayEx(0,100)
EntTable._WaterSFX = CreateSound( self, "LVS.Physics.Water" )
EntTable._WaterSFX:PlayEx(0,100)
end
function ENT:StopWindSounds()
local EntTable = self:GetTable()
if EntTable._WindSFX then
EntTable._WindSFX:Stop()
EntTable._WindSFX = nil
end
if EntTable._WaterSFX then
EntTable._WaterSFX:Stop()
EntTable._WaterSFX = nil
end
end
ENT.DustEffectSurfaces = {
["sand"] = true,
["dirt"] = true,
["grass"] = true,
}
ENT.GroundEffectsMultiplier = 1
function ENT:DoVehicleFX()
local EntTable = self:GetTable()
if EntTable.GroundEffectsMultiplier <= 0 or not LVS.ShowEffects then self:StopWindSounds() return end
local Vel = self:GetVelocity():Length() * EntTable.GroundEffectsMultiplier
if EntTable._WindSFX then EntTable._WindSFX:ChangeVolume( math.Clamp( (Vel - 1200) / 2800,0,1 ), 0.25 ) end
if Vel < 1500 then
if EntTable._WaterSFX then EntTable._WaterSFX:ChangeVolume( 0, 0.25 ) end
return
end
if (EntTable.nextFX or 0) < CurTime() then
EntTable.nextFX = CurTime() + 0.05
local LCenter = self:OBBCenter()
LCenter.z = self:OBBMins().z
local CenterPos = self:LocalToWorld( LCenter )
local trace = util.TraceLine( {
start = CenterPos + Vector(0,0,25),
endpos = CenterPos - Vector(0,0,450),
filter = self:GetCrosshairFilterEnts(),
} )
local traceWater = util.TraceLine( {
start = CenterPos + Vector(0,0,25),
endpos = CenterPos - Vector(0,0,450),
filter = self:GetCrosshairFilterEnts(),
mask = MASK_WATER,
} )
if EntTable._WaterSFX then EntTable._WaterSFX:ChangePitch( math.Clamp((Vel / 1000) * 50,80,150), 0.5 ) end
if traceWater.Hit and trace.HitPos.z < traceWater.HitPos.z then
local effectdata = EffectData()
effectdata:SetOrigin( traceWater.HitPos )
effectdata:SetEntity( self )
util.Effect( "lvs_physics_water", effectdata )
if EntTable._WaterSFX then EntTable._WaterSFX:ChangeVolume( 1 - math.Clamp(traceWater.Fraction,0,1), 0.5 ) end
else
if EntTable._WaterSFX then EntTable._WaterSFX:ChangeVolume( 0, 0.25 ) end
end
if trace.Hit and EntTable.DustEffectSurfaces[ util.GetSurfacePropName( trace.SurfaceProps ) ] then
local effectdata = EffectData()
effectdata:SetOrigin( trace.HitPos )
effectdata:SetEntity( self )
util.Effect( "lvs_physics_dust", effectdata )
end
end
end
function ENT:GetParticleEmitter( Pos )
local EntTable = self:GetTable()
local T = CurTime()
if IsValid( EntTable.Emitter ) and (EntTable.EmitterTime or 0) > T then
return EntTable.Emitter
end
self:StopEmitter()
EntTable.Emitter = ParticleEmitter( Pos, false )
EntTable.EmitterTime = T + 2
return EntTable.Emitter
end
function ENT:StopEmitter()
if IsValid( self.Emitter ) then
self.Emitter:Finish()
end
end
function ENT:GetParticleEmitter3D( Pos )
local EntTable = self:GetTable()
local T = CurTime()
if IsValid( EntTable.Emitter3D ) and (EntTable.EmitterTime3D or 0) > T then
return EntTable.Emitter3D
end
self:StopEmitter3D()
EntTable.Emitter3D = ParticleEmitter( Pos, true )
EntTable.EmitterTime3D = T + 2
return EntTable.Emitter3D
end
function ENT:StopEmitter3D()
if IsValid( self.Emitter3D ) then
self.Emitter3D:Finish()
end
end

View File

@@ -0,0 +1,253 @@
LVS:AddHudEditor( "VehicleHealth", 10, ScrH() - 85, 220, 75, 220, 75, "VEHICLE HEALTH",
function( self, vehicle, X, Y, W, H, ScrX, ScrY, ply )
if not vehicle.LVSHudPaintVehicleHealth then return end
vehicle:LVSHudPaintVehicleHealth( X, Y, W, H, ScrX, ScrY, ply )
end
)
LVS:AddHudEditor( "VehicleInfo", ScrW() - 460, ScrH() - 85, 220, 75, 220, 75, "VEHICLE INFORMATION",
function( self, vehicle, X, Y, W, H, ScrX, ScrY, ply )
if not vehicle.LVSHudPaintInfoText then return end
vehicle:LVSHudPaintInfoText( X, Y, W, H, ScrX, ScrY, ply )
end
)
function ENT:LVSHudPaintVehicleHealth( X, Y, W, H, ScrX, ScrY, ply )
draw.DrawText( "HEALTH ", "LVS_FONT", X + 102, Y + 35, color_white, TEXT_ALIGN_RIGHT )
draw.DrawText( math.Round( self:GetHP(), 0 ), "LVS_FONT_HUD_LARGE", X + 102, Y + 20, color_white, TEXT_ALIGN_LEFT )
end
ENT.VehicleIdentifierRange = 10000
function ENT:LVSHudPaintVehicleIdentifier( X, Y, In_Col )
local HP = self:GetHP()
surface.SetDrawColor( In_Col.r, In_Col.g, In_Col.b, In_Col.a )
LVS:DrawDiamond( X + 1, Y + 1, 20, HP / self:GetMaxHP() )
if self:GetMaxShield() > 0 and HP > 0 then
surface.SetDrawColor( 200, 200, 255, In_Col.a )
LVS:DrawDiamond( X + 1, Y + 1, 24, self:GetShield() / self:GetMaxShield() )
end
end
function ENT:LVSPreHudPaint( X, Y, ply )
return true
end
local zoom = 0
local zoom_mat = Material( "vgui/zoom" )
local zoom_switch = 0
local zoom_blinder = 0
local TargetZoom = 0
ENT.ZoomInSound = "weapons/sniper/sniper_zoomin.wav"
ENT.ZoomOutSound = "weapons/sniper/sniper_zoomout.wav"
function ENT:GetZoom()
return TargetZoom
end
function ENT:PaintZoom( X, Y, ply )
TargetZoom = ply:lvsKeyDown( "ZOOM" ) and 1 or 0
zoom = zoom + (TargetZoom - zoom) * RealFrameTime() * 10
if self.OpticsEnable then
if self:GetOpticsEnabled() then
if zoom_switch ~= TargetZoom then
zoom_switch = TargetZoom
zoom_blinder = 1
if TargetZoom == 1 then
surface.PlaySound( self.ZoomInSound )
else
surface.PlaySound( self.ZoomOutSound )
end
end
zoom_blinder = zoom_blinder - zoom_blinder * RealFrameTime() * 5
surface.SetDrawColor( Color(0,0,0,255 * zoom_blinder) )
surface.DrawRect( 0, 0, X, Y )
self.ZoomFov = self.OpticsFov
else
self.ZoomFov = nil
end
end
X = X * 0.5
Y = Y * 0.5
surface.SetDrawColor( Color(255,255,255,255 * zoom) )
surface.SetMaterial(zoom_mat )
surface.DrawTexturedRectRotated( X + X * 0.5, Y * 0.5, X, Y, 0 )
surface.DrawTexturedRectRotated( X + X * 0.5, Y + Y * 0.5, Y, X, 270 )
surface.DrawTexturedRectRotated( X * 0.5, Y * 0.5, Y, X, 90 )
surface.DrawTexturedRectRotated( X * 0.5, Y + Y * 0.5, X, Y, 180 )
end
function ENT:LVSHudPaint( X, Y, ply )
if not self:LVSPreHudPaint( X, Y, ply ) then return end
self:PaintZoom( X, Y, ply )
end
function ENT:HurtMarker( intensity )
LocalPlayer():EmitSound( "lvs/hit_receive"..math.random(1,2)..".wav", 75, math.random(95,105), 0.25 + intensity * 0.75, CHAN_STATIC )
util.ScreenShake( Vector(0, 0, 0), 25 * intensity, 25 * intensity, 0.5, 1 )
end
function ENT:KillMarker()
self.LastKillMarker = CurTime() + 0.5
LocalPlayer():EmitSound( "lvs/hit_kill.wav", 85, 100, 0.4, CHAN_VOICE )
end
local LastMarker = 0
function ENT:ArmorMarker( IsDamage )
local T = CurTime()
local DontHurtEars = math.Clamp( T - LastMarker, 0, 1 ) ^ 2
LastMarker = T
local ArmorFailed = IsDamage and "takedamage" or "pen"
local Volume = IsDamage and (0.3 * DontHurtEars) or 1
LocalPlayer():EmitSound( "lvs/armor_"..ArmorFailed.."_"..math.random(1,3)..".wav", 85, math.random(95,105), Volume, CHAN_ITEM2 )
end
function ENT:HitMarker()
self.LastHitMarker = CurTime() + 0.15
LocalPlayer():EmitSound( "lvs/hit.wav", 85, math.random(95,105), 0.4, CHAN_ITEM )
end
function ENT:CritMarker()
self.LastCritMarker = CurTime() + 0.15
LocalPlayer():EmitSound( "lvs/hit_crit.wav", 85, math.random(95,105), 0.4, CHAN_ITEM2 )
end
function ENT:GetHitMarker()
return self.LastHitMarker or 0
end
function ENT:GetCritMarker()
return self.LastCritMarker or 0
end
function ENT:GetKillMarker()
return self.LastKillMarker or 0
end
function ENT:LVSPaintHitMarker( scr )
local T = CurTime()
local aV = math.cos( math.rad( math.max(((self:GetHitMarker() - T) / 0.15) * 360,0) ) )
if aV ~= 1 then
local Start = 12 + (1 - aV) * 8
local dst = 10
surface.SetDrawColor( 255, 255, 0, 255 )
surface.DrawLine( scr.x + Start, scr.y + Start, scr.x + Start, scr.y + Start - dst )
surface.DrawLine( scr.x + Start, scr.y + Start, scr.x + Start - dst, scr.y + Start )
surface.DrawLine( scr.x + Start, scr.y - Start, scr.x + Start, scr.y - Start + dst )
surface.DrawLine( scr.x + Start, scr.y - Start, scr.x + Start - dst, scr.y - Start )
surface.DrawLine( scr.x - Start, scr.y + Start, scr.x - Start, scr.y + Start - dst )
surface.DrawLine( scr.x - Start, scr.y + Start, scr.x - Start + dst, scr.y + Start )
surface.DrawLine( scr.x - Start, scr.y - Start, scr.x - Start, scr.y - Start + dst )
surface.DrawLine( scr.x - Start, scr.y - Start, scr.x - Start + dst, scr.y - Start )
scr.x = scr.x + 1
scr.y = scr.y + 1
surface.SetDrawColor( 0, 0, 0, 80 )
surface.DrawLine( scr.x + Start, scr.y + Start, scr.x + Start, scr.y + Start - dst )
surface.DrawLine( scr.x + Start, scr.y + Start, scr.x + Start - dst, scr.y + Start )
surface.DrawLine( scr.x + Start, scr.y - Start, scr.x + Start, scr.y - Start + dst )
surface.DrawLine( scr.x + Start, scr.y - Start, scr.x + Start - dst, scr.y - Start )
surface.DrawLine( scr.x - Start, scr.y + Start, scr.x - Start, scr.y + Start - dst )
surface.DrawLine( scr.x - Start, scr.y + Start, scr.x - Start + dst, scr.y + Start )
surface.DrawLine( scr.x - Start, scr.y - Start, scr.x - Start, scr.y - Start + dst )
surface.DrawLine( scr.x - Start, scr.y - Start, scr.x - Start + dst, scr.y - Start )
end
local aV = math.sin( math.rad( math.max(((self:GetCritMarker() - T) / 0.15) * 180,0) ) )
if aV > 0.01 then
local Start = 10 + aV * 40
local End = 20 + aV * 45
surface.SetDrawColor( 255, 100, 0, 255 )
surface.DrawLine( scr.x + Start, scr.y + Start, scr.x + End, scr.y + End )
surface.DrawLine( scr.x - Start, scr.y + Start, scr.x - End, scr.y + End )
surface.DrawLine( scr.x + Start, scr.y - Start, scr.x + End, scr.y - End )
surface.DrawLine( scr.x - Start, scr.y - Start, scr.x - End, scr.y - End )
draw.NoTexture()
surface.DrawTexturedRectRotated( scr.x + Start, scr.y + Start, 3, 20, 45 )
surface.DrawTexturedRectRotated( scr.x - Start, scr.y + Start, 20, 3, 45 )
surface.DrawTexturedRectRotated( scr.x + Start, scr.y - Start, 20, 3, 45 )
surface.DrawTexturedRectRotated( scr.x - Start, scr.y - Start, 3, 20, 45 )
end
local aV = math.sin( math.rad( math.sin( math.rad( math.max(((self:GetKillMarker() - T) / 0.2) * 90,0) ) ) * 90 ) )
if aV > 0.01 then
surface.SetDrawColor( 255, 255, 255, 15 * (aV ^ 4) )
surface.DrawRect( 0, 0, ScrW(), ScrH() )
local Start = 10 + aV * 40
local End = 20 + aV * 45
surface.SetDrawColor( 255, 0, 0, 255 )
surface.DrawLine( scr.x + Start, scr.y + Start, scr.x + End, scr.y + End )
surface.DrawLine( scr.x - Start, scr.y + Start, scr.x - End, scr.y + End )
surface.DrawLine( scr.x + Start, scr.y - Start, scr.x + End, scr.y - End )
surface.DrawLine( scr.x - Start, scr.y - Start, scr.x - End, scr.y - End )
draw.NoTexture()
surface.DrawTexturedRectRotated( scr.x + Start, scr.y + Start, 5, 20, 45 )
surface.DrawTexturedRectRotated( scr.x - Start, scr.y + Start, 20, 5, 45 )
surface.DrawTexturedRectRotated( scr.x + Start, scr.y - Start, 20, 5, 45 )
surface.DrawTexturedRectRotated( scr.x - Start, scr.y - Start, 5, 20, 45 )
end
end
local Circles = {
[1] = {r = -1, col = Color(0,0,0,200)},
[2] = {r = 0, col = Color(255,255,255,200)},
[3] = {r = 1, col = Color(255,255,255,255)},
[4] = {r = 2, col = Color(255,255,255,200)},
[5] = {r = 3, col = Color(0,0,0,200)},
}
function ENT:LVSDrawCircle( X, Y, target_radius, value )
local endang = 360 * value
if endang == 0 then return end
for i = 1, #Circles do
local data = Circles[ i ]
local radius = target_radius + data.r
local segmentdist = endang / ( math.pi * radius / 2 )
for a = 0, endang, segmentdist do
surface.SetDrawColor( data.col )
surface.DrawLine( X - math.sin( math.rad( a ) ) * radius, Y + math.cos( math.rad( a ) ) * radius, X - math.sin( math.rad( a + segmentdist ) ) * radius, Y + math.cos( math.rad( a + segmentdist ) ) * radius )
end
end
end

View File

@@ -0,0 +1,224 @@
include("shared.lua")
include("sh_weapons.lua")
include("sh_velocity_changer.lua")
include("sh_variables.lua")
include("cl_effects.lua")
include("cl_hud.lua")
include("cl_optics.lua")
include("cl_seatswitcher.lua")
include("cl_trailsystem.lua")
include("cl_boneposeparemeter.lua")
local Zoom = 0
function ENT:LVSCalcFov( fov, ply )
local TargetZoom = ply:lvsKeyDown( "ZOOM" ) and 0 or 1
Zoom = Zoom + (TargetZoom - Zoom) * RealFrameTime() * 10
local newfov = fov * Zoom + (self.ZoomFov or 40) * (1 - Zoom)
return newfov
end
function ENT:LVSCalcView( ply, pos, angles, fov, pod )
return LVS:CalcView( self, ply, pos, angles, fov, pod )
end
function ENT:PreDraw( flags )
return true
end
function ENT:PreDrawTranslucent( flags )
return true
end
function ENT:PostDraw( flags )
end
function ENT:PostDrawTranslucent( flags )
end
function ENT:Draw( flags )
if self:PreDraw( flags ) then
if self.lvsLegacyDraw then
self:DrawModel() -- ugly, but required in order to fix old addons. Refract wont work on these.
else
self:DrawModel( flags )
end
end
self:PostDraw( flags )
end
function ENT:DrawTranslucent( flags )
self:DrawTrail()
if self:PreDrawTranslucent( flags ) then
self:DrawModel( flags )
else
self.lvsLegacyDraw = true -- insert puke simley
end
self:PostDrawTranslucent( flags )
end
function ENT:Initialize()
self:OnSpawn()
if not istable( self.GibModels ) then return end
for _, modelName in ipairs( self.GibModels ) do
util.PrecacheModel( modelName )
end
end
function ENT:OnSpawn()
end
function ENT:OnFrameActive()
end
function ENT:OnFrame()
end
function ENT:OnEngineActiveChanged( Active )
end
function ENT:OnActiveChanged( Active )
end
ENT._oldActive = false
ENT._oldEnActive = false
function ENT:HandleActive()
local EntTable = self:GetTable()
local Active = self:GetActive()
local EngineActive = self:GetEngineActive()
local ActiveChanged = false
if EntTable._oldActive ~= Active then
EntTable._oldActive = Active
EntTable:OnActiveChanged( Active )
ActiveChanged = true
end
if EntTable._oldEnActive ~= EngineActive then
EntTable._oldEnActive = EngineActive
self:OnEngineActiveChanged( EngineActive )
ActiveChanged = true
end
if ActiveChanged then
if Active or EngineActive then
self:StartWindSounds()
else
self:StopWindSounds()
end
end
if Active or EngineActive then
self:DoVehicleFX()
end
self:FlyByThink()
return EngineActive
end
function ENT:Think()
if not self:IsInitialized() then return end
if self:HandleActive() then
self:OnFrameActive()
end
self:HandleTrail()
self:OnFrame()
end
function ENT:OnRemove()
self:StopEmitter()
self:StopEmitter3D()
self:StopWindSounds()
self:StopFlyBy()
self:StopDeathSound()
self:OnRemoved()
end
function ENT:OnRemoved()
end
function ENT:CalcDoppler( Ent )
if not IsValid( Ent ) then return 1 end
if Ent:IsPlayer() then
local ViewEnt = Ent:GetViewEntity()
local Vehicle = Ent:lvsGetVehicle()
if IsValid( Vehicle ) then
if Ent == ViewEnt then
Ent = Vehicle
end
else
if IsValid( ViewEnt ) then
Ent = ViewEnt
end
end
end
local sVel = self:GetVelocity()
local oVel = Ent:GetVelocity()
local SubVel = oVel - sVel
local SubPos = self:GetPos() - Ent:GetPos()
local DirPos = SubPos:GetNormalized()
local DirVel = SubVel:GetNormalized()
local A = math.acos( math.Clamp( DirVel:Dot( DirPos ) ,-1,1) )
return (1 + math.cos( A ) * SubVel:Length() / 13503.9)
end
function ENT:GetCrosshairFilterEnts()
if not self:IsInitialized() or not LVS.MapDoneLoading then return { self } end -- wait for the server to be ready
if not istable( self.CrosshairFilterEnts ) then
self.CrosshairFilterEnts = {self}
timer.Simple(1, function()
if not IsValid( self ) then return end
-- lets ask the server to build the filter for us because it has access to constraint.GetAllConstrainedEntities()
net.Start( "lvs_player_request_filter" )
net.WriteEntity( self )
net.SendToServer()
end)
end
return self.CrosshairFilterEnts
end
function ENT:FlyByThink()
end
function ENT:StopFlyBy()
end
function ENT:StopDeathSound()
end
function ENT:OnDestroyed()
end
net.Receive( "lvs_vehicle_destroy", function( len )
local ent = net.ReadEntity()
if not IsValid( ent ) or not isfunction( ent.OnDestroyed ) then return end
ent:OnDestroyed()
end )

View File

@@ -0,0 +1,208 @@
ENT.OpticsFov = 30
ENT.OpticsEnable = false
ENT.OpticsZoomOnly = true
ENT.OpticsFirstPerson = true
ENT.OpticsThirdPerson = true
ENT.OpticsPodIndex = {
[1] = true,
}
ENT.OpticsCrosshairMaterial = Material( "vgui/circle" )
ENT.OpticsCrosshairColor = Color(0,0,0,255)
ENT.OpticsCrosshairSize = 5
function ENT:PaintOpticsCrosshair( Pos2D )
if not Pos2D.visible then return end
local size = self.OpticsCrosshairSize
surface.SetMaterial( self.OpticsCrosshairMaterial )
surface.SetDrawColor( self.OpticsCrosshairColor )
surface.DrawTexturedRect( Pos2D.x - size * 0.5, Pos2D.y - size * 0.5, size, size )
end
function ENT:CalcOpticsCrosshairDot( Pos2D )
self:PaintOpticsCrosshair( Pos2D )
end
function ENT:GetOpticsEnabled()
local EntTable = self:GetTable()
if not EntTable.OpticsEnable then return false end
local ply = LocalPlayer()
if not IsValid( ply ) then return false end
local pod = ply:GetVehicle()
local PodIndex = pod:lvsGetPodIndex()
if pod == self:GetDriverSeat() then
PodIndex = 1
end
if EntTable.OpticsPodIndex[ PodIndex ] then
if pod:GetThirdPersonMode() then
return EntTable.OpticsThirdPerson
else
return EntTable.OpticsFirstPerson
end
end
return false
end
function ENT:UseOptics()
if self.OpticsZoomOnly and self:GetZoom() ~= 1 then return false end
return self:GetOpticsEnabled()
end
function ENT:PaintCrosshairCenter( Pos2D, Col )
if self:UseOptics() then
if self.OpticsScreenCentered then
self:CalcOpticsCrosshairDot( Pos2D )
local ScreenCenter2D = {
x = ScrW() * 0.5,
y = ScrH() * 0.5,
visible = true,
}
self:PaintOptics( ScreenCenter2D, Col, LocalPlayer():GetVehicle():GetNWInt( "pPodIndex", -1 ), 1 )
else
self:PaintOptics( Pos2D, Col, LocalPlayer():GetVehicle():GetNWInt( "pPodIndex", -1 ), 1 )
end
return
end
if not Col then
Col = Color( 255, 255, 255, 255 )
end
local Alpha = Col.a / 255
local Shadow = Color( 0, 0, 0, 80 * Alpha )
surface.DrawCircle( Pos2D.x, Pos2D.y, 4, Shadow )
surface.DrawCircle( Pos2D.x, Pos2D.y, 5, Col )
surface.DrawCircle( Pos2D.x, Pos2D.y, 6, Shadow )
end
function ENT:PaintCrosshairOuter( Pos2D, Col )
if self:UseOptics() then
if self.OpticsScreenCentered then
self:CalcOpticsCrosshairDot( Pos2D )
local ScreenCenter2D = {
x = ScrW() * 0.5,
y = ScrH() * 0.5,
visible = true,
}
self:PaintOptics( ScreenCenter2D, Col, LocalPlayer():GetVehicle():GetNWInt( "pPodIndex", -1 ), 2 )
else
self:PaintOptics( Pos2D, Col, LocalPlayer():GetVehicle():GetNWInt( "pPodIndex", -1 ), 2 )
end
return
end
if not Col then
Col = Color( 255, 255, 255, 255 )
end
local Alpha = Col.a / 255
local Shadow = Color( 0, 0, 0, 80 * Alpha )
surface.DrawCircle( Pos2D.x,Pos2D.y, 17, Shadow )
surface.DrawCircle( Pos2D.x, Pos2D.y, 18, Col )
if LVS.AntiAliasingEnabled then
surface.DrawCircle( Pos2D.x, Pos2D.y, 19, Color( Col.r, Col.g, Col.b, 150 * Alpha ) )
surface.DrawCircle( Pos2D.x, Pos2D.y, 20, Shadow )
else
surface.DrawCircle( Pos2D.x, Pos2D.y, 19, Shadow )
end
end
function ENT:PaintCrosshairSquare( Pos2D, Col )
if self:UseOptics() then
if self.OpticsScreenCentered then
self:CalcOpticsCrosshairDot( Pos2D )
local ScreenCenter2D = {
x = ScrW() * 0.5,
y = ScrH() * 0.5,
visible = true,
}
self:PaintOptics( ScreenCenter2D, Col, LocalPlayer():GetVehicle():GetNWInt( "pPodIndex", -1 ), 3 )
else
self:PaintOptics( Pos2D, Col, LocalPlayer():GetVehicle():GetNWInt( "pPodIndex", -1 ), 3 )
end
return
end
if not Col then
Col = Color( 255, 255, 255, 255 )
end
local X = Pos2D.x + 1
local Y = Pos2D.y + 1
local Size = 20
surface.SetDrawColor( 0, 0, 0, 80 )
surface.DrawLine( X - Size, Y + Size, X - Size * 0.5, Y + Size )
surface.DrawLine( X + Size, Y + Size, X + Size * 0.5, Y + Size )
surface.DrawLine( X - Size, Y + Size, X - Size, Y + Size * 0.5 )
surface.DrawLine( X - Size, Y - Size, X - Size, Y - Size * 0.5 )
surface.DrawLine( X + Size, Y + Size, X + Size, Y + Size * 0.5 )
surface.DrawLine( X + Size, Y - Size, X + Size, Y - Size * 0.5 )
surface.DrawLine( X - Size, Y - Size, X - Size * 0.5, Y - Size )
surface.DrawLine( X + Size, Y - Size, X + Size * 0.5, Y - Size )
if Col then
surface.SetDrawColor( Col.r, Col.g, Col.b, Col.a )
else
surface.SetDrawColor( 255, 255, 255, 255 )
end
X = Pos2D.x
Y = Pos2D.y
surface.DrawLine( X - Size, Y + Size, X - Size * 0.5, Y + Size )
surface.DrawLine( X + Size, Y + Size, X + Size * 0.5, Y + Size )
surface.DrawLine( X - Size, Y + Size, X - Size, Y + Size * 0.5 )
surface.DrawLine( X - Size, Y - Size, X - Size, Y - Size * 0.5 )
surface.DrawLine( X + Size, Y + Size, X + Size, Y + Size * 0.5 )
surface.DrawLine( X + Size, Y - Size, X + Size, Y - Size * 0.5 )
surface.DrawLine( X - Size, Y - Size, X - Size * 0.5, Y - Size )
surface.DrawLine( X + Size, Y - Size, X + Size * 0.5, Y - Size )
end
function ENT:DrawRotatedText( text, x, y, font, color, ang)
render.PushFilterMag( TEXFILTER.ANISOTROPIC )
render.PushFilterMin( TEXFILTER.ANISOTROPIC )
local m = Matrix()
m:Translate( Vector( x, y, 0 ) )
m:Rotate( Angle( 0, ang, 0 ) )
surface.SetFont( font )
local w, h = surface.GetTextSize( text )
m:Translate( -Vector( w / 2, h / 2, 0 ) )
cam.PushModelMatrix( m )
draw.DrawText( text, font, 0, 0, color )
cam.PopModelMatrix()
render.PopFilterMag()
render.PopFilterMin()
end
function ENT:PaintOptics( Pos2D, Col, PodIndex, Type )
end

View File

@@ -0,0 +1,147 @@
ENT.IconVehicleLocked = Material( "lvs/locked.png" )
LVS:AddHudEditor( "SeatSwitcher", ScrW() - 360, 10, 350, 60, 350, 60, "SEAT SWITCHER",
function( self, vehicle, X, Y, W, H, ScrX, ScrY, ply )
if not vehicle.LVSHudPaintSeatSwitcher then return end
vehicle:LVSHudPaintSeatSwitcher( X, Y, W, 30, ScrX, ScrY, ply )
end
)
function ENT:LVSHudPaintSeatSwitcher( X, Y, w, h, ScrX, ScrY, ply )
local pSeats = table.Copy( self:GetPassengerSeats() )
local SeatCount = table.Count( pSeats )
if SeatCount <= 0 then return end
pSeats[0] = self:GetDriverSeat()
draw.NoTexture()
local HasAI = self:GetAI()
local HasAIGunners = self:GetAIGunners()
local MySeat = ply:GetVehicle():lvsGetPodIndex()
local Passengers = {}
for _, player in pairs( player.GetAll() ) do
if player:lvsGetVehicle() == self then
local Pod = player:GetVehicle()
Passengers[ Pod:lvsGetPodIndex() ] = player:GetName()
end
end
if HasAI then
Passengers[1] = "[AI] "..self.PrintName
end
if HasAIGunners then
for _, Pod in pairs( self:GetPassengerSeats() ) do
if IsValid( Pod:GetDriver() ) then continue end
local weapon = Pod:lvsGetWeapon()
if not IsValid( weapon ) then continue end
Passengers[ weapon:GetPodIndex() ] = "[AI] Gunner"
end
end
ply.SwitcherTime = ply.SwitcherTime or 0
ply._lvsoldPassengers = ply._lvsoldPassengers or {}
local Time = CurTime()
for k, v in pairs( Passengers ) do
if ply._lvsoldPassengers[k] ~= v then
ply._lvsoldPassengers[k] = v
ply.SwitcherTime = Time + 2
end
end
for k, v in pairs( ply._lvsoldPassengers ) do
if not Passengers[k] then
ply._lvsoldPassengers[k] = nil
ply.SwitcherTime = Time + 2
end
end
for _, v in pairs( LVS.pSwitchKeysInv ) do
if input.IsKeyDown(v) then
ply.SwitcherTime = Time + 2
end
end
local Hide = ply.SwitcherTime > Time
ply.smHider = ply.smHider and (ply.smHider + ((Hide and 1 or 0) - ply.smHider) * RealFrameTime() * 15) or 0
local Alpha1 = 135 + 110 * ply.smHider
local HiderOffset = 270 * ply.smHider
local xPos = w - 35
local yPos = Y - (SeatCount + 1) * 30 + h + 5
local SwapY = false
local SwapX = false
local xHider = HiderOffset
if X < (ScrX * 0.5 - w * 0.5) then
SwapX = true
xPos = 0
xHider = 0
end
if Y < (ScrY * 0.5 - h * 0.5) then
SwapY = true
yPos = Y - h
end
for _, Pod in pairs( pSeats ) do
if not IsValid( Pod ) then continue end
local I = Pod:lvsGetPodIndex()
if I <= 0 then continue end
if not Hide and I > 10 then continue end
if I == MySeat then
draw.RoundedBox(5, X + xPos - xHider, yPos + I * 30, 35 + HiderOffset, 25, Color(LVS.ThemeColor.r, LVS.ThemeColor.g, LVS.ThemeColor.b,100 + 50 * ply.smHider) )
else
draw.RoundedBox(5, X + xPos - xHider, yPos + I * 30, 35 + HiderOffset, 25, Color(0,0,0,100 + 50 * ply.smHider) )
end
if Hide then
if Passengers[I] then
draw.DrawText( Passengers[I], "LVS_FONT_SWITCHER", X + 40 + xPos - xHider, yPos + I * 30 + 2.5, Color( 255, 255, 255, Alpha1 ), TEXT_ALIGN_LEFT )
else
draw.DrawText( "-", "LVS_FONT_SWITCHER", X + 40 + xPos - xHider, yPos + I * 30 + 2.5, Color( 255, 255, 255, Alpha1 ), TEXT_ALIGN_LEFT )
end
draw.DrawText( "["..I.."]", "LVS_FONT_SWITCHER", X + 17 + xPos - xHider, yPos + I * 30 + 2.5, Color( 255, 255, 255, Alpha1 ), TEXT_ALIGN_CENTER )
else
if Passengers[I] then
draw.DrawText( "[^"..I.."]", "LVS_FONT_SWITCHER", X + 17 + xPos - xHider, yPos + I * 30 + 2.5, Color( 255, 255, 255, Alpha1 ), TEXT_ALIGN_CENTER )
else
draw.DrawText( "["..I.."]", "LVS_FONT_SWITCHER", X + 17 + xPos - xHider, yPos + I * 30 + 2.5, Color( 255, 255, 255, Alpha1 ), TEXT_ALIGN_CENTER )
end
end
if not self:GetlvsLockedStatus() then continue end
local xLocker = SwapX and 35 + HiderOffset or -25 - HiderOffset
if SwapY then
if I == 1 then
surface.SetDrawColor( 255, 255, 255, 255 )
surface.SetMaterial( self.IconVehicleLocked )
surface.DrawTexturedRect( X + xPos + xLocker, yPos + I * 30, 25, 25 )
end
else
if I == SeatCount then
surface.SetDrawColor( 255, 255, 255, 255 )
surface.SetMaterial( self.IconVehicleLocked )
surface.DrawTexturedRect( X + xPos + xLocker, yPos + I * 30, 25, 25 )
end
end
end
end

View File

@@ -0,0 +1,147 @@
ENT.TrailMaterial = Material( "trails/smoke" )
ENT.TrailRed = 255
ENT.TrailGreen = 255
ENT.TrailBlue = 255
ENT.TrailAlpha = 100
function ENT:OnTrail( active, id )
end
function ENT:HandleTrail()
if not self.RegisteredTrailPositions then return end
local FT = RealFrameTime()
local pos = self:GetPos()
local vel = self:GetVelocity()
local vel_length = vel:Length()
for id, data in pairs( self.RegisteredTrailPositions ) do
local cur_pos = self:LocalToWorld( data.pos )
local cur_vel = (cur_pos - data.oldpos) / FT
local cur_velL = math.abs( self:WorldToLocal( pos + cur_vel ).z )
if cur_velL > data.activation_speed and vel_length > data.min_flight_speed then
if not data.id then
data.id = self:StartTrail( data.pos, data.startsize, data.endsize, data.lifetime )
self:OnTrail( true, data.id )
end
else
if data.id then
self:OnTrail( false, data.id )
self:FinishTrail( data.id )
data.id = nil
end
end
data.oldpos = cur_pos
end
end
function ENT:RegisterTrail( Pos, StartSize, EndSize, LifeTime, min_flight_speed, activation_speed )
if not istable( self.RegisteredTrailPositions ) then
self.RegisteredTrailPositions = {}
end
local data = {
pos = Pos,
oldpos = self:LocalToWorld( Pos ),
startsize = StartSize,
endsize = EndSize,
lifetime = LifeTime,
min_flight_speed = min_flight_speed,
activation_speed = activation_speed,
}
table.insert( self.RegisteredTrailPositions, data )
end
function ENT:StartTrail( Pos, StartSize, EndSize, LifeTime )
if not LVS.ShowTraileffects then return end
if not istable( self.TrailActive ) then
self.TrailActive = {}
end
local ID = 1
for _,_ in ipairs( self.TrailActive ) do
ID = ID + 1
end
self.TrailActive[ ID ] = {
lifetime = LifeTime,
start_size = StartSize,
end_size = EndSize,
pos = Pos,
active = true,
positions = {},
}
return ID
end
function ENT:FinishTrail( ID )
self.TrailActive[ ID ].active = false
end
function ENT:DrawTrail()
local EntTable = self:GetTable()
if not EntTable.TrailActive then return end
local Time = CurTime()
EntTable._NextTrail = EntTable._NextTrail or 0
local Set = EntTable._NextTrail < Time
render.SetMaterial( EntTable.TrailMaterial )
for ID, data in pairs( EntTable.TrailActive ) do
for pos_id, pos_data in pairs( data.positions ) do
if Time - pos_data.time > data.lifetime then
data.positions[ pos_id ] = nil
end
end
if Set then
if data.active then
local cur_pos = {
time = Time,
pos = self:LocalToWorld( data.pos ),
}
table.insert( data.positions, cur_pos )
table.sort( data.positions, function( a, b ) return a.time > b.time end )
end
end
local num = #data.positions
if num == 0 then
if not data.active then
EntTable.TrailActive[ ID ] = nil
end
continue
end
render.StartBeam( num )
for _, pos_data in ipairs( data.positions ) do
local Scale = (pos_data.time + data.lifetime - Time) / data.lifetime
local InvScale = 1 - Scale
render.AddBeam( pos_data.pos, data.start_size * Scale + data.end_size * InvScale, pos_data.time * 50, Color( EntTable.TrailRed, EntTable.TrailGreen, EntTable.TrailBlue, EntTable.TrailAlpha * Scale ^ 2 ) )
end
render.EndBeam()
end
if Set then
EntTable._NextTrail = Time + 0.025
end
end

View File

@@ -0,0 +1,509 @@
AddCSLuaFile( "shared.lua" )
AddCSLuaFile( "sh_weapons.lua" )
AddCSLuaFile( "sh_velocity_changer.lua" )
AddCSLuaFile( "sh_variables.lua" )
AddCSLuaFile( "cl_init.lua" )
AddCSLuaFile( "cl_effects.lua" )
AddCSLuaFile( "cl_hud.lua" )
AddCSLuaFile( "cl_optics.lua" )
AddCSLuaFile( "cl_trailsystem.lua" )
AddCSLuaFile( "cl_seatswitcher.lua" )
AddCSLuaFile( "cl_boneposeparemeter.lua" )
include("shared.lua")
include("sh_weapons.lua")
include("sh_velocity_changer.lua")
include("sh_variables.lua")
include("sv_ai.lua")
include("sv_cppi.lua")
include("sv_duping.lua")
include("sv_pod.lua")
include("sv_engine.lua")
include("sv_physics.lua")
include("sv_physics_damagesystem.lua")
include("sv_damagesystem.lua")
include("sv_shieldsystem.lua")
include("sv_doorsystem.lua")
ENT.WaterLevelPreventStart = 1
ENT.WaterLevelAutoStop = 2
ENT.WaterLevelDestroyAI = 2
function ENT:SpawnFunction( ply, tr, ClassName )
if ply:InVehicle() then
local ent = ents.Create( ClassName )
ent:StoreCPPI( ply )
ent:SetPos( ply:GetPos() + Vector(0,0,100 + ent.SpawnNormalOffset) )
ent:SetAngles( Angle(0, ply:EyeAngles().y + ent.SpawnAngleOffset, 0 ) )
ent:Spawn()
ent:Activate()
return ent
else
if not tr.Hit then return end
local ent = ents.Create( ClassName )
ent:StoreCPPI( ply )
ent:SetPos( tr.HitPos + tr.HitNormal * ent.SpawnNormalOffset )
ent:SetAngles( Angle(0, ply:EyeAngles().y + ent.SpawnAngleOffset, 0 ) )
ent:Spawn()
ent:Activate()
return ent
end
end
function ENT:Initialize()
self:SetModel( self.MDL )
self:PhysicsInit( SOLID_VPHYSICS, self.MassCenterOverride )
self:SetMoveType( MOVETYPE_VPHYSICS )
self:SetSolid( SOLID_VPHYSICS )
self:SetUseType( SIMPLE_USE )
self:SetRenderMode( RENDERMODE_NORMAL )
-- this is so vj npcs can still see us
self:AddEFlags( EFL_DONTBLOCKLOS )
-- this is for our npc relationship system to work
self:AddFlags( FL_OBJECT )
local PObj = self:GetPhysicsObject()
if not IsValid( PObj ) then
self:Remove()
print("LVS: missing model. Vehicle terminated.")
return
end
PObj:SetMaterial( "default_silent" )
PObj:EnableMotion( false )
PObj:EnableDrag( false )
timer.Simple(0, function()
if not IsValid( self ) or not IsValid( PObj ) then print("LVS: ERROR couldn't initialize vehicle.") return end
self:PostInitialize( PObj )
end)
if not istable( self.GibModels ) then return end
for _, modelName in ipairs( self.GibModels ) do
util.PrecacheModel( modelName )
end
end
function ENT:PostInitialize( PObj )
local SpawnSuccess, ErrorMsg = pcall( function() self:OnSpawn( PObj ) end )
if not SpawnSuccess then
ErrorNoHalt( "\n[ERROR] "..ErrorMsg.."\n\n" )
end
self:StartMotionController()
self:AutoAI()
self:CallOnRemove( "finish_weapons_on_delete", function( ent )
ent:WeaponsFinish()
for _, pod in pairs( ent:GetPassengerSeats() ) do
if not IsValid( pod ) then continue end
local weapon = pod:lvsGetWeapon()
if not IsValid( weapon ) or not weapon._activeWeapon then continue end
local CurWeapon = self.WEAPONS[ weapon:GetPodIndex() ][ weapon._activeWeapon ]
if not CurWeapon then continue end
if CurWeapon.FinishAttack then
CurWeapon.FinishAttack( weapon )
end
end
ent:WeaponsOnRemove()
end)
self:SetlvsReady( true )
self:GetCrosshairFilterEnts()
self:OnSpawnFinish( PObj )
end
function ENT:OnSpawnFinish( PObj )
if GetConVar( "developer" ):GetInt() ~= 1 then
PObj:EnableMotion( true )
end
self:PhysWake()
end
function ENT:OnSpawn( PObj )
end
function ENT:Think()
self:NextThink( CurTime() )
if not self:IsInitialized() then return true end
self:HandleActive()
self:HandleStart()
self:PhysicsThink()
self:DamageThink()
self:WeaponsThink()
self:ShieldThink()
if self:GetAI() then self:RunAI() end
self:OnTick()
return true
end
function ENT:OnDriverChanged( Old, New, VehicleIsActive )
self:OnPassengerChanged( Old, New, 1 )
end
function ENT:OnPassengerChanged( Old, New, PodIndex )
end
function ENT:OnSwitchSeat( ply, oldpod, newpod )
end
function ENT:OnTick()
end
function ENT:OnRemoved()
end
function ENT:OnRemove()
self:OnRemoved()
end
function ENT:Lock()
for _, Handler in pairs( self:GetDoorHandlers() ) do
if not IsValid( Handler ) then continue end
Handler:Close( ply )
end
if self:GetlvsLockedStatus() then return end
self:SetlvsLockedStatus( true )
self:EmitSound( "doors/latchlocked2.wav" )
end
function ENT:UnLock()
if not self:GetlvsLockedStatus() then return end
self:SetlvsLockedStatus( false )
self:EmitSound( "doors/latchunlocked1.wav" )
end
function ENT:IsUseAllowed( ply )
if not IsValid( ply ) then return false end
if (ply._lvsNextUse or 0) > CurTime() then return false end
if self:GetlvsLockedStatus() or (LVS.TeamPassenger and ((self:GetAITEAM() ~= ply:lvsGetAITeam()) and ply:lvsGetAITeam() ~= 0 and self:GetAITEAM() ~= 0)) then
self:EmitSound( "doors/default_locked.wav" )
return false
end
return true
end
function ENT:Use( ply )
if not self:IsUseAllowed( ply ) then return end
if not istable( self._DoorHandlers ) then
self:SetPassenger( ply )
return
end
if ply:GetMoveType() == MOVETYPE_NOCLIP then
ply._lvsNextExit = CurTime() + 0.5
self:SetPassenger( ply )
return
end
if ply:KeyDown( IN_SPEED ) then return end
local Handler = self:GetDoorHandler( ply )
if not IsValid( Handler ) then
if self:HasDoorSystem() and ply:GetMoveType() == MOVETYPE_WALK then
return
end
self:SetPassenger( ply )
return
end
local Pod = Handler:GetLinkedSeat()
if not IsValid( Pod ) then Handler:Use( ply ) return end
if not Handler:IsOpen() then Handler:Open( ply ) return end
if Handler:IsOpen() then
Handler:Close( ply )
else
Handler:OpenAndClose( ply )
end
if ply:KeyDown( IN_WALK ) then
self:SetPassenger( ply )
return
end
local DriverSeat = self:GetDriverSeat()
if Pod ~= self:GetDriverSeat() then
if IsValid( Pod:GetDriver() ) then
self:SetPassenger( ply )
else
ply:EnterVehicle( Pod )
self:AlignView( ply )
hook.Run( "LVS.UpdateRelationship", self )
end
return
end
if self:GetAI() then
self:SetPassenger( ply )
return
end
if IsValid( Pod:GetDriver() ) then
self:SetPassenger( ply )
return
end
if hook.Run( "LVS.CanPlayerDrive", ply, self ) ~= false then
ply:EnterVehicle( Pod )
self:AlignView( ply )
hook.Run( "LVS.UpdateRelationship", self )
else
hook.Run( "LVS.OnPlayerCannotDrive", ply, self )
end
end
function ENT:OnTakeDamage( dmginfo )
self:CalcShieldDamage( dmginfo )
self:CalcDamage( dmginfo )
self:TakePhysicsDamage( dmginfo )
self:OnAITakeDamage( dmginfo )
self:RemoveAllDecals()
end
function ENT:OnMaintenance()
end
function ENT:UpdateTransmitState()
return TRANSMIT_ALWAYS
end
function ENT:GetMissileOffset()
return self:OBBCenter()
end
function ENT:RebuildCrosshairFilterEnts()
self.CrosshairFilterEnts = nil
local CrosshairFilterEnts = table.Copy( self:GetCrosshairFilterEnts() )
for id, entity in pairs( CrosshairFilterEnts ) do
if not IsValid( entity ) or entity:GetNoDraw() then
CrosshairFilterEnts[ id ] = nil
end
end
net.Start( "lvs_player_request_filter" )
net.WriteEntity( self )
net.WriteTable( CrosshairFilterEnts )
net.Broadcast()
end
function ENT:GetCrosshairFilterEnts()
if not self:IsInitialized() then return { self } end
if not istable( self.CrosshairFilterEnts ) then
self.CrosshairFilterEnts = {}
for _, Entity in pairs( constraint.GetAllConstrainedEntities( self ) ) do
if not IsValid( Entity ) then continue end
table.insert( self.CrosshairFilterEnts , Entity )
end
for _, Parent in pairs( self.CrosshairFilterEnts ) do
for _, Child in pairs( Parent:GetChildren() ) do
if not IsValid( Child ) then continue end
table.insert( self.CrosshairFilterEnts , Child )
end
end
end
return self.CrosshairFilterEnts
end
function ENT:LVSFireBullet( data )
data.Entity = self
data.Velocity = data.Velocity + self:GetVelocity():Length()
data.SrcEntity = self:WorldToLocal( data.Src )
LVS:FireBullet( data )
end
function ENT:AddSoundEmitter( pos, snd, snd_interior )
local Emitter = ents.Create( "lvs_soundemitter" )
if not IsValid( Emitter ) then
self:Remove()
print("LVS: Failed to create sound emitter entity. Vehicle terminated.")
return
end
Emitter:SetPos( self:LocalToWorld( pos ) )
Emitter:SetAngles( self:GetAngles() )
Emitter:Spawn()
Emitter:Activate()
Emitter:SetParent( self )
Emitter:SetBase( self )
if snd and not snd_interior then
Emitter:SetSound( snd )
Emitter:SetSoundInterior( snd )
else
Emitter:SetSound( snd or "" )
Emitter:SetSoundInterior( snd_interior )
end
self:DeleteOnRemove( Emitter )
self:TransferCPPI( Emitter )
return Emitter
end
function ENT:AddFlameEmitter( target, attachment )
if not IsValid( target ) then return end
local FlameEmitter = ents.Create( "lvs_firestreamemitter" )
FlameEmitter:AttachTo( target, attachment )
FlameEmitter:Spawn()
FlameEmitter:Activate()
return FlameEmitter
end
function ENT:AddAmmoRack( pos, fxpos, ang, mins, maxs, target )
local AmmoRack = ents.Create( "lvs_wheeldrive_ammorack" )
if not IsValid( AmmoRack ) then
self:Remove()
print("LVS: Failed to create fueltank entity. Vehicle terminated.")
return
end
if not target then target = self end
AmmoRack:SetPos( target:LocalToWorld( pos ) )
AmmoRack:SetAngles( target:GetAngles() )
AmmoRack:Spawn()
AmmoRack:Activate()
AmmoRack:SetParent( target )
AmmoRack:SetBase( self )
AmmoRack:SetEffectPosition( fxpos )
self:DeleteOnRemove( AmmoRack )
self:TransferCPPI( AmmoRack )
mins = mins or Vector(-30,-30,-30)
maxs = maxs or Vector(30,30,30)
debugoverlay.BoxAngles( target:LocalToWorld( pos ), mins, maxs, target:LocalToWorldAngles( ang ), 15, Color( 255, 0, 0, 255 ) )
self:AddDS( {
pos = pos,
ang = ang,
mins = mins,
maxs = maxs,
entity = target,
Callback = function( tbl, ent, dmginfo )
if not IsValid( AmmoRack ) then return end
AmmoRack:TakeTransmittedDamage( dmginfo )
if AmmoRack:GetDestroyed() then return end
local OriginalDamage = dmginfo:GetDamage()
dmginfo:SetDamage( math.min( 2, OriginalDamage ) )
end
} )
return AmmoRack
end
function ENT:AddTrailerHitch( pos, hitchtype )
if not hitchtype then
hitchtype = LVS.HITCHTYPE_MALE
end
local TrailerHitch = ents.Create( "lvs_wheeldrive_trailerhitch" )
if not IsValid( TrailerHitch ) then
self:Remove()
print("LVS: Failed to create trailerhitch entity. Vehicle terminated.")
return
end
TrailerHitch:SetPos( self:LocalToWorld( pos ) )
TrailerHitch:SetAngles( self:GetAngles() )
TrailerHitch:Spawn()
TrailerHitch:Activate()
TrailerHitch:SetParent( self )
TrailerHitch:SetBase( self )
TrailerHitch:SetHitchType( hitchtype )
self:TransferCPPI( TrailerHitch )
return TrailerHitch
end
function ENT:OnCoupleChanged( targetVehicle, targetHitch, active )
end
function ENT:OnStartDrag( caller, activator )
end
function ENT:OnStopDrag( caller, activator )
end

View File

@@ -0,0 +1,40 @@
function ENT:HasQuickVar( name )
name = "_smValue"..name
return self[ name ] ~= nil
end
function ENT:GetQuickVar( name )
name = "_smValue"..name
if not self[ name ] then return 0 end
return self[ name ]
end
if CLIENT then
function ENT:QuickLerp( name, target, rate )
name = "_smValue"..name
local EntTable = self:GetTable()
if not EntTable[ name ] then EntTable[ name ] = 0 end
EntTable[ name ] = EntTable[ name ] + (target - EntTable[ name ]) * math.min( RealFrameTime() * (rate or 10), 1 )
return EntTable[ name ]
end
return
end
function ENT:QuickLerp( name, target, rate )
name = "_smValue"..name
if not self[ name ] then self[ name ] = 0 end
self[ name ] = self[ name ] + (target - self[ name ]) * math.min( FrameTime() * (rate or 10), 1 )
return self[ name ]
end

View File

@@ -0,0 +1,69 @@
if CLIENT then
hook.Add( "InitPostEntity", "!!!lvsUpdateMaxVelocity", function()
net.Start( "lvs_maxvelocity_updater" )
net.SendToServer()
hook.Remove( "InitPostEntity", "!!!lvsUpdateMaxVelocity" )
end )
net.Receive( "lvs_maxvelocity_updater", function( len, ply )
for _, data in pairs( net.ReadTable() ) do
local entity = data.ent
if not IsValid( entity ) or not entity.LVS or not entity.MaxVelocity then continue end
entity.MaxVelocity = data.vel
end
end )
return
end
util.AddNetworkString( "lvs_maxvelocity_updater" )
local UpdatedVehicles = {}
net.Receive( "lvs_maxvelocity_updater", function( len, ply )
if ply._lvsAlreadyAskedForVelocityUpdate then return end
ply._lvsAlreadyAskedForVelocityUpdate = true
local TableSend = {}
for id, data in pairs( UpdatedVehicles ) do
local entity = data.ent
if IsValid( entity ) and entity.LVS and isfunction( entity.ChangeVelocity ) then
table.insert( TableSend, data )
continue
end
UpdatedVehicles[ id ] = nil
end
if #TableSend == 0 then return end
net.Start( "lvs_maxvelocity_updater" )
net.WriteTable( TableSend )
net.Send( ply )
end )
function ENT:ChangeVelocity( new )
new = math.Clamp( new, 1, physenv.GetPerformanceSettings().MaxVelocity )
self.MaxVelocity = new
timer.Simple(0, function()
if not IsValid( self ) then return end
local data = { ent = self, vel = new }
table.insert( UpdatedVehicles, data )
net.Start( "lvs_maxvelocity_updater" )
net.WriteTable( {data} )
net.Broadcast()
end)
end

View File

@@ -0,0 +1,663 @@
ENT.WEAPONS = {
[1] = {},
}
function ENT:InitWeapons()
end
function ENT:AddWeapon( weaponData, PodID )
if not istable( weaponData ) then print("[LVS] couldn't register weapon") return end
local data = table.Copy( weaponData )
if not PodID or PodID <= 1 then
PodID = 1
end
if not self.WEAPONS[ PodID ] then
self.WEAPONS[ PodID ] = {}
end
local default = LVS:GetWeaponPreset( "DEFAULT" )
data.Icon = data.Icon or Material("lvs/weapons/bullet.png")
data.Ammo = data.Ammo or -1
data.Delay = data.Delay or FrameTime()
if isnumber( data.Clip ) and data.Clip > 0 then
data.HeatIsClip = true
local ShootDelay = data.Delay
local Clip = data.Clip
local ReloadSpeed = data.ReloadSpeed or 2
data.HeatRateUp = 1.00001 / (ShootDelay * Clip)
data.HeatRateDown = 1 / ReloadSpeed
data.OnReload = data.OnReload or default.OnReload
else
data.HeatRateUp = data.HeatRateUp or default.HeatRateUp
data.HeatRateDown = data.HeatRateDown or default.HeatRateDown
end
data.Attack = data.Attack or default.Attack
data.StartAttack = data.StartAttack or default.StartAttack
data.FinishAttack = data.FinishAttack or default.FinishAttack
data.OnSelect = data.OnSelect or default.OnSelect
data.OnDeselect = data.OnDeselect or default.OnDeselect
data.OnThink = data.OnThink or default.OnThink
data.OnOverheat = data.OnOverheat or default.OnOverheat
data.OnRemove = data.OnRemove or default.OnRemove
data.UseableByAI = data.UseableByAI ~= false
table.insert( self.WEAPONS[ PodID ], data )
end
function ENT:UpdateWeapon( PodID, WeaponID, weaponData )
if not self.WEAPONS[ PodID ] then return end
if not self.WEAPONS[ PodID ][ WeaponID ] then return end
table.Merge( self.WEAPONS[ PodID ][ WeaponID ], weaponData )
end
function ENT:HasWeapon( ID )
return istable( self.WEAPONS[1][ ID ] )
end
function ENT:AIHasWeapon( ID )
local weapon = self.WEAPONS[1][ ID ]
if not istable( weapon ) then return false end
return weapon.UseableByAI
end
function ENT:GetActiveWeapon()
local SelectedID = self:GetSelectedWeapon()
local CurWeapon = self.WEAPONS[1][ SelectedID ]
return CurWeapon, SelectedID
end
function ENT:GetMaxAmmo()
local CurWeapon = self:GetActiveWeapon()
if not CurWeapon then return -1 end
return CurWeapon.Ammo or -1
end
function ENT:GetClip()
local CurWeapon = self:GetActiveWeapon()
if not CurWeapon then return 0 end
local HeatIncrement = (CurWeapon.HeatRateUp or 0.2) * math.max(CurWeapon.Delay or 0, FrameTime())
local Ammo = self:GetNWAmmo()
if self:GetMaxAmmo() <= 0 and CurWeapon.Clip then
Ammo = CurWeapon.Clip
end
return math.min( math.ceil( math.Round( (1 - self:GetNWHeat()) / HeatIncrement, 1 ) ), Ammo )
end
if SERVER then
function ENT:WeaponRestoreAmmo()
local AmmoIsSet = false
for PodID, data in pairs( self.WEAPONS ) do
for id, weapon in pairs( data ) do
local MaxAmmo = weapon.Ammo or -1
local CurAmmo = weapon._CurAmmo or -1
if CurAmmo == MaxAmmo then continue end
self.WEAPONS[PodID][ id ]._CurAmmo = MaxAmmo
AmmoIsSet = true
end
end
if AmmoIsSet then
self:SetNWAmmo( self:GetAmmo() )
for _, pod in pairs( self:GetPassengerSeats() ) do
local weapon = pod:lvsGetWeapon()
if not IsValid( weapon ) then continue end
weapon:SetNWAmmo( weapon:GetAmmo() )
end
end
return AmmoIsSet
end
function ENT:WeaponsOnRemove()
for _, data in pairs( self.WEAPONS ) do
for ID, Weapon in pairs( data ) do
if not Weapon.OnRemove then continue end
Weapon.OnRemove( self )
end
end
end
function ENT:WeaponsFinish()
if not self._activeWeapon then return end
local CurWeapon = self.WEAPONS[1][ self._activeWeapon ]
if not CurWeapon then return end
if CurWeapon.FinishAttack then
CurWeapon.FinishAttack( self )
end
self._activeWeapon = nil
self.OldAttack = false
end
function ENT:GetAmmo()
if self:GetAI() then return self:GetMaxAmmo() end
local CurWeapon = self:GetActiveWeapon()
if not CurWeapon then return -1 end
return CurWeapon._CurAmmo or self:GetMaxAmmo()
end
function ENT:TakeAmmo( num )
if self:GetMaxAmmo() <= 0 then return end
local CurWeapon = self:GetActiveWeapon()
CurWeapon._CurAmmo = math.max( self:GetAmmo() - (num or 1), 0 )
self:SetNWAmmo( CurWeapon._CurAmmo )
end
function ENT:GetHeat( weaponid )
local CurWeapon
if isnumber( weaponid ) and weaponid > 0 then
CurWeapon = self.WEAPONS[1][ weaponid ]
else
CurWeapon = self:GetActiveWeapon()
end
if not CurWeapon then return 0 end
return (CurWeapon._CurHeat or 0)
end
function ENT:GetOverheated()
local CurWeapon = self:GetActiveWeapon()
if not CurWeapon then return false end
return CurWeapon.Overheated == true
end
function ENT:SetOverheated( overheat )
if self:GetOverheated() == overheat then return end
local CurWeapon = self:GetActiveWeapon()
if not CurWeapon then return end
CurWeapon.Overheated = overheat
self:SetNWOverheated( overheat )
if self:GetHeat() == 0 then return end
if CurWeapon.OnOverheat then
CurWeapon.OnOverheat( self )
end
end
function ENT:SetHeat( heat )
local CurWeapon = self:GetActiveWeapon()
if not CurWeapon then return end
heat = math.Clamp( heat, 0, 1 )
CurWeapon._CurHeat = heat
if self:GetNWHeat() == heat then return end
self:SetNWHeat( heat )
end
function ENT:CanAttack()
local CurWeapon = self:GetActiveWeapon()
return (CurWeapon._NextFire or 0) < CurTime()
end
function ENT:SetNextAttack( time )
local CurWeapon = self:GetActiveWeapon()
CurWeapon._NextFire = time
end
function ENT:WeaponsShouldFire()
if self:GetAI() then return self._AIFireInput end
local ply = self:GetDriver()
if not IsValid( ply ) then return false end
return ply:lvsKeyDown( "ATTACK" )
end
function ENT:WeaponsThink()
local EntTable = self:GetTable()
local T = CurTime()
local FT = FrameTime()
local CurWeapon, SelectedID = self:GetActiveWeapon()
for ID, Weapon in pairs( EntTable.WEAPONS[1] ) do
local IsActive = ID == SelectedID
if Weapon.OnThink then Weapon.OnThink( self, IsActive ) end
if IsActive then continue end
if Weapon.HeatIsClip and not Weapon.Overheated and Weapon._CurHeat ~= 0 then
Weapon.Overheated = true
Weapon._CurHeat = 1
if Weapon.OnReload then Weapon.OnReload( self ) end
end
-- cool all inactive weapons down
Weapon._CurHeat = Weapon._CurHeat and Weapon._CurHeat - math.min( Weapon._CurHeat, (Weapon.HeatRateDown or 0.25) * FT ) or 0
end
if not CurWeapon then return end
local ShouldFire = self:WeaponsShouldFire()
local CurHeat = self:GetHeat()
if self:GetOverheated() then
if CurHeat <= 0 then
self:SetOverheated( false )
else
ShouldFire = false
end
else
if CurHeat >= 1 then
self:SetOverheated( true )
ShouldFire = false
if CurWeapon.OnReload then CurWeapon.OnReload( self ) end
end
end
if self:GetMaxAmmo() > 0 then
if self:GetAmmo() <= 0 then
ShouldFire = false
end
end
if ShouldFire ~= EntTable.OldAttack then
EntTable.OldAttack = ShouldFire
if ShouldFire then
if CurWeapon.StartAttack then
CurWeapon.StartAttack( self )
end
EntTable._activeWeapon = SelectedID
else
self:WeaponsFinish()
end
end
if ShouldFire then
if not self:CanAttack() then return end
local ShootDelay = (CurWeapon.Delay or 0)
self:SetNextAttack( T + ShootDelay )
self:SetHeat( CurHeat + (CurWeapon.HeatRateUp or 0.2) * math.max(ShootDelay, FT) )
if not CurWeapon.Attack then return end
if CurWeapon.Attack( self ) then
self:SetHeat( CurHeat - math.min( self:GetHeat(), (CurWeapon.HeatRateDown or 0.25) * FT ) )
self:SetNextAttack( T )
end
EntTable._lvsNextActiveWeaponCoolDown = T + 0.25
else
if (EntTable._lvsNextActiveWeaponCoolDown or 0) > T then return end
if CurWeapon.HeatIsClip and not CurWeapon.Overheated then
self:SetHeat( self:GetHeat() )
return
end
self:SetHeat( self:GetHeat() - math.min( self:GetHeat(), (CurWeapon.HeatRateDown or 0.25) * FT ) )
end
end
function ENT:SelectWeapon( ID )
if not isnumber( ID ) then return end
if self.WEAPONS[1][ ID ] then
self:SetSelectedWeapon( ID )
end
local ply = self:GetDriver()
if not IsValid( ply ) then return end
net.Start( "lvs_select_weapon" )
net.Send( ply )
end
function ENT:OnWeaponChanged( name, old, new)
if new == old then return end
self:WeaponsFinish()
local PrevWeapon = self.WEAPONS[1][ old ]
if PrevWeapon and PrevWeapon.OnDeselect then
PrevWeapon.OnDeselect( self, new )
end
local NextWeapon = self.WEAPONS[1][ new ]
if NextWeapon and NextWeapon.OnSelect then
NextWeapon.OnSelect( self, old )
self:SetNWAmmo( NextWeapon._CurAmmo or NextWeapon.Ammo or -1 )
self:SetNWOverheated( NextWeapon.Overheated == true )
end
end
return
end
function ENT:DrawWeaponIcon( PodID, ID, x, y, width, height, IsSelected, IconColor )
end
function ENT:SelectWeapon( ID )
if not isnumber( ID ) then return end
net.Start( "lvs_select_weapon" )
net.WriteInt( ID, 5 )
net.WriteBool( false )
net.SendToServer()
end
function ENT:NextWeapon()
net.Start( "lvs_select_weapon" )
net.WriteInt( 1, 5 )
net.WriteBool( true )
net.SendToServer()
end
function ENT:PrevWeapon()
net.Start( "lvs_select_weapon" )
net.WriteInt( -1, 5 )
net.WriteBool( true )
net.SendToServer()
end
LVS:AddHudEditor( "WeaponSwitcher", ScrW() - 210, ScrH() - 165, 200, 68, 200, 68, "WEAPON SELECTOR",
function( self, vehicle, X, Y, W, H, ScrX, ScrY, ply )
if not vehicle.LVSHudPaintWeapons then return end
vehicle:LVSHudPaintWeapons( X, Y, W, H, ScrX, ScrY, ply )
end
)
LVS:AddHudEditor( "WeaponInfo", ScrW() - 230, ScrH() - 85, 220, 75, 220, 75, "WEAPON INFO",
function( self, vehicle, X, Y, W, H, ScrX, ScrY, ply )
if not vehicle.LVSHudPaintWeaponInfo then return end
vehicle:LVSHudPaintWeaponInfo( X, Y, W, H, ScrX, ScrY, ply )
end
)
function ENT:GetAmmoID( ID )
local ply = LocalPlayer()
if not IsValid( ply ) then return end
local Base = ply:lvsGetWeaponHandler()
if not IsValid( Base ) then return -1 end
local selected = Base:GetSelectedWeapon()
local weapon = self.WEAPONS[ Base:GetPodIndex() ][ ID ]
if ID == selected then
weapon._CurAmmo = Base:GetNWAmmo()
else
weapon._CurAmmo = weapon._CurAmmo or weapon.Ammo or -1
end
return weapon._CurAmmo
end
local Circles = {
[1] = {r = -1, col = Color(0,0,0,200)},
[2] = {r = 0, col = Color(255,255,255,200)},
[3] = {r = 1, col = Color(255,255,255,255)},
[4] = {r = 2, col = Color(255,255,255,200)},
[5] = {r = 3, col = Color(0,0,0,200)},
}
local function DrawCircle( X, Y, target_radius, heatvalue, overheated )
local endang = 360 * heatvalue
if endang == 0 then return end
for i = 1, #Circles do
local data = Circles[ i ]
local radius = target_radius + data.r
local segmentdist = endang / ( math.pi * radius / 2 )
for a = 0, endang, segmentdist do
local r = data.col.r
local g = data.col.g * (1 - math.min(a / 270,1))
local b = data.col.b * (1 - math.min(a / 90,1))
surface.SetDrawColor( r, g, b, data.col.a )
surface.DrawLine( X - math.sin( math.rad( a ) ) * radius, Y + math.cos( math.rad( a ) ) * radius, X - math.sin( math.rad( a + segmentdist ) ) * radius, Y + math.cos( math.rad( a + segmentdist ) ) * radius )
end
end
end
ENT.HeatMat = Material( "lvs/heat.png" )
ENT.HeatIsClipMat = Material( "lvs/3d2dmats/refil.png" )
local color_white = color_white
local color_red = Color(255,0,0,255)
function ENT:LVSHudPaintWeaponInfo( X, Y, w, h, ScrX, ScrY, ply )
local Base = ply:lvsGetWeaponHandler()
if not IsValid( Base ) then return end
local ID = Base:GetSelectedWeapon()
if not Base:HasWeapon( ID ) then return end
local Weapon = Base:GetActiveWeapon()
local Heat = Base:GetNWHeat()
local OverHeated = Base:GetNWOverheated()
local Ammo = Base:GetNWAmmo()
if Weapon and Weapon.HeatIsClip then
local Pod = ply:GetVehicle()
if not IsValid( Pod ) then return end
local PodID = Base:GetPodIndex()
local FT = FrameTime()
local ShootDelay = math.max(Weapon.Delay or 0, FT)
local HeatIncrement = (Weapon.HeatRateUp or 0.2) * ShootDelay
local Clip = Base:GetClip()
if OverHeated then
Clip = 0
local hX = X + w - h * 0.5
local hY = Y + h * 0.25 + h * 0.25
surface.SetMaterial( self.HeatIsClipMat )
surface.SetDrawColor( 0, 0, 0, 200 )
surface.DrawTexturedRectRotated( hX + 3, hY + 1, h, h, 0 )
surface.SetDrawColor( 255, 0, 0, 255 )
surface.DrawTexturedRectRotated( hX + 1, hY - 1, h, h, 0 )
DrawCircle( hX, hY, h * 0.35, Heat )
end
Ammo = Ammo - Clip
local ColDyn = (Clip == 0 or OverHeated) and color_red or color_white
draw.DrawText( "AMMO ", "LVS_FONT", X + 72, Y + 35, ColDyn, TEXT_ALIGN_RIGHT )
draw.DrawText( Clip, "LVS_FONT_HUD_LARGE", X + 72, Y + 20, ColDyn, TEXT_ALIGN_LEFT )
if Base:GetMaxAmmo() <= 0 then return end
local ColDyn2 = Ammo <= Weapon.Clip and color_red or color_white
X = X + math.max( (#string.Explode( "", Clip ) - 1) * 18, 0 )
draw.DrawText( "/", "LVS_FONT_HUD_LARGE", X + 96, Y + 30, ColDyn2, TEXT_ALIGN_LEFT )
draw.DrawText( Ammo, "LVS_FONT", X + 110, Y + 40, ColDyn2, TEXT_ALIGN_LEFT )
return
end
local hX = X + w - h * 0.5
local hY = Y + h * 0.25 + h * 0.25
local hAng = math.cos( CurTime() * 50 ) * 5 * (OverHeated and 1 or Heat ^ 2)
surface.SetMaterial( self.HeatMat )
surface.SetDrawColor( 0, 0, 0, 200 )
surface.DrawTexturedRectRotated( hX + 4, hY + 1, h * 0.5, h * 0.5, hAng )
if OverHeated then
surface.SetDrawColor( 255, 0, 0, 255 )
else
surface.SetDrawColor( 255, 255 * (1 - Heat), 255 * math.max(1 - Heat * 1.5,0), 255 )
end
surface.DrawTexturedRectRotated( hX + 2, hY - 1, h * 0.5, h * 0.5, hAng )
DrawCircle( hX, hY, h * 0.35, Heat )
if Base:GetMaxAmmo() <= 0 then return end
draw.DrawText( "AMMO ", "LVS_FONT", X + 72, Y + 35, color_white, TEXT_ALIGN_RIGHT )
draw.DrawText( Ammo, "LVS_FONT_HUD_LARGE", X + 72, Y + 20, color_white, TEXT_ALIGN_LEFT )
end
function ENT:LVSHudPaintWeapons( X, Y, w, h, ScrX, ScrY, ply )
local EntTable = self:GetTable()
local Base = ply:lvsGetWeaponHandler()
if not IsValid( Base ) then return end
local Pod = ply:GetVehicle()
if not IsValid( Pod ) then return end
local PodID = Base:GetPodIndex()
local num = #self.WEAPONS[ PodID ]
if num <= 1 then return end
local CenterY = (Y + h * 0.5)
local CenterX = (X + w * 0.5)
local FlatSelector = CenterX > ScrX * 0.333 and CenterX < ScrX * 0.666
local T = CurTime()
local FT = RealFrameTime()
local gap = 4
local SizeY = h - gap
local Selected = Base:GetSelectedWeapon()
if Selected ~= EntTable._OldSelected then
EntTable._OldSelected = Selected
Pod._SelectActiveTime = T + 2
end
local tAlpha = (Pod._SelectActiveTime or 0) > T and 1 or 0
local tAlphaRate = FT * 15
EntTable.smAlphaSW = EntTable.smAlphaSW and (EntTable.smAlphaSW + math.Clamp(tAlpha - EntTable.smAlphaSW,-tAlphaRate,tAlphaRate)) or 0
if EntTable.smAlphaSW > 0.95 then
EntTable._DisplaySelected = Selected
else
EntTable._DisplaySelected = EntTable._DisplaySelected or Selected
end
local A255 = 255 * EntTable.smAlphaSW
local A150 = 150 * EntTable.smAlphaSW
local Col = Color(0,0,0,A150)
local ColSelect = Color(255,255,255,A150)
local SwapY = 0
if Y < (ScrY * 0.5 - h * 0.5) then
SwapY = 1
end
for ID = 1, num do
local IsSelected = EntTable._DisplaySelected == ID
local n = num - ID
local xPos = FlatSelector and X + (w + gap) * (ID - 1) - ((w + gap) * 0.5 * num - w * 0.5) or X
local yPos = FlatSelector and Y - h * math.min(SwapY,0) or Y - h * n + (num - 1) * h * SwapY
draw.RoundedBox(5, xPos, yPos, w, SizeY, IsSelected and ColSelect or Col )
if IsSelected then
surface.SetDrawColor( 0, 0, 0, A255 )
else
surface.SetDrawColor( 255, 255, 255, A255 )
end
if isbool( EntTable.WEAPONS[PodID][ID].Icon ) then
local col = IsSelected and Color(255,255,255,A255) or Color(0,0,0,A255)
self:DrawWeaponIcon( PodID, ID, xPos, yPos, SizeY * 2, SizeY, IsSelected, col )
else
surface.SetMaterial( self.WEAPONS[PodID][ID].Icon )
surface.DrawTexturedRect( xPos, yPos, SizeY * 2, SizeY )
end
local ammo = self:GetAmmoID( ID )
if ammo > -1 then
draw.DrawText( ammo, "LVS_FONT_HUD", xPos + w - 10, yPos + SizeY * 0.5 - 10, IsSelected and Color(0,0,0,A255) or Color(255,255,255,A255), TEXT_ALIGN_RIGHT )
else
draw.DrawText( "O", "LVS_FONT_HUD", xPos + w - 19, yPos + SizeY * 0.5 - 10, IsSelected and Color(0,0,0,A255) or Color(255,255,255,A255), TEXT_ALIGN_RIGHT )
draw.DrawText( "O", "LVS_FONT_HUD", xPos + w - 10, yPos + SizeY * 0.5 - 10, IsSelected and Color(0,0,0,A255) or Color(255,255,255,A255), TEXT_ALIGN_RIGHT )
end
end
end

View File

@@ -0,0 +1,352 @@
ENT.Type = "anim"
ENT.PrintName = "LBaseEntity"
ENT.Author = "Luna"
ENT.Information = "Luna's Vehicle Script"
ENT.Category = "[LVS]"
ENT.Spawnable = false
ENT.AdminSpawnable = false
ENT.VJ_ID_Destructible = true
ENT.AutomaticFrameAdvance = true
ENT.RenderGroup = RENDERGROUP_BOTH
ENT.Editable = true
ENT.LVS = true
ENT.MDL = "models/props_c17/trappropeller_engine.mdl"
ENT.AITEAM = 0
ENT.MaxHealth = 100
ENT.MaxShield = 0
ENT.SpawnNormalOffset = 15
ENT.SpawnAngleOffset = 0
ENT.HitGroundLength = 10
ENT.lvsDisableZoom = true
function ENT:AddDT( type, name, data )
if not self.DTlist then self.DTlist = {} end
if self.DTlist[ type ] then
self.DTlist[ type ] = self.DTlist[ type ] + 1
else
self.DTlist[ type ] = 0
end
self:NetworkVar( type, self.DTlist[ type ], name, data )
end
function ENT:CreateBaseDT()
local InitWeaponsSuccess, ErrorMsg = pcall( function() self:InitWeapons() end )
if not InitWeaponsSuccess then
ErrorNoHalt( "\n[ERROR] "..ErrorMsg.."\n\n" )
end
self:AddDT( "Entity", "Driver" )
self:AddDT( "Entity", "DriverSeat" )
self:AddDT( "Bool", "Active" )
self:AddDT( "Bool", "EngineActive" )
self:AddDT( "Bool", "AI", { KeyName = "aicontrolled", Edit = { type = "Boolean", order = 1, category = "AI"} } )
local ShowAIGunnerInMenu = false
if istable( self.WEAPONS ) then
for id, _ in pairs( self.WEAPONS ) do
if id == 1 then continue end
ShowAIGunnerInMenu = true
break
end
end
if ShowAIGunnerInMenu then
self:AddDT( "Bool", "AIGunners", { KeyName = "aigunners", Edit = { type = "Boolean", order = 2, category = "AI"} } )
else
self:AddDT( "Bool", "AIGunners" )
end
self:AddDT( "Bool", "lvsLockedStatus" )
self:AddDT( "Bool", "lvsReady" )
self:AddDT( "Bool", "NWOverheated" )
self:AddDT( "Int", "AITEAM", { KeyName = "aiteam", Edit = { type = "Int", order = 2,min = 0, max = 3, category = "AI"} } )
self:AddDT( "Int", "SelectedWeapon" )
self:AddDT( "Int", "NWAmmo" )
self:AddDT( "Float", "HP", { KeyName = "health", Edit = { type = "Float", order = 2,min = 0, max = self.MaxHealth, category = "Misc"} } )
self:AddDT( "Float", "Shield" )
self:AddDT( "Float", "NWHeat" )
self:TurretSystemDT()
self:OnSetupDataTables()
if SERVER then
self:NetworkVarNotify( "AI", self.OnToggleAI )
self:NetworkVarNotify( "HP", self.PDSHealthValueChanged )
self:NetworkVarNotify( "SelectedWeapon", self.OnWeaponChanged )
self:SetAITEAM( self.AITEAM )
self:SetHP( self.MaxHealth )
self:SetShield( self.MaxShield )
self:SetSelectedWeapon( 1 )
end
end
function ENT:TurretSystemDT()
end
function ENT:SetupDataTables()
self:CreateBaseDT()
end
function ENT:OnSetupDataTables()
end
function ENT:CalcMainActivity( ply )
end
function ENT:GetPlayerBoneManipulation( ply, PodID )
return self.PlayerBoneManipulate[ PodID ] or {}
end
function ENT:UpdateAnimation( ply, velocity, maxseqgroundspeed )
ply:SetPlaybackRate( 1 )
if CLIENT then
GAMEMODE:GrabEarAnimation( ply )
GAMEMODE:MouthMoveAnimation( ply )
end
return false
end
function ENT:StartCommand( ply, cmd )
end
function ENT:HitGround()
local trace = util.TraceLine( {
start = self:LocalToWorld( self:OBBCenter() ),
endpos = self:LocalToWorld( Vector(0,0,self:OBBMins().z - self.HitGroundLength) ),
filter = self:GetCrosshairFilterEnts()
} )
return trace.Hit
end
function ENT:Sign( n )
if n > 0 then return 1 end
if n < 0 then return -1 end
return 0
end
function ENT:VectorSubtractNormal( Normal, Velocity )
local VelForward = Velocity:GetNormalized()
local Ax = math.acos( math.Clamp( Normal:Dot( VelForward ) ,-1,1) )
local Fx = math.cos( Ax ) * Velocity:Length()
local NewVelocity = Velocity - Normal * math.abs( Fx )
return NewVelocity
end
function ENT:VectorSplitNormal( Normal, Velocity )
return math.cos( math.acos( math.Clamp( Normal:Dot( Velocity:GetNormalized() ) ,-1,1) ) ) * Velocity:Length()
end
function ENT:AngleBetweenNormal( Dir1, Dir2 )
return math.deg( math.acos( math.Clamp( Dir1:Dot( Dir2 ) ,-1,1) ) )
end
function ENT:GetMaxShield()
return self.MaxShield
end
function ENT:GetShieldPercent()
return self:GetShield() / self:GetMaxShield()
end
function ENT:GetMaxHP()
return self.MaxHealth
end
function ENT:IsInitialized()
if not self.GetlvsReady then return false end -- in case this is called BEFORE setupdatatables
return self:GetlvsReady()
end
function ENT:GetWeaponHandler( num )
if num == 1 then return self end
local pod = self:GetPassengerSeat( num )
if not IsValid( pod ) then return NULL end
return pod:lvsGetWeapon()
end
function ENT:GetPassengerSeat( num )
if num == 1 then
return self:GetDriverSeat()
else
for _, Pod in pairs( self:GetPassengerSeats() ) do
if not IsValid( Pod ) then continue end
local id = Pod:lvsGetPodIndex()
if id == -1 then continue end
if id == num then
return Pod
end
end
return NULL
end
end
function ENT:GetPassengerSeats()
if not self:IsInitialized() then return {} end
if not istable( self.pSeats ) then
self.pSeats = {}
local DriverSeat = self:GetDriverSeat()
for _, v in pairs( self:GetChildren() ) do
if v ~= DriverSeat and v:GetClass():lower() == "prop_vehicle_prisoner_pod" then
table.insert( self.pSeats, v )
end
end
end
return self.pSeats
end
function ENT:HasActiveSoundEmitters()
local active = false
for _, emitter in ipairs( self:GetChildren() ) do
if emitter:GetClass() ~= "lvs_soundemitter" then continue end
if not IsValid( emitter ) or not emitter.GetActive or not emitter.GetActiveVisible then continue end
if emitter:GetActive() and emitter:GetActiveVisible() then
active = true
break
end
end
return active
end
function ENT:GetPassenger( num )
if num == 1 then
return self:GetDriver()
else
for _, Pod in pairs( self:GetPassengerSeats() ) do
if not IsValid( Pod ) then
return NULL
end
local id = Pod:lvsGetPodIndex()
if id == -1 then continue end
if id == num then
return Pod:GetDriver()
end
end
return NULL
end
end
function ENT:GetEveryone()
local plys = {}
local Pilot = self:GetDriver()
if IsValid( Pilot ) then
table.insert( plys, Pilot )
end
for _, Pod in pairs( self:GetPassengerSeats() ) do
if not IsValid( Pod ) then continue end
local ply = Pod:GetDriver()
if not IsValid( ply ) then continue end
table.insert( plys, ply )
end
return plys
end
function ENT:GetPodIndex()
return 1
end
function ENT:PlayAnimation( animation, playbackrate )
playbackrate = playbackrate or 1
local sequence = self:LookupSequence( animation )
self:ResetSequence( sequence )
self:SetPlaybackRate( playbackrate )
self:SetSequence( sequence )
end
function ENT:GetVehicle()
return self
end
function ENT:GetVehicleType()
return "LBaseEntity"
end
function ENT:GetBoneInfo( BoneName )
local BoneID = self:LookupBone( BoneName )
local numHitBoxSets = self:GetHitboxSetCount()
if not BoneID then
goto SkipLoop
end
for hboxset = 0, numHitBoxSets - 1 do
local numHitBoxes = self:GetHitBoxCount( hboxset )
for hitbox=0, numHitBoxes - 1 do
local bone = self:GetHitBoxBone( hitbox, hboxset )
local name = self:GetBoneName( bone )
if BoneName ~= name then continue end
local mins, maxs = self:GetHitBoxBounds( hitbox, hboxset )
local pos, ang = self:GetBonePosition( BoneID )
return self:WorldToLocal( pos ), self:WorldToLocalAngles( ang ), mins, maxs
end
end
:: SkipLoop ::
return vector_origin, angle_zero, vector_origin, vector_origin
end

View File

@@ -0,0 +1,230 @@
function ENT:RunAI()
end
function ENT:AutoAI()
if not IsValid( self._OwnerEntLVS ) then return end
if self._OwnerEntLVS:InVehicle() then
if self._OwnerEntLVS:IsAdmin() then
self:SetAI( true )
end
end
end
function ENT:OnCreateAI()
end
function ENT:OnRemoveAI()
end
function ENT:OnToggleAI( name, old, new )
if new == old then return end
if not self:IsInitialized() then
timer.Simple( FrameTime(), function()
if not IsValid( self ) then return end
self:OnToggleAI( name, old, new )
end )
return
end
self:SetAIGunners( new )
if new == true then
local Driver = self:GetDriver()
if IsValid( Driver ) then
Driver:ExitVehicle()
end
self:SetActive( true )
self:OnCreateAI()
hook.Run( "LVS.UpdateRelationship", self )
else
self:SetActive( false )
self:OnRemoveAI()
end
end
function ENT:OnAITakeDamage( dmginfo )
end
function ENT:AITargetInFront( ent, range )
if not IsValid( ent ) then return false end
if not range then range = 45 end
if range >= 180 then return true end
local DirToTarget = (ent:GetPos() - self:GetPos()):GetNormalized()
local InFront = math.deg( math.acos( math.Clamp( self:GetForward():Dot( DirToTarget ) ,-1,1) ) ) < range
return InFront
end
function ENT:AICanSee( otherEnt )
if not IsValid( otherEnt ) then return false end
local PhysObj = otherEnt:GetPhysicsObject()
if IsValid( PhysObj ) then
local trace = {
start = self:LocalToWorld( self:OBBCenter() ),
endpos = otherEnt:LocalToWorld( PhysObj:GetMassCenter() ),
filter = self:GetCrosshairFilterEnts(),
}
return util.TraceLine( trace ).Entity == otherEnt
end
local trace = {
start = self:LocalToWorld( self:OBBCenter() ),
endpos = otherEnt:LocalToWorld( otherEnt:OBBCenter() ),
filter = self:GetCrosshairFilterEnts(),
}
return util.TraceLine( trace ).Entity == otherEnt
end
function ENT:AIGetTarget( viewcone )
if (self._lvsNextAICheck or 0) > CurTime() then return self._LastAITarget end
self._lvsNextAICheck = CurTime() + 2
local MyPos = self:GetPos()
local MyTeam = self:GetAITEAM()
if MyTeam == 0 then self._LastAITarget = NULL return NULL end
local ClosestTarget = NULL
local TargetDistance = 60000
if not LVS.IgnorePlayers then
for _, ply in pairs( player.GetAll() ) do
if not ply:Alive() then continue end
if ply:IsFlagSet( FL_NOTARGET ) then continue end
local Dist = (ply:GetPos() - MyPos):Length()
if Dist > TargetDistance then continue end
local Veh = ply:lvsGetVehicle()
if IsValid( Veh ) then
if self:AICanSee( Veh ) and Veh ~= self then
local HisTeam = Veh:GetAITEAM()
if HisTeam == 0 then continue end
if self.AISearchCone then
if not self:AITargetInFront( Veh, self.AISearchCone ) then continue end
end
if HisTeam ~= MyTeam or HisTeam == 3 then
ClosestTarget = Veh
TargetDistance = Dist
end
end
else
local HisTeam = ply:lvsGetAITeam()
if not ply:IsLineOfSightClear( self ) or HisTeam == 0 then continue end
if self.AISearchCone then
if not self:AITargetInFront( ply, self.AISearchCone ) then continue end
end
if HisTeam ~= MyTeam or HisTeam == 3 then
ClosestTarget = ply
TargetDistance = Dist
end
end
end
end
if not LVS.IgnoreNPCs then
for _, npc in pairs( LVS:GetNPCs() ) do
local HisTeam = LVS:GetNPCRelationship( npc:GetClass() )
if HisTeam == 0 or (HisTeam == MyTeam and HisTeam ~= 3) then continue end
local Dist = (npc:GetPos() - MyPos):Length()
if Dist > TargetDistance or not self:AICanSee( npc ) then continue end
if self.AISearchCone then
if not self:AITargetInFront( npc, self.AISearchCone ) then continue end
end
ClosestTarget = npc
TargetDistance = Dist
end
end
for _, veh in pairs( LVS:GetVehicles() ) do
if veh:IsDestroyed() then continue end
if veh == self then continue end
local Dist = (veh:GetPos() - MyPos):Length()
if Dist > TargetDistance or not self:AITargetInFront( veh, (viewcone or 100) ) then continue end
local HisTeam = veh:GetAITEAM()
if HisTeam == 0 then continue end
if HisTeam == self:GetAITEAM() then
if HisTeam ~= 3 then continue end
end
if self.AISearchCone then
if not self:AITargetInFront( veh, self.AISearchCone ) then continue end
end
if self:AICanSee( veh ) then
ClosestTarget = veh
TargetDistance = Dist
end
end
self._LastAITarget = ClosestTarget
return ClosestTarget
end
function ENT:IsEnemy( ent )
if not IsValid( ent ) then return false end
local HisTeam = 0
if ent:IsNPC() then
HisTeam = LVS:GetNPCRelationship( ent:GetClass() )
end
if ent:IsPlayer() then
if ent:IsFlagSet( FL_NOTARGET ) then return false end
local veh = ent:lvsGetVehicle()
if IsValid( veh ) then
HisTeam = veh:GetAITEAM()
else
HisTeam = ent:lvsGetAITeam()
end
end
if ent.LVS and ent.GetAITEAM then
HisTeam = ent:GetAITEAM()
end
if HisTeam == 0 then return false end
if HisTeam == 3 then return true end
return HisTeam ~= self:GetAITEAM()
end

View File

@@ -0,0 +1,18 @@
function ENT:StoreCPPI( owner )
self._OwnerEntLVS = owner
end
function ENT:TransferCPPI( target )
if not IsEntity( target ) or not IsValid( target ) then return end
if not CPPI then return end
local Owner = self._OwnerEntLVS
if not IsEntity( Owner ) then return end
if IsValid( Owner ) then
target:CPPISetOwner( Owner )
end
end

View File

@@ -0,0 +1,426 @@
ENT._armorParts = {}
ENT._dmgParts = {}
ENT.DSArmorDamageReduction = 0.1
ENT.DSArmorDamageReductionType = DMG_BULLET + DMG_CLUB
ENT.DSArmorIgnoreDamageType = DMG_SONIC
ENT.DSArmorIgnoreForce = 0
ENT.DSArmorBulletPenetrationAdd = 250
function ENT:AddDS( data )
if not data then return end
data.pos = data.pos or Vector(0,0,0)
data.ang = data.ang or Angle(0,0,0)
data.mins = data.mins or Vector(-1,-1,-1)
data.maxs = data.maxs or Vector(1,1,1)
data.entity = data.entity or self
data.Callback = data.Callback or function( tbl, ent, dmginfo ) end
debugoverlay.BoxAngles( self:LocalToWorld( data.pos ), data.mins, data.maxs, data.entity:LocalToWorldAngles( data.ang ), 5, Color( 50, 0, 50, 150 ) )
table.insert( self._dmgParts, data )
end
function ENT:AddDSArmor( data )
if not data then return end
data.pos = data.pos or Vector(0,0,0)
data.ang = data.ang or Angle(0,0,0)
data.mins = data.mins or Vector(-1,-1,-1)
data.maxs = data.maxs or Vector(1,1,1)
data.entity = data.entity or self
data.Callback = data.Callback or function( tbl, ent, dmginfo ) end
debugoverlay.BoxAngles( self:LocalToWorld( data.pos ), data.mins, data.maxs, data.entity:LocalToWorldAngles( data.ang ), 5, Color( 0, 50, 50, 150 ) )
table.insert( self._armorParts, data )
end
function ENT:CalcComponentDamage( dmginfo )
local Len = self:BoundingRadius()
local dmgPos = dmginfo:GetDamagePosition()
local dmgDir = dmginfo:GetDamageForce():GetNormalized()
local dmgPenetration = dmgDir * self.DSArmorBulletPenetrationAdd
debugoverlay.Line( dmgPos - dmgDir * self.DSArmorBulletPenetrationAdd, dmgPos + dmgPenetration, 4, Color( 0, 0, 255 ) )
local closestPart
local closestDist = Len * 2
local HitDistance
for index, part in ipairs( self._armorParts ) do
local target = part.entity
if not IsValid( target ) then continue end
local mins = part.mins
local maxs = part.maxs
local pos = target:LocalToWorld( part.pos )
local ang = target:LocalToWorldAngles( part.ang )
local HitPos, HitNormal, Fraction = util.IntersectRayWithOBB( dmgPos, dmgPenetration, pos, ang, mins, maxs )
if HitPos then
debugoverlay.Cross( HitPos, 50, 4, Color( 255, 0, 255 ) )
local dist = (HitPos - dmgPos):Length()
if closestDist > dist then
closestPart = part
closestDist = dist
HitDistance = (HitPos - dmgPos):Length()
end
end
end
local lastPartDS
local closestPartDS
local closestDistDS = Len * 2
for index, part in ipairs( self._dmgParts ) do
local target = part.entity
if not IsValid( target ) then continue end
local mins = part.mins
local maxs = part.maxs
local pos = target:LocalToWorld( part.pos )
local ang = target:LocalToWorldAngles( part.ang )
local HitPos, HitNormal, Fraction = util.IntersectRayWithOBB( dmgPos, dmgPenetration, pos, ang, mins, maxs )
if HitPos and HitDistance then
lastPartDS = part
if HitDistance < (HitPos - dmgPos):Length() then continue end
closestPart = nil
closestDist = Len * 2
end
if not HitPos then continue end
debugoverlay.Cross( HitPos, 50, 4, Color( 255, 0, 255 ) )
local dist = (HitPos - pos):Length()
if closestDistDS > dist then
closestPartDS = part
closestDistDS = dist
end
end
local Hit = false
for index, part in pairs( self._dmgParts ) do
local target = part.entity
if not IsValid( target ) then continue end
local mins = part.mins
local maxs = part.maxs
local pos = target:LocalToWorld( part.pos )
local ang = target:LocalToWorldAngles( part.ang )
if part == closestPartDS then
Hit = true
part:Callback( self, dmginfo )
debugoverlay.BoxAngles( pos, mins, maxs, ang, 1, Color( 255, 0, 0, 150 ) )
end
end
for index, part in pairs( self._armorParts ) do
local target = part.entity
if not IsValid( target ) then continue end
local mins = part.mins
local maxs = part.maxs
local pos = target:LocalToWorld( part.pos )
local ang = target:LocalToWorldAngles( part.ang )
if part == closestPart then
if not part:Callback( self, dmginfo ) then
lastPartDS = nil
end
debugoverlay.BoxAngles( pos, mins, maxs, ang, 1, Color( 0, 150, 0, 150 ) )
end
end
if lastPartDS then
lastPartDS:Callback( self, dmginfo )
local target = lastPartDS.entity
local mins = lastPartDS.mins
local maxs = lastPartDS.maxs
local pos = target:LocalToWorld( lastPartDS.pos )
local ang = target:LocalToWorldAngles( lastPartDS.ang )
debugoverlay.BoxAngles( pos, mins, maxs, ang, 1, Color( 255, 0, 0, 150 ) )
Hit = false
end
return Hit
end
function ENT:CalcDamage( dmginfo )
if dmginfo:IsDamageType( self.DSArmorIgnoreDamageType ) then return end
if dmginfo:IsDamageType( self.DSArmorDamageReductionType ) then
if dmginfo:GetDamage() ~= 0 then
dmginfo:ScaleDamage( self.DSArmorDamageReduction )
dmginfo:SetDamage( math.max(dmginfo:GetDamage(),1) )
end
end
local IsFireDamage = dmginfo:IsDamageType( DMG_BURN )
local IsCollisionDamage = dmginfo:GetDamageType() == (DMG_CRUSH + DMG_VEHICLE)
local CriticalHit = false
if dmginfo:GetDamageForce():Length() < self.DSArmorIgnoreForce and not IsFireDamage then return end
if not IsCollisionDamage then
CriticalHit = self:CalcComponentDamage( dmginfo )
end
local Damage = dmginfo:GetDamage()
if Damage <= 0 then return end
local CurHealth = self:GetHP()
local NewHealth = math.Clamp( CurHealth - Damage, -self:GetMaxHP(), self:GetMaxHP() )
self:SetHP( NewHealth )
if self:IsDestroyed() then return end
local Attacker = dmginfo:GetAttacker()
if IsValid( Attacker ) and Attacker:IsPlayer() and not IsFireDamage then
net.Start( "lvs_hitmarker" )
net.WriteBool( CriticalHit )
net.Send( Attacker )
end
if Damage > 1 and not IsCollisionDamage and not IsFireDamage then
net.Start( "lvs_hurtmarker" )
net.WriteFloat( math.min( Damage / 50, 1 ) )
net.Send( self:GetEveryone() )
end
if NewHealth <= 0 then
self.FinalAttacker = dmginfo:GetAttacker()
self.FinalInflictor = dmginfo:GetInflictor()
self:SetDestroyed( IsCollisionDamage )
self:ClearPDS()
local Attacker = self.FinalAttacker
if IsValid( Attacker ) and Attacker:IsPlayer() then
net.Start( "lvs_killmarker" )
net.Send( Attacker )
end
local ExplodeTime = self:PreExplode( math.Clamp((self:GetVelocity():Length() - 200) / 200,1.5,16) )
timer.Simple( ExplodeTime, function()
if not IsValid( self ) then return end
self:Explode()
end)
end
end
function ENT:PreExplode( ExplodeTime )
self:OnStartExplosion()
local PhysObj = self:GetPhysicsObject()
if not IsValid( PhysObj ) then return 0 end
self:OnStartFireTrail( PhysObj, ExplodeTime )
return ExplodeTime
end
function ENT:FindDS( PosToCheck, RadiusAdd )
if not isnumber( RadiusAdd ) then
RadiusAdd = 1
end
local closestPart
local closestDist = 50000
local ToCenter = (self:LocalToWorld( self:OBBCenter() ) - PosToCheck):GetNormalized()
debugoverlay.Cross( PosToCheck, 50, 4, Color( 255, 255, 0 ) )
for _, tbl in ipairs( { self._armorParts, self._dmgParts } ) do
for index, part in ipairs( tbl ) do
local target = part.entity
if not IsValid( target ) then continue end
local mins = part.mins
local maxs = part.maxs
local pos = target:LocalToWorld( part.pos )
local ang = target:LocalToWorldAngles( part.ang )
local HitPos, HitNormal, Fraction = util.IntersectRayWithOBB( PosToCheck, ToCenter * RadiusAdd, pos, ang, mins, maxs )
if HitPos then
local dist = (HitPos - PosToCheck):Length()
if closestDist > dist then
closestPart = part
closestDist = dist
end
end
end
end
if closestPart then
local target = closestPart.entity
local mins = closestPart.mins
local maxs = closestPart.maxs
local pos = target:LocalToWorld( closestPart.pos )
local ang = target:LocalToWorldAngles( closestPart.ang )
debugoverlay.BoxAngles( pos, mins, maxs, ang, 1, Color( 255, 255, 0, 150 ) )
end
return closestPart
end
function ENT:DamageThink()
local EntTable = self:GetTable()
if EntTable.MarkForDestruction then
self:Explode()
end
if EntTable._pdsPartsAutoProgress then
if self:PDSThink( EntTable._pdsPartsAutoProgress ) then
EntTable._pdsPartsAutoProgress = nil
end
end
if self:IsDestroyed() then
if self:GetVelocity():Length() < 800 then
self:Explode()
end
end
end
function ENT:HurtPlayer( ply, dmg, attacker, inflictor )
if not IsValid( ply ) then return end
if not IsValid( attacker ) then
attacker = game.GetWorld()
end
if not IsValid( inflictor ) then
inflictor = game.GetWorld()
end
local dmginfo = DamageInfo()
dmginfo:SetDamage( dmg )
dmginfo:SetAttacker( attacker )
dmginfo:SetInflictor( inflictor )
dmginfo:SetDamageType( DMG_DIRECT )
ply:TakeDamageInfo( dmginfo )
end
function ENT:Explode()
if self.ExplodedAlready then return end
self.ExplodedAlready = true
local Driver = self:GetDriver()
if IsValid( Driver ) then
self:HurtPlayer( Driver, Driver:Health() + Driver:Armor(), self.FinalAttacker, self.FinalInflictor )
end
if istable( self.pSeats ) then
for _, pSeat in pairs( self.pSeats ) do
if not IsValid( pSeat ) then continue end
local psgr = pSeat:GetDriver()
if not IsValid( psgr ) then continue end
self:HurtPlayer( psgr, psgr:Health() + psgr:Armor(), self.FinalAttacker, self.FinalInflictor )
end
end
self:OnFinishExplosion()
self:Remove()
end
function ENT:OnStartExplosion()
local effectdata = EffectData()
effectdata:SetOrigin( self:GetPos() )
util.Effect( "lvs_explosion_nodebris", effectdata )
end
function ENT:OnFinishExplosion()
local ent = ents.Create( "lvs_destruction" )
if not IsValid( ent ) then return end
ent:SetModel( self:GetModel() )
ent:SetPos( self:GetPos() )
ent:SetAngles( self:GetAngles() )
ent.GibModels = self.GibModels
ent.Vel = self:GetVelocity()
ent:Spawn()
ent:Activate()
end
function ENT:OnStartFireTrail( PhysObj, ExplodeTime )
local effectdata = EffectData()
effectdata:SetOrigin( self:GetPos() )
effectdata:SetStart( PhysObj:GetMassCenter() )
effectdata:SetEntity( self )
effectdata:SetScale( (self.FireTrailScale or 1) )
effectdata:SetMagnitude( ExplodeTime )
util.Effect( "lvs_firetrail", effectdata )
end
function ENT:IsDestroyed()
return self.Destroyed == true
end
function ENT:OnDestroyed()
end
util.AddNetworkString( "lvs_vehicle_destroy" )
function ENT:SetDestroyed( SuppressOnDestroy )
if self.Destroyed then return end
self.Destroyed = true
hook.Run( "LVS.OnVehicleDestroyed", self, self.FinalAttacker, self.FinalInflictor )
hook.Run( "LVS.UpdateRelationship", self )
if SuppressOnDestroy then return end
self:OnDestroyed()
net.Start("lvs_vehicle_destroy")
net.WriteEntity( self )
net.SendPAS( self:GetPos() )
end
include("sv_damagesystem_armor.lua")

View File

@@ -0,0 +1,105 @@
ENT.DSArmorBulletPenetrationType = DMG_AIRBOAT + DMG_SNIPER
function ENT:AddArmor( pos, ang, mins, maxs, health, minforce, target )
local Armor = ents.Create( "lvs_armor" )
if not IsValid( Armor ) then return end
if not target then target = self end
Armor:SetPos( target:LocalToWorld( pos ) )
Armor:SetAngles( target:LocalToWorldAngles( ang ) )
Armor:Spawn()
Armor:Activate()
Armor:SetParent( target )
Armor:SetBase( self )
Armor:SetMaxHP( health )
Armor:SetHP( health )
Armor:SetMins( mins )
Armor:SetMaxs( maxs )
if isnumber( minforce ) then
Armor:SetIgnoreForce( minforce + self.DSArmorIgnoreForce )
else
Armor:SetIgnoreForce( self.DSArmorIgnoreForce )
end
self:DeleteOnRemove( Armor )
self:TransferCPPI( Armor )
self:AddDSArmor( {
pos = pos,
ang = ang,
mins = mins,
maxs = maxs,
entity = target,
Callback = function( tbl, ent, dmginfo )
if not IsValid( Armor ) or not dmginfo:IsDamageType( self.DSArmorBulletPenetrationType + DMG_BLAST ) then return true end
local MaxHealth = self:GetMaxHP()
local MaxArmor = Armor:GetMaxHP()
local Damage = dmginfo:GetDamage()
local ArmoredHealth = MaxHealth + MaxArmor
local NumShotsToKill = ArmoredHealth / Damage
local ScaleDamage = math.Clamp( MaxHealth / (NumShotsToKill * Damage),0,1)
local DidDamage = Armor:TakeTransmittedDamage( dmginfo )
if DidDamage then
if dmginfo:IsDamageType( DMG_PREVENT_PHYSICS_FORCE ) then
dmginfo:ScaleDamage( 0.05 )
end
local Attacker = dmginfo:GetAttacker()
if IsValid( Attacker ) and Attacker:IsPlayer() then
local NonLethal = self:GetHP() > Damage * ScaleDamage
if not ent._preventArmorMarker then
net.Start( "lvs_armormarker" )
net.WriteBool( NonLethal )
net.Send( Attacker )
if not NonLethal then
ent._preventArmorMarker = true
end
end
end
dmginfo:ScaleDamage( ScaleDamage )
else
dmginfo:ScaleDamage( 0 )
end
return true
end
} )
return Armor
end
function ENT:OnArmorMaintenance()
local Repaired = false
for _, part in pairs( self:GetCrosshairFilterEnts() ) do
if not IsValid( part ) then continue end
if part:GetClass() ~= "lvs_armor" then continue end
part:OnRepaired()
if part:GetHP() ~= part:GetMaxHP() then
part:SetHP( part:GetMaxHP() )
if part:GetDestroyed() then part:SetDestroyed( false ) end
Repaired = true
end
end
return Repaired
end

View File

@@ -0,0 +1,108 @@
function ENT:AddDoorHandler( poseparameter, pos, ang, mins, maxs, openmins, openmaxs )
if not isstring( poseparameter ) then return end
if not isvector( pos ) or not isangle( ang ) or not isvector( mins ) or not isvector( maxs ) then
pos = vector_origin
ang = angle_zero
mins = self:OBBMins()
maxs = self:OBBMaxs()
end
if not isvector( openmins ) then
openmins = mins
end
if not isvector( openmaxs ) then
openmaxs = maxs
end
local Handler = ents.Create( "lvs_base_doorhandler" )
if not IsValid( Handler ) then
return
end
Handler:SetPos( self:LocalToWorld( pos ) )
Handler:SetAngles( self:LocalToWorldAngles( ang ) )
Handler:Spawn()
Handler:Activate()
Handler:SetParent( self )
Handler:SetBase( self )
Handler:SetMins( mins )
Handler:SetMinsOpen( openmins )
Handler:SetMinsClosed( mins )
Handler:SetMaxs( maxs )
Handler:SetMaxsOpen( openmaxs )
Handler:SetMaxsClosed( maxs )
Handler:SetPoseName( poseparameter )
self:DeleteOnRemove( Handler )
self:TransferCPPI( Handler )
if not istable( self._DoorHandlers ) then
self._DoorHandlers = {}
end
table.insert( self._DoorHandlers, Handler )
return Handler
end
function ENT:GetDoorHandler( ply )
if not IsValid( ply ) or not istable( self._DoorHandlers ) then return NULL end
local ShootPos = ply:GetShootPos()
local AimVector = ply:GetAimVector()
local radius = 99999999999
local target = NULL
for _, doorHandler in pairs( self._DoorHandlers ) do
if not IsValid( doorHandler ) then continue end
local boxOrigin = doorHandler:GetPos()
local boxAngles = doorHandler:GetAngles()
local boxMins = doorHandler:GetMins()
local boxMaxs = doorHandler:GetMaxs()
local HitPos, _, _ = util.IntersectRayWithOBB( ShootPos, AimVector * doorHandler.UseRange, boxOrigin, boxAngles, boxMins, boxMaxs )
local InRange = isvector( HitPos )
if not InRange then continue end
local dist = (ShootPos - HitPos):Length()
if dist < radius then
target = doorHandler
radius = dist
end
end
return target
end
function ENT:GetDoorHandlers()
if istable( self._DoorHandlers ) then return self._DoorHandlers end
return {}
end
function ENT:HasDoorSystem()
local HasDoorSystem = false
for _, Handler in pairs( self:GetDoorHandlers() ) do
if not IsValid( Handler ) then continue end
if IsValid( Handler:GetLinkedSeat() ) then
HasDoorSystem = true
break
end
end
return HasDoorSystem
end

View File

@@ -0,0 +1,74 @@
-- attempt at fixing dupe support
function ENT:PostEntityPaste( Player, Ent, CreatedEntities )
if isnumber( self._DuplicatorRestoreMaxHealthTo ) then
self.MaxHealth = self._DuplicatorRestoreMaxHealthTo
end
if self.SetlvsReady then
self:SetlvsReady( false )
end
if self.SetActive then
self:SetActive( false )
end
if self.SetEngineActive then
self:SetEngineActive( false )
end
if not self.SetAI then return end
if IsValid( Player ) and Player:IsAdmin() then return end
self:SetAI( false )
end
function ENT:OnEntityCopyTableFinish( data )
data.CrosshairFilterEnts = nil
data.pPodKeyIndex = nil
data.pSeats = nil
data.WEAPONS = nil
-- everything with "_" at the start, usually temporary variables or variables used for timing. This will fix vehicles that are saved at a high curtime and then being spawned on a fresh server with low curtime
for id, _ in pairs( data ) do
if not string.StartsWith( id, "_" ) then continue end
data[ id ] = nil
end
data._DuplicatorRestoreMaxHealthTo = self:GetMaxHP()
-- all functions need to go
for id, entry in pairs( data ) do
if not isfunction( entry ) then continue end
data[ id ] = nil
end
-- stuff below is things like constraints or DeleteOnRemove still referencing the old object. These need to go
data.OnDieFunctions = nil
data.Constraints = nil
data._children = nil
end
hook.Add("AdvDupe_FinishPasting", "!!lvs_vehicle_dupefinished_initialize", function( data, entry )
for _, v in pairs( data[ entry ].CreatedEntities ) do
if not IsValid( v ) or not isentity( v ) or not v.LVS then continue end
timer.Simple(1, function()
if not IsValid( v ) then return end
if v.SetlvsReady and not v:GetlvsReady() then
v:SetlvsReady( true )
end
if v.RebuildCrosshairFilterEnts then
v:RebuildCrosshairFilterEnts()
end
end)
end
end )

View File

@@ -0,0 +1,51 @@
function ENT:HandleStart()
local Driver = self:GetDriver()
if IsValid( Driver ) then
local KeyReload = Driver:lvsKeyDown( "ENGINE" )
if self.OldKeyReload ~= KeyReload then
self.OldKeyReload = KeyReload
if KeyReload then
self:ToggleEngine()
end
end
end
end
function ENT:ToggleEngine()
if self:GetEngineActive() then
self:StopEngine()
else
self:StartEngine()
end
end
function ENT:IsEngineStartAllowed()
if hook.Run( "LVS.IsEngineStartAllowed", self ) == false then return false end
if self:WaterLevel() > self.WaterLevelPreventStart then return false end
return true
end
function ENT:OnEngineActiveChanged( Active )
end
function ENT:StartEngine()
if self:GetEngineActive() or not self:IsEngineStartAllowed() then return end
self:PhysWake()
self:SetEngineActive( true )
self:OnEngineActiveChanged( true )
end
function ENT:StopEngine()
if not self:GetEngineActive() then return end
self:SetEngineActive( false )
self:OnEngineActiveChanged( false )
end

View File

@@ -0,0 +1,177 @@
function ENT:GetWorldGravity()
local PhysObj = self:GetPhysicsObject()
if not IsValid( PhysObj ) or not PhysObj:IsGravityEnabled() then return 0 end
return physenv.GetGravity():Length()
end
function ENT:GetWorldUp()
local Gravity = physenv.GetGravity()
if Gravity:Length() > 0 then
return -Gravity:GetNormalized()
else
return Vector(0,0,1)
end
end
function ENT:PhysicsSimulate( phys, deltatime )
end
function ENT:PhysicsStopScape()
if self._lvsScrapeData then
if self._lvsScrapeData.sound then
self._lvsScrapeData.sound:Stop()
end
end
self._lvsScrapeData = nil
end
function ENT:PhysicsStartScrape( pos, dir )
local startpos = self:LocalToWorld( pos )
local trace = util.TraceLine( {
start = startpos - dir * 5,
endpos = startpos + dir * 5,
filter = self:GetCrosshairFilterEnts()
} )
if trace.Hit then
local sound
if self._lvsScrapeData and self._lvsScrapeData.sound then
sound = self._lvsScrapeData.sound
else
sound = CreateSound( self, "LVS.Physics.Scrape" )
sound:PlayEx( 0, 90 + math.min( (self:GetVelocity():Length() / 2000) * 10,10) )
end
self._lvsScrapeData = {
dir = dir,
pos = pos,
sound = sound,
finishtime = CurTime() + 2,
}
self:CallOnRemove( "stop_scraping", function( self )
self:PhysicsStopScape()
end)
end
end
function ENT:PhysicsThink()
local EntTable = self:GetTable()
if not EntTable._lvsScrapeData then return end
if EntTable._lvsScrapeData.finishtime < CurTime() then
self:PhysicsStopScape()
return
end
local startpos = self:LocalToWorld( EntTable._lvsScrapeData.pos )
local trace = util.TraceLine( {
start = startpos - EntTable._lvsScrapeData.dir,
endpos = startpos + EntTable._lvsScrapeData.dir * 5,
filter = self:GetCrosshairFilterEnts()
} )
local Vel = self:GetVelocity():Length()
if trace.Hit and Vel > 25 then
local vol = math.min(math.max(Vel - 50,0) / 1000,1)
local effectdata = EffectData()
effectdata:SetOrigin( trace.HitPos + trace.HitNormal )
effectdata:SetNormal( trace.HitNormal )
effectdata:SetMagnitude( vol )
util.Effect( "lvs_physics_scrape", effectdata, true, true )
EntTable._lvsScrapeData.sound:ChangeVolume( vol, 0.1 )
else
self:PhysicsStopScape()
end
end
function ENT:TakeCollisionDamage( damage, attacker )
if not IsValid( attacker ) then
attacker = game.GetWorld()
end
local dmginfo = DamageInfo()
dmginfo:SetDamage( damage )
dmginfo:SetAttacker( attacker )
dmginfo:SetInflictor( attacker )
dmginfo:SetDamageType( DMG_CRUSH + DMG_VEHICLE ) -- this will communicate to the damage system to handle this kind of damage differently.
self:TakeDamageInfo( dmginfo )
end
function ENT:OnCollision( data, physobj )
return false
end
function ENT:OnSkyCollide( data, physobj )
return true
end
function ENT:PhysicsCollide( data, physobj )
local HitEnt = data.HitEntity
if not IsValid( HitEnt ) and util.GetSurfacePropName( data.TheirSurfaceProps ) == "default_silent" then
if self:OnSkyCollide( data, physobj ) then return end
end
if self:IsDestroyed() then
self.MarkForDestruction = true
end
if self:OnCollision( data, physobj ) then return end
self:PhysicsStartScrape( self:WorldToLocal( data.HitPos ), data.HitNormal )
if IsValid( HitEnt ) then
if HitEnt:IsPlayer() or HitEnt:IsNPC() then
return
end
end
if self:GetAI() and not self:IsPlayerHolding() then
if self:WaterLevel() >= self.WaterLevelDestroyAI then
self:SetDestroyed( true )
self.MarkForDestruction = true
return
end
end
local VelDif = (data.OurOldVelocity - data.OurNewVelocity):Length()
if VelDif > 60 and data.DeltaTime > 0.2 then
self:CalcPDS( data )
local effectdata = EffectData()
effectdata:SetOrigin( data.HitPos )
effectdata:SetNormal( -data.HitNormal )
util.Effect( "lvs_physics_impact", effectdata, true, true )
if VelDif > 1000 then
local damage = math.max( VelDif - 1000, 0 )
if not self:IsPlayerHolding() and damage > 0 then
self:EmitSound( "lvs/physics/impact_hard.wav", 85, 100 + math.random(-3,3), 1 )
self:TakeCollisionDamage( VelDif, HitEnt )
end
else
if VelDif > 800 then
self:EmitSound( "lvs/physics/impact_hard"..math.random(1,3)..".wav", 75, 100 + math.random(-3,3), 1 )
else
self:EmitSound( "lvs/physics/impact_soft"..math.random(1,5)..".wav", 75, 100 + math.random(-6,6), math.min( VelDif / 600, 1 ) )
end
end
end
end

View File

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

View File

@@ -0,0 +1,199 @@
ENT.DriverActiveSound = "vehicles/atv_ammo_close.wav"
ENT.DriverInActiveSound = "vehicles/atv_ammo_open.wav"
function ENT:AlignView( ply, SetZero )
if not IsValid( ply ) then return end
timer.Simple( 0, function()
if not IsValid( ply ) or not IsValid( self ) then return end
local Ang = Angle(0,90,0)
if not SetZero then
Ang = self:GetAngles()
Ang.r = 0
end
ply:SetEyeAngles( Ang )
end)
end
function ENT:HandleActive()
local Pod = self:GetDriverSeat()
if not IsValid( Pod ) then
self:SetActive( false )
return
end
local Driver = Pod:GetDriver()
local Active = self:GetActive()
if Driver ~= self:GetDriver() then
local NewDriver = Driver
local OldDriver = self:GetDriver()
local IsActive = IsValid( Driver )
self:SetDriver( Driver )
self:SetActive( IsActive )
self:OnDriverChanged( OldDriver, NewDriver, IsActive )
if IsActive then
Driver:lvsBuildControls()
self:AlignView( Driver )
if self.DriverActiveSound then self:EmitSound( self.DriverActiveSound ) end
else
self:WeaponsFinish()
if self.DriverInActiveSound then self:EmitSound( self.DriverInActiveSound ) end
end
end
end
function ENT:SetPassenger( ply )
if not IsValid( ply ) then return end
local AI = self:GetAI()
local DriverSeat = self:GetDriverSeat()
local AllowedToBeDriver = hook.Run( "LVS.CanPlayerDrive", ply, self ) ~= false
if IsValid( DriverSeat ) and not IsValid( DriverSeat:GetDriver() ) and not ply:KeyDown( IN_WALK ) and not AI and AllowedToBeDriver then
ply:EnterVehicle( DriverSeat )
self:AlignView( ply )
hook.Run( "LVS.UpdateRelationship", self )
else
local Seat = NULL
local Dist = 500000
for _, v in pairs( self:GetPassengerSeats() ) do
if not IsValid( v ) or IsValid( v:GetDriver() ) then continue end
if v:GetNWInt( "pPodIndex" ) == -1 then continue end
local cDist = (v:GetPos() - ply:GetPos()):Length()
if cDist < Dist then
Seat = v
Dist = cDist
end
end
if IsValid( Seat ) then
ply:EnterVehicle( Seat )
self:AlignView( ply, true )
hook.Run( "LVS.UpdateRelationship", self )
else
if IsValid( DriverSeat ) then
if not IsValid( self:GetDriver() ) and not AI then
if AllowedToBeDriver then
ply:EnterVehicle( DriverSeat )
self:AlignView( ply )
hook.Run( "LVS.UpdateRelationship", self )
else
hook.Run( "LVS.OnPlayerCannotDrive", ply, self )
end
end
end
end
end
end
function ENT:AddDriverSeat( Pos, Ang )
if IsValid( self:GetDriverSeat() ) then return self:GetDriverSeat() end
local Pod = ents.Create( "prop_vehicle_prisoner_pod" )
if not IsValid( Pod ) then
self:Remove()
print("LVS: Failed to create driverseat. Vehicle terminated.")
return
else
self:SetDriverSeat( Pod )
local DSPhys = Pod:GetPhysicsObject()
Pod:SetMoveType( MOVETYPE_NONE )
Pod:SetModel( "models/nova/airboat_seat.mdl" )
Pod:SetKeyValue( "vehiclescript","scripts/vehicles/prisoner_pod.txt" )
Pod:SetKeyValue( "limitview", 0 )
Pod:SetPos( self:LocalToWorld( Pos ) )
Pod:SetAngles( self:LocalToWorldAngles( Ang ) )
Pod:SetOwner( self )
Pod:Spawn()
Pod:Activate()
Pod:SetParent( self )
Pod:SetNotSolid( true )
Pod:SetRenderMode( RENDERMODE_TRANSALPHA )
Pod:DrawShadow( false )
Pod.DoNotDuplicate = true
Pod:lvsSetPodIndex( 1 )
Pod:PhysicsDestroy()
debugoverlay.BoxAngles( Pod:GetPos(), Pod:OBBMins(), Pod:OBBMaxs(), Pod:GetAngles(), 5, Color( 255, 93, 0, 200 ) )
self:DeleteOnRemove( Pod )
self:TransferCPPI( Pod )
end
return Pod
end
function ENT:AddPassengerSeat( Pos, Ang )
if not isvector( Pos ) or not isangle( Ang ) then return NULL end
local Pod = ents.Create( "prop_vehicle_prisoner_pod" )
if not IsValid( Pod ) then return NULL end
Pod:SetMoveType( MOVETYPE_NONE )
Pod:SetModel( "models/nova/airboat_seat.mdl" )
Pod:SetKeyValue( "vehiclescript","scripts/vehicles/prisoner_pod.txt" )
Pod:SetKeyValue( "limitview", 0 )
Pod:SetPos( self:LocalToWorld( Pos ) )
Pod:SetAngles( self:LocalToWorldAngles( Ang ) )
Pod:SetOwner( self )
Pod:Spawn()
Pod:Activate()
Pod:SetParent( self )
Pod:SetNotSolid( true )
Pod:SetRenderMode( RENDERMODE_TRANSALPHA )
debugoverlay.BoxAngles( Pod:GetPos(), Pod:OBBMins(), Pod:OBBMaxs(), Pod:GetAngles(), 5, Color( 100, 65, 127, 200 ) )
Pod:DrawShadow( false )
Pod.DoNotDuplicate = true
self.pPodKeyIndex = self.pPodKeyIndex and self.pPodKeyIndex + 1 or 2
if self.WEAPONS[ self.pPodKeyIndex ] then
local weapon = Pod:lvsAddWeapon( self.pPodKeyIndex )
if IsValid( weapon ) then
self:TransferCPPI( weapon )
self:DeleteOnRemove( weapon )
end
end
Pod:lvsSetPodIndex( self.pPodKeyIndex )
Pod:PhysicsDestroy()
self:DeleteOnRemove( Pod )
self:TransferCPPI( Pod )
if not istable( self.pSeats ) then self.pSeats = {} end
table.insert( self.pSeats, Pod )
return Pod
end

View File

@@ -0,0 +1,80 @@
ENT.ShieldRechargeDelay = 5
ENT.ShieldRechargeRate = 1
ENT.ShieldBlockableTypes = {
[1] = DMG_BULLET,
[2] = DMG_AIRBOAT,
[3] = DMG_BUCKSHOT,
[4] = DMG_SNIPER,
[5] = DMG_BLAST,
}
function ENT:CalcShieldDamage( dmginfo )
local MaxShield = self:GetMaxShield()
if MaxShield <= 0 then return end
local DMG_ENUM = DMG_GENERIC
for _, ENUM in ipairs( self.ShieldBlockableTypes ) do
DMG_ENUM = DMG_ENUM + ENUM
end
if not dmginfo:IsDamageType( DMG_ENUM ) then return end
self:DelayNextShieldRecharge( self.ShieldRechargeDelay )
local DamageRemaining = self:TakeShieldDamage( dmginfo:GetDamage() )
dmginfo:SetDamage( DamageRemaining )
self:OnTakeShieldDamage( dmginfo )
end
function ENT:CanShieldRecharge()
return (self.NextShieldRecharge or 0) < CurTime()
end
function ENT:DelayNextShieldRecharge( delay )
self.NextShieldRecharge = CurTime() + delay
end
function ENT:ShieldThink()
local MaxShield = self:GetMaxShield()
if MaxShield <= 0 or self:GetShieldPercent() == 1 then return end
if not self:CanShieldRecharge() then return end
local Cur = self:GetShield()
local Rate = FrameTime() * 20 * self.ShieldRechargeRate
self:SetShield( Cur + math.Clamp(MaxShield - Cur,-Rate,Rate) )
end
function ENT:TakeShieldDamage( damage )
local cur = self:GetShield()
local sub = cur - damage
local new = math.Clamp( sub , 0, self:GetMaxShield() )
self:SetShield( new )
if sub < 0 then
return math.abs( sub )
else
return 0
end
end
function ENT:OnTakeShieldDamage( dmginfo )
if dmginfo:GetDamage() ~= 0 then return end
local dmgNormal = -dmginfo:GetDamageForce():GetNormalized()
local dmgPos = dmginfo:GetDamagePosition()
dmginfo:SetDamagePosition( dmgPos + dmgNormal * 250 * self:GetShieldPercent() )
local effectdata = EffectData()
effectdata:SetOrigin( dmginfo:GetDamagePosition() )
effectdata:SetEntity( self )
util.Effect( "lvs_shield_impact", effectdata )
end

View File

@@ -0,0 +1,453 @@
AddCSLuaFile()
ENT.Type = "anim"
ENT.DoNotDuplicate = true
ENT.RenderGroup = RENDERGROUP_BOTH
ENT.UseRange = 75
ENT._UseTargetAllowed = true
function ENT:SetupDataTables()
self:NetworkVar( "Entity",0, "Base" )
self:NetworkVar( "Entity",1, "LinkedSeat" )
self:NetworkVar( "Bool",0, "Active" )
self:NetworkVar( "String",0, "PoseName" )
self:NetworkVar( "Vector",0, "Mins" )
self:NetworkVar( "Vector",1, "Maxs" )
self:NetworkVar( "Float",0, "Rate" )
self:NetworkVar( "Float",1, "RateExponent" )
self:NetworkVar( "Float",2, "PoseMin" )
self:NetworkVar( "Float",3, "PoseMax" )
if SERVER then
self:SetRate( 10 )
self:SetRateExponent( 2 )
self:SetPoseMax( 1 )
end
end
function ENT:IsServerSide()
local EntTable = self:GetTable()
if isbool( EntTable._IsServerSide ) then return EntTable._IsServerSide end
local PoseName = self:GetPoseName()
if PoseName == "" then return false end
local IsServerSide = string.StartsWith( PoseName, "^" )
EntTable._IsServerSide = IsServerSide
return IsServerSide
end
function ENT:IsOpen()
return self:GetActive()
end
function ENT:InRange( ply, Range )
local boxOrigin = self:GetPos()
local boxAngles = self:GetAngles()
local boxMins = self:GetMins()
local boxMaxs = self:GetMaxs()
local HitPos, _, _ = util.IntersectRayWithOBB( ply:GetShootPos(), ply:GetAimVector() * Range, boxOrigin, boxAngles, boxMins, boxMaxs )
return isvector( HitPos )
end
if SERVER then
AccessorFunc(ENT, "soundopen", "SoundOpen", FORCE_STRING)
AccessorFunc(ENT, "soundclose", "SoundClose", FORCE_STRING)
AccessorFunc(ENT, "maxsopen", "MaxsOpen", FORCE_VECTOR)
AccessorFunc(ENT, "minsopen", "MinsOpen", FORCE_VECTOR)
AccessorFunc(ENT, "maxsclosed", "MaxsClosed", FORCE_VECTOR)
AccessorFunc(ENT, "minsclosed", "MinsClosed", FORCE_VECTOR)
util.AddNetworkString( "lvs_doorhandler_interact" )
net.Receive( "lvs_doorhandler_interact", function( length, ply )
if not IsValid( ply ) then return end
local ent = net.ReadEntity()
if not IsValid( ent ) or not ent._UseTargetAllowed or not ent.UseRange or ply:InVehicle() then return end
local Range = ent.UseRange * 2
if (ply:GetPos() - ent:GetPos()):Length() > Range then return end
if not ent:InRange( ply, Range ) then return end
ent:Use( ply, ply )
end)
function ENT:LinkToSeat( ent )
if not IsValid( ent ) or not ent:IsVehicle() then
ErrorNoHalt( "[LVS] Couldn't link seat to doorsystem. Entity expected, got "..tostring( ent ).."\n" )
return
end
self:SetLinkedSeat( ent )
end
function ENT:Initialize()
self:SetMoveType( MOVETYPE_NONE )
self:SetSolid( SOLID_NONE )
self:SetUseType( SIMPLE_USE )
self:DrawShadow( false )
debugoverlay.Cross( self:GetPos(), 15, 5, Color( 255, 223, 127 ) )
end
function ENT:Use( ply )
if not IsValid( ply ) then return end
local Base = self:GetBase()
if not IsValid( Base ) then return end
if not Base:IsUseAllowed( ply ) then return end
if self:IsOpen() then
self:Close( ply )
else
self:Open( ply )
end
end
function ENT:OnOpen( ply )
end
function ENT:OnClosed( ply )
end
function ENT:OpenAndClose( ply )
self:Open( ply )
self._PreventClosing = true
timer.Simple(0.5, function()
if not IsValid( self ) then return end
self:Close( ply )
self._PreventClosing = false
end )
end
function ENT:DisableOnBodyGroup( group, subgroup )
self._BodyGroupDisable = group
self._BodySubGroupDisable = subgroup
end
function ENT:IsBodyGroupDisabled()
if not self._BodyGroupDisable or not self._BodySubGroupDisable then return false end
local base = self:GetBase()
if not IsValid( base ) then return false end
return base:GetBodygroup( self._BodyGroupDisable ) == self._BodySubGroupDisable
end
function ENT:Open( ply )
if self:IsOpen() then return end
self:SetActive( true )
self:SetMins( self:GetMinsOpen() )
self:SetMaxs( self:GetMaxsOpen() )
if self:IsBodyGroupDisabled() then return end
self:OnOpen( ply )
local snd = self:GetSoundOpen()
if not snd then return end
self:EmitSound( snd )
end
function ENT:Close( ply )
if not self:IsOpen() then
if self:IsBodyGroupDisabled() then
self:Open( ply )
end
return
end
if self:IsBodyGroupDisabled() then return end
self:SetActive( false )
self:SetMins( self:GetMinsClosed() )
self:SetMaxs( self:GetMaxsClosed() )
self:OnClosed( ply )
local snd = self:GetSoundClose()
if not snd then return end
self:EmitSound( snd )
end
function ENT:OnDriverChanged( oldDriver, newDriver, pod )
if self._PreventClosing then return end
if IsValid( newDriver ) then
if self:IsOpen() then
self:Close( newDriver )
end
else
timer.Simple( FrameTime() * 2, function()
if not IsValid( self ) or not IsValid( oldDriver ) or IsValid( self._Driver ) then return end
if oldDriver:lvsGetVehicle() == self:GetBase() then return end
if not self:IsOpen() then
self:OpenAndClose()
end
end )
end
end
function ENT:SetPoseParameterSV()
local Base = self:GetBase()
if not IsValid( Base ) then return end
local Target = self:GetActive() and self:GetPoseMax() or self:GetPoseMin()
local poseName = self:GetPoseName()
if poseName == "" then return end
local EntTable = self:GetTable()
EntTable.sm_pp = EntTable.sm_pp and EntTable.sm_pp + (Target - EntTable.sm_pp) * FrameTime() * self:GetRate() or 0
local value = EntTable.sm_pp ^ self:GetRateExponent()
Base:SetPoseParameter( string.Replace(poseName, "^", ""), value )
end
function ENT:Think()
local LinkedSeat = self:GetLinkedSeat()
if IsValid( LinkedSeat ) then
local Driver = LinkedSeat:GetDriver()
if self._Driver ~= Driver then
self:OnDriverChanged( self._Driver, Driver, LinkedSeat )
self._Driver = Driver
end
end
if self:IsServerSide() then
self:SetPoseParameterSV()
self:NextThink( CurTime() )
else
self:NextThink( CurTime() + 0.25 )
end
return true
end
function ENT:OnTakeDamage( dmginfo )
end
return
end
function ENT:Initialize()
end
function ENT:Think()
if self:IsServerSide() then return end
local Base = self:GetBase()
if not IsValid( Base ) then return end
local Target = self:GetActive() and self:GetPoseMax() or self:GetPoseMin()
local poseName = self:GetPoseName()
if poseName == "" then return end
local EntTable = self:GetTable()
EntTable.sm_pp = EntTable.sm_pp and EntTable.sm_pp + (Target - EntTable.sm_pp) * RealFrameTime() * self:GetRate() or 0
local value = EntTable.sm_pp ^ self:GetRateExponent()
if string.StartsWith( poseName, "!" ) then
Base:SetBonePoseParameter( poseName, value )
else
Base:SetPoseParameter( poseName, value )
end
end
function ENT:OnRemove()
end
function ENT:Draw()
end
local LVS = LVS
ENT.ColorSelect = Color(127,255,127,150)
ENT.ColorNormal = Color(255,0,0,150)
ENT.ColorTransBlack = Color(0,0,0,150)
ENT.ColorBlack = Color(75,75,75,255)
ENT.OutlineThickness = Vector(0.5,0.5,0.5)
local function DrawText( pos, text, align, col )
if not align then align = TEXT_ALIGN_CENTER end
cam.Start2D()
local data2D = pos:ToScreen()
if not data2D.visible then cam.End2D() return end
local font = "TargetIDSmall"
local x = data2D.x
local y = data2D.y
draw.DrawText( text, font, x + 1, y + 1, Color( 0, 0, 0, 120 ), align )
draw.DrawText( text, font, x + 2, y + 2, Color( 0, 0, 0, 50 ), align )
draw.DrawText( text, font, x, y, col or color_white, align )
cam.End2D()
end
local function bezier(p0, p1, p2, p3, t)
local e = p0 + t * (p1 - p0)
local f = p1 + t * (p2 - p1)
local g = p2 + t * (p3 - p2)
local h = e + t * (f - e)
local i = f + t * (g - f)
local p = h + t * (i - h)
return p
end
local function DrawBezier( startpos, midpos, endpos )
cam.Start2D()
local p0 = LocalPlayer():GetPos()
local p1 = startpos
local p2 = midpos
local p3 = endpos
local oldpos = p0:ToScreen()
if not oldpos.visible then cam.End2D() return end
local num = 100
for i = 0, num do
local newpos = bezier(p0,p1,p2,p3, i / num):ToScreen()
if not newpos.visible then
oldpos = newpos
continue
end
surface.SetDrawColor( 255, 255, 255, math.abs( math.cos( -CurTime() * 5 + i * 0.05 ) ) * 255 )
surface.DrawLine( oldpos.x, oldpos.y, newpos.x, newpos.y )
oldpos = newpos
end
cam.End2D()
end
function ENT:DrawTranslucent()
local ply = LocalPlayer()
if not IsValid( ply ) or ply:InVehicle() then return end
local KeySprint = ply:KeyDown( IN_SPEED )
local InRange = self:InRange( ply, self.UseRange )
if LVS.ShowDoorInfo and InRange then
local pos = (self:LocalToWorld( self:GetMins() ) + self:LocalToWorld( self:GetMaxs() )) * 0.5
local NameKeyUse = "["..(input.LookupBinding( "+use" ) or "+use is not bound to a key").."]"
local NameKeySprint = "["..(input.LookupBinding( "+speed" ) or "+speed is not bound to a key").."]"
local boxOrigin = self:GetPos()
local boxAngles = self:GetAngles()
local boxMins = self:GetMins()
local boxMaxs = self:GetMaxs()
if self:IsOpen() then
local LinkedSeat = self:GetLinkedSeat()
if IsValid( LinkedSeat ) then
if not KeySprint then
DrawText( LinkedSeat:LocalToWorld( LinkedSeat:OBBCenter() ), NameKeyUse.." \n".."Enter ", TEXT_ALIGN_CENTER )
DrawText( pos, " "..NameKeySprint.."\n".." Close", TEXT_ALIGN_CENTER, self.ColorBlack )
DrawBezier( Vector(pos.x,pos.y,self:LocalToWorld( Vector(0,0,self:GetMaxs().z) ).z), Vector(pos.x,pos.y,self:LocalToWorld( Vector(0,0,self:GetMins().z) ).z), LinkedSeat:LocalToWorld( LinkedSeat:OBBCenter() - Vector(0,0,5) ) )
else
DrawText( LinkedSeat:LocalToWorld( LinkedSeat:OBBCenter() ), NameKeySprint.." \n".."Enter ", TEXT_ALIGN_CENTER, self.ColorBlack )
DrawText( pos, " "..NameKeyUse.."\n".." Close", TEXT_ALIGN_CENTER )
end
else
DrawText( pos, NameKeyUse.."\n".."Close", TEXT_ALIGN_CENTER )
end
else
DrawText( pos, NameKeyUse.."\n".."Open", TEXT_ALIGN_CENTER )
end
end
if not KeySprint then return end
if InRange then
local EntTable = self:GetTable()
local Use = ply:KeyDown( IN_USE )
if EntTable.old_Use ~= Use then
EntTable.old_Use = Use
if Use then
net.Start( "lvs_doorhandler_interact" )
net.WriteEntity( self )
net.SendToServer()
end
end
end
if not LVS.DeveloperEnabled then return end
local boxOrigin = self:GetPos()
local boxAngles = self:GetAngles()
local boxMins = self:GetMins()
local boxMaxs = self:GetMaxs()
local EntTable = self:GetTable()
local Col = InRange and EntTable.ColorSelect or EntTable.ColorNormal
render.SetColorMaterial()
render.DrawBox( boxOrigin, boxAngles, boxMins, boxMaxs, Col )
render.DrawBox( boxOrigin, boxAngles, boxMaxs + EntTable.OutlineThickness, boxMins - EntTable.OutlineThickness, EntTable.ColorTransBlack )
end

View File

@@ -0,0 +1,60 @@
function ENT:CalcViewOverride( ply, pos, angles, fov, pod )
return pos, angles, fov
end
function ENT:CalcViewDriver( ply, pos, angles, fov, pod )
local view = {}
view.origin = pos
view.fov = fov
view.drawviewer = true
view.angles = ply:EyeAngles()
if not pod:GetThirdPersonMode() then
view.drawviewer = false
return view
end
local radius = 550
radius = radius + radius * pod:GetCameraDistance()
local TargetOrigin = view.origin - view.angles:Forward() * radius + view.angles:Up() * (radius * 0.2 + radius * pod:GetCameraHeight())
local WallOffset = 4
local tr = util.TraceHull( {
start = view.origin,
endpos = TargetOrigin,
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.origin = tr.HitPos
if tr.Hit and not tr.StartSolid then
view.origin = view.origin + tr.HitNormal * WallOffset
end
return view
end
function ENT:CalcViewPassenger( ply, pos, angles, fov, pod )
return LVS:CalcView( self, ply, pos, angles, fov, pod )
end
function ENT:LVSCalcView( ply, original_pos, original_angles, original_fov, pod )
local pos, angles, fov = self:CalcViewOverride( ply, original_pos, original_angles, original_fov, pod )
if self:GetDriverSeat() == pod then
return self:CalcViewDriver( ply, pos, angles, fov, pod )
else
return self:CalcViewPassenger( ply, pos, angles, fov, pod )
end
end

View File

@@ -0,0 +1,25 @@
ENT.IconEngine = Material( "lvs/engine.png" )
function ENT:LVSHudPaintInfoText( X, Y, W, H, ScrX, ScrY, ply )
local speed = math.Round( LVS:GetUnitValue( self:GetVelocity():Length() ) ,0)
draw.DrawText( LVS:GetUnitName().." ", "LVS_FONT", X + 72, Y + 35, color_white, TEXT_ALIGN_RIGHT )
draw.DrawText( speed, "LVS_FONT_HUD_LARGE", X + 72, Y + 20, color_white, TEXT_ALIGN_LEFT )
if ply ~= self:GetDriver() then return end
local hX = X + W - H * 0.5
local hY = Y + H * 0.25 + H * 0.25
surface.SetMaterial( self.IconEngine )
surface.SetDrawColor( 0, 0, 0, 200 )
surface.DrawTexturedRectRotated( hX + 4, hY + 1, H * 0.5, H * 0.5, 0 )
surface.SetDrawColor( color_white )
surface.DrawTexturedRectRotated( hX + 2, hY - 1, H * 0.5, H * 0.5, 0 )
if not self:GetEngineActive() then
draw.SimpleText( "X" , "LVS_FONT", hX, hY, Color(0,0,0,255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER )
else
self:LVSDrawCircle( hX, hY, H * 0.35, self:GetThrottle() )
end
end

View File

@@ -0,0 +1,4 @@
include("shared.lua")
include("sh_camera_eyetrace.lua")
include("cl_camera.lua")
include("cl_hud.lua")

View File

@@ -0,0 +1,90 @@
AddCSLuaFile( "shared.lua" )
AddCSLuaFile( "cl_init.lua" )
AddCSLuaFile( "cl_camera.lua" )
AddCSLuaFile( "cl_hud.lua" )
AddCSLuaFile( "sh_camera_eyetrace.lua" )
include("shared.lua")
include("sv_controls.lua")
include("sv_components.lua")
include("sv_vehiclespecific.lua")
include("sh_camera_eyetrace.lua")
include("sv_ai.lua")
DEFINE_BASECLASS( "lvs_base" )
function ENT:OnDriverChanged( Old, New, VehicleIsActive )
if VehicleIsActive then
if not self:GetEngineActive() and self:IsEngineStartAllowed() then
self:SetEngineActive( true )
end
return
end
self:SetEngineActive( false )
self:SetMove( 0, 0 )
end
function ENT:StartEngine()
for _, wheel in pairs( self:GetWheels() ) do
if not IsValid( wheel ) then continue end
wheel:PhysWake()
end
BaseClass.StartEngine( self )
end
function ENT:PhysicsSimulate( phys, deltatime )
if self:GetEngineActive() then phys:Wake() end
local OnGroundMul = self:HitGround() and 1 or 0
local VelL = phys:WorldToLocal( phys:GetPos() + phys:GetVelocity() )
local InputMove = self:GetMove()
self._smMove = self._smMove and (self._smMove + (Vector(InputMove.x,InputMove.y,0):GetNormalized() - self._smMove) * deltatime * self.ForceLinearRate * 10) or InputMove
local MoveX = (self.MaxVelocityX + self.BoostAddVelocityX * InputMove.z) * self._smMove.x
local MoveY = (self.MaxVelocityY + self.BoostAddVelocityY * InputMove.z) * self._smMove.y
local Ang = self:GetAngles()
if not self:GetEngineActive() then
self:SetSteerTo( Ang.y )
self.smY = Ang.y
end
self.smY = self.smY and math.ApproachAngle( self.smY, self:GetSteerTo(), self.MaxTurnRate * deltatime * 100 ) or Ang.y
local Steer = self:WorldToLocalAngles( Angle(Ang.p,self.smY,Ang.y) ).y
local ForceLinear = ((Vector( MoveX, MoveY, 0 ) - Vector(VelL.x,VelL.y,0)) * self.ForceLinearMultiplier) * OnGroundMul * deltatime * 500
local ForceAngle = (Vector(0,0,Steer) * self.ForceAngleMultiplier * 2 - phys:GetAngleVelocity() * self.ForceAngleDampingMultiplier) * OnGroundMul * deltatime * 600
return self:PhysicsSimulateOverride( ForceAngle, ForceLinear, phys, deltatime, self:GetDisabled() and SIM_NOTHING or SIM_LOCAL_ACCELERATION )
end
function ENT:PhysicsSimulateOverride( ForceAngle, ForceLinear, phys, deltatime, simulate )
return ForceAngle, ForceLinear, simulate
end
function ENT:IsEngineStartAllowed()
if hook.Run( "LVS.IsEngineStartAllowed", self ) == false then return false end
if self:GetDisabled() then return false end
if self:WaterLevel() > self.WaterLevelPreventStart then return false end
return true
end
function ENT:OnDisabled( name, old, new)
if new == old then return end
if new then
if not self:GetEngineActive() then return end
self:SetEngineActive( false )
end
end

View File

@@ -0,0 +1,42 @@
function ENT:GetEyeTrace( trace_forward )
local startpos = self:LocalToWorld( self:OBBCenter() )
local pod = self:GetDriverSeat()
if IsValid( pod ) then
startpos = pod:LocalToWorld( pod:OBBCenter() )
end
local AimVector = trace_forward and self:GetForward() or self:GetAimVector()
local data = {
start = startpos,
endpos = (startpos + AimVector * 50000),
filter = self:GetCrosshairFilterEnts(),
}
local trace = util.TraceLine( data )
return trace
end
function ENT:GetAimVector()
if self:GetAI() then
return self:GetAIAimVector()
end
local Driver = self:GetDriver()
if not IsValid( Driver ) then return self:GetForward() end
if SERVER then
local pod = self:GetDriverSeat()
if not IsValid( pod ) then return Driver:EyeAngles():Forward() end
return pod:WorldToLocalAngles( Driver:EyeAngles() ):Forward()
else
return Driver:EyeAngles():Forward()
end
end

View File

@@ -0,0 +1,72 @@
ENT.Base = "lvs_base"
ENT.PrintName = "[LVS] Generic Fake Hover"
ENT.Author = "Luna"
ENT.Information = "Luna's Vehicle Script"
ENT.Category = "[LVS]"
ENT.ForceAngleMultiplier = 1
ENT.ForceAngleDampingMultiplier = 1
ENT.ForceLinearMultiplier = 1
ENT.ForceLinearRate = 1
ENT.MaxVelocityX = 300
ENT.MaxVelocityY = 300
ENT.MaxTurnRate = 1
ENT.BoostAddVelocityX = 200
ENT.BoostAddVelocityY = 200
ENT.GroundTraceHitWater = true
ENT.GroundTraceLength = 50
ENT.GroundTraceHull = 100
ENT.DisableBallistics = true
function ENT:SetupDataTables()
self:AddDT( "Vector", "AIAimVector" )
self:AddDT( "Bool", "Disabled" )
if SERVER then
self:NetworkVarNotify( "Disabled", self.OnDisabled )
end
self:CreateBaseDT()
end
function ENT:HitGround()
local data = {
start = self:LocalToWorld( self:OBBCenter() ),
endpos = self:LocalToWorld( Vector(0,0,self:OBBMins().z - self.GroundTraceLength) ),
mins = Vector( -self.GroundTraceHull, -self.GroundTraceHull, 0 ),
maxs = Vector( self.GroundTraceHull, self.GroundTraceHull, 0 ),
filter = self:GetCrosshairFilterEnts()
}
local trace = util.TraceHull( data )
data.mask = MASK_WATER
local traceWater = util.TraceHull( data )
return ((trace.Hit or (traceWater.Hit and self.GroundTraceHitWater)) and not trace.HitSky)
end
function ENT:GetThrottle()
return math.min( self:GetVelocity():Length() / math.abs( self.MaxVelocityX + self.BoostAddVelocityX, self.MaxVelocityY + self.BoostAddVelocityY ), 1 )
end
function ENT:GetMaxThrottle()
return 1
end
function ENT:GetThrustStrenght()
return 0
end
function ENT:GetVehicleType()
return "fakehover"
end

View File

@@ -0,0 +1,125 @@
function ENT:OnCreateAI()
self:StartEngine()
end
function ENT:OnRemoveAI()
self:StopEngine()
end
function ENT:RunAI()
local RangerLength = 25000
local Target = self:AIGetTarget( 180 )
local StartPos = self:LocalToWorld( self:OBBCenter() )
local TraceFilter = self:GetCrosshairFilterEnts()
local Front = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:GetForward() * RangerLength } )
local FrontLeft = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(0,15,0) ):Forward() * RangerLength } )
local FrontRight = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(0,-15,0) ):Forward() * RangerLength } )
local FrontLeft1 = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(0,60,0) ):Forward() * RangerLength } )
local FrontRight1 = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(0,-60,0) ):Forward() * RangerLength } )
local FrontLeft2 = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(0,85,0) ):Forward() * RangerLength } )
local FrontRight2 = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(0,-85,0) ):Forward() * RangerLength } )
local TargetPos = (Front.HitPos + FrontLeft.HitPos + FrontRight.HitPos + FrontLeft1.HitPos + FrontRight1.HitPos + FrontLeft2.HitPos + FrontRight2.HitPos) / 7
self._AIFireInput = false
if IsValid( self:GetHardLockTarget() ) then
TargetPos = self:GetHardLockTarget():GetPos()
if self:AITargetInFront( self:GetHardLockTarget(), 65 ) then
self._AIFireInput = true
end
else
if IsValid( Target ) then
TargetPos = Target:LocalToWorld( Target:OBBCenter() )
if self:AITargetInFront( Target, 65 ) then
self._AIFireInput = true
end
end
end
if self._AIFireInput then
local CurHeat = self:GetNWHeat()
local CurWeapon = self:GetSelectedWeapon()
if CurWeapon > 2 then
self:AISelectWeapon( 1 )
else
if CurHeat > 0.9 then
if CurWeapon == 1 and self:AIHasWeapon( 2 ) then
self:AISelectWeapon( 2 )
elseif CurWeapon == 2 then
self:AISelectWeapon( 1 )
end
else
if CurHeat == 0 and math.cos( CurTime() ) > 0 then
self:AISelectWeapon( 1 )
end
end
end
end
local DistToTarget = (TargetPos - self:GetPos()):Length()
local LocalMove = self:WorldToLocal( TargetPos )
if DistToTarget < 1000 then
LocalMove.x = -1
end
if DistToTarget > 800 and DistToTarget < 1200 then
LocalMove.y = math.sin( CurTime() * 1.5 + self:EntIndex() * 1337 ) * 10
end
self:SetMove( LocalMove.x, LocalMove.y )
local pod = self:GetDriverSeat()
if not IsValid( pod ) then return end
local AimVector = (TargetPos - pod:LocalToWorld( Vector(0,0,33) )):GetNormalized()
self:SetAIAimVector( AimVector )
self:SetSteerTo( AimVector:Angle().y )
end
function ENT:OnAITakeDamage( dmginfo )
local attacker = dmginfo:GetAttacker()
if not IsValid( attacker ) then return end
if not self:AITargetInFront( attacker, IsValid( self:AIGetTarget() ) and 120 or 45 ) then
self:SetHardLockTarget( attacker )
end
end
function ENT:AISelectWeapon( ID )
if ID == self:GetSelectedWeapon() then return end
local T = CurTime()
if (self._nextAISwitchWeapon or 0) > T then return end
self._nextAISwitchWeapon = T + 1
self:SelectWeapon( ID )
end
function ENT:SetHardLockTarget( target )
if not self:IsEnemy( target ) then return end
self._HardLockTarget = target
self._HardLockTime = CurTime() + 4
end
function ENT:GetHardLockTarget()
if (self._HardLockTime or 0) < CurTime() then return NULL end
return self._HardLockTarget
end

View File

@@ -0,0 +1,106 @@
ENT._WheelEnts = {}
function ENT:GetWheels()
for id, ent in pairs( self._WheelEnts ) do
if IsValid( ent ) then continue end
self._WheelEnts[ id ] = nil
end
return self._WheelEnts
end
function ENT:AddWheel( pos, radius, mass, buoyancyratio, brakeforce )
if not isvector( pos ) or not isnumber( radius ) or not isnumber( mass ) then return end
local wheel = ents.Create( "lvs_fakehover_wheel" )
if not IsValid( wheel ) then
self:Remove()
print("LVS: Failed to initialize wheel. Vehicle terminated.")
return
end
local WheelPos = self:LocalToWorld( pos )
local CenterPos = self:LocalToWorld( self:OBBCenter() )
debugoverlay.Sphere( WheelPos, radius, 5, Color(150,150,150), true )
debugoverlay.Line( CenterPos, WheelPos, 5, Color(150,150,150), true )
wheel:SetPos( WheelPos )
wheel:SetAngles( self:LocalToWorldAngles( Angle(0,90,0) ) )
wheel:Spawn()
wheel:Activate()
wheel:SetBase( self )
wheel:SetNoDraw( true )
wheel:DrawShadow( false )
wheel.DoNotDuplicate = true
wheel:Define(
{
radius = radius,
mass = mass,
buoyancyratio = buoyancyratio or 0,
}
)
table.insert( self._WheelEnts, wheel )
local PhysObj = wheel:GetPhysicsObject()
if not IsValid( PhysObj ) then
self:Remove()
print("LVS: Failed to initialize wheel phys model. Vehicle terminated.")
return
end
if PhysObj:GetMaterial() ~= "gmod_silent" then
self:Remove()
print("LVS: Failed to initialize physprop material on wheel. Vehicle terminated.")
return
end
self:DeleteOnRemove( wheel )
self:TransferCPPI( wheel )
local ballsocket = constraint.AdvBallsocket(wheel, self,0,0,Vector(0,0,0),Vector(0,0,0),0,0, -1, -1, -1, 1, 1, 1, 0, 0, 0, 0, 1)
ballsocket.DoNotDuplicate = true
self:TransferCPPI( ballsocket )
local nocollide = constraint.NoCollide( wheel, self, 0, 0 )
nocollide.DoNotDuplicate = true
self:TransferCPPI( nocollide )
PhysObj:EnableMotion( true )
return wheel
end
function ENT:AddEngineSound( pos )
local EngineSND = ents.Create( "lvs_fakehover_soundemitter" )
if not IsValid( EngineSND ) then
self:Remove()
print("LVS: Failed to create engine sound entity. Vehicle terminated.")
return
end
EngineSND:SetPos( self:LocalToWorld( pos ) )
EngineSND:SetAngles( self:GetAngles() )
EngineSND:Spawn()
EngineSND:Activate()
EngineSND:SetParent( self )
EngineSND:SetBase( self )
self:DeleteOnRemove( EngineSND )
self:TransferCPPI( EngineSND )
return EngineSND
end

View File

@@ -0,0 +1,65 @@
function ENT:StartCommand( ply, cmd )
if self:GetDriver() ~= ply then return end
local KeyJump = ply:lvsKeyDown( "VSPEC" )
if self._lvsOldKeyJump ~= KeyJump then
self._lvsOldKeyJump = KeyJump
if KeyJump then
self:ToggleVehicleSpecific()
end
end
local Forward = cmd:KeyDown( IN_FORWARD )
local Back = cmd:KeyDown( IN_BACK )
local Left = cmd:KeyDown( IN_MOVELEFT )
local Right = cmd:KeyDown( IN_MOVERIGHT )
local Boost = cmd:KeyDown( IN_SPEED )
local X = (Forward and 1 or 0) - (Back and 1 or 0)
local Y = (Left and 1 or 0) - (Right and 1 or 0)
self:SetMove( X, Y, Boost )
local pod = self:GetDriverSeat()
if not IsValid( pod ) then return end
if ply:lvsKeyDown( "FREELOOK" ) then
self:SetSteerTo( self:GetAngles().y)
return
end
self:SetSteerTo( pod:WorldToLocalAngles( ply:EyeAngles() ).y )
end
function ENT:SetSteerTo( Steer )
if not isnumber( Steer ) then return end
self._steer = Steer
end
function ENT:GetSteerTo()
if not self:GetEngineActive() then return self:GetAngles().y end
return (self._steer or self:GetAngles().y)
end
function ENT:SetMove( X, Y, Boost )
if not isnumber( X ) or not isnumber( Y ) then return end
X = math.Clamp( X, -1, 1 )
Y = math.Clamp( Y, -1, 1 )
Z = Boost and 1 or 0
self._move = Vector( X, Y, Z )
end
function ENT:GetMove()
if not self:GetEngineActive() then return Vector(0,0,0) end
return (self._move or Vector(0,0,0))
end

View File

@@ -0,0 +1,25 @@
function ENT:ToggleVehicleSpecific()
self._VSPEC = not self._VSPEC
self:OnVehicleSpecificToggled( self._VSPEC )
end
function ENT:EnableVehicleSpecific()
if self._VSPEC then return end
self._VSPEC = true
self:OnVehicleSpecificToggled( self._VSPEC )
end
function ENT:DisableVehicleSpecific()
if not self._VSPEC then return end
self._VSPEC = false
self:OnVehicleSpecificToggled( self._VSPEC )
end
function ENT:OnVehicleSpecificToggled( IsActive )
end

View File

@@ -0,0 +1,189 @@
ENT._lvsSmoothFreeLook = 0
function ENT:CalcViewDirectInput( ply, pos, angles, fov, pod )
local ViewPosL = pod:WorldToLocal( pos )
local view = {}
view.fov = fov
view.drawviewer = true
view.angles = self:GetAngles()
local FreeLook = ply:lvsKeyDown( "FREELOOK" )
local Zoom = ply:lvsKeyDown( "ZOOM" )
if not pod:GetThirdPersonMode() then
if FreeLook then
view.angles = pod:LocalToWorldAngles( ply:EyeAngles() )
self._lvsSmoothFreeLook = 1
self._lvsSmoothFreeLookAngles = self:WorldToLocalAngles( view.angles )
else
if self._lvsSmoothFreeLook and self._lvsSmoothFreeLookAngles then
view.angles = self:LocalToWorldAngles( self._lvsSmoothFreeLookAngles * self._lvsSmoothFreeLook )
if self._lvsSmoothFreeLook <= 0 then
self._lvsSmoothFreeLookAngles = nil
self._lvsSmoothFreeLook = nil
else
self._lvsSmoothFreeLook = self._lvsSmoothFreeLook - self._lvsSmoothFreeLook * RealFrameTime() * 8
end
end
end
local velL = self:WorldToLocal( self:GetPos() + self:GetVelocity() )
local Dividor = math.abs( velL.x )
local SideForce = math.Clamp( velL.y / Dividor, -1, 1)
local UpForce = math.Clamp( velL.z / Dividor, -1, 1)
local ViewPunch = Vector(0,math.Clamp(SideForce * 10,-1,1),math.Clamp(UpForce * 10,-1,1))
if Zoom then
ViewPunch = Vector(0,0,0)
end
pod._lerpPosOffset = pod._lerpPosOffset and pod._lerpPosOffset + (ViewPunch - pod._lerpPosOffset) * RealFrameTime() * 5 or Vector(0,0,0)
pod._lerpPos = pos
view.origin = pos + pod:GetForward() * -pod._lerpPosOffset.y * 0.5 + pod:GetUp() * pod._lerpPosOffset.z * 0.5
view.angles.p = view.angles.p - pod._lerpPosOffset.z * 0.1
view.angles.y = view.angles.y + pod._lerpPosOffset.y * 0.1
view.drawviewer = false
return view
end
pod._lerpPos = pod._lerpPos or self:GetPos()
local radius = 550
radius = radius + radius * pod:GetCameraDistance()
if FreeLook then
local velL = self:WorldToLocal( self:GetPos() + self:GetVelocity() )
local SideForce = math.Clamp(velL.y / 10,-250,250)
local UpForce = math.Clamp(velL.z / 10,-250,250)
pod._lerpPosL = pod._lerpPosL and (pod._lerpPosL + (Vector(radius, SideForce,150 + radius * 0.1 + radius * pod:GetCameraHeight() + UpForce) - pod._lerpPosL) * RealFrameTime() * 12) or Vector(0,0,0)
pod._lerpPos = self:LocalToWorld( pod._lerpPosL )
view.origin = pod._lerpPos
view.angles = self:LocalToWorldAngles( Angle(0,180,0) )
else
local TargetPos = self:LocalToWorld( Vector(500,0,150 + radius * 0.1 + radius * pod:GetCameraHeight()) )
local Sub = TargetPos - pod._lerpPos
local Dir = Sub:GetNormalized()
local Dist = Sub:Length()
local DesiredPos = TargetPos - self:GetForward() * (300 + radius) - Dir * 100
pod._lerpPos = pod._lerpPos + (DesiredPos - pod._lerpPos) * RealFrameTime() * (Zoom and 30 or 12)
pod._lerpPosL = self:WorldToLocal( pod._lerpPos )
local vel = self:GetVelocity()
view.origin = pod._lerpPos
view.angles = self:GetAngles()
end
view.origin = view.origin + ViewPosL
return view
end
function ENT:CalcViewMouseAim( ply, pos, angles, fov, pod )
local cvarFocus = math.Clamp( LVS.cvarCamFocus:GetFloat() , -1, 1 )
self._lvsSmoothFreeLook = self._lvsSmoothFreeLook + ((ply:lvsKeyDown( "FREELOOK" ) and 0 or 1) - self._lvsSmoothFreeLook) * RealFrameTime() * 10
local Zoom = ply:lvsKeyDown( "ZOOM" )
local velL = self:WorldToLocal( self:GetPos() + self:GetVelocity() )
local Dividor = math.abs( velL.x )
local SideForce = math.Clamp( velL.y / Dividor, -1, 1)
local UpForce = math.Clamp( velL.z / Dividor, -1, 1)
local ViewPunch = Vector(0,math.Clamp(SideForce * 10,-1,1),math.Clamp(UpForce * 10,-1,1))
if Zoom then
ViewPunch = Vector(0,0,0)
end
pod._lerpPosOffset = pod._lerpPosOffset and pod._lerpPosOffset + (ViewPunch - pod._lerpPosOffset) * RealFrameTime() * 5 or Vector(0,0,0)
pod._lerpPos = pos
local view = {}
view.origin = pos + pod:GetForward() * -pod._lerpPosOffset.y * 0.5 + pod:GetUp() * pod._lerpPosOffset.z * 0.5
view.fov = fov
view.drawviewer = true
view.angles = (self:GetForward() * (1 + cvarFocus) * self._lvsSmoothFreeLook * 0.8 + ply:EyeAngles():Forward() * math.max(1 - cvarFocus, 1 - self._lvsSmoothFreeLook)):Angle()
if cvarFocus >= 1 then
view.angles = LerpAngle( self._lvsSmoothFreeLook, ply:EyeAngles(), self:GetAngles() )
else
view.angles.r = 0
end
if not pod:GetThirdPersonMode() then
view.drawviewer = false
return view
end
local radius = 550
radius = radius + radius * pod:GetCameraDistance()
local TargetOrigin = view.origin - view.angles:Forward() * radius + view.angles:Up() * (radius * 0.2 + radius * pod:GetCameraHeight())
local WallOffset = 4
local tr = util.TraceHull( {
start = view.origin,
endpos = TargetOrigin,
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.origin = tr.HitPos
if tr.Hit and not tr.StartSolid then
view.origin = view.origin + tr.HitNormal * WallOffset
end
return view
end
function ENT:CalcViewOverride( ply, pos, angles, fov, pod )
return pos, angles, fov
end
function ENT:CalcViewDriver( ply, pos, angles, fov, pod )
if ply:lvsMouseAim() then
return self:CalcViewMouseAim( ply, pos, angles, fov, pod )
else
return self:CalcViewDirectInput( ply, pos, angles, fov, pod )
end
end
function ENT:CalcViewPassenger( ply, pos, angles, fov, pod )
return LVS:CalcView( self, ply, pos, angles, fov, pod )
end
function ENT:LVSCalcView( ply, original_pos, original_angles, original_fov, pod )
local pos, angles, fov = self:CalcViewOverride( ply, original_pos, original_angles, original_fov, pod )
if self:GetDriverSeat() == pod then
return self:CalcViewDriver( ply, pos, angles, fov, pod )
else
return self:CalcViewPassenger( ply, pos, angles, fov, pod )
end
end

View File

@@ -0,0 +1,17 @@
function ENT:OnDestroyed()
if not self.DeathSound then return end
if self:GetVelocity():Length() <= self.MaxVelocity * 0.5 then return end
self._sndDeath = CreateSound( self, self.DeathSound )
self._sndDeath:SetSoundLevel( 125 )
self._sndDeath:PlayEx( 1, 50 + 50 * self:CalcDoppler( LocalPlayer() ) )
end
function ENT:StopDeathSound()
if not self._sndDeath then return end
self._sndDeath:Stop()
end

View File

@@ -0,0 +1,63 @@
ENT.FlyByAdvance = 0
function ENT:FlyByThink()
local ply = LocalPlayer()
if not IsValid( ply ) then return end
local EntTable = self:GetTable()
if ply:lvsGetVehicle() == self then EntTable.OldApproaching = false return end
local ViewEnt = ply:GetViewEntity()
if not IsValid( ViewEnt ) then return end
local Time = CurTime()
if (EntTable._nextflyby or 0) > Time then return end
EntTable._nextflyby = Time + 0.1
local Vel = self:GetVelocity()
if self:GetThrottle() <= 0.75 or Vel:Length() <= EntTable.MaxVelocity * 0.75 then return end
local Sub = ViewEnt:GetPos() - self:GetPos() - Vel * EntTable.FlyByAdvance
local ToPlayer = Sub:GetNormalized()
local VelDir = Vel:GetNormalized()
local ApproachAngle = math.deg( math.acos( math.Clamp( ToPlayer:Dot( VelDir ) ,-1,1) ) )
local Approaching = ApproachAngle < 80
if Approaching ~= EntTable.OldApproaching then
EntTable.OldApproaching = Approaching
if Approaching then
self:StopFlyBy()
else
self:OnFlyBy( 60 + 80 * math.min(ApproachAngle / 140,1) )
end
end
end
function ENT:OnFlyBy( Pitch )
if not self.FlyBySound then return end
local EntTable = self:GetTable()
EntTable.flybysnd = CreateSound( self, EntTable.FlyBySound )
EntTable.flybysnd:SetSoundLevel( 95 )
EntTable.flybysnd:PlayEx( 1, Pitch )
end
function ENT:StopFlyBy()
local EntTable = self:GetTable()
if not EntTable.flybysnd then return end
EntTable.flybysnd:Stop()
EntTable.flybysnd = nil
end

View File

@@ -0,0 +1,98 @@
ENT.IconEngine = Material( "lvs/engine.png" )
function ENT:LVSHudPaintInfoText( X, Y, W, H, ScrX, ScrY, ply )
local speed = math.Round( LVS:GetUnitValue( self:GetVelocity():Length() ) ,0)
draw.DrawText( LVS:GetUnitName().." ", "LVS_FONT", X + 72, Y + 35, color_white, TEXT_ALIGN_RIGHT )
draw.DrawText( speed, "LVS_FONT_HUD_LARGE", X + 72, Y + 20, color_white, TEXT_ALIGN_LEFT )
if ply ~= self:GetDriver() then return end
local Throttle = self:GetThrottle()
local Col = Throttle <= 1 and color_white or Color(0,0,0,255)
local hX = X + W - H * 0.5
local hY = Y + H * 0.25 + H * 0.25
surface.SetMaterial( self.IconEngine )
surface.SetDrawColor( 0, 0, 0, 200 )
surface.DrawTexturedRectRotated( hX + 4, hY + 1, H * 0.5, H * 0.5, 0 )
surface.SetDrawColor( color_white )
surface.DrawTexturedRectRotated( hX + 2, hY - 1, H * 0.5, H * 0.5, 0 )
if not self:GetEngineActive() then
draw.SimpleText( "X" , "LVS_FONT", hX, hY, Color(0,0,0,255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER )
end
self:LVSDrawCircle( hX, hY, H * 0.35, math.min( Throttle, 1 ) )
if Throttle > 1 then
draw.SimpleText( "+"..math.Round((Throttle - 1) * 100,0).."%" , "LVS_FONT", hX, hY, Col, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER )
end
end
function ENT:LVSPreHudPaint( X, Y, ply )
return true
end
function ENT:LVSHudPaint( X, Y, ply )
if not self:LVSPreHudPaint( X, Y, ply ) then return end
if ply ~= self:GetDriver() then return end
local HitPlane = self:GetEyeTrace( true ).HitPos:ToScreen()
local HitPilot = self:GetEyeTrace().HitPos:ToScreen()
local MouseAim = ply:lvsMouseAim()
if self:IsDrawingReflectorSight() then
self:DrawReflectorSight( HitPlane )
if MouseAim then
local LineVisible = false
if not ply:lvsKeyDown( "FREELOOK" ) then
LineVisible = self:LVSHudPaintMouseAim( HitPlane, HitPilot )
end
if LineVisible then
self:PaintCrosshairOuter( HitPilot )
end
end
self:LVSPaintHitMarker( HitPilot )
return
end
self:PaintCrosshairCenter( HitPlane )
self:PaintCrosshairOuter( HitPilot )
if MouseAim and not ply:lvsKeyDown( "FREELOOK" ) then
self:LVSHudPaintMouseAim( HitPlane, HitPilot )
end
self:LVSPaintHitMarker( HitPilot )
end
function ENT:LVSHudPaintDirectInput( Pos2D )
self:PaintCrosshairCenter( Pos2D )
self:PaintCrosshairOuter( Pos2D )
end
function ENT:LVSHudPaintMouseAim( HitPlane, HitPilot )
local Sub = Vector(HitPilot.x,HitPilot.y,0) - Vector(HitPlane.x,HitPlane.y,0)
local Len = Sub:Length()
if Len <= 20 then return false end
local Dir = Sub:GetNormalized()
surface.SetDrawColor( 255, 255, 255, 100 )
surface.DrawLine( HitPlane.x + Dir.x * 5, HitPlane.y + Dir.y * 5, HitPilot.x - Dir.x * 20, HitPilot.y- Dir.y * 20 )
-- shadow
surface.SetDrawColor( 0, 0, 0, 50 )
surface.DrawLine( HitPlane.x + Dir.x * 5 + 1, HitPlane.y + Dir.y * 5 + 1, HitPilot.x - Dir.x * 20+ 1, HitPilot.y- Dir.y * 20 + 1 )
return true
end

View File

@@ -0,0 +1,74 @@
include("shared.lua")
include("cl_camera.lua")
include("sh_camera_eyetrace.lua")
include("cl_hud.lua")
include("cl_flyby.lua")
include("cl_deathsound.lua")
include("cl_reflectorsight.lua")
local ExhaustSprite = Material( "effects/muzzleflash2" )
function ENT:DoExhaustFX()
local EntTable = self:GetTable()
local Throttle = self:GetThrottle()
local OffsetMagnitude = (8 + 4 * Throttle)
local T = CurTime()
local FT = RealFrameTime()
local HP = self:GetHP()
local MaxHP = self:GetMaxHP()
if HP <= 0 then return end
render.SetMaterial( ExhaustSprite )
local ShouldDoEffect = false
if (EntTable.NextFX or 0) < T then
EntTable.NextFX = T + 0.05 + (1 - Throttle) / 10
ShouldDoEffect = true
end
for id, data in pairs( EntTable.ExhaustPositions ) do
if not EntTable.ExhaustPositions[ id ].PosOffset then
EntTable.ExhaustPositions[ id ].PosOffset = 0
end
if not EntTable.ExhaustPositions[ id ].NextFX then
EntTable.ExhaustPositions[ id ].NextFX = 0
end
local Pos = self:LocalToWorld( data.pos )
local Dir = self:LocalToWorldAngles( data.ang ):Up()
self.ExhaustPositions[ id ].PosOffset = EntTable.ExhaustPositions[ id ].PosOffset + FT * (8 + 4 * Throttle)
if ShouldDoEffect then
if math.random(0,1) == 1 then
EntTable.ExhaustPositions[ id ].PosOffset = 0
local effectdata = EffectData()
effectdata:SetOrigin( Pos )
effectdata:SetNormal( Dir )
effectdata:SetMagnitude( Throttle )
effectdata:SetEntity( self )
if HP > MaxHP * 0.25 then
util.Effect( "lvs_exhaust", effectdata )
else
util.Effect( "lvs_exhaust_fire", effectdata )
end
end
end
if EntTable.ExhaustPositions[ id ].PosOffset > 1 or Throttle < 0.5 then continue end
local Size = math.min( 10 * (1 - EntTable.ExhaustPositions[ id ].PosOffset ) ^ 2, 5 + 5 * Throttle )
render.SetMaterial( ExhaustSprite )
render.DrawSprite( Pos + Dir * EntTable.ExhaustPositions[ id ].PosOffset * (5 + 5 * Throttle), Size, Size, color_white )
end
end

View File

@@ -0,0 +1,95 @@
ENT.ReflectorSight = false
ENT.ReflectorSightPos = vector_origin
ENT.ReflectorSightColor = color_white
ENT.ReflectorSightColorBG = color_black
ENT.ReflectorSightMaterial = Material("lvs/sights/german.png")
ENT.ReflectorSightMaterialRes = 128
ENT.ReflectorSightHeight = 3
ENT.ReflectorSightWidth = 1.5
ENT.ReflectorSightGlow = false
ENT.ReflectorSightGlowMaterial = Material( "sprites/light_glow02_add" )
ENT.ReflectorSightGlowMaterialRes = 600
ENT.ReflectorSightGlowColor = color_white
function ENT:PaintReflectorSight( Pos2D, Ang, Origin2D )
if self.ReflectorSightGlow then
surface.SetDrawColor( self.ReflectorSightGlowColor )
surface.SetMaterial( self.ReflectorSightGlowMaterial )
surface.DrawTexturedRectRotated( Pos2D.x, Pos2D.y, self.ReflectorSightGlowMaterialRes, self.ReflectorSightGlowMaterialRes, -Ang )
end
surface.SetDrawColor( self.ReflectorSightColor )
surface.SetMaterial( self.ReflectorSightMaterial )
surface.DrawTexturedRectRotated( Pos2D.x, Pos2D.y, self.ReflectorSightMaterialRes, self.ReflectorSightMaterialRes, -Ang )
end
function ENT:IsDrawingReflectorSight()
if not self.ReflectorSight then return false end
local Pod = self:GetDriverSeat()
if not IsValid( Pod ) then return false end
return not Pod:GetThirdPersonMode()
end
function ENT:DrawReflectorSight( Pos2D )
local Pos = self:LocalToWorld( self.ReflectorSightPos )
local Up = self:GetUp()
local Right = self:GetRight()
local Width = self.ReflectorSightWidth
local Height = self.ReflectorSightHeight
local TopLeft = (Pos + Up * Height - Right * Width):ToScreen()
local TopRight = (Pos + Up * Height + Right * Width):ToScreen()
local BottomLeft = (Pos - Right * Width):ToScreen()
local BottomRight = (Pos + Right * Width):ToScreen()
Pos = Pos:ToScreen()
if not Pos.visible then return end
local poly = {
{ x = TopLeft.x, y = TopLeft.y },
{ x = TopRight.x, y = TopRight.y },
{ x = BottomRight.x, y = BottomRight.y },
{ x = BottomLeft.x, y = BottomLeft.y },
}
local Ang = 0
if TopLeft.x < TopRight.x then
Ang = (Vector( TopLeft.x, 0, TopLeft.y ) - Vector( TopRight.x, 0, TopRight.y )):Angle().p
else
Ang = (Vector( TopRight.x, 0, TopRight.y ) - Vector( TopLeft.x, 0, TopLeft.y )):Angle().p - 180
end
draw.NoTexture()
surface.SetDrawColor( self.ReflectorSightColorBG )
surface.DrawPoly( poly )
render.SetStencilWriteMask( 0xFF )
render.SetStencilTestMask( 0xFF )
render.SetStencilReferenceValue( 0 )
render.SetStencilPassOperation( STENCIL_KEEP )
render.SetStencilZFailOperation( STENCIL_KEEP )
render.ClearStencil()
render.SetStencilEnable( true )
render.SetStencilReferenceValue( 1 )
render.SetStencilCompareFunction( STENCIL_NEVER )
render.SetStencilFailOperation( STENCIL_REPLACE )
draw.NoTexture()
surface.SetDrawColor( color_white )
surface.DrawPoly( poly )
render.SetStencilCompareFunction( STENCIL_EQUAL )
render.SetStencilFailOperation( STENCIL_KEEP )
self:PaintReflectorSight( Pos2D, Ang, Pos )
render.SetStencilEnable( false )
end

View File

@@ -0,0 +1,189 @@
AddCSLuaFile( "shared.lua" )
AddCSLuaFile( "cl_init.lua" )
AddCSLuaFile( "cl_camera.lua" )
AddCSLuaFile( "sh_camera_eyetrace.lua" )
AddCSLuaFile( "cl_hud.lua" )
AddCSLuaFile( "cl_flyby.lua" )
AddCSLuaFile( "cl_deathsound.lua" )
AddCSLuaFile( "cl_reflectorsight.lua" )
include("shared.lua")
include("sv_wheels.lua")
include("sv_landinggear.lua")
include("sv_components.lua")
include("sv_ai.lua")
include("sv_mouseaim.lua")
include("sh_camera_eyetrace.lua")
function ENT:OnCreateAI()
self:StartEngine()
self.COL_GROUP_OLD = self:GetCollisionGroup()
self:SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS )
end
function ENT:OnRemoveAI()
self:StopEngine()
self:SetCollisionGroup( self.COL_GROUP_OLD or COLLISION_GROUP_NONE )
end
function ENT:ApproachTargetAngle( TargetAngle, OverridePitch, OverrideYaw, OverrideRoll, FreeMovement )
local LocalAngles = self:WorldToLocalAngles( TargetAngle )
if self:GetAI() then self:SetAIAimVector( LocalAngles:Forward() ) end
local LocalAngPitch = LocalAngles.p
local LocalAngYaw = LocalAngles.y
local LocalAngRoll = LocalAngles.r
local TargetForward = TargetAngle:Forward()
local Forward = self:GetForward()
local AngDiff = math.deg( math.acos( math.Clamp( Forward:Dot( TargetForward ) ,-1,1) ) )
local WingFinFadeOut = math.max( (90 - AngDiff ) / 90, 0 )
local RudderFadeOut = math.min( math.max( (120 - AngDiff ) / 120, 0 ) * 3, 1 )
local AngVel = self:GetPhysicsObject():GetAngleVelocity()
local SmoothPitch = math.Clamp( math.Clamp(AngVel.y / 100,-0.25,0.25) / math.abs( LocalAngPitch ), -1, 1 )
local SmoothYaw = math.Clamp( math.Clamp(AngVel.z / 100,-0.25,0.25) / math.abs( LocalAngYaw ), -1, 1 )
local Pitch = math.Clamp( -LocalAngPitch / 10 + SmoothPitch, -1, 1 )
local Yaw = math.Clamp( -LocalAngYaw / 2 + SmoothYaw,-1,1) * RudderFadeOut
local Roll = math.Clamp( (-math.Clamp(LocalAngYaw * 16,-90,90) + LocalAngRoll * RudderFadeOut * 0.75) * WingFinFadeOut / 180 , -1 , 1 )
if FreeMovement then
Roll = math.Clamp( -LocalAngYaw * WingFinFadeOut / 180 , -1 , 1 )
end
if OverridePitch and OverridePitch ~= 0 then
Pitch = OverridePitch
end
if OverrideYaw and OverrideYaw ~= 0 then
Yaw = OverrideYaw
end
if OverrideRoll and OverrideRoll ~= 0 then
Roll = OverrideRoll
end
self:SetSteer( Vector( math.Clamp(Roll * 1.25,-1,1), math.Clamp(-Pitch * 1.25,-1,1), -Yaw) )
end
function ENT:CalcAero( phys, deltatime, EntTable )
if not EntTable then
EntTable = self:GetTable()
end
-- mouse aim needs to run at high speed.
if self:GetAI() then
if EntTable._lvsAITargetAng then
self:ApproachTargetAngle( EntTable._lvsAITargetAng )
end
else
local ply = self:GetDriver()
if IsValid( ply ) and ply:lvsMouseAim() then
self:PlayerMouseAim( ply )
end
end
local WorldGravity = self:GetWorldGravity()
local WorldUp = self:GetWorldUp()
local Steer = self:GetSteer()
local Stability, InvStability, ForwardVelocity = self:GetStability()
local Forward = self:GetForward()
local Left = -self:GetRight()
local Up = self:GetUp()
local Vel = self:GetVelocity()
local VelForward = Vel:GetNormalized()
local PitchPull = math.max( (math.deg( math.acos( math.Clamp( WorldUp:Dot( Up ) ,-1,1) ) ) - 90) / 90, 0 )
local YawPull = (math.deg( math.acos( math.Clamp( WorldUp:Dot( Left ) ,-1,1) ) ) - 90) / 90
local GravMul = (WorldGravity / 600) * 0.25
-- crash behavior
if self:IsDestroyed() then
Steer = phys:GetAngleVelocity() / 200
PitchPull = (math.deg( math.acos( math.Clamp( WorldUp:Dot( Up ) ,-1,1) ) ) - 90) / 90
GravMul = WorldGravity / 600
end
local GravityPitch = math.abs( PitchPull ) ^ 1.25 * self:Sign( PitchPull ) * GravMul * EntTable.GravityTurnRatePitch
local GravityYaw = math.abs( YawPull ) ^ 1.25 * self:Sign( YawPull ) * GravMul * EntTable.GravityTurnRateYaw
local StallMul = math.min( (-math.min(Vel.z + EntTable.StallVelocity,0) / 100) * EntTable.StallForceMultiplier, EntTable.StallForceMax )
local StallPitch = 0
local StallYaw = 0
if StallMul > 0 then
if InvStability < 1 then
StallPitch = PitchPull* GravMul * StallMul
StallYaw = YawPull * GravMul * StallMul
else
local StallPitchDir = self:Sign( math.deg( math.acos( math.Clamp( -VelForward:Dot( self:LocalToWorldAngles( Angle(-10,0,0) ):Up() ) ,-1,1) ) ) - 90 )
local StallYawDir = self:Sign( math.deg( math.acos( math.Clamp( -VelForward:Dot( Left ) ,-1,1) ) ) - 90 )
local StallPitchPull = ((90 - math.abs( math.deg( math.acos( math.Clamp( -WorldUp:Dot( Up ) ,-1,1) ) ) - 90 )) / 90) * StallPitchDir
local StallYawPull = ((90 - math.abs( math.deg( math.acos( math.Clamp( -WorldUp:Dot( Left ) ,-1,1) ) ) - 90 )) / 90) * StallYawDir * 0.5
StallPitch = StallPitchPull * GravMul * StallMul
StallYaw = StallYawPull * GravMul * StallMul
end
end
local Pitch = math.Clamp(Steer.y - GravityPitch,-1,1) * EntTable.TurnRatePitch * 3 * Stability - StallPitch * InvStability
local Yaw = math.Clamp(Steer.z * 4 + GravityYaw,-1,1) * EntTable.TurnRateYaw * Stability + StallYaw * InvStability
local Roll = math.Clamp(Steer.x * 1.5,-1,1) * EntTable.TurnRateRoll * 12 * Stability
self:HandleLandingGear( deltatime )
self:SetWheelSteer( Steer.z * EntTable.WheelSteerAngle )
local VelL = self:WorldToLocal( self:GetPos() + Vel )
local SlipMul = 1 - math.Clamp( math.max( math.abs( VelL.x ) - EntTable.MaxPerfVelocity, 0 ) / math.max(EntTable.MaxVelocity - EntTable.MaxPerfVelocity, 0 ),0,1)
local MulZ = (math.max( math.deg( math.acos( math.Clamp( VelForward:Dot( Forward ) ,-1,1) ) ) - EntTable.MaxSlipAnglePitch * SlipMul * math.abs( Steer.y ), 0 ) / 90) * 0.3
local MulY = (math.max( math.abs( math.deg( math.acos( math.Clamp( VelForward:Dot( Left ) ,-1,1) ) ) - 90 ) - EntTable.MaxSlipAngleYaw * SlipMul * math.abs( Steer.z ), 0 ) / 90) * 0.15
local Lift = -math.min( (math.deg( math.acos( math.Clamp( WorldUp:Dot( Up ) ,-1,1) ) ) - 90) / 180,0) * (WorldGravity / (1 / deltatime))
return Vector(0, -VelL.y * MulY, Lift - VelL.z * MulZ ) * Stability, Vector( Roll, Pitch, Yaw )
end
function ENT:OnSkyCollide( data, PhysObj )
local NewVelocity = self:VectorSubtractNormal( data.HitNormal, data.OurOldVelocity ) - data.HitNormal * math.Clamp(self:GetThrustStrenght() * self.MaxThrust,250,800)
PhysObj:SetVelocityInstantaneous( NewVelocity )
PhysObj:SetAngleVelocityInstantaneous( data.OurOldAngularVelocity )
self:FreezeStability()
return true
end
function ENT:PhysicsSimulate( phys, deltatime )
local EntTable = self:GetTable()
local Aero, Torque = self:CalcAero( phys, deltatime, EntTable )
if self:GetEngineActive() then phys:Wake() end
local Thrust = math.max( self:GetThrustStrenght(), 0 ) * EntTable.MaxThrust * 100
local ForceLinear = (Aero * 10000 * EntTable.ForceLinearMultiplier + Vector(Thrust,0,0)) * deltatime
local ForceAngle = (Torque * 25 * EntTable.ForceAngleMultiplier - phys:GetAngleVelocity() * 1.5 * EntTable.ForceAngleDampingMultiplier) * deltatime * 250
return self:PhysicsSimulateOverride( ForceAngle, ForceLinear, phys, deltatime, SIM_LOCAL_ACCELERATION )
end
function ENT:PhysicsSimulateOverride( ForceAngle, ForceLinear, phys, deltatime, simulate )
return ForceAngle, ForceLinear, simulate
end

View File

@@ -0,0 +1,58 @@
function ENT:GetEyeTrace( trace_forward )
local startpos = self:LocalToWorld( self:OBBCenter() )
local pod = self:GetDriverSeat()
if IsValid( pod ) then
startpos = pod:LocalToWorld( pod:OBBCenter() )
end
local AimVector = trace_forward and self:GetForward() or self:GetAimVector()
local data = {
start = startpos,
endpos = (startpos + AimVector * 50000),
filter = self:GetCrosshairFilterEnts(),
}
local trace = util.TraceLine( data )
return trace
end
function ENT:GetAimVector()
if self:GetAI() then
return self:GetAIAimVector()
end
local Driver = self:GetDriver()
if not IsValid( Driver ) then return self:GetForward() end
if not Driver:lvsMouseAim() then
if Driver:lvsKeyDown( "FREELOOK" ) then
local pod = self:GetDriverSeat()
if not IsValid( pod ) then return Driver:EyeAngles():Forward() end
if pod:GetThirdPersonMode() then
return -self:GetForward()
else
return Driver:GetAimVector()
end
else
return self:GetForward()
end
end
if SERVER then
local pod = self:GetDriverSeat()
if not IsValid( pod ) then return Driver:EyeAngles():Forward() end
return pod:WorldToLocalAngles( Driver:EyeAngles() ):Forward()
else
return Driver:EyeAngles():Forward()
end
end

View File

@@ -0,0 +1,182 @@
ENT.Base = "lvs_base"
ENT.PrintName = "[LVS] Base Fighter Plane"
ENT.Author = "Luna"
ENT.Information = "Luna's Vehicle Script"
ENT.Category = "[LVS]"
ENT.Spawnable = false
ENT.AdminSpawnable = false
ENT.MaxVelocity = 2500
ENT.MaxPerfVelocity = 1500
ENT.MaxThrust = 250
ENT.ThrottleRateUp = 0.6
ENT.ThrottleRateDown = 0.3
ENT.TurnRatePitch = 1
ENT.TurnRateYaw = 1
ENT.TurnRateRoll = 1
ENT.GravityTurnRatePitch = 1
ENT.GravityTurnRateYaw = 1
ENT.ForceLinearMultiplier = 1
ENT.ForceAngleMultiplier = 1
ENT.ForceAngleDampingMultiplier = 1
ENT.MaxSlipAnglePitch = 20
ENT.MaxSlipAngleYaw = 10
ENT.StallVelocity = 150
ENT.StallForceMultiplier = 4
ENT.StallForceMax = 40
function ENT:SetupDataTables()
self:CreateBaseDT()
self:AddDT( "Vector", "Steer" )
self:AddDT( "Vector", "AIAimVector" )
self:AddDT( "Float", "NWThrottle" )
self:AddDT( "Float", "MaxThrottle" )
self:AddDT( "Float", "LandingGear" )
if SERVER then
self:SetLandingGear( 1 )
self:SetMaxThrottle( 1 )
end
end
function ENT:PlayerDirectInput( ply, cmd )
local Pod = self:GetDriverSeat()
local Delta = FrameTime()
local KeyLeft = ply:lvsKeyDown( "-ROLL" )
local KeyRight = ply:lvsKeyDown( "+ROLL" )
local KeyPitchUp = ply:lvsKeyDown( "+PITCH" )
local KeyPitchDown = ply:lvsKeyDown( "-PITCH" )
local KeyRollRight = ply:lvsKeyDown( "+YAW" )
local KeyRollLeft = ply:lvsKeyDown( "-YAW" )
local MouseX = cmd:GetMouseX()
local MouseY = cmd:GetMouseY()
if ply:lvsKeyDown( "FREELOOK" ) and not Pod:GetThirdPersonMode() then
MouseX = 0
MouseY = 0
else
ply:SetEyeAngles( Angle(0,90,0) )
end
local SensX, SensY, ReturnDelta = ply:lvsMouseSensitivity()
if KeyPitchDown then MouseY = (10 / SensY) * ReturnDelta end
if KeyPitchUp then MouseY = -(10 / SensY) * ReturnDelta end
if KeyRollRight or KeyRollLeft then
local NewX = (KeyRollRight and 10 or 0) - (KeyRollLeft and 10 or 0)
MouseX = (NewX / SensX) * ReturnDelta
end
local Input = Vector( MouseX * 0.4 * SensX, MouseY * SensY, 0 )
local Cur = self:GetSteer()
local Rate = Delta * 3 * ReturnDelta
local New = Vector(Cur.x, Cur.y, 0) - Vector( math.Clamp(Cur.x * Delta * 5 * ReturnDelta,-Rate,Rate), math.Clamp(Cur.y * Delta * 5 * ReturnDelta,-Rate,Rate), 0)
local Target = New + Input * Delta * 0.8
local Fx = math.Clamp( Target.x, -1, 1 )
local Fy = math.Clamp( Target.y, -1, 1 )
local TargetFz = (KeyLeft and 1 or 0) - (KeyRight and 1 or 0)
local Fz = Cur.z + math.Clamp(TargetFz - Cur.z,-Rate * 3,Rate * 3)
local F = Cur + (Vector( Fx, Fy, Fz ) - Cur) * math.min(Delta * 100,1)
self:SetSteer( F )
end
function ENT:CalcThrottle( ply, cmd )
if CLIENT then return end
local Delta = FrameTime()
local ThrottleUp = ply:lvsKeyDown( "+THROTTLE" ) and self.ThrottleRateUp or 0
local ThrottleDown = ply:lvsKeyDown( "-THROTTLE" ) and -self.ThrottleRateDown or 0
local Throttle = (ThrottleUp + ThrottleDown) * Delta
self:SetThrottle( self:GetThrottle() + Throttle )
end
function ENT:SetThrottle( NewThrottle )
if self:GetEngineActive() then
self:SetNWThrottle( math.Clamp(NewThrottle,0,self:GetMaxThrottle()) )
else
self:SetNWThrottle( 0 )
end
end
function ENT:GetThrottle()
if self:GetEngineActive() then
return self:GetNWThrottle()
else
return 0
end
end
function ENT:StartCommand( ply, cmd )
if self:GetDriver() ~= ply then return end
if SERVER and not self.WheelAutoRetract then
local KeyJump = ply:lvsKeyDown( "VSPEC" )
if self._lvsOldKeyJump ~= KeyJump then
self._lvsOldKeyJump = KeyJump
if KeyJump then
self:ToggleLandingGear()
self:PhysWake()
end
end
end
if not ply:lvsMouseAim() then
self:PlayerDirectInput( ply, cmd )
end
self:CalcThrottle( ply, cmd )
end
function ENT:FreezeStability()
self._StabilityFrozen = CurTime() + 2
end
function ENT:GetStability()
if (self._StabilityFrozen or 0) > CurTime() then
return 1, 0, self.MaxPerfVelocity
end
local ForwardVelocity = self:WorldToLocal( self:GetPos() + self:GetVelocity() ).x
local Stability = math.Clamp(ForwardVelocity / self.MaxPerfVelocity,0,1) ^ 2
local InvStability = 1 - Stability
return Stability, InvStability, ForwardVelocity
end
function ENT:GetThrustStrenght()
local ForwardVelocity = self:WorldToLocal( self:GetPos() + self:GetVelocity() ).x
return (self.MaxVelocity - ForwardVelocity) * self:GetThrottle() / self.MaxVelocity
end
function ENT:GetVehicleType()
return "plane"
end

View File

@@ -0,0 +1,203 @@
function ENT:OnCreateAI()
self:StartEngine()
end
function ENT:OnRemoveAI()
self:StopEngine()
end
function ENT:RunAI()
local RangerLength = 15000
local mySpeed = self:GetVelocity():Length()
local MinDist = 600 + mySpeed
local StartPos = self:LocalToWorld( self:OBBCenter() )
local TraceFilter = self:GetCrosshairFilterEnts()
local FrontLeft = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(0,20,0) ):Forward() * RangerLength } )
local FrontRight = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(0,-20,0) ):Forward() * RangerLength } )
local FrontLeft2 = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(25,65,0) ):Forward() * RangerLength } )
local FrontRight2 = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(25,-65,0) ):Forward() * RangerLength } )
local FrontLeft3 = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(-25,65,0) ):Forward() * RangerLength } )
local FrontRight3 = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(-25,-65,0) ):Forward() * RangerLength } )
local FrontUp = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(-20,0,0) ):Forward() * RangerLength } )
local FrontDown = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(20,0,0) ):Forward() * RangerLength } )
local TraceForward = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:GetForward() * RangerLength } )
local TraceDown = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + Vector(0,0,-RangerLength) } )
local TraceUp = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + Vector(0,0,RangerLength) } )
local cAvoid = Vector(0,0,0)
local myRadius = self:BoundingRadius()
local myPos = self:GetPos()
local myDir = self:GetForward()
for _, v in pairs( LVS:GetVehicles() ) do
if v == self then continue end
local theirRadius = v:BoundingRadius()
local Sub = (myPos - v:GetPos())
local Dir = Sub:GetNormalized()
local Dist = Sub:Length()
if Dist < (theirRadius + myRadius + 200) then
if math.deg( math.acos( math.Clamp( myDir:Dot( -Dir ) ,-1,1) ) ) < 90 then
cAvoid = cAvoid + Dir * (theirRadius + myRadius + 500)
end
end
end
local FLp = FrontLeft.HitPos + FrontLeft.HitNormal * MinDist + cAvoid * 8
local FRp = FrontRight.HitPos + FrontRight.HitNormal * MinDist + cAvoid * 8
local FL2p = FrontLeft2.HitPos + FrontLeft2.HitNormal * MinDist
local FR2p = FrontRight2.HitPos + FrontRight2.HitNormal * MinDist
local FL3p = FrontLeft3.HitPos + FrontLeft3.HitNormal * MinDist
local FR3p = FrontRight3.HitPos + FrontRight3.HitNormal * MinDist
local FUp = FrontUp.HitPos + FrontUp.HitNormal * MinDist
local FDp = FrontDown.HitPos + FrontDown.HitNormal * MinDist
local Up = TraceUp.HitPos + TraceUp.HitNormal * MinDist
local Dp = TraceDown.HitPos + TraceDown.HitNormal * MinDist
local TargetPos = (FLp+FRp+FL2p+FR2p+FL3p+FR3p+FUp+FDp+Up+Dp) / 10
local alt = (StartPos - TraceDown.HitPos):Length()
local ceiling = (StartPos - TraceUp.HitPos):Length()
local WallDist = (StartPos - TraceForward.HitPos):Length()
local Throttle = math.min( WallDist / mySpeed, 1 )
self._AIFireInput = false
if alt < 600 or ceiling < 600 or WallDist < (MinDist * 3 * (math.deg( math.acos( math.Clamp( Vector(0,0,1):Dot( myDir ) ,-1,1) ) ) / 180) ^ 2) then
if ceiling < 600 then
Throttle = 0
else
Throttle = 1
if self:HitGround() then
TargetPos.z = StartPos.z + 750
else
if self:GetStability() < 0.5 then
TargetPos.z = StartPos.z + 1500
end
end
end
else
if self:GetStability() < 0.5 then
TargetPos.z = StartPos.z + 600
else
if IsValid( self:GetHardLockTarget() ) then
TargetPos = self:GetHardLockTarget():GetPos() + cAvoid * 8
else
if alt > mySpeed then
local Target = self._LastAITarget
if not IsValid( self._LastAITarget ) or not self:AITargetInFront( self._LastAITarget, 135 ) or not self:AICanSee( self._LastAITarget ) then
Target = self:AIGetTarget()
end
if IsValid( Target ) then
if self:AITargetInFront( Target, 65 ) then
local T = CurTime() + self:EntIndex() * 1337
TargetPos = Target:GetPos() + cAvoid * 8 + Vector(0,0, math.sin( T * 5 ) * 500 ) + Target:GetVelocity() * math.abs( math.cos( T * 13.37 ) ) * 5
Throttle = math.min( (StartPos - TargetPos):Length() / mySpeed, 1 )
local tr = util.TraceHull( {
start = StartPos,
endpos = (StartPos + self:GetForward() * 50000),
mins = Vector( -50, -50, -50 ),
maxs = Vector( 50, 50, 50 ),
filter = TraceFilter
} )
local CanShoot = (IsValid( tr.Entity ) and tr.Entity.LVS and tr.Entity.GetAITEAM) and (tr.Entity:GetAITEAM() ~= self:GetAITEAM() or tr.Entity:GetAITEAM() == 0) or true
if CanShoot and self:AITargetInFront( Target, 22 ) then
local CurHeat = self:GetNWHeat()
local CurWeapon = self:GetSelectedWeapon()
if CurWeapon > 2 then
self:AISelectWeapon( 1 )
else
if CurHeat > 0.9 then
if CurWeapon == 1 and self:AIHasWeapon( 2 ) then
self:AISelectWeapon( 2 )
elseif CurWeapon == 2 then
self:AISelectWeapon( 1 )
end
else
if CurHeat == 0 and math.cos( T ) > 0 then
self:AISelectWeapon( 1 )
end
end
end
self._AIFireInput = true
end
else
self:AISelectWeapon( 1 )
if alt > 6000 and self:AITargetInFront( Target, 90 ) then
TargetPos = Target:GetPos()
end
end
end
else
TargetPos.z = StartPos.z + 2000
end
end
end
self:RaiseLandingGear()
end
self:SetThrottle( Throttle )
self.smTargetPos = self.smTargetPos and self.smTargetPos + (TargetPos - self.smTargetPos) * FrameTime() or self:GetPos()
self._lvsAITargetAng = (self.smTargetPos - self:GetPos()):GetNormalized():Angle()
end
function ENT:AISelectWeapon( ID )
if ID == self:GetSelectedWeapon() then return end
local T = CurTime()
if (self._nextAISwitchWeapon or 0) > T then return end
self._nextAISwitchWeapon = T + math.random(3,6)
self:SelectWeapon( ID )
end
function ENT:OnAITakeDamage( dmginfo )
local attacker = dmginfo:GetAttacker()
if not IsValid( attacker ) then return end
if not self:AITargetInFront( attacker, IsValid( self:AIGetTarget() ) and 120 or 45 ) then
self:SetHardLockTarget( attacker )
end
end
function ENT:SetHardLockTarget( target )
self._HardLockTarget = target
self._HardLockTime = CurTime() + 4
end
function ENT:GetHardLockTarget()
if (self._HardLockTime or 0) < CurTime() then return NULL end
return self._HardLockTarget
end

View File

@@ -0,0 +1,100 @@
function ENT:AddThruster( pos )
local Thruster = ents.Create( "lvs_fighterplane_thruster" )
if not IsValid( Thruster ) then
self:Remove()
print("LVS: Failed to create thruster entity. Vehicle terminated.")
return
end
Thruster:SetPos( self:LocalToWorld( pos ) )
Thruster:SetAngles( self:GetAngles() )
Thruster:Spawn()
Thruster:Activate()
Thruster:SetParent( self )
Thruster:SetBase( self )
self:DeleteOnRemove( Thruster )
self:TransferCPPI( Thruster )
return Thruster
end
function ENT:AddEngine( pos )
local Engine = ents.Create( "lvs_fighterplane_engine" )
if not IsValid( Engine ) then
self:Remove()
print("LVS: Failed to create engine entity. Vehicle terminated.")
return
end
Engine:SetPos( self:LocalToWorld( pos ) )
Engine:SetAngles( self:GetAngles() )
Engine:Spawn()
Engine:Activate()
Engine:SetParent( self )
Engine:SetBase( self )
self:DeleteOnRemove( Engine )
self:TransferCPPI( Engine )
self:AddDS( {
pos = pos,
ang = Angle(0,0,0),
mins = Vector(-40,-20,-30),
maxs = Vector(40,20,30),
Callback = function( tbl, ent, dmginfo )
dmginfo:ScaleDamage( 2.5 )
end
} )
return Engine
end
function ENT:AddRotor( pos )
local Rotor = ents.Create( "lvs_fighterplane_rotor" )
if not IsValid( Rotor ) then
self:Remove()
print("LVS: Failed to create rotor entity. Vehicle terminated.")
return
end
Rotor:SetPos( self:LocalToWorld( pos ) )
Rotor:SetAngles( self:GetAngles() )
Rotor:Spawn()
Rotor:Activate()
Rotor:SetParent( self )
Rotor:SetBase( self )
if self:BoundingRadius() >= 600 then
Rotor:SetSound("lvs/vehicles/generic/bomber_propeller.wav")
Rotor:SetSoundStrain("lvs/vehicles/generic/bomber_propeller_strain.wav")
end
self:DeleteOnRemove( Rotor )
self:TransferCPPI( Rotor )
return Rotor
end
function ENT:AddExhaust( pos, ang )
if not istable( self.ExhaustPositions ) then self.ExhaustPositions = {} end
local Exhaust = {
pos = pos,
ang = ang,
}
table.insert( self.ExhaustPositions, Exhaust )
end

View File

@@ -0,0 +1,67 @@
function ENT:HandleLandingGear( Rate )
local EnableBrakes = self:GetThrottle() <= 0
local Cur = self:GetLandingGear()
if self.WheelAutoRetract then
if self:HitGround() then
self.LandingGearUp = false
else
self.LandingGearUp = self:GetStability() > 0.4
end
end
local New = Cur + math.Clamp((self.LandingGearUp and 0 or 1) - Cur,-Rate,Rate)
local SetValue = Cur ~= New
if SetValue then
self:SetLandingGear( New )
end
for _, data in pairs( self:GetWheels() ) do
local wheel = data.entity
local mass = data.mass
local physobj = data.physobj
if not IsValid( wheel ) or not IsValid( physobj ) then continue end
wheel:SetBrakes( EnableBrakes )
if not SetValue then continue end
physobj:SetMass( 1 + (mass - 1) * New ^ 4 )
end
end
function ENT:ToggleLandingGear()
if self.WheelAutoRetract then return end
self.LandingGearUp = not self.LandingGearUp
self:OnLandingGearToggled( self.LandingGearUp )
end
function ENT:RaiseLandingGear()
if self.WheelAutoRetract then return end
if not self.LandingGearUp then
self.LandingGearUp = true
self:OnLandingGearToggled( self.LandingGearUp )
end
end
function ENT:DeployLandingGear()
if self.WheelAutoRetract then return end
if self.LandingGearUp then
self.LandingGearUp = false
self:OnLandingGearToggled( self.LandingGearUp )
end
end
function ENT:OnLandingGearToggled( IsDeployed )
end

View File

@@ -0,0 +1,45 @@
function ENT:PlayerMouseAim( ply )
local Pod = self:GetDriverSeat()
local PitchUp = ply:lvsKeyDown( "+PITCH" )
local PitchDown = ply:lvsKeyDown( "-PITCH" )
local YawRight = ply:lvsKeyDown( "+YAW" )
local YawLeft = ply:lvsKeyDown( "-YAW" )
local RollRight = ply:lvsKeyDown( "+ROLL" )
local RollLeft = ply:lvsKeyDown( "-ROLL" )
local FreeLook = ply:lvsKeyDown( "FREELOOK" )
local EyeAngles = Pod:WorldToLocalAngles( ply:EyeAngles() )
if FreeLook then
if isangle( self.StoredEyeAngles ) then
EyeAngles = self.StoredEyeAngles
end
else
self.StoredEyeAngles = EyeAngles
end
local OverridePitch = 0
local OverrideYaw = 0
local OverrideRoll = (RollRight and 1 or 0) - (RollLeft and 1 or 0)
if PitchUp or PitchDown then
EyeAngles = self:GetAngles()
self.StoredEyeAngles = Angle(EyeAngles.p,EyeAngles.y,0)
OverridePitch = (PitchUp and 1 or 0) - (PitchDown and 1 or 0)
end
if YawRight or YawLeft then
EyeAngles = self:GetAngles()
self.StoredEyeAngles = Angle(EyeAngles.p,EyeAngles.y,0)
OverrideYaw = (YawRight and 1 or 0) - (YawLeft and 1 or 0)
end
self:ApproachTargetAngle( EyeAngles, OverridePitch, OverrideYaw, OverrideRoll, FreeLook )
end

View File

@@ -0,0 +1,202 @@
ENT.WheelSteerAngle = 45
function ENT:AddWheelSteeringPlate( rear )
if rear then
if IsValid( self._lvsSteerPlateRear ) then
return self._lvsSteerPlateRear
end
else
if IsValid( self._lvsSteerPlate ) then
return self._lvsSteerPlate
end
end
local SteerMaster = ents.Create( "prop_physics" )
if not IsValid( SteerMaster ) then
self:Remove()
print("LVS: Failed to initialize steering plate. Vehicle terminated.")
return
end
SteerMaster:SetModel( "models/hunter/plates/plate025x025.mdl" )
SteerMaster:SetPos( self:GetPos() )
SteerMaster:SetAngles( Angle(0,90,0) )
SteerMaster:Spawn()
SteerMaster:Activate()
local PhysObj = SteerMaster:GetPhysicsObject()
if IsValid( PhysObj ) then
PhysObj:EnableMotion( false )
else
self:Remove()
print("LVS: Failed to initialize steering plate. Vehicle terminated.")
return
end
SteerMaster:SetOwner( self )
SteerMaster:DrawShadow( false )
SteerMaster:SetNotSolid( true )
SteerMaster:SetNoDraw( true )
SteerMaster.DoNotDuplicate = true
self:DeleteOnRemove( SteerMaster )
self:TransferCPPI( SteerMaster )
if rear then
self._lvsSteerPlateRear = SteerMaster
else
self._lvsSteerPlate = SteerMaster
end
return SteerMaster
end
function ENT:SetWheelSteer( SteerAngle )
if IsValid( self._lvsSteerPlate ) then
local PhysObj = self._lvsSteerPlate:GetPhysicsObject()
if IsValid( PhysObj ) then
if PhysObj:IsMotionEnabled() then
PhysObj:EnableMotion( false )
end
end
self._lvsSteerPlate:SetAngles( self:LocalToWorldAngles( Angle(0,math.Clamp(SteerAngle,-self.WheelSteerAngle,self.WheelSteerAngle),0) ) )
end
if not IsValid( self._lvsSteerPlateRear ) then return end
local PhysObj = self._lvsSteerPlateRear:GetPhysicsObject()
if not IsValid( PhysObj ) then return end
if PhysObj:IsMotionEnabled() then
PhysObj:EnableMotion( false )
end
self._lvsSteerPlateRear:SetAngles( self:LocalToWorldAngles( Angle(0,math.Clamp(-SteerAngle,-self.WheelSteerAngle,self.WheelSteerAngle),0) ) )
end
function ENT:GetWheels()
if not istable( self._lvsWheels ) then self._lvsWheels = {} end
return self._lvsWheels
end
function ENT:AddWheel( pos, radius, mass, type )
if not isvector( pos ) or not isnumber( radius ) or not isnumber( mass ) then return end
if not type then
type = LVS.WHEEL_BRAKE
end
local wheel = ents.Create( "lvs_fighterplane_wheel" )
if not IsValid( wheel ) then
self:Remove()
print("LVS: Failed to initialize wheel. Vehicle terminated.")
return
end
local WheelPos = self:LocalToWorld( pos )
local CenterPos = self:LocalToWorld( self:OBBCenter() )
debugoverlay.Sphere( WheelPos, radius, 5, Color(150,150,150), true )
debugoverlay.Line( CenterPos, WheelPos, 5, Color(150,150,150), true )
wheel:SetPos( WheelPos )
wheel:SetAngles( self:LocalToWorldAngles( Angle(0,90,0) ) )
wheel:Spawn()
wheel:Activate()
wheel:SetBase( self )
wheel:Define(
{
physmat = "jeeptire",
radius = radius,
mass = mass,
brake = type == LVS.WHEEL_BRAKE,
}
)
local PhysObj = wheel:GetPhysicsObject()
if not IsValid( PhysObj ) then
self:Remove()
print("LVS: Failed to initialize wheel phys model. Vehicle terminated.")
return
end
PhysObj:EnableMotion( false )
self:DeleteOnRemove( wheel )
self:TransferCPPI( wheel )
if type == LVS.WHEEL_STEER_NONE then
local ballsocket = constraint.AdvBallsocket(wheel, self,0,0,Vector(0,0,0),Vector(0,0,0),0,0, -180, -180, -180, 180, 180, 180, 0, 0, 0, 0, 1)
ballsocket.DoNotDuplicate = true
self:TransferCPPI( ballsocket )
end
if type == LVS.WHEEL_BRAKE then
local axis = constraint.Axis( wheel, self, 0, 0, PhysObj:GetMassCenter(), wheel:GetPos(), 0, 0, 0, 0, Vector(1,0,0) , false )
axis.DoNotDuplicate = true
self:TransferCPPI( axis )
wheel:SetBrakes( true )
end
if type == LVS.WHEEL_STEER_FRONT then
wheel:SetAngles( Angle(0,0,0) )
local SteerMaster = self:AddWheelSteeringPlate( false )
local ballsocket1 = constraint.AdvBallsocket(wheel, SteerMaster,0,0,Vector(0,0,0),Vector(0,0,0),0,0, -180, -0.01, -0.01, 180, 0.01, 0.01, 0, 0, 0, 1, 0)
ballsocket1.DoNotDuplicate = true
self:TransferCPPI( ballsocket1 )
local ballsocket2 = constraint.AdvBallsocket(wheel,self,0,0,Vector(0,0,0),Vector(0,0,0),0,0, -180, -180, -180, 180, 180, 180, 0, 0, 0, 0, 0)
ballsocket2.DoNotDuplicate = true
self:TransferCPPI( ballsocket2 )
end
if type == LVS.WHEEL_STEER_REAR then
wheel:SetAngles( Angle(0,0,0) )
local SteerMaster = self:AddWheelSteeringPlate( true )
local ballsocket1 = constraint.AdvBallsocket(wheel, SteerMaster,0,0,Vector(0,0,0),Vector(0,0,0),0,0, -180, -0.01, -0.01, 180, 0.01, 0.01, 0, 0, 0, 1, 0)
ballsocket1.DoNotDuplicate = true
self:TransferCPPI( ballsocket1 )
local ballsocket2 = constraint.AdvBallsocket(wheel,self,0,0,Vector(0,0,0),Vector(0,0,0),0,0, -180, -180, -180, 180, 180, 180, 0, 0, 0, 0, 0)
ballsocket2.DoNotDuplicate = true
self:TransferCPPI( ballsocket2 )
end
local nocollide = constraint.NoCollide( wheel, self, 0, 0 )
nocollide.DoNotDuplicate = true
self:TransferCPPI( nocollide )
PhysObj:EnableMotion( true )
PhysObj:EnableDrag( false )
local WheelData = {
entity = wheel,
physobj = PhysObj,
mass = mass,
}
if not istable( self._lvsWheels ) then self._lvsWheels = {} end
table.insert( self._lvsWheels, WheelData )
return wheel
end

View File

@@ -0,0 +1,75 @@
include("shared.lua")
function ENT:Think()
end
function ENT:OnRemove()
end
function ENT:Draw()
end
function ENT:DrawTranslucent()
end
function ENT:GetAimVector()
if self:GetAI() then
return self:GetNWAimVector()
end
local Driver = self:GetDriver()
if IsValid( Driver ) then
if self._AimVectorUnlocked then
local pod = self:GetDriverSeat()
if IsValid( pod ) then
return pod:WorldToLocalAngles( Driver:EyeAngles() ):Forward()
end
end
return Driver:GetAimVector()
else
return self:GetForward()
end
end
function ENT:LVSPaintHitMarker( scr )
local Base = self:GetVehicle()
if not IsValid( Base ) then return end
Base:LVSPaintHitMarker( scr )
end
function ENT:LVSDrawCircle( X, Y, target_radius, value )
local Base = self:GetVehicle()
if not IsValid( Base ) then return end
Base:LVSDrawCircle( X, Y, target_radius, value )
end
function ENT:PaintCrosshairCenter( Pos2D, Col )
local Base = self:GetVehicle()
if not IsValid( Base ) then return end
Base:PaintCrosshairCenter( Pos2D, Col )
end
function ENT:PaintCrosshairOuter( Pos2D, Col )
local Base = self:GetVehicle()
if not IsValid( Base ) then return end
Base:PaintCrosshairOuter( Pos2D, Col )
end
function ENT:PaintCrosshairSquare( Pos2D, Col )
local Base = self:GetVehicle()
if not IsValid( Base ) then return end
Base:PaintCrosshairSquare( Pos2D, Col )
end

View File

@@ -0,0 +1,327 @@
AddCSLuaFile( "shared.lua" )
AddCSLuaFile( "cl_init.lua" )
include("shared.lua")
include("sv_ai.lua")
function ENT:Initialize()
self:SetMoveType( MOVETYPE_NONE )
self:SetSolid( SOLID_NONE )
self:DrawShadow( false )
end
function ENT:Think()
self:HandleActive()
self:WeaponsThink()
if self:GetAI() then
self:RunAI()
end
self:NextThink( CurTime() )
return true
end
function ENT:HandleActive()
local Pod = self:GetDriverSeat()
if not IsValid( Pod ) then
return
end
local Driver = Pod:GetDriver()
if Driver ~= self:GetDriver() then
local NewDriver = Driver
local OldDriver = self:GetDriver()
self:SetDriver( Driver )
local Base = self:GetVehicle()
if IsValid( Base ) then
Base:OnPassengerChanged( OldDriver, NewDriver, Pod:GetNWInt( "pPodIndex", -1 ) )
end
if IsValid( Driver ) then
Driver:lvsBuildControls()
else
self:WeaponsFinish()
end
end
end
function ENT:OnRemove()
end
function ENT:UpdateTransmitState()
return TRANSMIT_ALWAYS
end
function ENT:WeaponsFinish()
if not self._activeWeapon then return end
local Base = self:GetVehicle()
if not IsValid( Base ) then return end
local CurWeapon = Base.WEAPONS[ self:GetPodIndex() ][ self._activeWeapon ]
if not CurWeapon then return end
if CurWeapon.FinishAttack then
CurWeapon.FinishAttack( self )
end
self._activeWeapon = nil
self.OldAttack = false
end
function ENT:GetAmmo()
if self:GetAI() then return self:GetMaxAmmo() end
local CurWeapon = self:GetActiveWeapon()
if not CurWeapon then return -1 end
return CurWeapon._CurAmmo or self:GetMaxAmmo()
end
function ENT:TakeAmmo( num )
if self:GetMaxAmmo() <= 0 then return end
local CurWeapon = self:GetActiveWeapon()
CurWeapon._CurAmmo = math.max( self:GetAmmo() - (num or 1), 0 )
self:SetNWAmmo( CurWeapon._CurAmmo )
end
function ENT:GetHeat( weaponid )
local CurWeapon
if isnumber( weaponid ) and weaponid > 0 then
local Base = self:GetVehicle()
if IsValid( Base ) then
CurWeapon = Base.WEAPONS[ self:GetPodIndex() ][ weaponid ]
else
CurWeapon = self:GetActiveWeapon()
end
else
CurWeapon = self:GetActiveWeapon()
end
if not CurWeapon then return 0 end
return (CurWeapon._CurHeat or 0)
end
function ENT:GetOverheated()
local CurWeapon = self:GetActiveWeapon()
if not CurWeapon then return false end
return CurWeapon.Overheated == true
end
function ENT:SetOverheated( overheat )
if self:GetOverheated() == overheat then return end
local CurWeapon = self:GetActiveWeapon()
if not CurWeapon then return end
CurWeapon.Overheated = overheat
self:SetNWOverheated( overheat )
if self:GetHeat() == 0 then return end
if CurWeapon.OnOverheat then
CurWeapon.OnOverheat( self )
end
end
function ENT:SetHeat( heat )
local CurWeapon = self:GetActiveWeapon()
if not CurWeapon then return end
heat = math.Clamp( heat, 0, 1 )
CurWeapon._CurHeat = heat
if self:GetNWHeat() == heat then return end
self:SetNWHeat( heat )
end
function ENT:CanAttack()
local CurWeapon = self:GetActiveWeapon()
return (CurWeapon._NextFire or 0) < CurTime()
end
function ENT:SetNextAttack( time )
local CurWeapon = self:GetActiveWeapon()
CurWeapon._NextFire = time
end
function ENT:WeaponsShouldFire()
if self:GetAI() then return self._AIFireInput end
local ply = self:GetDriver()
if not IsValid( ply ) then return false end
return ply:lvsKeyDown( "ATTACK" )
end
function ENT:WeaponsThink()
local T = CurTime()
local FT = FrameTime()
local CurWeapon, SelectedID = self:GetActiveWeapon()
local Base = self:GetVehicle()
if not IsValid( Base ) then return end
for ID, Weapon in pairs( Base.WEAPONS[ self:GetPodIndex() ] ) do
local IsActive = ID == SelectedID
if Weapon.OnThink then Weapon.OnThink( self, IsActive ) end
if IsActive then continue end
if Weapon.HeatIsClip and not Weapon.Overheated and Weapon._CurHeat ~= 0 then
Weapon.Overheated = true
Weapon._CurHeat = 1
if Weapon.OnReload then Weapon.OnReload( self ) end
end
-- cool all inactive weapons down
Weapon._CurHeat = Weapon._CurHeat and Weapon._CurHeat - math.min( Weapon._CurHeat, (Weapon.HeatRateDown or 0.25) * FT ) or 0
end
if not CurWeapon then return end
local ShouldFire = self:WeaponsShouldFire()
local CurHeat = self:GetHeat()
if self:GetOverheated() then
if CurHeat <= 0 then
self:SetOverheated( false )
else
ShouldFire = false
end
else
if CurHeat >= 1 then
self:SetOverheated( true )
ShouldFire = false
if CurWeapon.OnReload then CurWeapon.OnReload( self ) end
end
end
if self:GetMaxAmmo() > 0 then
if self:GetAmmo() <= 0 then
ShouldFire = false
end
end
if ShouldFire ~= self.OldAttack then
self.OldAttack = ShouldFire
if ShouldFire then
if CurWeapon.StartAttack then
CurWeapon.StartAttack( self )
end
self._activeWeapon = SelectedID
else
self:WeaponsFinish()
end
end
if ShouldFire then
if not self:CanAttack() then return end
local ShootDelay = (CurWeapon.Delay or 0)
self:SetNextAttack( T + ShootDelay )
self:SetHeat( CurHeat + (CurWeapon.HeatRateUp or 0.2) * math.max(ShootDelay, FT) )
if not CurWeapon.Attack then return end
if CurWeapon.Attack( self ) then
self:SetHeat( CurHeat - math.min( self:GetHeat(), (CurWeapon.HeatRateDown or 0.25) * FT ) )
self:SetNextAttack( T )
end
self._lvsNextActiveWeaponCoolDown = T + 0.25
else
if (self._lvsNextActiveWeaponCoolDown or 0) > T then return end
if CurWeapon.HeatIsClip and not CurWeapon.Overheated then
self:SetHeat( self:GetHeat() )
return
end
self:SetHeat( self:GetHeat() - math.min( self:GetHeat(), (CurWeapon.HeatRateDown or 0.25) * FT ) )
end
end
function ENT:SelectWeapon( ID )
if not isnumber( ID ) then return end
local Base = self:GetVehicle()
if not IsValid( Base ) then return end
if Base.WEAPONS[ self:GetPodIndex() ][ ID ] then
self:SetSelectedWeapon( ID )
end
local ply = self:GetDriver()
if not IsValid( ply ) then return end
net.Start( "lvs_select_weapon" )
net.Send( ply )
end
function ENT:OnWeaponChanged( name, old, new)
if new == old then return end
self:WeaponsFinish()
local Base = self:GetVehicle()
if not IsValid( Base ) then return end
local PrevWeapon = Base.WEAPONS[ self:GetPodIndex() ][ old ]
if PrevWeapon and PrevWeapon.OnDeselect then
PrevWeapon.OnDeselect( self )
end
local NextWeapon = Base.WEAPONS[ self:GetPodIndex() ][ new ]
if NextWeapon and NextWeapon.OnSelect then
NextWeapon.OnSelect( self )
self:SetNWAmmo( NextWeapon._CurAmmo or NextWeapon.Ammo or -1 )
self:SetNWOverheated( NextWeapon.Overheated == true )
end
end
function ENT:LVSFireBullet( data )
local Base = self:GetVehicle()
if not IsValid( Base ) then return end
data.Entity = Base
data.Velocity = data.Velocity + self:GetVelocity():Length()
data.SrcEntity = Base:WorldToLocal( data.Src )
LVS:FireBullet( data )
end

View File

@@ -0,0 +1,174 @@
ENT.Type = "anim"
ENT.PrintName = "LBaseGunner"
ENT.Author = "Luna"
ENT.Information = "Luna's Vehicle Script"
ENT.Category = "[LVS]"
ENT.Spawnable = false
ENT.AdminSpawnable = false
ENT.DoNotDuplicate = true
ENT.LVS_GUNNER = true
ENT.VectorNull = Vector(0,0,0)
function ENT:SetupDataTables()
self:NetworkVar( "Entity",0, "Driver" )
self:NetworkVar( "Entity",1, "DriverSeat" )
self:NetworkVar( "Int", 0, "PodIndex")
self:NetworkVar( "Int", 1, "NWAmmo")
self:NetworkVar( "Int", 2, "SelectedWeapon" )
self:NetworkVar( "Float", 0, "NWHeat" )
self:NetworkVar( "Bool", 0, "NWOverheated" )
self:NetworkVar( "Vector", 0, "NWAimVector" )
if SERVER then
self:NetworkVarNotify( "SelectedWeapon", self.OnWeaponChanged )
end
end
function ENT:UnlockAimVector()
self._AimVectorUnlocked = true
end
function ENT:LockAimVector()
self._AimVectorUnlocked = nil
end
function ENT:GetEyeTrace()
local startpos = self:GetPos()
local trace = util.TraceLine( {
start = startpos,
endpos = (startpos + self:GetAimVector() * 50000),
filter = self:GetCrosshairFilterEnts()
} )
return trace
end
function ENT:GetAI()
if IsValid( self:GetDriver() ) then return false end
local veh = self:GetVehicle()
if not IsValid( veh ) then return false end
return veh:GetAIGunners()
end
function ENT:GetAITEAM()
local Base = self:GetVehicle()
if not IsValid( Base ) then return 0 end
return Base:GetAITEAM()
end
function ENT:GetVehicle()
local Pod = self:GetParent()
if not IsValid( Pod ) then return NULL end
return Pod:GetParent()
end
function ENT:HasWeapon( ID )
local Base = self:GetVehicle()
if not IsValid( Base ) then return false end
return istable( Base.WEAPONS[ self:GetPodIndex() ][ ID ] )
end
function ENT:AIHasWeapon( ID )
local Base = self:GetVehicle()
if not IsValid( Base ) then return false end
local weapon = Base.WEAPONS[ self:GetPodIndex() ][ ID ]
if not istable( weapon ) then return false end
return weapon.UseableByAI
end
function ENT:GetActiveWeapon()
local SelectedID = self:GetSelectedWeapon()
local Base = self:GetVehicle()
if not IsValid( Base ) then return {}, SelectedID end
local CurWeapon = Base.WEAPONS[ self:GetPodIndex() ][ SelectedID ]
return CurWeapon, SelectedID
end
function ENT:GetMaxAmmo()
local CurWeapon = self:GetActiveWeapon()
if not CurWeapon then return -1 end
return CurWeapon.Ammo or -1
end
function ENT:GetClip()
local CurWeapon = self:GetActiveWeapon()
if not CurWeapon then return 0 end
local HeatIncrement = (CurWeapon.HeatRateUp or 0.2) * math.max(CurWeapon.Delay or 0, FrameTime())
local Ammo = self:GetNWAmmo()
if self:GetMaxAmmo() <= 0 and CurWeapon.Clip then
Ammo = CurWeapon.Clip
end
return math.min( math.ceil( math.Round( (1 - self:GetNWHeat()) / HeatIncrement, 1 ) ), Ammo )
end
function ENT:GetCrosshairFilterEnts()
local Base = self:GetVehicle()
if not IsValid( Base ) then return {} end
return Base:GetCrosshairFilterEnts()
end
function ENT:Sign( n )
if n > 0 then return 1 end
if n < 0 then return -1 end
return 0
end
function ENT:VectorSubtractNormal( Normal, Velocity )
local VelForward = Velocity:GetNormalized()
local Ax = math.acos( math.Clamp( Normal:Dot( VelForward ) ,-1,1) )
local Fx = math.cos( Ax ) * Velocity:Length()
local NewVelocity = Velocity - Normal * math.abs( Fx )
return NewVelocity
end
function ENT:VectorSplitNormal( Normal, Velocity )
return math.cos( math.acos( math.Clamp( Normal:Dot( Velocity:GetNormalized() ) ,-1,1) ) ) * Velocity:Length()
end
function ENT:AngleBetweenNormal( Dir1, Dir2 )
return math.deg( math.acos( math.Clamp( Dir1:Dot( Dir2 ) ,-1,1) ) )
end
function ENT:GetVehicleType()
return "LBaseGunner"
end

View File

@@ -0,0 +1,98 @@
function ENT:GetAimVector()
if self:GetAI() then
local Dir = self._ai_look_dir or self.VectorNull
self:SetNWAimVector( Dir )
return Dir
end
local Driver = self:GetDriver()
if IsValid( Driver ) then
if self._AimVectorUnlocked then
local pod = self:GetDriverSeat()
if IsValid( pod ) then
return pod:WorldToLocalAngles( Driver:EyeAngles() ):Forward()
end
end
return Driver:GetAimVector()
else
return self:GetForward()
end
end
function ENT:RunAI()
local EntTable = self:GetTable()
local Target = self:AIGetTarget( EntTable )
if not IsValid( Target ) then
EntTable._ai_look_dir = self:GetForward()
EntTable._AIFireInput = false
return
end
local TargetPos = Target:GetPos()
if EntTable._AIFireInput then
local T = CurTime() * 0.5 + self:EntIndex()
local X = math.cos( T ) * 32
local Y = math.sin( T ) * 32
local Z = math.sin( math.cos( T / 0.5 ) * math.pi ) * 32
TargetPos = Target:LocalToWorld( Target:OBBCenter() + Vector(X,Y,Z) )
end
EntTable._ai_look_dir = (TargetPos - self:GetPos()):GetNormalized()
local StartPos = self:GetPos()
local trace = util.TraceHull( {
start = StartPos,
endpos = (StartPos + EntTable._ai_look_dir * 50000),
mins = Vector( -50, -50, -50 ),
maxs = Vector( 50, 50, 50 ),
filter = self:GetCrosshairFilterEnts()
} )
if not self:AIHasWeapon( self:GetSelectedWeapon() ) then
EntTable._AIFireInput = false
return
end
if IsValid( trace.Entity ) and trace.Entity.GetAITEAM then
EntTable._AIFireInput = (trace.Entity:GetAITEAM() ~= self:GetAITEAM() or trace.Entity:GetAITEAM() == 0)
else
EntTable._AIFireInput = true
end
end
function ENT:AIGetTarget( EntTable )
local Base = self:GetVehicle()
if not IsValid( Base ) then return NULL end
if Base:GetAI() then
return Base:AIGetTarget()
end
if not isnumber( EntTable.ViewConeAdd ) then
EntTable.ViewConeAdd = math.min( 100 + math.abs( Base:WorldToLocalAngles( self:GetAngles() ).y ), 360 )
end
return Base:AIGetTarget( EntTable.ViewConeAdd )
end
function ENT:AITargetInFront( ent, range )
local Base = self:GetVehicle()
if not IsValid( Base ) then return NULL end
return Base:AITargetInFront( ent, range )
end

View File

@@ -0,0 +1,158 @@
ENT._lvsSmoothFreeLook = 0
function ENT:CalcViewDirectInput( ply, pos, angles, fov, pod )
local ViewPosL = pod:WorldToLocal( pos )
local view = {}
view.fov = fov
view.drawviewer = true
view.angles = self:GetAngles()
local FreeLook = ply:lvsKeyDown( "FREELOOK" )
local Zoom = ply:lvsKeyDown( "ZOOM" )
if not pod:GetThirdPersonMode() then
if FreeLook then
view.angles = pod:LocalToWorldAngles( ply:EyeAngles() )
end
local velL = self:WorldToLocal( self:GetPos() + self:GetVelocity() )
local Dividor = math.abs( velL.x )
local SideForce = math.Clamp( velL.y / Dividor, -1, 1)
local UpForce = math.Clamp( velL.z / Dividor, -1, 1)
local ViewPunch = Vector(0,math.Clamp(SideForce * 10,-1,1),math.Clamp(UpForce * 10,-1,1))
if Zoom then
ViewPunch = Vector(0,0,0)
end
pod._lerpPosOffset = pod._lerpPosOffset and pod._lerpPosOffset + (ViewPunch - pod._lerpPosOffset) * RealFrameTime() * 5 or Vector(0,0,0)
pod._lerpPos = pos
view.origin = pos + pod:GetForward() * -pod._lerpPosOffset.y * 0.5 + pod:GetUp() * pod._lerpPosOffset.z * 0.5
view.angles.p = view.angles.p - pod._lerpPosOffset.z * 0.1
view.angles.y = view.angles.y + pod._lerpPosOffset.y * 0.1
view.drawviewer = false
return view
end
pod._lerpPos = pod._lerpPos or self:GetPos()
local radius = 550
radius = radius + radius * pod:GetCameraDistance()
if FreeLook then
local velL = self:WorldToLocal( self:GetPos() + self:GetVelocity() )
local SideForce = math.Clamp(velL.y / 10,-250,250)
local UpForce = math.Clamp(velL.z / 10,-250,250)
pod._lerpPosL = pod._lerpPosL and (pod._lerpPosL + (Vector(radius, SideForce,150 + radius * 0.1 + radius * pod:GetCameraHeight() + UpForce) - pod._lerpPosL) * RealFrameTime() * 12) or Vector(0,0,0)
pod._lerpPos = self:LocalToWorld( pod._lerpPosL )
view.origin = pod._lerpPos
view.angles = self:LocalToWorldAngles( Angle(0,180,0) )
else
local TargetPos = self:LocalToWorld( Vector(500,0,150 + radius * 0.1 + radius * pod:GetCameraHeight()) )
local Sub = TargetPos - pod._lerpPos
local Dir = Sub:GetNormalized()
local Dist = Sub:Length()
local DesiredPos = TargetPos - self:GetForward() * (300 + radius) - Dir * 100
pod._lerpPos = pod._lerpPos + (DesiredPos - pod._lerpPos) * RealFrameTime() * (Zoom and 30 or 12)
pod._lerpPosL = self:WorldToLocal( pod._lerpPos )
local vel = self:GetVelocity()
view.origin = pod._lerpPos
view.angles = self:GetAngles()
end
view.origin = view.origin + ViewPosL
return view
end
function ENT:CalcViewMouseAim( ply, pos, angles, fov, pod )
local cvarFocus = math.Clamp( LVS.cvarCamFocus:GetFloat() , -1, 1 )
self._lvsSmoothFreeLook = self._lvsSmoothFreeLook + ((ply:lvsKeyDown( "FREELOOK" ) and 0 or 1) - self._lvsSmoothFreeLook) * RealFrameTime() * 10
local view = {}
view.origin = pos
view.fov = fov
view.drawviewer = true
view.angles = (self:GetForward() * (1 + cvarFocus) * self._lvsSmoothFreeLook * 0.8 + ply:EyeAngles():Forward() * math.max(1 - cvarFocus, 1 - self._lvsSmoothFreeLook)):Angle()
if cvarFocus >= 1 then
view.angles = LerpAngle( self._lvsSmoothFreeLook, ply:EyeAngles(), self:GetAngles() )
else
view.angles.r = 0
end
if not pod:GetThirdPersonMode() then
view.drawviewer = false
return view
end
local radius = 550
radius = radius + radius * pod:GetCameraDistance()
local TargetOrigin = view.origin - view.angles:Forward() * radius + view.angles:Up() * (radius * 0.2 + radius * pod:GetCameraHeight())
local WallOffset = 4
local tr = util.TraceHull( {
start = view.origin,
endpos = TargetOrigin,
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.origin = tr.HitPos
if tr.Hit and not tr.StartSolid then
view.origin = view.origin + tr.HitNormal * WallOffset
end
return view
end
function ENT:CalcViewOverride( ply, pos, angles, fov, pod )
return pos, angles, fov
end
function ENT:CalcViewDriver( ply, pos, angles, fov, pod )
if ply:lvsMouseAim() then
return self:CalcViewMouseAim( ply, pos, angles, fov, pod )
else
return self:CalcViewDirectInput( ply, pos, angles, fov, pod )
end
end
function ENT:CalcViewPassenger( ply, pos, angles, fov, pod )
return LVS:CalcView( self, ply, pos, angles, fov, pod )
end
function ENT:LVSCalcView( ply, original_pos, original_angles, original_fov, pod )
local pos, angles, fov = self:CalcViewOverride( ply, original_pos, original_angles, original_fov, pod )
if self:GetDriverSeat() == pod then
return self:CalcViewDriver( ply, pos, angles, fov, pod )
else
return self:CalcViewPassenger( ply, pos, angles, fov, pod )
end
end

View File

@@ -0,0 +1,63 @@
ENT.FlyByAdvance = 0
function ENT:FlyByThink()
local ply = LocalPlayer()
if not IsValid( ply ) then return end
local EntTable = self:GetTable()
if ply:lvsGetVehicle() == self then EntTable.OldApproaching = false return end
local ViewEnt = ply:GetViewEntity()
if not IsValid( ViewEnt ) then return end
local Time = CurTime()
if (EntTable._nextflyby or 0) > Time then return end
EntTable._nextflyby = Time + 0.1
local Vel = self:GetVelocity()
if self:GetThrottle() <= 0.75 or Vel:Length() <= EntTable.MaxVelocity * 0.75 then return end
local Sub = ViewEnt:GetPos() - self:GetPos() - Vel * EntTable.FlyByAdvance
local ToPlayer = Sub:GetNormalized()
local VelDir = Vel:GetNormalized()
local ApproachAngle = math.deg( math.acos( math.Clamp( ToPlayer:Dot( VelDir ) ,-1,1) ) )
local Approaching = ApproachAngle < 80
if Approaching ~= EntTable.OldApproaching then
EntTable.OldApproaching = Approaching
if Approaching then
self:StopFlyBy()
else
self:OnFlyBy( 60 + 80 * math.min(ApproachAngle / 140,1) )
end
end
end
function ENT:OnFlyBy( Pitch )
if not self.FlyBySound then return end
local EntTable = self:GetTable()
EntTable.flybysnd = CreateSound( self, EntTable.FlyBySound )
EntTable.flybysnd:SetSoundLevel( 95 )
EntTable.flybysnd:PlayEx( 1, Pitch )
end
function ENT:StopFlyBy()
local EntTable = self:GetTable()
if not EntTable.flybysnd then return end
EntTable.flybysnd:Stop()
EntTable.flybysnd = nil
end

View File

@@ -0,0 +1,168 @@
ENT.IconEngine = Material( "lvs/engine.png" )
function ENT:LVSHudPaintInfoText( X, Y, W, H, ScrX, ScrY, ply )
local speed = math.Round( LVS:GetUnitValue( self:GetVelocity():Length() ) ,0)
draw.DrawText( LVS:GetUnitName().." ", "LVS_FONT", X + 72, Y + 35, color_white, TEXT_ALIGN_RIGHT )
draw.DrawText( speed, "LVS_FONT_HUD_LARGE", X + 72, Y + 20, color_white, TEXT_ALIGN_LEFT )
if ply ~= self:GetDriver() then return end
local hX = X + W - H * 0.5
local hY = Y + H * 0.25 + H * 0.25
surface.SetMaterial( self.IconEngine )
surface.SetDrawColor( 0, 0, 0, 200 )
surface.DrawTexturedRectRotated( hX + 4, hY + 1, H * 0.5, H * 0.5, 0 )
surface.SetDrawColor( color_white )
surface.DrawTexturedRectRotated( hX + 2, hY - 1, H * 0.5, H * 0.5, 0 )
if not self:GetEngineActive() then
draw.SimpleText( "X" , "LVS_FONT", hX, hY, Color(0,0,0,255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER )
end
self:LVSDrawCircle( hX, hY, H * 0.35, self:GetThrustPercent() )
end
function ENT:LVSPreHudPaint( X, Y, ply )
return true
end
ENT.Hud = true
ENT.HudThirdPerson = false
ENT.HudGradient = Material("gui/center_gradient")
ENT.HudColor = Color(255,255,255)
function ENT:PaintHeliFlightInfo( X, Y, ply, Pos2D )
local Roll = self:GetAngles().r
surface.SetDrawColor(0,0,0,40)
surface.SetMaterial( self.HudGradient )
surface.DrawTexturedRect( Pos2D.x - 270, Pos2D.y - 10, 140, 20 )
surface.DrawTexturedRect( Pos2D.x + 130, Pos2D.y - 10, 140, 20 )
local X = math.cos( math.rad( Roll ) )
local Y = math.sin( math.rad( Roll ) )
surface.SetDrawColor( self.HudColor.r, self.HudColor.g, self.HudColor.b, 255 )
surface.DrawLine( Pos2D.x + X * 50, Pos2D.y + Y * 50, Pos2D.x + X * 125, Pos2D.y + Y * 125 )
surface.DrawLine( Pos2D.x - X * 50, Pos2D.y - Y * 50, Pos2D.x - X * 125, Pos2D.y - Y * 125 )
surface.DrawLine( Pos2D.x + 125, Pos2D.y, Pos2D.x + 130, Pos2D.y + 5 )
surface.DrawLine( Pos2D.x + 125, Pos2D.y, Pos2D.x + 130, Pos2D.y - 5 )
surface.DrawLine( Pos2D.x - 125, Pos2D.y, Pos2D.x - 130, Pos2D.y + 5 )
surface.DrawLine( Pos2D.x - 125, Pos2D.y, Pos2D.x - 130, Pos2D.y - 5 )
surface.SetDrawColor( 0, 0, 0, 80 )
surface.DrawLine( Pos2D.x + X * 50 + 1, Pos2D.y + Y * 50 + 1, Pos2D.x + X * 125 + 1, Pos2D.y + Y * 125 + 1 )
surface.DrawLine( Pos2D.x - X * 50 + 1, Pos2D.y - Y * 50 + 1, Pos2D.x - X * 125 + 1, Pos2D.y - Y * 125 + 1 )
surface.DrawLine( Pos2D.x + 126, Pos2D.y + 1, Pos2D.x + 131, Pos2D.y + 6 )
surface.DrawLine( Pos2D.x + 126, Pos2D.y + 1, Pos2D.x + 131, Pos2D.y - 4 )
surface.DrawLine( Pos2D.x - 126, Pos2D.y + 1, Pos2D.x - 129, Pos2D.y + 6 )
surface.DrawLine( Pos2D.x - 126, Pos2D.y + 1, Pos2D.x - 129, Pos2D.y - 4 )
local X = math.cos( math.rad( Roll + 45 ) )
local Y = math.sin( math.rad( Roll + 45 ) )
surface.DrawLine( Pos2D.x + X * 30 - 1, Pos2D.y + Y * 30 + 1, Pos2D.x + X * 60 - 1, Pos2D.y + Y * 60 + 1 )
local X = math.cos( math.rad( Roll + 135 ) )
local Y = math.sin( math.rad( Roll + 135 ) )
surface.DrawLine( Pos2D.x + X * 30 + 1, Pos2D.y + Y * 30 + 1, Pos2D.x + X * 60 + 1, Pos2D.y + Y * 60 + 1 )
surface.SetDrawColor( self.HudColor.r, self.HudColor.g, self.HudColor.b, 255 )
local X = math.cos( math.rad( Roll + 45 ) )
local Y = math.sin( math.rad( Roll + 45 ) )
surface.DrawLine( Pos2D.x + X * 30, Pos2D.y + Y * 30, Pos2D.x + X * 60, Pos2D.y + Y * 60 )
local X = math.cos( math.rad( Roll + 135 ) )
local Y = math.sin( math.rad( Roll + 135 ) )
surface.DrawLine( Pos2D.x + X * 30, Pos2D.y + Y * 30, Pos2D.x + X * 60, Pos2D.y + Y * 60 )
local Pitch = -self:GetAngles().p
surface.DrawLine( Pos2D.x - 220, Pos2D.y, Pos2D.x - 180, Pos2D.y )
surface.DrawLine( Pos2D.x + 220, Pos2D.y, Pos2D.x + 180, Pos2D.y )
surface.SetDrawColor( 0, 0, 0, 80 )
surface.DrawLine( Pos2D.x - 220, Pos2D.y + 1, Pos2D.x - 180, Pos2D.y + 1 )
surface.DrawLine( Pos2D.x + 220, Pos2D.y + 1, Pos2D.x + 180, Pos2D.y + 1 )
draw.DrawText( math.Round( Pitch, 2 ), "LVS_FONT_PANEL", Pos2D.x - 175, Pos2D.y - 7, Color( self.HudColor.r, self.HudColor.g, self.HudColor.b, 255 ), TEXT_ALIGN_LEFT )
draw.DrawText( math.Round( Pitch, 2 ), "LVS_FONT_PANEL", Pos2D.x + 175, Pos2D.y - 7, Color( self.HudColor.r, self.HudColor.g, self.HudColor.b, 255 ), TEXT_ALIGN_RIGHT )
for i = -180, 180 do
local Y = -i * 10 + Pitch * 10
local absN = math.abs( i )
local IsTen = absN == math.Round( absN / 10, 0 ) * 10
local SizeX = IsTen and 20 or 10
local Alpha = 255 - (math.min( math.abs( Y ) / 200,1) ^ 2) * 255
if Alpha <= 0 then continue end
surface.SetDrawColor( self.HudColor.r, self.HudColor.g, self.HudColor.b, Alpha * 0.75 )
surface.DrawLine(Pos2D.x - 200 - SizeX, Pos2D.y + Y, Pos2D.x - 200, Pos2D.y + Y )
surface.DrawLine(Pos2D.x + 200 + SizeX, Pos2D.y + Y, Pos2D.x + 200, Pos2D.y + Y )
surface.SetDrawColor( 0, 0, 0, Alpha * 0.25 )
surface.DrawLine(Pos2D.x - 200 - SizeX, Pos2D.y + Y + 1, Pos2D.x - 200, Pos2D.y + Y + 1 )
surface.DrawLine(Pos2D.x + 200 + SizeX, Pos2D.y + Y + 1, Pos2D.x + 200, Pos2D.y + Y + 1)
if not IsTen then continue end
draw.DrawText( i, "LVS_FONT_HUD", Pos2D.x - 225, Pos2D.y + Y - 10, Color( self.HudColor.r, self.HudColor.g, self.HudColor.b, Alpha * 0.5 ), TEXT_ALIGN_RIGHT )
draw.DrawText( i, "LVS_FONT_HUD", Pos2D.x + 225, Pos2D.y + Y - 10, Color( self.HudColor.r, self.HudColor.g, self.HudColor.b, Alpha * 0.5 ), TEXT_ALIGN_LEFT )
end
end
function ENT:LVSHudPaint( X, Y, ply )
if not self:LVSPreHudPaint( X, Y, ply ) then return end
if ply ~= self:GetDriver() then return end
local HitPlane = self:GetEyeTrace( true ).HitPos:ToScreen()
local HitPilot = self:GetEyeTrace().HitPos:ToScreen()
local pod = ply:GetVehicle()
if self.Hud then
if not pod:GetThirdPersonMode() then
self:PaintHeliFlightInfo( X, Y, ply, HitPilot )
end
end
if self.HudThirdPerson then
if pod:GetThirdPersonMode() then
self:PaintHeliFlightInfo( X, Y, ply, HitPilot )
end
end
self:PaintCrosshairCenter( HitPlane )
self:PaintCrosshairOuter( HitPilot )
if ply:lvsMouseAim() and not ply:lvsKeyDown( "FREELOOK" ) then
self:LVSHudPaintMouseAim( HitPlane, HitPilot )
end
self:LVSPaintHitMarker( HitPilot )
end
function ENT:LVSHudPaintDirectInput( Pos2D )
self:PaintCrosshairCenter( Pos2D )
self:PaintCrosshairOuter( Pos2D )
end
function ENT:LVSHudPaintMouseAim( HitPlane, HitPilot )
local Sub = Vector(HitPilot.x,HitPilot.y,0) - Vector(HitPlane.x,HitPlane.y,0)
local Len = Sub:Length()
local Dir = Sub:GetNormalized()
surface.SetDrawColor( 255, 255, 255, 100 )
if Len > 20 then
surface.DrawLine( HitPlane.x + Dir.x * 5, HitPlane.y + Dir.y * 5, HitPilot.x - Dir.x * 20, HitPilot.y- Dir.y * 20 )
-- shadow
surface.SetDrawColor( 0, 0, 0, 50 )
surface.DrawLine( HitPlane.x + Dir.x * 5 + 1, HitPlane.y + Dir.y * 5 + 1, HitPilot.x - Dir.x * 20+ 1, HitPilot.y- Dir.y * 20 + 1 )
end
end

View File

@@ -0,0 +1,5 @@
include("shared.lua")
include("cl_camera.lua")
include("sh_camera_eyetrace.lua")
include("cl_hud.lua")
include("cl_flyby.lua")

View File

@@ -0,0 +1,210 @@
AddCSLuaFile( "shared.lua" )
AddCSLuaFile( "cl_init.lua" )
AddCSLuaFile( "cl_camera.lua" )
AddCSLuaFile( "sh_camera_eyetrace.lua" )
AddCSLuaFile( "cl_hud.lua" )
AddCSLuaFile( "cl_flyby.lua" )
include("shared.lua")
include("sv_ai.lua")
include("sv_mouseaim.lua")
include("sv_components.lua")
include("sv_engine.lua")
include("sv_vehiclespecific.lua")
include("sv_damage_extension.lua")
include("sh_camera_eyetrace.lua")
function ENT:OnCreateAI()
self:StartEngine()
self.COL_GROUP_OLD = self:GetCollisionGroup()
self:SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS )
end
function ENT:OnRemoveAI()
self:StopEngine()
self:SetCollisionGroup( self.COL_GROUP_OLD or COLLISION_GROUP_NONE )
end
function ENT:ApproachTargetAngle( TargetAngle, OverridePitch, OverrideYaw, OverrideRoll, FreeMovement, phys, deltatime )
if not IsValid( phys ) then
phys = self:GetPhysicsObject()
end
if not deltatime then
deltatime = FrameTime()
end
local LocalAngles = self:WorldToLocalAngles( TargetAngle )
local LocalAngPitch = LocalAngles.p
local LocalAngYaw = LocalAngles.y
local LocalAngRoll = LocalAngles.r
local TargetForward = TargetAngle:Forward()
local Forward = self:GetForward()
local Ang = self:GetAngles()
local AngVel = phys:GetAngleVelocity()
local SmoothPitch = math.Clamp( math.Clamp(AngVel.y / 50,-0.25,0.25) / math.abs( LocalAngPitch ), -1, 1 )
local SmoothYaw = math.Clamp( math.Clamp(AngVel.z / 50,-0.25,0.25) / math.abs( LocalAngYaw ), -1, 1 )
local VelL = self:WorldToLocal( self:GetPos() + self:GetVelocity() )
local Pitch = math.Clamp(-LocalAngPitch / 10 + SmoothPitch,-1,1)
local Yaw = math.Clamp(-LocalAngYaw + SmoothYaw,-1,1)
local Roll = math.Clamp(LocalAngRoll / 100 + Yaw * 0.25 + math.Clamp(VelL.y / math.abs( VelL.x ) / 10,-0.25,0.25),-1,1)
if OverrideRoll ~= 0 then
Roll = math.Clamp( self:WorldToLocalAngles( Angle( Ang.p, Ang.y, OverrideRoll * 60 ) ).r / 40,-1,1)
end
self.Roll = Roll
if OverridePitch and OverridePitch ~= 0 then
Pitch = OverridePitch
end
if OverrideYaw and OverrideYaw ~= 0 then
Yaw = OverrideYaw
end
self:SetSteer( Vector( Roll, -Pitch, -Yaw) )
end
function ENT:OnSkyCollide( data, PhysObj )
local NewVelocity = self:VectorSubtractNormal( data.HitNormal, data.OurOldVelocity ) - data.HitNormal * 50
PhysObj:SetVelocityInstantaneous( NewVelocity )
PhysObj:SetAngleVelocityInstantaneous( data.OurOldAngularVelocity )
return true
end
function ENT:PhysicsSimulate( phys, deltatime )
if self:GetEngineActive() then phys:Wake() end
local EntTable = self:GetTable()
local WorldGravity = self:GetWorldGravity()
local WorldUp = self:GetWorldUp()
local Up = self:GetUp()
local Left = -self:GetRight()
local Mul = self:GetThrottle()
local InputThrust = math.min( self:GetThrust() , 0 ) * EntTable.ThrustDown + math.max( self:GetThrust(), 0 ) * EntTable.ThrustUp
if self:HitGround() and InputThrust <= 0 then
Mul = 0
end
-- mouse aim needs to run at high speed.
if self:GetAI() then
self:CalcAIMove( phys, deltatime )
else
local ply = self:GetDriver()
if IsValid( ply ) and ply:lvsMouseAim() then
self:PlayerMouseAim( ply, phys, deltatime )
end
end
local Steer = self:GetSteer()
local Vel = phys:GetVelocity()
local VelL = phys:WorldToLocal( phys:GetPos() + Vel )
local YawPull = (math.deg( math.acos( math.Clamp( WorldUp:Dot( Left ) ,-1,1) ) ) - 90) / 90
local GravityYaw = math.abs( YawPull ) ^ 1.25 * self:Sign( YawPull ) * (WorldGravity / 100) * (math.min( Vector(VelL.x,VelL.y,0):Length() / EntTable.MaxVelocity,1) ^ 2)
local Pitch = math.Clamp(Steer.y,-1,1) * EntTable.TurnRatePitch
local Yaw = math.Clamp(Steer.z + GravityYaw * 0.25 * EntTable.GravityTurnRateYaw,-1,1) * EntTable.TurnRateYaw * 60
local Roll = math.Clamp(Steer.x,-1,1) * 1.5 * EntTable.TurnRateRoll
local Ang = self:GetAngles()
local FadeMul = (1 - math.max( (45 - self:AngleBetweenNormal( WorldUp, Up )) / 45,0)) ^ 2
local ThrustMul = math.Clamp( 1 - (Vel:Length() / EntTable.MaxVelocity) * FadeMul, 0, 1 )
local Thrust = self:LocalToWorldAngles( Angle(Pitch,0,Roll) ):Up() * (WorldGravity + InputThrust * 500 * ThrustMul) * Mul
local Force, ForceAng = phys:CalculateForceOffset( Thrust, phys:LocalToWorld( phys:GetMassCenter() ) + self:GetUp() * 1000 )
local ForceLinear = (Force - Vel * 0.15 * EntTable.ForceLinearDampingMultiplier) * Mul
local ForceAngle = (ForceAng + (Vector(0,0,Yaw) - phys:GetAngleVelocity() * 1.5 * EntTable.ForceAngleDampingMultiplier) * deltatime * 250) * Mul
if EntTable._SteerOverride then
ForceAngle.z = (EntTable._SteerOverrideMove * math.max( self:GetThrust() * 2, 1 ) * 100 - phys:GetAngleVelocity().z) * Mul
end
return self:PhysicsSimulateOverride( ForceAngle, ForceLinear, phys, deltatime, SIM_GLOBAL_ACCELERATION )
end
function ENT:PhysicsSimulateOverride( ForceAngle, ForceLinear, phys, deltatime, simulate )
return ForceAngle, ForceLinear, simulate
end
function ENT:ApproachThrust( New, Delta )
if not Delta then
Delta = FrameTime()
end
local Cur = self:GetThrust()
self:SetThrust( Cur + (New - Cur) * Delta * self.ThrustRate * 2.5 )
end
function ENT:CalcThrust( KeyUp, KeyDown, Delta )
if self:HitGround() and not KeyUp then
self:ApproachThrust( -1, Delta )
self.Roll = self:GetAngles().r
return
end
local Up = KeyUp and 1 or 0
local Down = KeyDown and -1 or 0
self:ApproachThrust( Up + Down, Delta )
end
function ENT:CalcHover( InputLeft, InputRight, InputUp, InputDown, ThrustUp, ThrustDown, PhysObj, deltatime )
if not IsValid( PhysObj ) then
PhysObj = self:GetPhysicsObject()
end
local VelL = PhysObj:WorldToLocal( PhysObj:GetPos() + PhysObj:GetVelocity() )
local AngVel = PhysObj:GetAngleVelocity()
local KeyLeft = InputLeft and 60 or 0
local KeyRight = InputRight and 60 or 0
local KeyPitchUp = InputUp and 60 or 0
local KeyPitchDown = InputDown and 60 or 0
local Pitch = KeyPitchDown - KeyPitchUp
local Roll = KeyRight - KeyLeft
if (Pitch + Roll) == 0 then
Pitch = math.Clamp(-VelL.x / 200,-1,1) * 60
Roll = math.Clamp(VelL.y / 250,-1,1) * 60
end
local Ang = self:GetAngles()
local Steer = self:GetSteer()
Steer.x = math.Clamp( Roll - Ang.r - AngVel.x,-1,1)
Steer.y = math.Clamp( Pitch - Ang.p - AngVel.y,-1,1)
self:SetSteer( Steer )
self.Roll = Ang.r
if ThrustUp or ThrustDown then
self:CalcThrust( ThrustUp, ThrustDown, deltatime )
return
end
self:ApproachThrust( math.Clamp(-VelL.z / 100,-1,1), deltatime )
end

View File

@@ -0,0 +1,58 @@
function ENT:GetEyeTrace( trace_forward )
local startpos = self:LocalToWorld( self:OBBCenter() )
local pod = self:GetDriverSeat()
if IsValid( pod ) then
startpos = pod:LocalToWorld( pod:OBBCenter() )
end
local AimVector = trace_forward and self:GetForward() or self:GetAimVector()
local data = {
start = startpos,
endpos = (startpos + AimVector * 50000),
filter = self:GetCrosshairFilterEnts(),
}
local trace = util.TraceLine( data )
return trace
end
function ENT:GetAimVector()
if self:GetAI() then
return self:GetAIAimVector()
end
local Driver = self:GetDriver()
if not IsValid( Driver ) then return self:GetForward() end
if not Driver:lvsMouseAim() then
if Driver:lvsKeyDown( "FREELOOK" ) then
local pod = self:GetDriverSeat()
if not IsValid( pod ) then return Driver:EyeAngles():Forward() end
if pod:GetThirdPersonMode() then
return -self:GetForward()
else
return Driver:GetAimVector()
end
else
return self:GetForward()
end
end
if SERVER then
local pod = self:GetDriverSeat()
if not IsValid( pod ) then return Driver:EyeAngles():Forward() end
return pod:WorldToLocalAngles( Driver:EyeAngles() ):Forward()
else
return Driver:EyeAngles():Forward()
end
end

View File

@@ -0,0 +1,153 @@
ENT.Base = "lvs_base"
ENT.PrintName = "[LVS] Base Helicopter"
ENT.Author = "Luna"
ENT.Information = "Luna's Vehicle Script"
ENT.Category = "[LVS]"
ENT.Spawnable = false
ENT.AdminSpawnable = false
ENT.MaxVelocity = 2150
ENT.ThrustUp = 1
ENT.ThrustDown = 0.8
ENT.ThrustRate = 1
ENT.ThrottleRateUp = 0.2
ENT.ThrottleRateDown = 0.2
ENT.TurnRatePitch = 1
ENT.TurnRateYaw = 1
ENT.TurnRateRoll = 1
ENT.GravityTurnRateYaw = 1
ENT.ForceLinearDampingMultiplier = 1.5
ENT.ForceAngleMultiplier = 1
ENT.ForceAngleDampingMultiplier = 1
function ENT:SetupDataTables()
self:CreateBaseDT()
self:AddDT( "Vector", "Steer" )
self:AddDT( "Vector", "AIAimVector" )
self:AddDT( "Float", "Throttle" )
self:AddDT( "Float", "NWThrust" )
end
function ENT:PlayerDirectInput( ply, cmd )
local Pod = self:GetDriverSeat()
local Delta = FrameTime()
local KeyLeft = ply:lvsKeyDown( "-ROLL_HELI" )
local KeyRight = ply:lvsKeyDown( "+ROLL_HELI" )
local KeyPitchUp = ply:lvsKeyDown( "+PITCH_HELI" )
local KeyPitchDown = ply:lvsKeyDown( "-PITCH_HELI" )
local KeyRollRight = ply:lvsKeyDown( "+YAW_HELI" )
local KeyRollLeft = ply:lvsKeyDown( "-YAW_HELI" )
local MouseX = cmd:GetMouseX()
local MouseY = cmd:GetMouseY()
if ply:lvsKeyDown( "FREELOOK" ) and not Pod:GetThirdPersonMode() then
MouseX = 0
MouseY = 0
else
ply:SetEyeAngles( Angle(0,90,0) )
end
local SensX, SensY, ReturnDelta = ply:lvsMouseSensitivity()
if KeyPitchDown then MouseY = (10 / SensY) * ReturnDelta end
if KeyPitchUp then MouseY = -(10 / SensY) * ReturnDelta end
if KeyRollRight or KeyRollLeft then
local NewX = (KeyRollRight and 10 or 0) - (KeyRollLeft and 10 or 0)
MouseX = (NewX / SensX) * ReturnDelta
end
local Input = Vector( MouseX * 0.4 * SensX, MouseY * SensY, 0 )
local Cur = self:GetSteer()
local Rate = Delta * 3 * ReturnDelta
local New = Vector(Cur.x, Cur.y, 0) - Vector( math.Clamp(Cur.x * Delta * 5 * ReturnDelta,-Rate,Rate), math.Clamp(Cur.y * Delta * 5 * ReturnDelta,-Rate,Rate), 0)
local Target = New + Input * Delta * 0.8
local Fx = math.Clamp( Target.x, -1, 1 )
local Fy = math.Clamp( Target.y, -1, 1 )
local TargetFz = (KeyLeft and 1 or 0) - (KeyRight and 1 or 0)
local Fz = Cur.z + math.Clamp(TargetFz - Cur.z,-Rate * 3,Rate * 3)
local F = Cur + (Vector( Fx, Fy, Fz ) - Cur) * math.min(Delta * 100,1)
self:SetSteer( F )
if CLIENT then return end
if ply:lvsKeyDown( "HELI_HOVER" ) then
self:CalcHover( ply:lvsKeyDown( "-YAW_HELI" ), ply:lvsKeyDown( "+YAW_HELI" ), KeyPitchUp, KeyPitchDown, ply:lvsKeyDown( "+THRUST_HELI" ), ply:lvsKeyDown( "-THRUST_HELI" ) )
self.ResetSteer = true
else
if self.ResetSteer then
self.ResetSteer = nil
self:SetSteer( Vector(0,0,0) )
end
self:CalcThrust( ply:lvsKeyDown( "+THRUST_HELI" ), ply:lvsKeyDown( "-THRUST_HELI" ) )
end
end
function ENT:StartCommand( ply, cmd )
if self:GetDriver() ~= ply then return end
if SERVER then
local KeyJump = ply:lvsKeyDown( "VSPEC" )
if self._lvsOldKeyJump ~= KeyJump then
self._lvsOldKeyJump = KeyJump
if KeyJump then
self:ToggleVehicleSpecific()
end
end
end
if not ply:lvsMouseAim() then
self:PlayerDirectInput( ply, cmd )
end
end
function ENT:SetThrust( New )
if self:GetEngineActive() then
self:SetNWThrust( math.Clamp(New,-1,1) )
else
self:SetNWThrust( 0 )
end
end
function ENT:GetThrust()
return self:GetNWThrust()
end
function ENT:GetThrustPercent()
return math.Clamp(0.5 * self:GetThrottle() + self:GetThrust() * 0.5,0,1)
end
function ENT:GetThrustStrenght()
return (1 - (self:GetVelocity():Length() / self.MaxVelocity)) * self:GetThrustPercent()
end
function ENT:GetVehicleType()
return "helicopter"
end

View File

@@ -0,0 +1,200 @@
function ENT:OnCreateAI()
self:StartEngine()
end
function ENT:OnRemoveAI()
self:StopEngine()
end
function ENT:RunAI()
local RangerLength = 15000
local mySpeed = self:GetVelocity():Length()
local myRadius = self:BoundingRadius()
local myPos = self:GetPos()
local myDir = self:GetForward()
local MinDist = 600 + mySpeed
local StartPos = self:LocalToWorld( self:OBBCenter() )
local TraceFilter = self:GetCrosshairFilterEnts()
local FrontLeft = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(0,20,0) ):Forward() * RangerLength } )
local FrontRight = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(0,-20,0) ):Forward() * RangerLength } )
local FrontLeft2 = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(25,65,0) ):Forward() * RangerLength } )
local FrontRight2 = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(25,-65,0) ):Forward() * RangerLength } )
local FrontLeft3 = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(-25,65,0) ):Forward() * RangerLength } )
local FrontRight3 = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(-25,-65,0) ):Forward() * RangerLength } )
local FrontUp = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(-20,0,0) ):Forward() * RangerLength } )
local FrontDown = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(20,0,0) ):Forward() * RangerLength } )
local TraceForward = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:GetForward() * RangerLength } )
local TraceDown = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + Vector(0,0,-RangerLength) } )
local TraceUp = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + Vector(0,0,RangerLength) } )
local cAvoid = Vector(0,0,0)
for _, v in pairs( LVS:GetVehicles() ) do
if v == self then continue end
local theirRadius = v:BoundingRadius()
local Sub = (myPos - v:GetPos())
local Dir = Sub:GetNormalized()
local Dist = Sub:Length()
if Dist < (theirRadius + myRadius + 200) then
if math.deg( math.acos( math.Clamp( myDir:Dot( -Dir ) ,-1,1) ) ) < 90 then
cAvoid = cAvoid + Dir * (theirRadius + myRadius + 500)
end
end
end
local FLp = FrontLeft.HitPos + FrontLeft.HitNormal * MinDist + cAvoid * 8
local FRp = FrontRight.HitPos + FrontRight.HitNormal * MinDist + cAvoid * 8
local FL2p = FrontLeft2.HitPos + FrontLeft2.HitNormal * MinDist
local FR2p = FrontRight2.HitPos + FrontRight2.HitNormal * MinDist
local FL3p = FrontLeft3.HitPos + FrontLeft3.HitNormal * MinDist
local FR3p = FrontRight3.HitPos + FrontRight3.HitNormal * MinDist
local FUp = FrontUp.HitPos + FrontUp.HitNormal * MinDist
local FDp = FrontDown.HitPos + FrontDown.HitNormal * MinDist
local Up = TraceUp.HitPos + TraceUp.HitNormal * MinDist
local Dp = TraceDown.HitPos + TraceDown.HitNormal * MinDist
local TargetPos = (FLp+FRp+FL2p+FR2p+FL3p+FR3p+FUp+FDp+Up+Dp) / 10
local alt = (StartPos - TraceDown.HitPos):Length()
local ceiling = (StartPos - TraceUp.HitPos):Length()
self._AIFireInput = false
local Target = self:AIGetTarget()
if alt < 600 or ceiling < 600 then
if ceiling < 600 then
TargetPos.z = StartPos.z - 2000
else
TargetPos.z = StartPos.z + 2000
end
else
if IsValid( self:GetHardLockTarget() ) then
TargetPos = self:GetHardLockTarget():GetPos() + cAvoid * 8
else
if IsValid( Target ) then
local HisRadius = Target:BoundingRadius()
local HisPos = Target:GetPos() + Vector(0,0,600)
TargetPos = HisPos + (myPos - HisPos):GetNormalized() * (myRadius + HisRadius + 500) + cAvoid * 8
end
end
end
self._lvsTargetPos = TargetPos
if IsValid( Target ) then
TargetPos = Target:LocalToWorld( Target:OBBCenter() )
if self:AITargetInFront( Target, 65 ) then
local tr = self:GetEyeTrace()
if (IsValid( tr.Entity ) and tr.Entity.LVS and tr.Entity.GetAITEAM) and (tr.Entity:GetAITEAM() ~= self:GetAITEAM() or tr.Entity:GetAITEAM() == 0) or true then
local CurHeat = self:GetNWHeat()
local CurWeapon = self:GetSelectedWeapon()
if CurWeapon > 2 then
self:AISelectWeapon( 1 )
else
if CurHeat > 0.9 then
if CurWeapon == 1 and self:AIHasWeapon( 2 ) then
self:AISelectWeapon( 2 )
elseif CurWeapon == 2 then
self:AISelectWeapon( 1 )
end
else
if CurHeat == 0 and math.cos( CurTime() ) > 0 then
self:AISelectWeapon( 1 )
end
end
end
self._AIFireInput = true
end
else
self:AISelectWeapon( 1 )
end
end
self:SetAIAimVector( (TargetPos - myPos):GetNormalized() )
end
function ENT:CalcAIMove( PhysObj, deltatime )
if not self._lvsTargetPos then return end
local StartPos = self:LocalToWorld( self:OBBCenter() )
local Target = self:AIGetTarget()
local TargetPos = self._lvsTargetPos
local VelL = PhysObj:WorldToLocal( PhysObj:GetPos() + PhysObj:GetVelocity() )
local AngVel = PhysObj:GetAngleVelocity()
local LPos = self:WorldToLocal( TargetPos )
local Pitch = math.Clamp(LPos.x * 0.01 - VelL.x * 0.01,-1,1) * 40
local Yaw = ((IsValid( Target ) and Target:GetPos() or TargetPos) - StartPos):Angle().y
local Roll = math.Clamp(VelL.y * 0.01,-1,1) * 40
local Ang = self:GetAngles()
local Steer = self:GetSteer()
Steer.x = math.Clamp( Roll - Ang.r - AngVel.x,-1,1)
Steer.y = math.Clamp( Pitch - Ang.p - AngVel.y,-1,1)
Steer.z = math.Clamp( self:WorldToLocalAngles( Angle(0,Yaw,0) ).y / 10,-1,1)
self:SetSteer( Steer )
self:SetThrust( math.Clamp( LPos.z - VelL.z,-1,1) )
end
function ENT:AISelectWeapon( ID )
if ID == self:GetSelectedWeapon() then return end
local T = CurTime()
if (self._nextAISwitchWeapon or 0) > T then return end
self._nextAISwitchWeapon = T + math.random(3,6)
self:SelectWeapon( ID )
end
function ENT:OnAITakeDamage( dmginfo )
local attacker = dmginfo:GetAttacker()
if not IsValid( attacker ) then return end
if not self:AITargetInFront( attacker, IsValid( self:AIGetTarget() ) and 120 or 45 ) then
self:SetHardLockTarget( attacker )
end
end
function ENT:SetHardLockTarget( target )
if not self:IsEnemy( target ) then return end
self._HardLockTarget = target
self._HardLockTime = CurTime() + 4
end
function ENT:GetHardLockTarget()
if (self._HardLockTime or 0) < CurTime() then return NULL end
return self._HardLockTarget
end

View File

@@ -0,0 +1,54 @@
function ENT:AddEngineSound( pos )
local Engine = ents.Create( "lvs_helicopter_engine" )
if not IsValid( Engine ) then
self:Remove()
print("LVS: Failed to create engine entity. Vehicle terminated.")
return
end
Engine:SetPos( self:LocalToWorld( pos ) )
Engine:SetAngles( self:GetAngles() )
Engine:Spawn()
Engine:Activate()
Engine:SetParent( self )
Engine:SetBase( self )
self:DeleteOnRemove( Engine )
self:TransferCPPI( Engine )
return Engine
end
function ENT:AddRotor( pos, ang, radius, speed )
if not pos or not ang or not radius or not speed then return end
local Rotor = ents.Create( "lvs_helicopter_rotor" )
if not IsValid( Rotor ) then
self:Remove()
print("LVS: Failed to create rotor entity. Vehicle terminated.")
return
end
Rotor:SetPos( self:LocalToWorld( pos ) )
Rotor:SetAngles( self:LocalToWorldAngles( ang ) )
Rotor:Spawn()
Rotor:Activate()
Rotor:SetParent( self )
Rotor:SetBase( self )
Rotor:SetRadius( radius )
Rotor:SetSpeed( speed )
self:DeleteOnRemove( Rotor )
self:TransferCPPI( Rotor )
return Rotor
end

View File

@@ -0,0 +1,41 @@
function ENT:StartDestroyTimer()
if self._DestroyTimerStarted then return end
self._DestroyTimerStarted = true
timer.Simple( self:GetAI() and 5 or 60, function()
if not IsValid( self ) then return end
self.MarkForDestruction = true
end )
end
function ENT:DestroySteering( movevalue )
if self._SteerOverride then return end
self._SteerOverride = true
self._SteerOverrideMove = (movevalue or 1)
self:StartDestroyTimer()
end
function ENT:DestroyEngine()
if self._EngineDestroyed then return end
self._EngineDestroyed = true
self:TurnOffEngine()
self:SetThrottle( 0 )
self:SetThrust( 0 )
self:StartDestroyTimer()
end
function ENT:IsSteeringDestroyed()
return self._SteerOverride == true
end
function ENT:IsEngineDestroyed()
return self._EngineDestroyed == true
end

View File

@@ -0,0 +1,83 @@
function ENT:CalcThrottle()
if not self:GetEngineActive() then
if self:GetThrottle() ~= 0 then self:SetThrottle( 0 ) end
return
end
local Delta = FrameTime()
local Cur = self:GetThrottle()
local New = self._StopEngine and 0 or 1
if self:IsDestroyed() then New = 0 end
if Cur == New and New == 0 then self:TurnOffEngine() return end
self:SetThrottle( Cur + math.Clamp( (New - Cur), -self.ThrottleRateDown * Delta, self.ThrottleRateUp * Delta ) )
end
function ENT:HandleStart()
local Driver = self:GetDriver()
if IsValid( Driver ) then
local KeyReload = Driver:lvsKeyDown( "ENGINE" )
if self.OldKeyReload ~= KeyReload then
self.OldKeyReload = KeyReload
if KeyReload then
self:ToggleEngine()
end
end
end
self:CalcThrottle()
end
function ENT:ToggleEngine()
if self:GetEngineActive() and not self._StopEngine then
self:StopEngine()
else
self:StartEngine()
end
end
function ENT:StartEngine()
if not self:IsEngineStartAllowed() then return end
if self._EngineDestroyed then return end
if self:GetEngineActive() then
self._StopEngine = nil
return
end
self:PhysWake()
self:SetEngineActive( true )
self:OnEngineActiveChanged( true )
self._StopEngine = nil
end
function ENT:StopEngine()
if self._StopEngine then return end
self._StopEngine = true
self:OnEngineActiveChanged( false )
if self:WaterLevel() >= self.WaterLevelAutoStop then
self:TurnOffEngine()
end
end
function ENT:TurnOffEngine()
if not self:GetEngineActive() then return end
self:SetEngineActive( false )
self._StopEngine = nil
end

View File

@@ -0,0 +1,60 @@
function ENT:PlayerMouseAim( ply, phys, deltatime )
local Pod = self:GetDriverSeat()
local PitchUp = ply:lvsKeyDown( "+PITCH_HELI" )
local PitchDown = ply:lvsKeyDown( "-PITCH_HELI" )
local YawRight = ply:lvsKeyDown( "+YAW_HELI" )
local YawLeft = ply:lvsKeyDown( "-YAW_HELI" )
local RollRight = ply:lvsKeyDown( "+ROLL_HELI" )
local RollLeft = ply:lvsKeyDown( "-ROLL_HELI" )
local FreeLook = ply:lvsKeyDown( "FREELOOK" )
local EyeAngles = Pod:WorldToLocalAngles( ply:EyeAngles() )
if FreeLook then
if isangle( self.StoredEyeAngles ) then
EyeAngles = self.StoredEyeAngles
end
else
self.StoredEyeAngles = EyeAngles
end
local OverridePitch = 0
local OverrideYaw = 0
local OverrideRoll = (RollRight and 1 or 0) - (RollLeft and 1 or 0)
if PitchUp or PitchDown then
EyeAngles = self:GetAngles()
self.StoredEyeAngles = Angle(EyeAngles.p,EyeAngles.y,0)
OverridePitch = (PitchUp and 1 or 0) - (PitchDown and 1 or 0)
end
if YawRight or YawLeft then
EyeAngles = self:GetAngles()
self.StoredEyeAngles = Angle(EyeAngles.p,EyeAngles.y,0)
OverrideYaw = (YawRight and 1 or 0) - (YawLeft and 1 or 0)
end
self:ApproachTargetAngle( EyeAngles, OverridePitch, OverrideYaw, OverrideRoll, FreeLook, phys, deltatime )
if ply:lvsKeyDown( "HELI_HOVER" ) then
self:CalcHover( RollLeft, RollRight, PitchUp, PitchDown, ply:lvsKeyDown( "+THRUST_HELI" ), ply:lvsKeyDown( "-THRUST_HELI" ), phys, deltatime )
self.ResetSteer = true
else
if self.ResetSteer then
self.ResetSteer = nil
self:SetSteer( Vector(0,0,0) )
end
self:CalcThrust( ply:lvsKeyDown( "+THRUST_HELI" ), ply:lvsKeyDown( "-THRUST_HELI" ), deltatime )
end
end

View File

@@ -0,0 +1,25 @@
function ENT:ToggleVehicleSpecific()
self._VSPEC = not self._VSPEC
self:OnVehicleSpecificToggled( self._VSPEC )
end
function ENT:EnableVehicleSpecific()
if self._VSPEC then return end
self._VSPEC = true
self:OnVehicleSpecificToggled( self._VSPEC )
end
function ENT:DisableVehicleSpecific()
if not self._VSPEC then return end
self._VSPEC = false
self:OnVehicleSpecificToggled( self._VSPEC )
end
function ENT:OnVehicleSpecificToggled( IsActive )
end

View File

@@ -0,0 +1,64 @@
include("shared.lua")
local up = Vector(0,0,1)
local down = Vector(0,0,-1)
function ENT:DoVehicleFX()
local EntTable = self:GetTable()
local Vel = self:GetVelocity():Length()
if EntTable._WindSFX then
EntTable._WindSFX:ChangeVolume( math.Clamp( (Vel * EntTable.GroundEffectsMultiplier - 1200) / 2800,0,1 ), 0.25 )
end
local T = CurTime()
if (EntTable.nextFX or 0) < T then
EntTable.nextFX = T + 0.01
self:DoAdvancedWaterEffects( EntTable, Vel )
end
end
function ENT:DoAdvancedWaterEffects( EntTable, Vel )
local pos = self:LocalToWorld( self:OBBCenter() )
local traceSky = util.TraceLine( {
start = pos,
endpos = pos + up * 50000,
filter = self:GetCrosshairFilterEnts()
} )
local traceWater = util.TraceLine( {
start = traceSky.HitPos,
endpos = pos + down * 50000,
filter = self:GetCrosshairFilterEnts(),
mask = MASK_WATER
} )
local traceSoil = util.TraceLine( {
start = traceSky.HitPos,
endpos = pos + down * 50000,
filter = self:GetCrosshairFilterEnts(),
mask = MASK_ALL
} )
if traceSoil.HitPos.z > traceWater.HitPos.z then
if EntTable._WaterSFX then
EntTable._WaterSFX:ChangeVolume( 0, 0.25 )
end
return
end
if EntTable._WaterSFX then
EntTable._WaterSFX:ChangeVolume( math.min(Vel / EntTable.MaxVelocity,1) ^ 2, 0.25 )
EntTable._WaterSFX:ChangePitch( math.Clamp((Vel / EntTable.MaxVelocity) * 50,80,150), 0.5 )
end
local effectdata = EffectData()
effectdata:SetOrigin( traceWater.HitPos )
effectdata:SetEntity( self )
util.Effect( "lvs_physics_water_advanced", effectdata )
end

View File

@@ -0,0 +1,167 @@
AddCSLuaFile( "shared.lua" )
AddCSLuaFile( "cl_init.lua" )
include("shared.lua")
include("sv_controls.lua")
include("sv_components.lua")
function ENT:EnableManualTransmission()
return false
end
function ENT:DisableManualTransmission()
return false
end
function ENT:SpawnFunction( ply, tr, ClassName )
local startpos = ply:GetShootPos()
local endpos = startpos + ply:GetAimVector() * 10000
local waterTrace = util.TraceLine( {
start = startpos,
endpos = endpos,
mask = MASK_WATER,
filter = ply
} )
if waterTrace.Hit and ((waterTrace.HitPos - startpos):Length() < (tr.HitPos - startpos):Length()) then
tr = waterTrace
end
if not tr.Hit then return end
local ent = ents.Create( ClassName )
ent:StoreCPPI( ply )
ent:SetPos( tr.HitPos + tr.HitNormal * ent.SpawnNormalOffset )
ent:SetAngles( Angle(0, ply:EyeAngles().y, 0 ) )
ent:Spawn()
ent:Activate()
return ent
end
function ENT:RunAI()
end
function ENT:GetEnginePos()
local Engine = self:GetEngine()
if IsValid( Engine ) then return Engine:GetPos() end
return self:LocalToWorld( self:OBBCenter() )
end
local up = Vector(0,0,1)
local down = Vector(0,0,-1)
function ENT:PhysicsSimulate( phys, deltatime )
if self:GetEngineActive() then phys:Wake() end
local EntTable = self:GetTable()
local pos = self:GetEnginePos()
local traceSky = util.TraceLine( {
start = pos,
endpos = pos + up * 50000,
filter = self:GetCrosshairFilterEnts()
} )
local traceData = {
start = traceSky.HitPos,
endpos = pos + down * 50000,
filter = self:GetCrosshairFilterEnts()
}
pos = phys:LocalToWorld( phys:GetMassCenter() )
local traceSoil = util.TraceLine( traceData )
traceData.mask = MASK_WATER
local traceWater = util.TraceLine( traceData )
local EngineInWater = traceWater.Hit
local Engine = self:GetEngine()
if IsValid( Engine ) then
traceData.start = Engine:GetPos()
traceData.endpos = Engine:LocalToWorld( Vector(0,0,-EntTable.EngineSplashDistance) )
EngineInWater = util.TraceLine( traceData ).Hit
end
if not EngineInWater then return vector_origin, vector_origin, SIM_NOTHING end
local BuoyancyForce = math.min( math.max( traceWater.HitPos.z - pos.z + EntTable.FloatHeight, 0 ), 10 )
local Grav = physenv.GetGravity()
local Vel = phys:GetVelocity()
local AngVel = phys:GetAngleVelocity()
local mul = BuoyancyForce / 10
local invmul = math.Clamp( 1 - mul, 0, 1 )
local Force = (-Grav + Vector(0,0,-Vel.z * invmul * EntTable.FloatForce)) * mul
local ForcePos = pos + self:GetUp() * BuoyancyForce
local ForceLinear, ForceAngle = phys:CalculateForceOffset( Force, ForcePos )
ForceAngle = (ForceAngle - AngVel * invmul * 2) * 15 * EntTable.ForceAngleMultiplier
local VelL = self:WorldToLocal( self:GetPos() + Vel )
local Thrust = self:GetThrust()
local ThrustStrength = math.abs( self:GetThrustStrenght() )
local SteerInput = self:GetSteer()
local Steer = SteerInput * math.Clamp( ThrustStrength + math.min(math.abs( VelL.x ) / math.min( EntTable.MaxVelocity, EntTable.MaxVelocityReverse ), 1 ), 0, 1 )
local Brake = self:GetBrake()
if Brake > 0 then
Steer = Steer * -Brake
end
local Pitch = -(math.max( math.cos( CurTime() * EntTable.FloatWaveFrequency + self:EntIndex() * 1337 ), 0 ) * VelL.x * 0.25 * EntTable.FloatWaveIntensity + Thrust * 0.25 * math.Clamp( VelL.x / EntTable.MaxVelocity,0,1) * EntTable.FloatThrottleIntensity)
local Yaw = Steer * EntTable.TurnForceYaw
local Roll = - AngVel.x * 5 - Steer * EntTable.TurnForceRoll
if math.abs( SteerInput ) < 1 and BuoyancyForce > 0 and not self:IsPlayerHolding() then
Yaw = Yaw - AngVel.z * math.max( ((1 - math.abs( SteerInput )) ^ 2) * 100 - 100 * ThrustStrength, 1 )
end
ForceLinear:Add( self:GetForward() * Thrust + self:GetRight() * VelL.y )
ForceAngle:Add( Vector(Roll,Pitch,Yaw) )
local FloatExp = math.max( self:GetUp().z, 0 ) ^ EntTable.FloatExponent
ForceLinear:Mul( FloatExp )
ForceAngle:Mul( FloatExp )
return self:PhysicsSimulateOverride( ForceAngle, ForceLinear, phys, deltatime, SIM_GLOBAL_ACCELERATION )
end
function ENT:PhysicsSimulateOverride( ForceAngle, ForceLinear, phys, deltatime, simulate )
return ForceAngle, ForceLinear, simulate
end
function ENT:ApproachTargetAngle( TargetAngle )
local pod = self:GetDriverSeat()
if not IsValid( pod ) then return end
local ang = pod:GetAngles()
ang:RotateAroundAxis( self:GetUp(), 90 )
local Forward = ang:Right()
local View = pod:WorldToLocalAngles( TargetAngle ):Forward()
local Reversed = false
if self:AngleBetweenNormal( View, ang:Forward() ) < 90 then
Reversed = self:GetReverse()
end
local LocalAngSteer = (self:AngleBetweenNormal( View, ang:Right() ) - 90) / self.MouseSteerAngle
local Steer = (math.min( math.abs( LocalAngSteer ), 1 ) ^ self.MouseSteerExponent * self:Sign( LocalAngSteer ))
self:SetSteer( Steer )
end

View File

@@ -0,0 +1,101 @@
ENT.Base = "lvs_base_wheeldrive"
ENT.PrintName = "[LVS] Base Boat"
ENT.Author = "Luna"
ENT.Information = "Luna's Vehicle Script"
ENT.Category = "[LVS]"
ENT.Spawnable = false
ENT.AdminSpawnable = false
ENT.MaxHealth = 600
ENT.MaxHealthEngine = 50
ENT.MaxHealthFuelTank = 10
ENT.EngineIdleRPM = 1000
ENT.EngineMaxRPM = 6000
ENT.EngineSplash = false
ENT.EngineSplashStartSize = 50
ENT.EngineSplashEndSize = 200
ENT.EngineSplashVelocity = 500
ENT.EngineSplashVelocityRandomAdd = 200
ENT.EngineSplashThrowAngle = 0
ENT.EngineSplashDistance = 100
ENT.DeleteOnExplode = true
ENT.lvsAllowEngineTool = false
ENT.lvsShowInSpawner = false
ENT.AllowSuperCharger = false
ENT.AllowTurbo = false
ENT.FloatHeight = 0
ENT.FloatForce = 20
ENT.FloatWaveFrequency = 5
ENT.FloatExponent = 2
ENT.FloatWaveIntensity = 1
ENT.FloatThrottleIntensity = 1
ENT.TurnRate = 5
ENT.TurnForceYaw = 600
ENT.TurnForceRoll = 400
ENT.MaxThrust = 1000
ENT.MaxVelocity = 1000
ENT.MaxVelocityReverse = 350
ENT.MinVelocityAutoBrake = 200
ENT.ForceLinearMultiplier = 1
ENT.ForceAngleMultiplier = 1
function ENT:GetVehicleType()
return "boat"
end
function ENT:UpdateAnimation( ply, velocity, maxseqgroundspeed )
ply:SetPlaybackRate( 1 )
if CLIENT then
if ply == self:GetDriver() then
ply:SetPoseParameter( "vehicle_steer", -self:GetSteer() )
ply:InvalidateBoneCache()
end
GAMEMODE:GrabEarAnimation( ply )
GAMEMODE:MouthMoveAnimation( ply )
end
return false
end
function ENT:GetThrust()
return self:GetThrustStrenght() * self.MaxThrust
end
function ENT:GetThrustStrenght()
local EntTable = self:GetTable()
local VelL = self:WorldToLocal( self:GetPos() + self:GetVelocity() )
local DesiredVelocity = EntTable.MaxVelocity * self:GetThrottle() - EntTable.MaxVelocityReverse * self:GetBrake()
if DesiredVelocity == 0 and math.abs( VelL.x ) > EntTable.MinVelocityAutoBrake then
return 0
end
return math.Clamp((DesiredVelocity - VelL.x) / EntTable.MaxVelocity,-1,1)
end
function ENT:GetGear()
return -1
end
function ENT:IsManualTransmission()
return false
end

View File

@@ -0,0 +1,55 @@
function ENT:AddEngine( pos, ang, mins, maxs )
if IsValid( self:GetEngine() ) then return end
ang = ang or angle_zero
mins = mins or Vector(-10,-10,-10)
maxs = maxs or Vector(10,10,10)
local Engine = ents.Create( "lvs_powerboat_engine" )
if not IsValid( Engine ) then
self:Remove()
print("LVS: Failed to create engine entity. Vehicle terminated.")
return
end
Engine:SetPos( self:LocalToWorld( pos ) )
Engine:SetAngles( self:LocalToWorldAngles( ang ) )
Engine:Spawn()
Engine:Activate()
Engine:SetParent( self )
Engine:SetBase( self )
Engine:SetMaxHP( self.MaxHealthEngine )
Engine:SetHP( self.MaxHealthEngine )
self:SetEngine( Engine )
self:DeleteOnRemove( Engine )
self:TransferCPPI( Engine )
debugoverlay.BoxAngles( self:LocalToWorld( pos ), mins, maxs, self:LocalToWorldAngles( ang ), 15, Color( 0, 255, 255, 255 ) )
self:AddDS( {
pos = pos,
ang = ang,
mins = mins,
maxs = maxs,
Callback = function( tbl, ent, dmginfo )
local Engine = self:GetEngine()
if not IsValid( Engine ) then return end
Engine:TakeTransmittedDamage( dmginfo )
if not Engine:GetDestroyed() then
dmginfo:ScaleDamage( 0.25 )
end
end
} )
return Engine
end

View File

@@ -0,0 +1,46 @@
function ENT:CalcMouseSteer( ply )
self:ApproachTargetAngle( ply:EyeAngles() )
end
function ENT:CalcSteer( ply, Steer )
local KeyLeft = ply:lvsKeyDown( "CAR_STEER_LEFT" )
local KeyRight = ply:lvsKeyDown( "CAR_STEER_RIGHT" )
local Steer = (KeyLeft and 1 or 0) - (KeyRight and 1 or 0)
self:LerpSteer( Steer )
end
function ENT:LerpSteer( Steer )
local Rate = FrameTime() * self.TurnRate
local Cur = self:GetSteer()
local New = Cur + math.Clamp(Steer - Cur,-Rate,Rate)
self:SetSteer( New )
end
function ENT:CalcThrottle( ply )
local KeyThrottle = ply:lvsKeyDown( "CAR_THROTTLE" )
local KeyBrake = ply:lvsKeyDown( "CAR_BRAKE" )
if KeyBrake then
KeyThrottle = false
end
local ThrottleValue = ply:lvsKeyDown( "CAR_THROTTLE_MOD" ) and self:GetMaxThrottle() or 0.5
local Throttle = KeyThrottle and ThrottleValue or 0
local Brake = KeyBrake and ThrottleValue or 0
self:LerpThrottle( Throttle )
self:LerpBrake( Brake )
end
function ENT:CalcHandbrake( ply )
end
function ENT:CalcTransmission( ply, T )
end
function ENT:OnHandbrakeActiveChanged( Active )
end

View File

@@ -0,0 +1 @@
include("shared.lua")

View File

@@ -0,0 +1,106 @@
AddCSLuaFile( "shared.lua" )
AddCSLuaFile( "cl_init.lua" )
include("shared.lua")
function ENT:ApproachTargetAngle( TargetAngle, OverridePitch, OverrideYaw, OverrideRoll, FreeMovement )
if self:GetAI() then self:SetAIAimVector( self:WorldToLocalAngles( TargetAngle ):Forward() ) end
local Ang = self:GetAngles()
local AngY = self:WorldToLocalAngles( Angle(TargetAngle.p,TargetAngle.y,Ang.r ) )
local AngP = self:WorldToLocalAngles( Angle(TargetAngle.p,Ang.y,Ang.r ) )
local LocalAngPitch = AngP.p
local LocalAngYaw = AngY.y
local TargetForward = TargetAngle:Forward()
local Forward = self:GetForward()
local AngVel = self:GetPhysicsObject():GetAngleVelocity()
local Pitch = math.Clamp( -LocalAngPitch / 22 * math.Clamp( math.abs( AngY.y ) / 0.9,1,10), -1, 1 ) + math.Clamp(AngVel.y / 100,-0.25,0.25) / math.abs( LocalAngPitch )
local Yaw = math.Clamp( -LocalAngYaw / 22,-1,1) + math.Clamp(AngVel.z / 100,-0.25,0.25) / math.abs( LocalAngYaw )
if OverridePitch and OverridePitch ~= 0 then
Pitch = OverridePitch
end
if OverrideYaw and OverrideYaw ~= 0 then
Yaw = OverrideYaw
end
self:SetSteer( Vector( math.Clamp(Yaw,-1,1), -math.Clamp(Pitch,-1,1), 0) )
end
function ENT:CalcAero( phys, deltatime )
if self:GetAI() then
if self._lvsAITargetAng then
self:ApproachTargetAngle( self._lvsAITargetAng )
end
end
local Steer = self:GetSteer()
local Forward = self:GetForward()
local Left = -self:GetRight()
local Vel = self:GetVelocity()
local VelForward = Vel:GetNormalized()
local GravityPitch = 0
local GravityYaw = 0
-- crash bebehavior
if self:IsDestroyed() then
local WorldGravity = self:GetWorldGravity()
local WorldUp = self:GetWorldUp()
local Up = self:GetUp()
Steer = phys:GetAngleVelocity() / 200
local PitchPull = (math.deg( math.acos( math.Clamp( WorldUp:Dot( Up ) ,-1,1) ) ) - 90) / 90
local YawPull = (math.deg( math.acos( math.Clamp( WorldUp:Dot( Left ) ,-1,1) ) ) - 90) / 90
local GravMul = WorldGravity / 600
GravityPitch = math.abs( PitchPull ) ^ 1.25 * self:Sign( PitchPull ) * GravMul
GravityYaw = math.abs( YawPull ) ^ 1.25 * self:Sign( YawPull ) * GravMul
if not phys:IsGravityEnabled() then
phys:EnableGravity( true )
end
end
local Ang = self:GetAngles()
Steer.y = math.max(math.abs( Steer.y ) - math.min( math.abs( Ang.p / 90 ), 1 ) ^ 3,0) * self:Sign( Steer.y )
if Ang.p > self.MaxPitch and Steer.y > 0 then
Steer.y = 0
end
if Ang.p < -self.MaxPitch and Steer.y < 0 then
Steer.y = 0
end
local Pitch = math.Clamp(Steer.y - GravityPitch,-1,1) * self.TurnRatePitch * 4
local Yaw = math.Clamp(-Steer.x + GravityYaw,-1,1) * self.TurnRateYaw * 4
local VtolMove = self:GetVtolMove()
local RollMul = (math.abs(self:AngleBetweenNormal( Vector(0,0,1) , self:GetForward() ) - 90) / 90) ^ 2
local TargetRoll = (Steer.x * math.min( self:GetThrottle() ^ 2, 1 ) - (VtolMove.y / self.ThrustVtol) * 0.25) * 45 * (1 - RollMul)
local Roll = math.Clamp( self:WorldToLocalAngles( Angle( 0, Ang.y, TargetRoll ) ).r / 90,-1,1) * self.TurnRateRoll * 16
local VelL = self:WorldToLocal( self:GetPos() + Vel )
local MulZ = (math.max( math.deg( math.acos( math.Clamp( VelForward:Dot( Forward ) ,-1,1) ) ) - math.abs( Steer.y ), 0 ) / 90) * 0.3
local MulY = (math.max( math.abs( math.deg( math.acos( math.Clamp( VelForward:Dot( Left ) ,-1,1) ) ) - 90 ) - math.abs( Steer.z ), 0 ) / 90) * 0.15
local Move = Vector( (VtolMove.x < 0) and -math.min(VelL.x * 0.15,0) or 0, -VelL.y * MulY, -VelL.z * MulZ ) + VtolMove
return Move, Vector( Roll, Pitch, Yaw )
end

View File

@@ -0,0 +1,126 @@
ENT.Base = "lvs_base_starfighter"
ENT.PrintName = "[LVS] Base Gunship"
ENT.Author = "Luna"
ENT.Information = "Luna's Vehicle Script"
ENT.Category = "[LVS]"
ENT.ThrustVtol = 30
ENT.ThrustRateVtol = 2
ENT.MaxPitch = 60
ENT.DisableBallistics = true
function ENT:CalcVtolThrottle( ply, cmd )
local Delta = FrameTime()
local ThrottleZero = self:GetThrottle() <= 0
local VtolX = ThrottleZero and (ply:lvsKeyDown( "-VTOL_X_SF" ) and -1 or 0) or 0
local VtolY = ((ply:lvsKeyDown( "+VTOL_Y_SF" ) and 1 or 0) - (ply:lvsKeyDown( "-VTOL_Y_SF" ) and 1 or 0))
if ply:lvsMouseAim() then
VtolY = math.Clamp( VtolY + ((ply:lvsKeyDown( "-ROLL_SF" ) and 1 or 0) - (ply:lvsKeyDown( "+ROLL_SF" ) and 1 or 0)), -1 , 1)
end
VtolY = VtolY * math.max( 1 - self:GetThrottle() ^ 2, 0 )
local VtolZ = ((ply:lvsKeyDown( "+VTOL_Z_SF" ) and 1 or 0) - (ply:lvsKeyDown( "-VTOL_Z_SF" ) and 1 or 0))
local DesiredVtol = Vector(VtolX,VtolY,VtolZ)
local NewVtolMove = self:GetNWVtolMove() + (DesiredVtol - self:GetNWVtolMove()) * self.ThrustRateVtol * Delta
if not ThrottleZero or self:WorldToLocal( self:GetPos() + self:GetVelocity() ).x > 100 then
NewVtolMove.x = 0
end
self:SetVtolMove( NewVtolMove )
end
function ENT:GetVtolMove()
if self:GetEngineActive() and not self:GetAI() then
return self:GetNWVtolMove() * self.ThrustVtol
else
return Vector(0,0,0)
end
end
function ENT:PlayerDirectInput( ply, cmd )
local Pod = self:GetDriverSeat()
local Delta = FrameTime()
local KeyLeft = ply:lvsKeyDown( "-ROLL_SF" )
local KeyRight = ply:lvsKeyDown( "+ROLL_SF" )
local KeyPitchUp = ply:lvsKeyDown( "+PITCH_SF" )
local KeyPitchDown = ply:lvsKeyDown( "-PITCH_SF" )
local MouseX = cmd:GetMouseX()
local MouseY = cmd:GetMouseY()
if ply:lvsKeyDown( "FREELOOK" ) and not Pod:GetThirdPersonMode() then
MouseX = 0
MouseY = 0
else
ply:SetEyeAngles( Angle(0,90,0) )
end
local SensX, SensY, ReturnDelta = ply:lvsMouseSensitivity()
if KeyPitchDown then MouseY = (10 / SensY) * ReturnDelta end
if KeyPitchUp then MouseY = -(10 / SensY) * ReturnDelta end
if KeyLeft then MouseX = -(10 / SensX) * ReturnDelta end
if KeyRight then MouseX = (10 / SensX) * ReturnDelta end
local Input = Vector( MouseX * 0.4 * SensX, MouseY * SensY, 0 )
local Cur = self:GetSteer()
local Rate = Delta * 3 * ReturnDelta
local New = Vector(Cur.x, Cur.y, 0) - Vector( math.Clamp(Cur.x * Delta * 5 * ReturnDelta,-Rate,Rate), math.Clamp(Cur.y * Delta * 5 * ReturnDelta,-Rate,Rate), 0)
local Target = New + Input * Delta * 0.8
local Fx = math.Clamp( Target.x, -1, 1 )
local Fy = math.Clamp( Target.y, -1, 1 )
local TargetFz = (KeyLeft and 1 or 0) - (KeyRight and 1 or 0)
local Fz = Cur.z + math.Clamp(TargetFz - Cur.z,-Rate * 3,Rate * 3)
local F = Cur + (Vector( Fx, Fy, Fz ) - Cur) * math.min(Delta * 100,1)
self:SetSteer( F )
end
function ENT:StartCommand( ply, cmd )
if self:GetDriver() ~= ply then return end
if SERVER then
local KeyJump = ply:lvsKeyDown( "VSPEC" )
if self._lvsOldKeyJump ~= KeyJump then
self._lvsOldKeyJump = KeyJump
if KeyJump then
self:ToggleVehicleSpecific()
end
end
end
if ply:lvsMouseAim() then
if SERVER then
self:PlayerMouseAim( ply, cmd )
end
else
self:PlayerDirectInput( ply, cmd )
end
self:CalcThrottle( ply, cmd )
self:CalcVtolThrottle( ply, cmd )
end
function ENT:GetVehicleType()
return "repulsorlift"
end

View File

@@ -0,0 +1,158 @@
ENT._lvsSmoothFreeLook = 0
function ENT:CalcViewDirectInput( ply, pos, angles, fov, pod )
local ViewPosL = pod:WorldToLocal( pos )
local view = {}
view.fov = fov
view.drawviewer = true
view.angles = self:GetAngles()
local FreeLook = ply:lvsKeyDown( "FREELOOK" )
local Zoom = ply:lvsKeyDown( "ZOOM" )
if not pod:GetThirdPersonMode() then
if FreeLook then
view.angles = pod:LocalToWorldAngles( ply:EyeAngles() )
end
local velL = self:WorldToLocal( self:GetPos() + self:GetVelocity() )
local Dividor = math.abs( velL.x )
local SideForce = math.Clamp( velL.y / Dividor, -1, 1)
local UpForce = math.Clamp( velL.z / Dividor, -1, 1)
local ViewPunch = Vector(0,math.Clamp(SideForce * 10,-1,1),math.Clamp(UpForce * 10,-1,1))
if Zoom then
ViewPunch = Vector(0,0,0)
end
pod._lerpPosOffset = pod._lerpPosOffset and pod._lerpPosOffset + (ViewPunch - pod._lerpPosOffset) * RealFrameTime() * 5 or Vector(0,0,0)
pod._lerpPos = pos
view.origin = pos + pod:GetForward() * -pod._lerpPosOffset.y * 0.5 + pod:GetUp() * pod._lerpPosOffset.z * 0.5
view.angles.p = view.angles.p - pod._lerpPosOffset.z * 0.1
view.angles.y = view.angles.y + pod._lerpPosOffset.y * 0.1
view.drawviewer = false
return view
end
pod._lerpPos = pod._lerpPos or self:GetPos()
local radius = 550
radius = radius + radius * pod:GetCameraDistance()
if FreeLook then
local velL = self:WorldToLocal( self:GetPos() + self:GetVelocity() )
local SideForce = math.Clamp(velL.y / 10,-250,250)
local UpForce = math.Clamp(velL.z / 10,-250,250)
pod._lerpPosL = pod._lerpPosL and (pod._lerpPosL + (Vector(radius, SideForce,150 + radius * 0.1 + radius * pod:GetCameraHeight() + UpForce) - pod._lerpPosL) * RealFrameTime() * 12) or Vector(0,0,0)
pod._lerpPos = self:LocalToWorld( pod._lerpPosL )
view.origin = pod._lerpPos
view.angles = self:LocalToWorldAngles( Angle(0,180,0) )
else
local TargetPos = self:LocalToWorld( Vector(500,0,150 + radius * 0.1 + radius * pod:GetCameraHeight()) )
local Sub = TargetPos - pod._lerpPos
local Dir = Sub:GetNormalized()
local Dist = Sub:Length()
local DesiredPos = TargetPos - self:GetForward() * (300 + radius) - Dir * 100
pod._lerpPos = pod._lerpPos + (DesiredPos - pod._lerpPos) * RealFrameTime() * (Zoom and 30 or 12)
pod._lerpPosL = self:WorldToLocal( pod._lerpPos )
local vel = self:GetVelocity()
view.origin = pod._lerpPos
view.angles = self:GetAngles()
end
view.origin = view.origin + ViewPosL
return view
end
function ENT:CalcViewMouseAim( ply, pos, angles, fov, pod )
local cvarFocus = math.Clamp( LVS.cvarCamFocus:GetFloat() , -1, 1 )
self._lvsSmoothFreeLook = self._lvsSmoothFreeLook + ((ply:lvsKeyDown( "FREELOOK" ) and 0 or 1) - self._lvsSmoothFreeLook) * RealFrameTime() * 10
local view = {}
view.origin = pos
view.fov = fov
view.drawviewer = true
view.angles = (self:GetForward() * (1 + cvarFocus) * self._lvsSmoothFreeLook * 0.8 + ply:EyeAngles():Forward() * math.max(1 - cvarFocus, 1 - self._lvsSmoothFreeLook)):Angle()
if cvarFocus >= 1 then
view.angles = LerpAngle( self._lvsSmoothFreeLook, ply:EyeAngles(), self:GetAngles() )
else
view.angles.r = 0
end
if not pod:GetThirdPersonMode() then
view.drawviewer = false
return view
end
local radius = 550
radius = radius + radius * pod:GetCameraDistance()
local TargetOrigin = view.origin - view.angles:Forward() * radius + view.angles:Up() * (radius * 0.2 + radius * pod:GetCameraHeight())
local WallOffset = 4
local tr = util.TraceHull( {
start = view.origin,
endpos = TargetOrigin,
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.origin = tr.HitPos
if tr.Hit and not tr.StartSolid then
view.origin = view.origin + tr.HitNormal * WallOffset
end
return view
end
function ENT:CalcViewOverride( ply, pos, angles, fov, pod )
return pos, angles, fov
end
function ENT:CalcViewDriver( ply, pos, angles, fov, pod )
if ply:lvsMouseAim() then
return self:CalcViewMouseAim( ply, pos, angles, fov, pod )
else
return self:CalcViewDirectInput( ply, pos, angles, fov, pod )
end
end
function ENT:CalcViewPassenger( ply, pos, angles, fov, pod )
return LVS:CalcView( self, ply, pos, angles, fov, pod )
end
function ENT:LVSCalcView( ply, original_pos, original_angles, original_fov, pod )
local pos, angles, fov = self:CalcViewOverride( ply, original_pos, original_angles, original_fov, pod )
if self:GetDriverSeat() == pod then
return self:CalcViewDriver( ply, pos, angles, fov, pod )
else
return self:CalcViewPassenger( ply, pos, angles, fov, pod )
end
end

View File

@@ -0,0 +1,17 @@
function ENT:OnDestroyed()
if not self.DeathSound then return end
if self:GetVelocity():Length() <= self.MaxVelocity * 0.5 then return end
self._sndDeath = CreateSound( self, self.DeathSound )
self._sndDeath:SetSoundLevel( 125 )
self._sndDeath:PlayEx( 1, 50 + 50 * self:CalcDoppler( LocalPlayer() ) )
end
function ENT:StopDeathSound()
if not self._sndDeath then return end
self._sndDeath:Stop()
end

View File

@@ -0,0 +1,63 @@
ENT.FlyByAdvance = 0
function ENT:FlyByThink()
local ply = LocalPlayer()
if not IsValid( ply ) then return end
local EntTable = self:GetTable()
if ply:lvsGetVehicle() == self then self.OldApproaching = false return end
local ViewEnt = ply:GetViewEntity()
if not IsValid( ViewEnt ) then return end
local Time = CurTime()
if (EntTable._nextflyby or 0) > Time then return end
EntTable._nextflyby = Time + 0.1
local Vel = self:GetVelocity()
if self:GetThrottle() <= 0.75 or Vel:Length() <= EntTable.MaxVelocity * 0.75 then return end
local Sub = ViewEnt:GetPos() - self:GetPos() - Vel * EntTable.FlyByAdvance
local ToPlayer = Sub:GetNormalized()
local VelDir = Vel:GetNormalized()
local ApproachAngle = math.deg( math.acos( math.Clamp( ToPlayer:Dot( VelDir ) ,-1,1) ) )
local Approaching = ApproachAngle < 80
if Approaching ~= EntTable.OldApproaching then
EntTable.OldApproaching = Approaching
if Approaching then
self:StopFlyBy()
else
self:OnFlyBy( 60 + 80 * math.min(ApproachAngle / 140,1) )
end
end
end
function ENT:OnFlyBy( Pitch )
if not self.FlyBySound then return end
local EntTable = self:GetTable()
EntTable.flybysnd = CreateSound( self, EntTable.FlyBySound )
EntTable.flybysnd:SetSoundLevel( 95 )
EntTable.flybysnd:PlayEx( 1, Pitch )
end
function ENT:StopFlyBy()
local EntTable = self:GetTable()
if not EntTable.flybysnd then return end
EntTable.flybysnd:Stop()
EntTable.flybysnd = nil
end

View File

@@ -0,0 +1,72 @@
ENT.IconEngine = Material( "lvs/engine.png" )
function ENT:LVSHudPaintInfoText( X, Y, W, H, ScrX, ScrY, ply )
local speed = math.Round( LVS:GetUnitValue( self:GetVelocity():Length() ) ,0)
draw.DrawText( LVS:GetUnitName().." ", "LVS_FONT", X + 72, Y + 35, color_white, TEXT_ALIGN_RIGHT )
draw.DrawText( speed, "LVS_FONT_HUD_LARGE", X + 72, Y + 20, color_white, TEXT_ALIGN_LEFT )
if ply ~= self:GetDriver() then return end
local Throttle = self:GetThrottle()
local Col = Throttle <= 1 and color_white or Color(0,0,0,255)
local hX = X + W - H * 0.5
local hY = Y + H * 0.25 + H * 0.25
surface.SetMaterial( self.IconEngine )
surface.SetDrawColor( 0, 0, 0, 200 )
surface.DrawTexturedRectRotated( hX + 4, hY + 1, H * 0.5, H * 0.5, 0 )
surface.SetDrawColor( color_white )
surface.DrawTexturedRectRotated( hX + 2, hY - 1, H * 0.5, H * 0.5, 0 )
if not self:GetEngineActive() then
draw.SimpleText( "X" , "LVS_FONT", hX, hY, Color(0,0,0,255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER )
end
self:LVSDrawCircle( hX, hY, H * 0.35, math.min( Throttle, 1 ) )
if Throttle > 1 then
draw.SimpleText( "+"..math.Round((Throttle - 1) * 100,0).."%" , "LVS_FONT", hX, hY, Col, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER )
end
end
function ENT:LVSPreHudPaint( X, Y, ply )
return true
end
function ENT:LVSHudPaint( X, Y, ply )
if not self:LVSPreHudPaint( X, Y, ply ) then return end
if ply ~= self:GetDriver() then return end
local HitPlane = self:GetEyeTrace( true ).HitPos:ToScreen()
local HitPilot = self:GetEyeTrace().HitPos:ToScreen()
self:PaintCrosshairCenter( HitPlane )
self:PaintCrosshairOuter( HitPilot )
if ply:lvsMouseAim() and not ply:lvsKeyDown( "FREELOOK" ) then
self:LVSHudPaintMouseAim( HitPlane, HitPilot )
end
self:LVSPaintHitMarker( HitPilot )
end
function ENT:LVSHudPaintDirectInput( Pos2D )
self:PaintCrosshairCenter( Pos2D )
self:PaintCrosshairOuter( Pos2D )
end
function ENT:LVSHudPaintMouseAim( HitPlane, HitPilot )
local Sub = Vector(HitPilot.x,HitPilot.y,0) - Vector(HitPlane.x,HitPlane.y,0)
local Len = Sub:Length()
local Dir = Sub:GetNormalized()
surface.SetDrawColor( 255, 255, 255, 100 )
if Len > 20 then
surface.DrawLine( HitPlane.x + Dir.x * 5, HitPlane.y + Dir.y * 5, HitPilot.x - Dir.x * 20, HitPilot.y- Dir.y * 20 )
-- shadow
surface.SetDrawColor( 0, 0, 0, 50 )
surface.DrawLine( HitPlane.x + Dir.x * 5 + 1, HitPlane.y + Dir.y * 5 + 1, HitPilot.x - Dir.x * 20+ 1, HitPilot.y- Dir.y * 20 + 1 )
end
end

View File

@@ -0,0 +1,86 @@
include("shared.lua")
include("cl_camera.lua")
include("sh_camera_eyetrace.lua")
include("cl_hud.lua")
include("cl_flyby.lua")
include("cl_deathsound.lua")
ENT.TrailAlpha = 25
DEFINE_BASECLASS( "lvs_base" )
function ENT:Think()
BaseClass.Think( self )
self.EFxScale = self.EFxScale and (self.EFxScale - self.EFxScale * RealFrameTime()) or 0
self:CalcOnThrottle()
end
function ENT:CalcOnThrottle()
if not self:GetEngineActive() then
self._oldOnTHR = nil
return
end
local Throttle = self:GetThrottle()
if self._oldOnTHR ~= Throttle then
if self._oldOnTHR == 0 and Throttle > 0 then
self._IsAccelerating = true
end
if Throttle > (self._oldOnTHR or 0) then
self._IsAccelerating = true
else
self._IsAccelerating = false
end
if self._oldOnTHR == 1 then
self:StopBoost()
end
self._oldOnTHR = Throttle
end
if self._oldAccelerating ~= self._IsAccelerating then
self._oldAccelerating = self._IsAccelerating
if not self._IsAccelerating then return end
self:StartBoost()
end
end
function ENT:StartBoost()
local T = CurTime()
if (self._NextSND or 0) > T then return end
self._NextSND = T + 1
self.EFxScale = 100
self:OnStartBoost()
end
function ENT:StopBoost()
local T = CurTime()
if (self._NextSND or 0) > T then return end
self._NextSND = T + 1
self:OnStopBoost()
end
function ENT:GetBoost()
return (self.EFxScale or 0)
end
function ENT:OnStartBoost()
end
function ENT:OnStopBoost()
end

View File

@@ -0,0 +1,210 @@
AddCSLuaFile( "shared.lua" )
AddCSLuaFile( "cl_init.lua" )
AddCSLuaFile( "cl_camera.lua" )
AddCSLuaFile( "sh_camera_eyetrace.lua" )
AddCSLuaFile( "cl_hud.lua" )
AddCSLuaFile( "cl_flyby.lua" )
AddCSLuaFile( "cl_deathsound.lua" )
include("shared.lua")
include("sv_ai.lua")
include("sv_mouseaim.lua")
include("sv_components.lua")
include("sv_vehiclespecific.lua")
include("sh_camera_eyetrace.lua")
DEFINE_BASECLASS( "lvs_base" )
function ENT:OnDriverChanged( Old, New, VehicleIsActive )
if not VehicleIsActive and self:GetThrottle() == 0 then
self:SetSteer( vector_origin )
self:SetVtolMove( vector_origin )
end
self:OnPassengerChanged( Old, New, 1 )
end
function ENT:StartEngine()
if self:GetEngineActive() or not self:IsEngineStartAllowed() then return end
self:GetPhysicsObject():EnableGravity( false )
BaseClass.StartEngine( self )
end
function ENT:StopEngine()
if not self:GetEngineActive() then return end
self:GetPhysicsObject():EnableGravity( true )
BaseClass.StopEngine( self )
end
function ENT:OnCreateAI()
self:StartEngine()
self.COL_GROUP_OLD = self:GetCollisionGroup()
self:SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS )
end
function ENT:OnRemoveAI()
self:StopEngine()
self:SetCollisionGroup( self.COL_GROUP_OLD or COLLISION_GROUP_NONE )
end
function ENT:ApproachTargetAngle( TargetAngle, OverridePitch, OverrideYaw, OverrideRoll, FreeMovement )
local LocalAngles = self:WorldToLocalAngles( TargetAngle )
if self:GetAI() then self:SetAIAimVector( LocalAngles:Forward() ) end
local LocalAngPitch = LocalAngles.p
local LocalAngYaw = LocalAngles.y
local LocalAngRoll = LocalAngles.r
local TargetForward = TargetAngle:Forward()
local Forward = self:GetForward()
local AngDiff = math.deg( math.acos( math.Clamp( Forward:Dot( TargetForward ) ,-1,1) ) )
local WingFinFadeOut = math.max( (90 - AngDiff ) / 90, 0 )
local RudderFadeOut = math.min( math.max( (120 - AngDiff ) / 120, 0 ) * 3, 1 )
local AngVel = self:GetPhysicsObject():GetAngleVelocity()
local SmoothPitch = math.Clamp( math.Clamp(AngVel.y / 100,-0.25,0.25) / math.abs( LocalAngPitch ), -1, 1 )
local SmoothYaw = math.Clamp( math.Clamp(AngVel.z / 100,-0.25,0.25) / math.abs( LocalAngYaw ), -1, 1 )
local Pitch = math.Clamp( -LocalAngPitch / 10 + SmoothPitch, -1, 1 )
local Yaw = math.Clamp( -LocalAngYaw / 2 + SmoothYaw,-1,1) * RudderFadeOut
local Roll = math.Clamp( (-math.Clamp(LocalAngYaw * 16 * self:GetThrottle(),-90,90) + LocalAngRoll * RudderFadeOut * 0.75) * WingFinFadeOut / 180 , -1 , 1 )
if FreeMovement then
Roll = math.Clamp( -LocalAngYaw * WingFinFadeOut / 180 , -1 , 1 )
end
if OverridePitch and OverridePitch ~= 0 then
Pitch = OverridePitch
end
if OverrideYaw and OverrideYaw ~= 0 then
Yaw = OverrideYaw
end
if OverrideRoll and OverrideRoll ~= 0 then
Roll = OverrideRoll
end
self:SetSteer( Vector( math.Clamp(Roll * 1.25,-1,1), math.Clamp(-Pitch * 1.25,-1,1), -Yaw) )
end
function ENT:CalcAero( phys, deltatime, EntTable )
if not EntTable then
EntTable = self:GetTable()
end
-- mouse aim needs to run at high speed.
if self:GetAI() then
if EntTable._lvsAITargetAng then
self:ApproachTargetAngle( EntTable._lvsAITargetAng )
end
else
local ply = self:GetDriver()
if IsValid( ply ) and ply:lvsMouseAim() then
self:PlayerMouseAim( ply )
end
end
local Steer = self:GetSteer()
local Forward = self:GetForward()
local Left = -self:GetRight()
local Vel = self:GetVelocity()
local VelForward = Vel:GetNormalized()
local GravityPitch = 0
local GravityYaw = 0
-- crash bebehavior
if self:IsDestroyed() then
local WorldGravity = self:GetWorldGravity()
local WorldUp = self:GetWorldUp()
local Up = self:GetUp()
Steer = phys:GetAngleVelocity() / 200
local PitchPull = (math.deg( math.acos( math.Clamp( WorldUp:Dot( Up ) ,-1,1) ) ) - 90) / 90
local YawPull = (math.deg( math.acos( math.Clamp( WorldUp:Dot( Left ) ,-1,1) ) ) - 90) / 90
local GravMul = WorldGravity / 600
GravityPitch = math.abs( PitchPull ) ^ 1.25 * self:Sign( PitchPull ) * GravMul
GravityYaw = math.abs( YawPull ) ^ 1.25 * self:Sign( YawPull ) * GravMul
if not phys:IsGravityEnabled() then
phys:EnableGravity( true )
end
end
local Pitch = math.Clamp(Steer.y - GravityPitch,-1,1) * EntTable.TurnRatePitch * 3
local Yaw = math.Clamp(Steer.z * 4 + GravityYaw,-1,1) * EntTable.TurnRateYaw
local Roll = math.Clamp(Steer.x * 1.5,-1,1) * EntTable.TurnRateRoll * 12
local VelL = self:WorldToLocal( self:GetPos() + Vel )
local MulZ = (math.max( math.deg( math.acos( math.Clamp( VelForward:Dot( Forward ) ,-1,1) ) ) - math.abs( Steer.y ), 0 ) / 90) * 0.3
local MulY = (math.max( math.abs( math.deg( math.acos( math.Clamp( VelForward:Dot( Left ) ,-1,1) ) ) - 90 ) - math.abs( Steer.z ), 0 ) / 90) * 0.15
local VtolMove = self:GetVtolMove()
local Move = Vector( (VtolMove.x < 0) and -math.min(VelL.x * 0.15,0) or 0, -VelL.y * MulY, -VelL.z * MulZ ) + VtolMove
return Move, Vector( Roll, Pitch, Yaw )
end
function ENT:OnSkyCollide( data, PhysObj )
local NewVelocity = self:VectorSubtractNormal( data.HitNormal, data.OurOldVelocity ) - data.HitNormal * math.Clamp(self:GetThrustStrenght() * self.MaxThrust,250,800)
PhysObj:SetVelocityInstantaneous( NewVelocity )
PhysObj:SetAngleVelocityInstantaneous( data.OurOldAngularVelocity )
return true
end
function ENT:PhysicsSimulate( phys, deltatime )
if self:GetEngineActive() then
phys:Wake()
else
return vector_origin, vector_origin, SIM_NOTHING
end
local EntTable = self:GetTable()
local Aero, Torque = self:CalcAero( phys, deltatime, EntTable )
local Thrust = self:GetThrustStrenght() * EntTable.MaxThrust * 100
if self:IsDestroyed() then
Thrust = math.max( Thrust, 0 ) -- dont allow braking, but allow accelerating while destroyed
end
local ForceLinear = (Aero * 10000 * EntTable.ForceLinearMultiplier + Vector(Thrust,0,0)) * deltatime
local ForceAngle = (Torque * 25 * EntTable.ForceAngleMultiplier - phys:GetAngleVelocity() * 1.5 * EntTable.ForceAngleDampingMultiplier) * deltatime * 250
return self:PhysicsSimulateOverride( ForceAngle, ForceLinear, phys, deltatime, SIM_LOCAL_ACCELERATION )
end
function ENT:PhysicsSimulateOverride( ForceAngle, ForceLinear, phys, deltatime, simulate )
return ForceAngle, ForceLinear, simulate
end
function ENT:OnMaintenance()
for _, Engine in pairs( self:GetEngines() ) do
if not IsValid( Engine ) then continue end
if not Engine.SetHP or not Engine.GetMaxHP or not Engine.SetDestroyed then continue end
Engine:SetHP( Engine:GetMaxHP() )
Engine:SetDestroyed( false )
end
end

View File

@@ -0,0 +1,58 @@
function ENT:GetEyeTrace( trace_forward )
local startpos = self:LocalToWorld( self:OBBCenter() )
local pod = self:GetDriverSeat()
if IsValid( pod ) then
startpos = pod:LocalToWorld( pod:OBBCenter() )
end
local AimVector = trace_forward and self:GetForward() or self:GetAimVector()
local data = {
start = startpos,
endpos = (startpos + AimVector * 50000),
filter = self:GetCrosshairFilterEnts(),
}
local trace = util.TraceLine( data )
return trace
end
function ENT:GetAimVector()
if self:GetAI() then
return self:GetAIAimVector()
end
local Driver = self:GetDriver()
if not IsValid( Driver ) then return self:GetForward() end
if not Driver:lvsMouseAim() then
if Driver:lvsKeyDown( "FREELOOK" ) then
local pod = self:GetDriverSeat()
if not IsValid( pod ) then return Driver:EyeAngles():Forward() end
if pod:GetThirdPersonMode() then
return -self:GetForward()
else
return Driver:GetAimVector()
end
else
return self:GetForward()
end
end
if SERVER then
local pod = self:GetDriverSeat()
if not IsValid( pod ) then return Driver:EyeAngles():Forward() end
return pod:WorldToLocalAngles( Driver:EyeAngles() ):Forward()
else
return Driver:EyeAngles():Forward()
end
end

View File

@@ -0,0 +1,194 @@
ENT.Base = "lvs_base"
ENT.PrintName = "[LVS] Base Starfighter"
ENT.Author = "Luna"
ENT.Information = "Luna's Vehicle Script"
ENT.Category = "[LVS]"
ENT.Spawnable = false
ENT.AdminSpawnable = false
ENT.MaxVelocity = 3000
ENT.MaxThrust = 3000
ENT.ThrustVtol = 55
ENT.ThrustRateVtol = 3
ENT.ThrottleRateUp = 0.6
ENT.ThrottleRateDown = 0.6
ENT.TurnRatePitch = 1
ENT.TurnRateYaw = 1
ENT.TurnRateRoll = 1
ENT.ForceLinearMultiplier = 1
ENT.ForceAngleMultiplier = 1
ENT.ForceAngleDampingMultiplier = 1
ENT.DisableBallistics = true
function ENT:SetupDataTables()
self:AddDT( "Vector", "Steer" )
self:AddDT( "Vector", "AIAimVector" )
self:AddDT( "Vector", "NWVtolMove" )
self:AddDT( "Float", "NWThrottle" )
self:AddDT( "Float", "MaxThrottle" )
if SERVER then
self:SetMaxThrottle( 1 )
end
self:CreateBaseDT()
end
function ENT:PlayerDirectInput( ply, cmd )
local Pod = self:GetDriverSeat()
local Delta = FrameTime()
local KeyLeft = ply:lvsKeyDown( "-ROLL_SF" )
local KeyRight = ply:lvsKeyDown( "+ROLL_SF" )
local KeyPitchUp = ply:lvsKeyDown( "+PITCH_SF" )
local KeyPitchDown = ply:lvsKeyDown( "-PITCH_SF" )
local KeyRollRight = ply:lvsKeyDown( "+YAW_SF" )
local KeyRollLeft = ply:lvsKeyDown( "-YAW_SF" )
local MouseX = cmd:GetMouseX()
local MouseY = cmd:GetMouseY()
if ply:lvsKeyDown( "FREELOOK" ) and not Pod:GetThirdPersonMode() then
MouseX = 0
MouseY = 0
else
ply:SetEyeAngles( Angle(0,90,0) )
end
local SensX, SensY, ReturnDelta = ply:lvsMouseSensitivity()
if KeyPitchDown then MouseY = (10 / SensY) * ReturnDelta end
if KeyPitchUp then MouseY = -(10 / SensY) * ReturnDelta end
if KeyRollRight or KeyRollLeft then
local NewX = (KeyRollRight and 10 or 0) - (KeyRollLeft and 10 or 0)
MouseX = (NewX / SensX) * ReturnDelta
end
local Input = Vector( MouseX * 0.4 * SensX, MouseY * SensY, 0 )
local Cur = self:GetSteer()
local Rate = Delta * 3 * ReturnDelta
local New = Vector(Cur.x, Cur.y, 0) - Vector( math.Clamp(Cur.x * Delta * 5 * ReturnDelta,-Rate,Rate), math.Clamp(Cur.y * Delta * 5 * ReturnDelta,-Rate,Rate), 0)
local Target = New + Input * Delta * 0.8
local Fx = math.Clamp( Target.x, -1, 1 )
local Fy = math.Clamp( Target.y, -1, 1 )
local TargetFz = (KeyLeft and 1 or 0) - (KeyRight and 1 or 0)
local Fz = Cur.z + math.Clamp(TargetFz - Cur.z,-Rate * 3,Rate * 3)
local F = Cur + (Vector( Fx, Fy, Fz ) - Cur) * math.min(Delta * 100,1)
self:SetSteer( F )
end
function ENT:CalcThrottle( ply, cmd )
if CLIENT then return end
local Delta = FrameTime()
local ThrottleUp = ply:lvsKeyDown( "+THRUST_SF" ) and self.ThrottleRateUp or 0
local ThrottleDown = ply:lvsKeyDown( "-THRUST_SF" ) and -self.ThrottleRateDown or 0
local Throttle = (ThrottleUp + ThrottleDown) * Delta
self:SetThrottle( self:GetThrottle() + Throttle )
end
function ENT:CalcVtolThrottle( ply, cmd )
local Delta = FrameTime()
local ThrottleZero = self:GetThrottle() <= 0
local VtolX = ThrottleZero and (ply:lvsKeyDown( "-VTOL_X_SF" ) and -1 or 0) or 0
local VtolY = ((ply:lvsKeyDown( "+VTOL_Y_SF" ) and 1 or 0) - (ply:lvsKeyDown( "-VTOL_Y_SF" ) and 1 or 0))
local VtolZ = ((ply:lvsKeyDown( "+VTOL_Z_SF" ) and 1 or 0) - (ply:lvsKeyDown( "-VTOL_Z_SF" ) and 1 or 0))
local DesiredVtol = Vector(VtolX,VtolY,VtolZ)
local NewVtolMove = self:GetNWVtolMove() + (DesiredVtol - self:GetNWVtolMove()) * self.ThrustRateVtol * Delta
if not ThrottleZero or self:WorldToLocal( self:GetPos() + self:GetVelocity() ).x > 100 then
NewVtolMove.x = 0
end
self:SetVtolMove( NewVtolMove )
end
function ENT:SetVtolMove( NewMove )
if self:GetEngineActive() then
self:SetNWVtolMove( NewMove )
else
self:SetNWVtolMove( Vector(0,0,0) )
end
end
function ENT:SetThrottle( NewThrottle )
if self:GetEngineActive() then
self:SetNWThrottle( math.Clamp(NewThrottle,0,self:GetMaxThrottle()) )
else
self:SetNWThrottle( 0 )
end
end
function ENT:GetThrottle()
if self:GetEngineActive() then
return self:GetNWThrottle()
else
return 0
end
end
function ENT:GetVtolMove()
if self:GetEngineActive() and not self:GetAI() then
return self:GetNWVtolMove() * self.ThrustVtol * (1 - math.min( self:GetThrottle(), 1 ))
else
return Vector(0,0,0)
end
end
function ENT:StartCommand( ply, cmd )
if self:GetDriver() ~= ply then return end
if SERVER then
local KeyJump = ply:lvsKeyDown( "VSPEC" )
if self._lvsOldKeyJump ~= KeyJump then
self._lvsOldKeyJump = KeyJump
if KeyJump then
self:ToggleVehicleSpecific()
end
end
end
if not ply:lvsMouseAim() then
self:PlayerDirectInput( ply, cmd )
end
self:CalcThrottle( ply, cmd )
self:CalcVtolThrottle( ply, cmd )
end
function ENT:GetThrustStrenght()
local ForwardVelocity = self:WorldToLocal( self:GetPos() + self:GetVelocity() ).x
return (self.MaxVelocity * self:GetThrottle() - ForwardVelocity) / self.MaxVelocity
end
function ENT:GetVehicleType()
return "starfighter"
end

View File

@@ -0,0 +1,192 @@
function ENT:OnCreateAI()
self:StartEngine()
end
function ENT:OnRemoveAI()
self:StopEngine()
end
function ENT:RunAI()
local RangerLength = 15000
local mySpeed = self:GetVelocity():Length()
local MinDist = 600 + mySpeed
local StartPos = self:LocalToWorld( self:OBBCenter() )
local TraceFilter = self:GetCrosshairFilterEnts()
local FrontLeft = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(0,20,0) ):Forward() * RangerLength } )
local FrontRight = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(0,-20,0) ):Forward() * RangerLength } )
local FrontLeft2 = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(25,65,0) ):Forward() * RangerLength } )
local FrontRight2 = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(25,-65,0) ):Forward() * RangerLength } )
local FrontLeft3 = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(-25,65,0) ):Forward() * RangerLength } )
local FrontRight3 = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(-25,-65,0) ):Forward() * RangerLength } )
local FrontUp = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(-20,0,0) ):Forward() * RangerLength } )
local FrontDown = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:LocalToWorldAngles( Angle(20,0,0) ):Forward() * RangerLength } )
local TraceForward = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + self:GetForward() * RangerLength } )
local TraceDown = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + Vector(0,0,-RangerLength) } )
local TraceUp = util.TraceLine( { start = StartPos, filter = TraceFilter, endpos = StartPos + Vector(0,0,RangerLength) } )
local cAvoid = Vector(0,0,0)
local myRadius = self:BoundingRadius()
local myPos = self:GetPos()
local myDir = self:GetForward()
for _, v in pairs( LVS:GetVehicles() ) do
if v == self then continue end
local theirRadius = v:BoundingRadius()
local Sub = (myPos - v:GetPos())
local Dir = Sub:GetNormalized()
local Dist = Sub:Length()
if Dist < (theirRadius + myRadius + 200) then
if math.deg( math.acos( math.Clamp( myDir:Dot( -Dir ) ,-1,1) ) ) < 90 then
cAvoid = cAvoid + Dir * (theirRadius + myRadius + 500)
end
end
end
local FLp = FrontLeft.HitPos + FrontLeft.HitNormal * MinDist + cAvoid * 8
local FRp = FrontRight.HitPos + FrontRight.HitNormal * MinDist + cAvoid * 8
local FL2p = FrontLeft2.HitPos + FrontLeft2.HitNormal * MinDist
local FR2p = FrontRight2.HitPos + FrontRight2.HitNormal * MinDist
local FL3p = FrontLeft3.HitPos + FrontLeft3.HitNormal * MinDist
local FR3p = FrontRight3.HitPos + FrontRight3.HitNormal * MinDist
local FUp = FrontUp.HitPos + FrontUp.HitNormal * MinDist
local FDp = FrontDown.HitPos + FrontDown.HitNormal * MinDist
local Up = TraceUp.HitPos + TraceUp.HitNormal * MinDist
local Dp = TraceDown.HitPos + TraceDown.HitNormal * MinDist
local TargetPos = (FLp+FRp+FL2p+FR2p+FL3p+FR3p+FUp+FDp+Up+Dp) / 10
local alt = (StartPos - TraceDown.HitPos):Length()
local ceiling = (StartPos - TraceUp.HitPos):Length()
local WallDist = (StartPos - TraceForward.HitPos):Length()
local Throttle = math.min( WallDist / mySpeed, 1 )
self._AIFireInput = false
if alt < 600 or ceiling < 600 or WallDist < (MinDist * 3 * (math.deg( math.acos( math.Clamp( Vector(0,0,1):Dot( myDir ) ,-1,1) ) ) / 180) ^ 2) then
Throttle = 1
if self:HitGround() then
TargetPos.z = StartPos.z + 750
end
else
if IsValid( self:GetHardLockTarget() ) then
TargetPos = self:GetHardLockTarget():GetPos() + cAvoid * 8
else
if alt > mySpeed then
local Target = self._LastAITarget
if not IsValid( self._LastAITarget ) or not self:AITargetInFront( self._LastAITarget, 135 ) or not self:AICanSee( self._LastAITarget ) then
Target = self:AIGetTarget()
end
if IsValid( Target ) then
if self:AITargetInFront( Target, 65 ) then
local T = CurTime() + self:EntIndex() * 1337
TargetPos = Target:GetPos() + cAvoid * 8 + Vector(0,0, math.sin( T * 5 ) * 500 ) + Target:GetVelocity() * math.abs( math.cos( T * 13.37 ) ) * 5
Throttle = math.min( (StartPos - TargetPos):Length() / mySpeed, 1 )
local tr = util.TraceHull( {
start = StartPos,
endpos = (StartPos + self:GetForward() * 50000),
mins = Vector( -50, -50, -50 ),
maxs = Vector( 50, 50, 50 ),
filter = TraceFilter
} )
local CanShoot = (IsValid( tr.Entity ) and tr.Entity.LVS and tr.Entity.GetAITEAM) and (tr.Entity:GetAITEAM() ~= self:GetAITEAM() or tr.Entity:GetAITEAM() == 0) or true
if CanShoot and self:AITargetInFront( Target, 22 ) then
local CurHeat = self:GetNWHeat()
local CurWeapon = self:GetSelectedWeapon()
if CurWeapon > 2 then
self:AISelectWeapon( 1 )
else
if CurHeat > 0.9 then
if CurWeapon == 1 and self:AIHasWeapon( 2 ) then
self:AISelectWeapon( 2 )
elseif CurWeapon == 2 then
self:AISelectWeapon( 1 )
end
else
if CurHeat == 0 and math.cos( T ) > 0 then
self:AISelectWeapon( 1 )
end
end
end
self._AIFireInput = true
end
else
self:AISelectWeapon( 1 )
if alt > 6000 and self:AITargetInFront( Target, 90 ) then
TargetPos = Target:GetPos()
end
end
end
else
TargetPos.z = StartPos.z + 2000
self:EnableVehicleSpecific()
end
end
end
self:SetThrottle( Throttle )
self.smTargetPos = self.smTargetPos and self.smTargetPos + (TargetPos - self.smTargetPos) * FrameTime() or self:GetPos()
self._lvsAITargetAng = (self.smTargetPos - self:GetPos()):GetNormalized():Angle()
end
function ENT:AISelectWeapon( ID )
if ID == self:GetSelectedWeapon() then return end
local T = CurTime()
if (self._nextAISwitchWeapon or 0) > T then return end
self._nextAISwitchWeapon = T + math.random(3,6)
self:SelectWeapon( ID )
end
function ENT:OnAITakeDamage( dmginfo )
local attacker = dmginfo:GetAttacker()
if not IsValid( attacker ) then return end
if not self:AITargetInFront( attacker, IsValid( self:AIGetTarget() ) and 120 or 45 ) then
self:SetHardLockTarget( attacker )
end
end
function ENT:SetHardLockTarget( target )
self._HardLockTarget = target
self._HardLockTime = CurTime() + 4
end
function ENT:GetHardLockTarget()
if (self._HardLockTime or 0) < CurTime() then return NULL end
return self._HardLockTarget
end

View File

@@ -0,0 +1,78 @@
function ENT:AddEngine( pos, ang, mins, maxs, health )
local Engine = ents.Create( "lvs_starfighter_engine" )
if not IsValid( Engine ) then
self:Remove()
print("LVS: Failed to create engine entity. Vehicle terminated.")
return
end
Engine:SetPos( self:LocalToWorld( pos ) )
Engine:SetAngles( self:GetAngles() )
Engine:Spawn()
Engine:Activate()
Engine:SetParent( self )
Engine:SetBase( self )
if not health then
health = self.MaxHealth / 8
end
Engine:SetMaxHP( health )
Engine:SetHP( health )
self:DeleteOnRemove( Engine )
self:TransferCPPI( Engine )
self:AddDS( {
pos = pos,
ang = (ang or Angle(0,0,0)),
mins = (mins or Vector(-40,-20,-30)),
maxs = (maxs or Vector(40,20,30)),
Callback = function( tbl, ent, dmginfo )
if dmginfo:GetDamage() <= 0 then return end
Engine:TakeDamageInfo( dmginfo )
end
} )
if not istable( self._lvsEngines ) then
self._lvsEngines = {}
end
table.insert( self._lvsEngines, Engine )
return Engine
end
function ENT:GetEngines()
return self._lvsEngines or {}
end
function ENT:AddEngineSound( pos )
local EngineSND = ents.Create( "lvs_starfighter_soundemitter" )
if not IsValid( EngineSND ) then
self:Remove()
print("LVS: Failed to create engine sound entity. Vehicle terminated.")
return
end
EngineSND:SetPos( self:LocalToWorld( pos ) )
EngineSND:SetAngles( self:GetAngles() )
EngineSND:Spawn()
EngineSND:Activate()
EngineSND:SetParent( self )
EngineSND:SetBase( self )
self:DeleteOnRemove( EngineSND )
self:TransferCPPI( EngineSND )
return EngineSND
end

View File

@@ -0,0 +1,45 @@
function ENT:PlayerMouseAim( ply )
local Pod = self:GetDriverSeat()
local PitchUp = ply:lvsKeyDown( "+PITCH_SF" )
local PitchDown = ply:lvsKeyDown( "-PITCH_SF" )
local YawRight = ply:lvsKeyDown( "+YAW_SF" )
local YawLeft = ply:lvsKeyDown( "-YAW_SF" )
local RollRight = ply:lvsKeyDown( "+ROLL_SF" )
local RollLeft = ply:lvsKeyDown( "-ROLL_SF" )
local FreeLook = ply:lvsKeyDown( "FREELOOK" )
local EyeAngles = Pod:WorldToLocalAngles( ply:EyeAngles() )
if FreeLook then
if isangle( self.StoredEyeAngles ) then
EyeAngles = self.StoredEyeAngles
end
else
self.StoredEyeAngles = EyeAngles
end
local OverridePitch = 0
local OverrideYaw = 0
local OverrideRoll = (RollRight and 1 or 0) - (RollLeft and 1 or 0)
if PitchUp or PitchDown then
EyeAngles = self:GetAngles()
self.StoredEyeAngles = Angle(EyeAngles.p,EyeAngles.y,0)
OverridePitch = (PitchUp and 1 or 0) - (PitchDown and 1 or 0)
end
if YawRight or YawLeft then
EyeAngles = self:GetAngles()
self.StoredEyeAngles = Angle(EyeAngles.p,EyeAngles.y,0)
OverrideYaw = (YawRight and 1 or 0) - (YawLeft and 1 or 0)
end
self:ApproachTargetAngle( EyeAngles, OverridePitch, OverrideYaw, OverrideRoll, FreeLook )
end

View File

@@ -0,0 +1,25 @@
function ENT:ToggleVehicleSpecific()
self._VSPEC = not self._VSPEC
self:OnVehicleSpecificToggled( self._VSPEC )
end
function ENT:EnableVehicleSpecific()
if self._VSPEC then return end
self._VSPEC = true
self:OnVehicleSpecificToggled( self._VSPEC )
end
function ENT:DisableVehicleSpecific()
if not self._VSPEC then return end
self._VSPEC = false
self:OnVehicleSpecificToggled( self._VSPEC )
end
function ENT:OnVehicleSpecificToggled( IsActive )
end

View File

@@ -0,0 +1,75 @@
function ENT:CalcViewOverride( ply, pos, angles, fov, pod )
return pos, angles, fov
end
function ENT:CalcViewDirectInput( ply, pos, angles, fov, pod )
return LVS:CalcView( self, ply, pos, angles, fov, pod )
end
function ENT:CalcViewMouseAim( ply, pos, angles, fov, pod )
return LVS:CalcView( self, ply, pos, angles, fov, pod )
end
function ENT:CalcViewDriver( ply, pos, angles, fov, pod )
pos = pos + pod:GetUp() * 7 - pod:GetRight() * 11
if ply:lvsMouseAim() then
angles = ply:EyeAngles()
return self:CalcViewMouseAim( ply, pos, angles, fov, pod )
else
return self:CalcViewDirectInput( ply, pos, angles, fov, pod )
end
end
function ENT:CalcViewPassenger( ply, pos, angles, fov, pod )
return LVS:CalcView( self, ply, pos, angles, fov, pod )
end
function ENT:LVSCalcView( ply, original_pos, original_angles, original_fov, pod )
local pos, angles, fov = self:CalcViewOverride( ply, original_pos, original_angles, original_fov, pod )
local new_fov = math.min( fov + self:CalcViewPunch( ply, pos, angles, fov, pod ), 180 )
if self:GetDriverSeat() == pod then
return self:CalcViewDriver( ply, pos, angles, new_fov, pod )
else
return self:CalcViewPassenger( ply, pos, angles, new_fov, pod )
end
end
function ENT:SuppressViewPunch( time )
self._viewpunch_supressed_time = CurTime() + (time or 0.2)
end
function ENT:IsViewPunchSuppressed()
return (self._viewpunch_supressed_time or 0) > CurTime()
end
function ENT:CalcViewPunch( ply, pos, angles, fov, pod )
local Vel = self:GetVelocity()
local VelLength = Vel:Length()
local VelPercentMaxSpeed = math.min( VelLength / 1000, 1 )
if ply:lvsMouseAim() then
angles = ply:EyeAngles()
end
local direction = (90 - self:AngleBetweenNormal( angles:Forward(), Vel:GetNormalized() )) / 90
local FovValue = math.min( VelPercentMaxSpeed ^ 2 * 100, 15 )
local Throttle = self:GetThrottle()
local Brake = self:GetBrake()
if self:IsViewPunchSuppressed() then
self._viewpunch_fov = self._viewpunch_fov and self._viewpunch_fov + (-VelPercentMaxSpeed * FovValue - self._viewpunch_fov) * RealFrameTime() or 0
else
local newFov =(1 - VelPercentMaxSpeed) * Throttle * FovValue - VelPercentMaxSpeed * Brake * FovValue
self._viewpunch_fov = self._viewpunch_fov and self._viewpunch_fov + (newFov - self._viewpunch_fov) * RealFrameTime() * 10 or 0
end
return self._viewpunch_fov * (90 - self:AngleBetweenNormal( angles:Forward(), Vel:GetNormalized() )) / 90
end

View File

@@ -0,0 +1,112 @@
function ENT:IsBackFireEnabled()
if not isfunction( self.GetBackfire ) then return false end
return self:GetBackfire()
end
function ENT:DoExhaustFX( Magnitude )
for _, data in ipairs( self.ExhaustPositions ) do
if data.bodygroup then
if not self:BodygroupIsValid( data.bodygroup.name, data.bodygroup.active ) then continue end
end
local effectdata = EffectData()
effectdata:SetOrigin( self:LocalToWorld( data.pos ) )
effectdata:SetNormal( self:LocalToWorldAngles( data.ang ):Forward() )
effectdata:SetMagnitude( Magnitude )
effectdata:SetEntity( self )
util.Effect( data.effect or "lvs_exhaust", effectdata )
end
end
function ENT:ExhaustEffectsThink()
if not self:IsBackFireEnabled() then return end
local Throttle = self:GetThrottle()
if self._backfireTHR ~= Throttle then
self._backfireTHR = Throttle
if Throttle ~= 0 then return end
self:CalcExhaustPop()
end
end
function ENT:CalcExhaustPop()
local Engine = self:GetEngine()
if not IsValid( Engine ) then return end
local RPM = Engine:GetRPM()
local num = (Engine:GetRPM() / 500)
local Throttle = self:GetThrottle()
if Throttle > 0 and Throttle < 0.6 then return end
if Throttle ~= 0 or (not IsValid( self:GetTurbo() ) and not IsValid( self:GetCompressor() )) then num = 0 end
for i = 0, num do
timer.Simple( self.TransShiftSpeed + i * 0.1 , function()
if not IsValid( self ) then return end
if i > 0 and self:GetThrottle() ~= 0 then return end
local Engine = self:GetEngine()
if not IsValid( Engine ) then return end
local RPM = Engine:GetRPM()
if RPM < self.EngineMaxRPM * 0.6 then return end
if i == 0 then
self:DoExhaustPop( LVS.EngineVolume )
else
self:DoExhaustPop( 0.75 * LVS.EngineVolume )
end
end )
end
end
function ENT:DoExhaustPop( volume )
if not istable( self.ExhaustPositions ) then return end
for _, data in ipairs( self.ExhaustPositions ) do
if data.bodygroup then
if not self:BodygroupIsValid( data.bodygroup.name, data.bodygroup.active ) then continue end
end
timer.Simple( math.Rand(0,0.2), function()
local effectdata = EffectData()
effectdata:SetOrigin( data.pos )
effectdata:SetAngles( data.ang )
effectdata:SetEntity( self )
effectdata:SetMagnitude( volume or 1 )
util.Effect( "lvs_carexhaust_pop", effectdata )
end )
end
end
function ENT:DoExhaustBackFire()
if not istable( self.ExhaustPositions ) then return end
for _, data in ipairs( self.ExhaustPositions ) do
if data.bodygroup then
if not self:BodygroupIsValid( data.bodygroup.name, data.bodygroup.active ) then continue end
end
if math.random( 1, math.floor( #self.ExhaustPositions * 0.75 ) ) ~= 1 then continue end
timer.Simple( math.Rand(0.5,1), function()
local effectdata = EffectData()
effectdata:SetOrigin( data.pos )
effectdata:SetAngles( data.ang )
effectdata:SetEntity( self )
util.Effect( "lvs_carexhaust_backfire", effectdata )
end )
end
end

View File

@@ -0,0 +1,72 @@
ENT.FlyByVelocity = 500
ENT.FlyByMinThrottle = 0
ENT.FlyByAdvance = 1
ENT.FlyBySound = "lvs/vehicles/generic/car_flyby.wav"
function ENT:FlyByThink()
local ply = LocalPlayer()
if not IsValid( ply ) then return end
local veh = ply:lvsGetVehicle()
local EntTable = self:GetTable()
if veh == self then EntTable.OldApproaching = false return end
local ViewEnt = ply:GetViewEntity()
if not IsValid( ViewEnt ) then return end
if IsValid( veh ) and ViewEnt == ply then
ViewEnt = veh
end
local Time = CurTime()
if (EntTable._nextflyby or 0) > Time then return end
EntTable._nextflyby = Time + 0.1
local Vel = self:GetVelocity()
if self:GetThrottle() <= EntTable.FlyByMinThrottle or Vel:Length() <= EntTable.FlyByVelocity then return end
local Sub = ViewEnt:GetPos() - self:GetPos() - Vel * EntTable.FlyByAdvance
local ToPlayer = Sub:GetNormalized()
local VelDir = Vel:GetNormalized()
local ApproachAngle = self:AngleBetweenNormal( ToPlayer, VelDir )
local Approaching = ApproachAngle < 80
if Approaching ~= EntTable.OldApproaching then
EntTable.OldApproaching = Approaching
if Approaching then
self:StopFlyBy()
else
self:OnFlyBy( 60 + 80 * math.min(ApproachAngle / 140,1) )
end
end
end
function ENT:OnFlyBy( Pitch )
if not self.FlyBySound then return end
local EntTable = self:GetTable()
EntTable.flybysnd = CreateSound( self, EntTable.FlyBySound )
EntTable.flybysnd:SetSoundLevel( 95 )
EntTable.flybysnd:PlayEx( 1, Pitch )
end
function ENT:StopFlyBy()
local EntTable = self:GetTable()
if not EntTable.flybysnd then return end
EntTable.flybysnd:Stop()
EntTable.flybysnd = nil
end

View File

@@ -0,0 +1,237 @@
include("cl_hud_speedometer.lua")
ENT.IconEngine = Material( "lvs/engine.png" )
ENT.IconFuel = Material( "lvs/fuel.png" )
local WaveScale = 0
local WaveMaterial = Material( "effects/select_ring" )
local oldThrottleActive = false
local oldReverse = false
local oldGear = -1
function ENT:LVSHudPaintInfoText( X, Y, W, H, ScrX, ScrY, ply )
if self:GetRacingHud() then return end
local EntTable = self:GetTable()
local T = CurTime()
if (EntTable._nextRefreshVel or 0) < T then
EntTable._nextRefreshVel = T + 0.1
EntTable._refreshVel = self:GetVelocity():Length()
end
local speed = math.Round( LVS:GetUnitValue( EntTable._refreshVel or 0 ) , 0 )
draw.DrawText( LVS:GetUnitName().." ", "LVS_FONT", X + 72, Y + 35, color_white, TEXT_ALIGN_RIGHT )
draw.DrawText( speed, "LVS_FONT_HUD_LARGE", X + 72, Y + 20, color_white, TEXT_ALIGN_LEFT )
if ply ~= self:GetDriver() then return end
local Throttle = self:GetThrottle()
local Col = Throttle <= 1 and color_white or Color(0,0,0,255)
local hX = X + W - H * 0.5
local hY = Y + H * 0.25 + H * 0.25
local fueltank = self:GetFuelTank()
if IsValid( fueltank ) and fueltank:GetFuel() <= 0 then
surface.SetMaterial( EntTable.IconFuel )
else
surface.SetMaterial( EntTable.IconEngine )
end
surface.SetDrawColor( 0, 0, 0, 200 )
surface.DrawTexturedRectRotated( hX + 4, hY + 1, H * 0.5, H * 0.5, 0 )
surface.SetDrawColor( color_white )
surface.DrawTexturedRectRotated( hX + 2, hY - 1, H * 0.5, H * 0.5, 0 )
if not self:GetEngineActive() then
draw.SimpleText( "X" , "LVS_FONT", hX, hY, Color(0,0,0,255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER )
else
oldThrottleActive = false
local Reverse = self:GetReverse()
if oldReverse ~= Reverse then
oldReverse = Reverse
WaveScale = 1
end
local IsManual = self:IsManualTransmission()
local Gear = self:GetGear()
if oldGear ~= Gear then
oldGear = Gear
WaveScale = 1
end
if WaveScale > 0 then
WaveScale = math.max( WaveScale - RealFrameTime() * 2, 0 )
local WaveRadius = (1 - WaveScale) * H * 1.5
surface.SetDrawColor( 0, 127, 255, 255 * WaveScale ^ 2 )
surface.SetMaterial( WaveMaterial )
surface.DrawTexturedRectRotated( hX, hY, WaveRadius, WaveRadius, 0 )
if not Reverse and not IsManual then
draw.SimpleText( "D" , "LVS_FONT", hX, hY, Color(0,0,0,math.min(800 * WaveScale ^ 2,255)), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER )
end
end
if IsManual then
draw.SimpleText( (Reverse and -1 or 1) * Gear , "LVS_FONT", hX, hY, Color(0,0,0,255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER )
else
if Reverse then
draw.SimpleText( "R" , "LVS_FONT", hX, hY, Color(0,0,0,255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER )
end
end
end
self:LVSDrawCircle( hX, hY, H * 0.35, math.min( Throttle, 1 ) )
if Throttle > 1 then
draw.SimpleText( "+"..math.Round((Throttle - 1) * 100,0).."%" , "LVS_FONT", hX, hY, Col, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER )
end
end
LVS:AddHudEditor( "CarMenu", ScrW() - 690, ScrH() - 85, 220, 75, 220, 75, "CAR MENU",
function( self, vehicle, X, Y, W, H, ScrX, ScrY, ply )
if not vehicle.LVSHudPaintCarMenu then return end
vehicle:LVSHudPaintCarMenu( X, Y, W, H, ScrX, ScrY, ply )
end
)
local function DrawTexturedRect( X, Y, size, selected )
local oz = 2
surface.SetDrawColor( 0, 0, 0, 150 )
surface.DrawTexturedRectRotated( X + oz, Y + oz, size, size, 0 )
if IsColor( selected ) then
surface.SetDrawColor( selected.r, selected.g, selected.b, selected.a )
else
if selected then
surface.SetDrawColor( 255, 255, 255, 255 )
else
surface.SetDrawColor( 150, 150, 150, 150 )
end
end
surface.DrawTexturedRectRotated( X, Y, size, size, 0 )
end
ENT.CarMenuSiren = Material( "lvs/carmenu_siren.png" )
ENT.CarMenuHighbeam = Material( "lvs/carmenu_highbeam.png" )
ENT.CarMenuLowbeam = Material( "lvs/carmenu_lowbeam.png" )
ENT.CarMenuDisable = Material( "lvs/carmenu_cross.png" )
ENT.CarMenuFog = Material( "lvs/carmenu_fog.png" )
ENT.CarMenuHazard = Material( "lvs/carmenu_hazard.png" )
ENT.CarMenuLeft = Material( "lvs/carmenu_turnleft.png" )
ENT.CarMenuRight = Material( "lvs/carmenu_turnRight.png" )
function ENT:LVSHudPaintCarMenu( X, Y, w, h, ScrX, ScrY, ply )
if self:GetDriver() ~= ply then return end
local MenuOpen = ply:lvsKeyDown( "CAR_MENU" ) and self:HasTurnSignals()
local EntTable = self:GetTable()
if MenuOpen then
if ply:lvsKeyDown( "CAR_BRAKE" ) then
EntTable._SelectedMode = 3
end
if ply:lvsKeyDown( "CAR_THROTTLE" ) then
EntTable._SelectedMode = 0
end
if ply:lvsKeyDown( "CAR_STEER_LEFT" ) then
EntTable._SelectedMode = 1
end
if ply:lvsKeyDown( "CAR_STEER_RIGHT" ) then
EntTable._SelectedMode = 2
end
if EntTable._oldSelectedMode ~= EntTable._SelectedMode then
EntTable._oldSelectedMode = EntTable._SelectedMode
self:EmitSound("buttons/lightswitch2.wav",75,120,0.25)
end
else
if EntTable._oldSelectedMode and isnumber( EntTable._SelectedMode ) then
self:EmitSound("buttons/lightswitch2.wav",75,100,0.25)
net.Start( "lvs_car_turnsignal" )
net.WriteInt( EntTable._SelectedMode, 4 )
net.SendToServer()
EntTable._SelectedMode = 0
EntTable._oldSelectedMode = nil
end
local size = 32
local dist = 5
local cX = X + w * 0.5
local cY = Y + h - size * 0.5 - dist
local LightsHandler = self:GetLightsHandler()
if IsValid( LightsHandler ) then
if LightsHandler:GetActive() then
if LightsHandler:GetHighActive() then
surface.SetMaterial( self.CarMenuHighbeam )
DrawTexturedRect( cX, cY, size, Color(0,255,255,255) )
else
surface.SetMaterial( self.CarMenuLowbeam )
DrawTexturedRect( cX, cY, size, Color(0,255,0,255) )
end
end
if LightsHandler:GetFogActive() then
surface.SetMaterial( self.CarMenuFog )
DrawTexturedRect( cX, cY - (size + dist), size, Color(255,100,0,255) )
end
end
local TurnMode = self:GetTurnMode()
if TurnMode == 0 then return end
local Alpha = self:GetTurnFlasher() and 255 or 0
if TurnMode == 1 or TurnMode == 3 then
surface.SetMaterial( EntTable.CarMenuLeft )
DrawTexturedRect( cX - (size + dist), cY, size, Color(0,255,157,Alpha) )
end
if TurnMode == 2 or TurnMode == 3 then
surface.SetMaterial( EntTable.CarMenuRight )
DrawTexturedRect( cX + (size + dist), cY, size, Color(0,255,157,Alpha) )
end
return
end
local SelectedThing = EntTable._SelectedMode or 0
local size = 32
local dist = 5
local cX = X + w * 0.5
local cY = Y + h - size * 0.5 - dist
surface.SetMaterial( EntTable.CarMenuDisable )
DrawTexturedRect( cX, cY - (size + dist), size, SelectedThing == 0 )
surface.SetMaterial( EntTable.CarMenuLeft )
DrawTexturedRect( cX - (size + dist), cY, size, SelectedThing == 1 )
surface.SetMaterial( EntTable.CarMenuRight)
DrawTexturedRect( cX + (size + dist), cY, size, SelectedThing == 2 )
surface.SetMaterial( EntTable.CarMenuHazard )
DrawTexturedRect( cX, cY, size, SelectedThing == 3 )
end

View File

@@ -0,0 +1,403 @@
LVS:AddHudEditor( "Tachometer", ScrW() - 530, ScrH() - 250, 300, 220, 300, 220, "TACH",
function( self, vehicle, X, Y, W, H, ScrX, ScrY, ply )
if not vehicle.LVSHudPaintTach or not vehicle.GetRacingHud then return end
vehicle:LVSHudPaintTach( X, Y, W, H, ScrX, ScrY, ply )
end
)
local THE_FONT = {
font = "Verdana",
extended = false,
size = 100,
weight = 2000,
blursize = 0,
scanlines = 0,
antialias = false,
underline = false,
italic = false,
strikeout = false,
symbol = false,
rotary = false,
shadow = false,
additive = false,
outline = false,
}
surface.CreateFont( "LVS_TACHOMETER", THE_FONT )
local circles = include("includes/circles/circles.lua")
local Center = 650
local startAngleSpeedo = 180
local endAngleSpeedo = 375
local startAngleTach = 165
local endAngleTach = 360
local Ring = circles.New( CIRCLE_OUTLINED, 300, 0, 0, 60 )
Ring:SetMaterial( true )
local Circle = circles.New( CIRCLE_OUTLINED, 625, 0, 0, 230 )
Circle:SetX( Center )
Circle:SetY( Center )
Circle:SetMaterial( true )
local RingOuter = circles.New( CIRCLE_OUTLINED, 645, 0, 0, 35 )
RingOuter:SetX( Center )
RingOuter:SetY( Center )
RingOuter:SetMaterial( true )
local RingInner = circles.New( CIRCLE_OUTLINED, 640, 0, 0, 25 )
RingInner:SetX( Center )
RingInner:SetY( Center )
RingInner:SetMaterial( true )
local RingFrame = circles.New( CIRCLE_OUTLINED, 390, 0, 0, 10 )
RingFrame:SetX( Center )
RingFrame:SetY( Center )
RingFrame:SetMaterial( true )
local RingFrameOuter = circles.New( CIRCLE_OUTLINED, 395, 0, 0, 20 )
RingFrameOuter:SetX( Center )
RingFrameOuter:SetY( Center )
RingFrameOuter:SetMaterial( true )
local RingOuterRedline = circles.New( CIRCLE_OUTLINED, 645, 0, 0, 20 )
RingOuterRedline:SetX( Center )
RingOuterRedline:SetY( Center )
RingOuterRedline:SetMaterial( true )
local RingInnerRedline = circles.New( CIRCLE_OUTLINED, 640, 0, 0, 10 )
RingInnerRedline:SetX( Center )
RingInnerRedline:SetY( Center )
RingInnerRedline:SetMaterial( true )
local VehicleTach = {}
function ENT:GetBakedTachMaterial( MaxRPM )
local Class = self:GetClass()
if VehicleTach[ Class ] then return VehicleTach[ Class ] end
local TachRange = endAngleTach - startAngleTach
local Steps = math.ceil(MaxRPM / 1000)
local AngleStep = TachRange / Steps
local AngleRedline = startAngleTach + (TachRange / MaxRPM) * self.EngineMaxRPM
local tachRT = GetRenderTarget( "lvs_tach_"..Class, Center * 2, Center * 2 )
local old = DisableClipping( true )
render.OverrideAlphaWriteEnable( true, true )
render.PushRenderTarget( tachRT )
cam.Start2D()
render.ClearDepth()
render.Clear( 0, 0, 0, 0 )
surface.SetDrawColor( Color( 0, 0, 0, 150 ) )
Circle:SetStartAngle( startAngleTach )
Circle:SetEndAngle( endAngleTach )
Circle()
surface.SetDrawColor( Color( 0, 0, 0, 200 ) )
RingOuter:SetStartAngle( startAngleTach )
RingOuter:SetEndAngle( AngleRedline )
RingOuter()
RingOuterRedline:SetStartAngle( AngleRedline )
RingOuterRedline:SetEndAngle( endAngleTach )
RingOuterRedline()
RingFrameOuter:SetStartAngle( startAngleTach )
RingFrameOuter:SetEndAngle( endAngleTach )
RingFrameOuter()
surface.SetDrawColor( color_white )
for i = 0, Steps do
local Ang = AngleStep * i + startAngleTach
local AngX = math.cos( math.rad( Ang ) )
local AngY = math.sin( math.rad( Ang ) )
local StartX = Center + AngX * 554
local StartY = Center + AngY * 554
local EndX = Center + AngX * 635
local EndY = Center + AngY * 635
if Ang > AngleRedline then
surface.SetDrawColor( Color(255,0,0,255) )
else
surface.SetDrawColor( color_white )
end
draw.NoTexture()
surface.DrawTexturedRectRotated( (StartX + EndX) * 0.5, (StartY + EndY) * 0.5, 90, 15, -Ang )
local TextX = Center + AngX * 485
local TextY = Center + AngY * 485
if Ang > AngleRedline then
draw.SimpleText( i, "LVS_TACHOMETER", TextX, TextY, Color(255,0,0,255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER )
else
draw.SimpleText( i, "LVS_TACHOMETER", TextX, TextY, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER )
end
end
for i = 1, Steps do
local Start = AngleStep * i + startAngleTach
for n = 1, 9 do
local Ang = Start - (AngleStep / 10) * n
if Ang > AngleRedline then
surface.SetDrawColor( Color(150,0,0,255) )
else
surface.SetDrawColor( Color(150,150,150,255) )
end
local AngX = math.cos( math.rad( Ang ) )
local AngY = math.sin( math.rad( Ang ) )
local StartX = Center + AngX * 575
local StartY = Center + AngY * 575
local EndX = Center + AngX * 635
local EndY = Center + AngY * 635
draw.NoTexture()
surface.DrawTexturedRectRotated( (StartX + EndX) * 0.5, (StartY + EndY) * 0.5, 60, 5, -Ang )
end
end
surface.SetDrawColor( color_white )
RingInner:SetStartAngle( startAngleTach )
RingInner:SetEndAngle( AngleRedline )
RingInner()
RingFrame:SetStartAngle( startAngleTach )
RingFrame:SetEndAngle( endAngleTach )
RingFrame()
surface.SetDrawColor( Color(255,0,0,255) )
RingInnerRedline:SetStartAngle( AngleRedline )
RingInnerRedline:SetEndAngle( endAngleTach )
RingInnerRedline()
cam.End2D()
render.OverrideAlphaWriteEnable( false )
render.PopRenderTarget()
local Mat = CreateMaterial( "lvs_tach_"..Class.."_mat", "UnlitGeneric", { ["$basetexture"] = tachRT:GetName(), ["$translucent"] = 1, ["$vertexcolor"] = 1 } )
VehicleTach[ Class ] = Mat
DisableClipping( old )
return Mat
end
local TachNeedleColor = Color(255,0,0,255)
local TachNeedleRadiusInner = 90
local TachNeedleRadiusOuter = 145
local TachNeedleBlurTime = 0.1
local TachNeedles = {}
local CurRPM = 0
local CurSpeed = 0
function ENT:LVSHudPaintTach( X, Y, w, h, ScrX, ScrY, ply )
if ply ~= self:GetDriver() then return end
if not self:GetRacingHud() then return end
local Engine = self:GetEngine()
if not IsValid( Engine ) then return end
local Delta = (Engine:GetRPM() - CurRPM) * RealFrameTime() * 20
CurRPM = CurRPM + Delta
local EntTable = self:GetTable()
local MaxRPM = EntTable.EngineMaxRPM + 3000
local Ang = startAngleTach + (endAngleTach - startAngleTach) * (CurRPM / MaxRPM)
local T = CurTime()
local FuelTank = self:GetFuelTank()
local UsesFuel = IsValid( FuelTank )
if self:GetEngineActive() then
local Gear = self:GetGear()
local printGear = Gear
if Gear == -1 then
printGear = self:GetReverse() and "R" or "D"
else
if self:GetReverse() then
printGear = "-"..Gear
end
end
draw.DrawText( printGear, "LVS_FONT_HUD_HUMONGOUS", X + w * 0.5, Y + w * 0.23, color_white, TEXT_ALIGN_CENTER )
else
surface.SetMaterial( EntTable.IconEngine )
if UsesFuel and FuelTank:GetFuel() <= 0 then
surface.SetMaterial( EntTable.IconFuel )
end
surface.SetDrawColor( Color(255,0,0, math.abs( math.cos( T * 5 ) ) * 255 ) )
surface.DrawTexturedRectRotated( X + w * 0.5 + 2, Y + w * 0.35 - 1, w * 0.15, w * 0.15, 0 )
end
if (EntTable._nextRefreshVel or 0) < T then
EntTable._nextRefreshVel = T + 0.1
EntTable._refreshVel = self:GetVelocity():Length()
end
local speed = math.Round( LVS:GetUnitValue( EntTable._refreshVel or 0 ) , 0 )
draw.DrawText( LVS:GetUnitName().." ", "LVS_FONT", X + w * 0.81, Y + w * 0.6, color_white, TEXT_ALIGN_LEFT )
draw.DrawText( speed, "LVS_FONT_HUD_LARGE", X + w * 0.81 - 5, Y + w * 0.6, color_white, TEXT_ALIGN_RIGHT )
-- fuel, oil, coolant
local barlength = w * 0.2
if UsesFuel then
surface.SetDrawColor( 0, 0, 0, 200 )
surface.DrawRect( X + w * 0.5 - barlength * 0.5 - 1, Y + w * 0.5 - 1, barlength + 2, 7 )
local col = LVS.FUELTYPES[ FuelTank:GetFuelType() ].color
surface.SetDrawColor( Color(col.r,col.g,col.b,255) )
surface.DrawRect( X + w * 0.5 - barlength * 0.5, Y + w * 0.5, barlength * FuelTank:GetFuel(), 5 )
draw.DrawText( "fuel", "LVS_FONT_PANEL", X + w * 0.5 + barlength * 0.5 + 5, Y + w * 0.5 - 5, Color(255,150,0,255), TEXT_ALIGN_LEFT )
end
if self:HasQuickVar( "oil" ) then
surface.SetDrawColor( 0, 0, 0, 200 )
surface.DrawRect( X + w * 0.5 - barlength * 0.5 - 1, Y + w * 0.5 - 1 + 10, barlength + 2, 7 )
surface.SetDrawColor( 80, 80, 80, 255 )
surface.DrawRect( X + w * 0.5 - barlength * 0.5, Y + w * 0.5 + 10, barlength * math.min(self:GetQuickVar( "oil" ),1), 5 )
draw.DrawText( "oil pressure", "LVS_FONT_PANEL", X + w * 0.5 + barlength * 0.5 + 5, Y + w * 0.5 + 5, Color(0, 0, 0, 255), TEXT_ALIGN_LEFT )
end
if self:HasQuickVar( "temp" ) then
surface.SetDrawColor( 0, 0, 0, 200 )
surface.DrawRect( X + w * 0.5 - barlength * 0.5 - 1, Y + w * 0.5 - 1 + 20, barlength + 2, 7 )
surface.SetDrawColor( 0, 127, 255, 255 )
surface.DrawRect( X + w * 0.5 - barlength * 0.5, Y + w * 0.5 + 20, barlength * math.min(self:GetQuickVar( "temp" ),1), 5 )
draw.DrawText( "coolant temp", "LVS_FONT_PANEL", X + w * 0.5 + barlength * 0.5 + 5, Y + w * 0.5 + 15, Color(0, 0, 255, 255), TEXT_ALIGN_LEFT )
end
-- brake, clutch, throttle bar
local throttle = self:GetThrottle()
local clutch = self:GetQuickVar( "clutch" )
local hasClutch = self:HasQuickVar( "clutch" )
local brake = self:GetBrake()
local engine = self:GetEngine()
if IsValid( engine ) then
local ClutchActive = engine:GetClutch()
if not clutch then
clutch = ClutchActive and 1 or 0
end
if ClutchActive then
throttle = math.max( throttle - clutch, 0 )
end
end
surface.SetDrawColor( 0, 0, 0, 200 )
surface.DrawRect( X + w * 0.3 - 1, Y + w * 0.4 - 1, 7, barlength + 2 )
surface.DrawRect( X + w * 0.3 + 10 - 1, Y + w * 0.4 - 1, 7, barlength + 2 )
if hasClutch then surface.DrawRect( X + w * 0.3 - 10 - 1, Y + w * 0.4 - 1, 7, barlength + 2 ) end
surface.SetDrawColor( 255, 255, 255, 255 )
if hasClutch then
local cllength = barlength * clutch
surface.DrawRect( X + w * 0.3 - 10, Y + w * 0.4 + barlength - cllength, 5, cllength )
end
local brlength = barlength * brake
surface.DrawRect( X + w * 0.3, Y + w * 0.4 + barlength - brlength, 5, brlength )
local thrlength = barlength * throttle
surface.DrawRect( X + w * 0.3 + 10, Y + w * 0.4 + barlength - thrlength, 5, thrlength )
if hasClutch then draw.DrawText( "c", "LVS_FONT_PANEL", X + w * 0.3 - 7, Y + w * 0.4 + barlength, color_white, TEXT_ALIGN_CENTER ) end
draw.DrawText( "b", "LVS_FONT_PANEL", X + w * 0.3 + 3, Y + w * 0.4 + barlength, color_white, TEXT_ALIGN_CENTER )
draw.DrawText( "t", "LVS_FONT_PANEL", X + w * 0.3 + 13, Y + w * 0.4 + barlength, color_white, TEXT_ALIGN_CENTER )
local TachRange = endAngleTach - startAngleTach
local AngleRedline = startAngleTach + (TachRange / MaxRPM) * EntTable.EngineMaxRPM
Ring:SetX( X + w * 0.5 )
Ring:SetY( Y + w * 0.5 )
Ring:SetRadius( w * 0.49 )
Ring:SetOutlineWidth( w * 0.04 )
Ring:SetStartAngle( startAngleTach )
Ring:SetEndAngle( math.min( Ang, AngleRedline ) )
Ring()
if Ang > AngleRedline then
surface.SetDrawColor( 255, 0, 0, 255 )
Ring:SetStartAngle( AngleRedline )
Ring:SetEndAngle( math.min( Ang, endAngleTach ) )
Ring()
end
surface.SetDrawColor( 255, 255, 255, 255 )
surface.SetMaterial( self:GetBakedTachMaterial( MaxRPM ) )
surface.DrawTexturedRect( X, Y, w, w )
local CenterX = X + w * 0.5
local CenterY = Y + w * 0.5
local T = CurTime()
local AngX = math.cos( math.rad( Ang ) )
local AngY = math.sin( math.rad( Ang ) )
if math.abs( Delta ) > 1 then
local data = {
StartX = (CenterX + AngX * TachNeedleRadiusInner),
StartY = (CenterY + AngY * TachNeedleRadiusInner),
EndX = (CenterX + AngX * TachNeedleRadiusOuter),
EndY = (CenterY + AngY * TachNeedleRadiusOuter),
Time = T + TachNeedleBlurTime
}
table.insert( TachNeedles, data )
else
local StartX = CenterX + AngX * TachNeedleRadiusInner
local StartY = CenterY + AngY * TachNeedleRadiusInner
local EndX = CenterX + AngX * TachNeedleRadiusOuter
local EndY = CenterY + AngY * TachNeedleRadiusOuter
surface.SetDrawColor( TachNeedleColor )
surface.DrawLine( StartX, StartY, EndX, EndY )
end
for index, data in pairs( TachNeedles ) do
if data.Time < T then
TachNeedles[ index ] = nil
continue
end
local Brightness = (data.Time - T) / TachNeedleBlurTime
surface.SetDrawColor( Color( TachNeedleColor.r * Brightness, TachNeedleColor.g * Brightness, TachNeedleColor.b * Brightness, TachNeedleColor.a * Brightness ^ 2 ) )
surface.DrawLine( data.StartX, data.StartY, data.EndX, data.EndY )
end
end

View File

@@ -0,0 +1,192 @@
include("shared.lua")
include("sh_animations.lua")
include("sh_camera_eyetrace.lua")
include("cl_flyby.lua")
include("cl_tiresounds.lua")
include("cl_camera.lua")
include("cl_hud.lua")
include("cl_scrolltexture.lua")
include("cl_exhausteffects.lua")
DEFINE_BASECLASS( "lvs_base" )
function ENT:CreateSubMaterial( SubMaterialID, name )
if not SubMaterialID then return end
local mat = self:GetMaterials()[ SubMaterialID + 1 ]
if not mat then return end
local string_data = file.Read( "materials/"..mat..".vmt", "GAME" )
if not string_data then return end
return CreateMaterial( name, "VertexLitGeneric", util.KeyValuesToTable( string_data ) )
end
function ENT:CalcPoseParameters()
local steer = self:GetSteer() / self:GetMaxSteerAngle()
local kmh = math.Round( self:GetVelocity():Length() * 0.09144, 0 )
local lights = self:GetLightsHandler()
local ammeter = 0.5
local rpm = 0
local oil = 0.25
local gear = 1
local clutch = 0
local throttle = self:GetThrottle()
local engine = self:GetEngine()
local engineActive = self:GetEngineActive()
local fuel = 1
local fueltank = self:GetFuelTank()
local handbrake = self:QuickLerp( "handbrake", self:GetNWHandBrake() and 1 or 0 )
local temperature = 0
if IsValid( engine ) then
local EngineHealthFraction = engine:GetHP() / engine:GetMaxHP()
rpm = self:QuickLerp( "rpm", engine:GetRPM() )
gear = engine:GetGear()
oil = self:QuickLerp( "oil", engineActive and math.min( (EngineHealthFraction ^ 2) * 0.2 + (rpm / self.EngineMaxRPM) * 1.25 - (math.max( rpm - self.EngineMaxRPM, 0 ) / 2000) * 1.6, 1 ) or 0, 2 ) ^ 2
local ClutchActive = engine:GetClutch()
clutch = self:QuickLerp( "clutch", ClutchActive and 1 or 0 )
if ClutchActive then
throttle = math.max( throttle - clutch, 0 )
end
temperature = self:QuickLerp( "temp", self:QuickLerp( "base_temp", engineActive and 0.5 or 0, 0.025 + throttle * 0.1 ) + (1 - EngineHealthFraction) ^ 2 * 1.25, 0.5 )
else
temperature = self:QuickLerp( "temp", self:QuickLerp( "base_temp", engineActive and 0.5 or 0, 0.025 + throttle * 0.1 ) + (1 - self:GetHP() / self:GetMaxHP()) ^ 2 * 1.25, 0.5 )
end
if IsValid( lights ) then
local Available = 0.5 + (rpm / self.EngineMaxRPM) * 0.25
local Use1 = lights:GetActive() and 0.1 or 0
local Use2 = lights:GetHighActive() and 0.15 or 0
local Use3 = lights:GetFogActive() and 0.05 or 0
local Use4 = (self:GetTurnMode() ~= 0 and self:GetTurnFlasher()) and 0.03 or 0
ammeter = self:QuickLerp( "ammeter", math.max( Available - Use1 - Use2 - Use3 - Use4, 0 ), math.Rand(1,10) )
end
if IsValid( fueltank ) then
fuel = self:QuickLerp( "fuel", fueltank:GetFuel() )
end
self:UpdatePoseParameters( steer, self:QuickLerp( "kmh", kmh ), rpm, throttle, self:GetBrake(), handbrake, clutch, (self:GetReverse() and -gear or gear), temperature, fuel, oil, ammeter )
self:InvalidateBoneCache()
end
function ENT:Think()
if not self:IsInitialized() then return end
BaseClass.Think( self )
self:TireSoundThink()
self:ExhaustEffectsThink()
if isfunction( self.UpdatePoseParameters ) then
self:CalcPoseParameters()
else
self:SetPoseParameter( "vehicle_steer", self:GetSteer() / self:GetMaxSteerAngle() )
self:InvalidateBoneCache()
end
end
function ENT:OnRemove()
self:TireSoundRemove()
BaseClass.OnRemove( self )
end
function ENT:PostDrawTranslucent()
local Handler = self:GetLightsHandler()
if not IsValid( Handler ) or not istable( self.Lights ) then return end
Handler:RenderLights( self, self.Lights )
end
function ENT:OnEngineStallBroken()
for i = 0,math.random(3,6) do
timer.Simple( math.Rand(0,1.5) , function()
if not IsValid( self ) then return end
self:DoExhaustBackFire()
end )
end
end
function ENT:OnChangeGear( oldGear, newGear )
local HP = self:GetHP()
local MaxHP = self:GetMaxHP()
local Engine = self:GetEngine()
local EngineHP = 0
local EngineMaxHP = 0
if IsValid( Engine ) then
EngineHP = Engine:GetHP()
EngineMaxHP = Engine:GetMaxHP()
end
local Damaged = HP < MaxHP * 0.5
local EngineDamaged = EngineHP < EngineMaxHP * 0.5
if (Damaged or EngineDamaged) then
if oldGear > newGear then
if Damaged then
self:EmitSound( "lvs/vehicles/generic/gear_grind"..math.random(1,6)..".ogg", 75, math.Rand(70,100), 0.25 )
self:DoExhaustBackFire()
end
else
if EngineDamaged then
self:DoExhaustBackFire()
end
end
else
self:EmitSound( self.TransShiftSound, 75 )
if self:IsBackFireEnabled() then
self:CalcExhaustPop()
end
end
self:SuppressViewPunch( self.TransShiftSpeed )
end
function ENT:GetTurnFlasher()
return math.cos( CurTime() * 8 + self:EntIndex() * 1337 ) > 0
end
function ENT:OnEngineActiveChanged( Active )
if Active then
self:EmitSound( "lvs/vehicles/generic/engine_start1.wav", 75, 100, LVS.EngineVolume )
else
self:EmitSound( "vehicles/jetski/jetski_off.wav", 75, 100, LVS.EngineVolume )
end
end
function ENT:GetWheels()
local wheels = {}
for _, ent in pairs( self:GetCrosshairFilterEnts() ) do
if not IsValid( ent ) or ent:GetClass() ~= "lvs_wheeldrive_wheel" then continue end
table.insert( wheels, ent )
end
return wheels
end

View File

@@ -0,0 +1,83 @@
ENT.ScrollTextureData = {
["$alphatest"] = "1",
["$translate"] = "[0.0 0.0 0.0]",
["$colorfix"] = "{255 255 255}",
["Proxies"] = {
["TextureTransform"] = {
["translateVar"] = "$translate",
["centerVar"] = "$center",
["resultVar"] = "$basetexturetransform",
},
["Equals"] = {
["srcVar1"] = "$colorfix",
["resultVar"] = "$color",
}
}
}
function ENT:GetRotationDelta( name, rot )
name = "_deltaAng"..name
if not self[ name ] then self[ name ] = Angle(0,0,0) end
local ang = Angle(0,0,rot)
local cur = ang:Right()
local old = self[ name ]:Up()
local delta = self:AngleBetweenNormal( cur, old ) - 90
self[ name ] = ang
return delta
end
function ENT:CalcScroll( name, rot )
local delta = self:GetRotationDelta( name, rot )
name = "_deltaScroll"..name
if not self[ name ] then self[ name ] = 0 end
self[ name ] = self[ name ] + delta
if self[ name ] > 32768 then
self[ name ] = self[ name ] - 32768
end
if self[ name ] < -32768 then
self[ name ] = self[ name ] + 32768
end
return self[ name ]
end
function ENT:ScrollTexture( name, material, pos )
if not isstring( name ) or not isstring( material ) or not isvector( pos ) then return "" end
local id = self:EntIndex()
local class = self:GetClass()
local EntTable = self:GetTable()
local texture_name = class.."_["..id.."]_"..name
if istable( EntTable._StoredScrollTextures ) then
if EntTable._StoredScrollTextures[ texture_name ] then
self._StoredScrollTextures[ texture_name ]:SetVector("$translate", pos )
return "!"..texture_name
end
else
EntTable._StoredScrollTextures = {}
end
local data = table.Copy( EntTable.ScrollTextureData )
data["$basetexture"] = material
local mat = CreateMaterial(texture_name, "VertexLitGeneric", data )
EntTable._StoredScrollTextures[ texture_name ] = mat
return "!"..texture_name
end

View File

@@ -0,0 +1,84 @@
ENT.TireSoundFade = 0.15
ENT.TireSoundTypes = {
["roll"] = "lvs/vehicles/generic/wheel_roll.wav",
["roll_racing"] = "lvs/vehicles/generic/wheel_roll.wav",
["roll_dirt"] = "lvs/vehicles/generic/wheel_roll_dirt.wav",
["roll_wet"] = "lvs/vehicles/generic/wheel_roll_wet.wav",
["roll_damaged"] = "lvs/wheel_damaged_loop.wav",
["skid"] = "lvs/vehicles/generic/wheel_skid.wav",
["skid_racing"] = "lvs/vehicles/generic/wheel_skid_racing.wav",
["skid_dirt"] = "lvs/vehicles/generic/wheel_skid_dirt.wav",
["skid_wet"] = "lvs/vehicles/generic/wheel_skid_wet.wav",
["tire_damage_layer"] = "lvs/wheel_destroyed_loop.wav",
}
ENT.TireSoundLevelSkid = 85
ENT.TireSoundLevelRoll = 75
function ENT:TireSoundRemove()
for snd, _ in pairs( self.TireSoundTypes ) do
self:StopTireSound( snd )
end
end
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 / 1000,1) ^ 2 * T
local pitch = 100 + math.Clamp((speed - 400) / 200,0,155)
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
self._TireSounds[ snd ] = CurTime() + self.TireSoundFade
end
function ENT:GetTireSoundTime( snd )
if not istable( self._TireSounds ) or not self._TireSounds[ snd ] then return 0 end
return math.max(self._TireSounds[ snd ] - CurTime(),0) / 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, 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
function ENT:StopTireSound( snd )
if not istable( self._ActiveTireSounds ) or not self._ActiveTireSounds[ snd ] then return end
self._ActiveTireSounds[ snd ]:Stop()
self._ActiveTireSounds[ snd ] = nil
end

View File

@@ -0,0 +1,534 @@
AddCSLuaFile( "shared.lua" )
AddCSLuaFile( "cl_init.lua" )
AddCSLuaFile( "cl_flyby.lua" )
AddCSLuaFile( "cl_camera.lua" )
AddCSLuaFile( "cl_hud.lua" )
AddCSLuaFile( "cl_hud_speedometer.lua" )
AddCSLuaFile( "cl_tiresounds.lua" )
AddCSLuaFile( "cl_scrolltexture.lua" )
AddCSLuaFile( "cl_exhausteffects.lua" )
AddCSLuaFile( "sh_animations.lua" )
AddCSLuaFile( "sh_camera_eyetrace.lua" )
include("shared.lua")
include("sh_animations.lua")
include("sv_controls.lua")
include("sv_controls_handbrake.lua")
include("sv_components.lua")
include("sv_ai.lua")
include("sv_riggedwheels.lua")
include("sv_wheelsystem.lua")
include("sv_damage.lua")
include("sv_pivotsteer.lua")
include("sv_manualtransmission.lua")
include("sv_engine.lua")
include("sv_hydraulics.lua")
include("sh_camera_eyetrace.lua")
ENT.DriverActiveSound = "common/null.wav"
ENT.DriverInActiveSound = "common/null.wav"
DEFINE_BASECLASS( "lvs_base" )
local function SetMinimumAngularVelocityTo( new )
local tbl = physenv.GetPerformanceSettings()
if tbl.MaxAngularVelocity < new then
local OldAngVel = tbl.MaxAngularVelocity
tbl.MaxAngularVelocity = new
physenv.SetPerformanceSettings( tbl )
print("[LVS-Cars] Wheels require higher MaxAngularVelocity to perform correctly! Increasing! "..OldAngVel.." =>"..new)
end
end
local function IsServerOK( class )
if GetConVar( "gmod_physiterations" ):GetInt() ~= 4 then
RunConsoleCommand("gmod_physiterations", "4")
return false
end
return true
end
function ENT:TracksCreate( PObj )
end
local function DontDuplicatePaintSheme( ply, ent, data )
ent.RandomColor = nil
if not duplicator or not duplicator.StoreEntityModifier then return end
duplicator.StoreEntityModifier( ent, "lvsVehiclePaintSheme", data )
end
if duplicator and duplicator.RegisterEntityModifier then
duplicator.RegisterEntityModifier( "lvsVehiclePaintSheme", DontDuplicatePaintSheme )
end
function ENT:PostInitialize( PObj )
self:TracksCreate( PObj )
if not IsServerOK( self:GetClass() ) then
self:Remove()
print("[LVS] ERROR COULDN'T INITIALIZE VEHICLE!")
end
if istable( self.Lights ) then
self:AddLights()
end
if istable( self.RandomColor ) then
local data = self.RandomColor[ math.random( #self.RandomColor ) ]
if IsColor( data ) then
self:SetColor( data )
else
self:SetSkin( data.Skin or 0 )
self:SetColor( data.Color or color_white )
if istable( data.Wheels ) then
self._WheelSkin = data.Wheels.Skin or 0
self._WheelColor = data.Wheels.Color or color_white
end
if istable( data.BodyGroups ) then
for id, subgroup in pairs( data.BodyGroups ) do
self:SetBodygroup( id, subgroup )
end
end
end
DontDuplicatePaintSheme( NULL, self, {} )
end
BaseClass.PostInitialize( self, PObj )
if isstring( self.HornSound ) and isvector( self.HornPos ) and #self.WEAPONS[1] == 0 then
if IsValid( self.HornSND ) then self.HornSND:Remove() end
self.HornSND = self:AddSoundEmitter( self.HornPos or vector_origin, self.HornSound, self.HornSoundInterior )
self.HornSND:SetSoundLevel( 75 )
self.HornSND:SetDoppler( true )
end
if istable( self.SirenSound ) then
if IsValid( self.SirenSND ) then self.SirenSND:Remove() end
self.SirenSND = self:AddSoundEmitter( self.SirenPos or vector_origin, "common/null.wav" )
self.SirenSND:SetSoundLevel( 75 )
self.SirenSND:SetDoppler( true )
end
PObj:SetMass( self.PhysicsMass * self.PhysicsWeightScale )
PObj:EnableDrag( false )
PObj:SetInertia( self.PhysicsInertia * self.PhysicsWeightScale )
SetMinimumAngularVelocityTo( 24000 )
if BRANCH == "dev" or BRANCH == "x86-64" then
for _, wheel in pairs( self:GetWheels() ) do
if not IsValid( wheel ) then continue end
wheel:SetLightingOriginEntity( self )
end
end
self:EnableHandbrake()
end
function ENT:AlignView( ply )
if not IsValid( ply ) then return end
timer.Simple( 0, function()
if not IsValid( ply ) or not IsValid( self ) then return end
local Ang = Angle(0,90,0)
local pod = ply:GetVehicle()
local MouseAim = ply:lvsMouseAim() and self:GetDriver() == ply
if MouseAim and IsValid( pod ) then
Ang = pod:LocalToWorldAngles( Angle(0,90,0) )
Ang.r = 0
end
ply:SetEyeAngles( Ang )
end)
end
function ENT:PhysicsSimulateOverride( ForceAngle, phys, deltatime, simulate )
if not self:GetRacingTires() then
return ForceAngle, vector_origin, simulate
end
local EntTable = self:GetTable()
local WheelSideForce = EntTable.WheelSideForce * EntTable.ForceLinearMultiplierRacingTires
local ForceLinear = Vector(0,0,0)
for id, wheel in pairs( self:GetWheels() ) do
if wheel:IsHandbrakeActive() or not wheel:PhysicsOnGround() then continue end
local AxleAng = wheel:GetDirectionAngle()
local Forward = AxleAng:Forward()
local Right = AxleAng:Right()
local Up = AxleAng:Up()
local wheelPos = wheel:GetPos()
local wheelVel = phys:GetVelocityAtPoint( wheelPos )
local wheelRadius = wheel:GetRadius()
local Slip = math.Clamp(1 - self:AngleBetweenNormal( Forward, wheelVel:GetNormalized() ) / 90,0,1)
local ForwardVel = self:VectorSplitNormal( Forward, wheelVel )
Force = -Right * self:VectorSplitNormal( Right, wheelVel ) * WheelSideForce * Slip
local wSideForce, wAngSideForce = phys:CalculateVelocityOffset( Force, wheelPos )
ForceAngle:Add( Vector(0,0,wAngSideForce.z) )
ForceLinear:Add( wSideForce )
end
return ForceAngle, ForceLinear, simulate
end
function ENT:PhysicsSimulate( phys, deltatime )
if self:GetEngineActive() then phys:Wake() end
local ent = phys:GetEntity()
if ent == self then
local Vel = 0
for _, wheel in pairs( self:GetWheels() ) do
if wheel:GetTorqueFactor() <= 0 then continue end
local wheelVel = wheel:RPMToVel( math.abs( wheel:GetRPM() or 0 ) )
if wheelVel > Vel then
Vel = wheelVel
end
end
self:SetWheelVelocity( Vel )
if not self:StabilityAssist() or not self:WheelsOnGround() then return self:PhysicsSimulateOverride( Vector(0,0,0), phys, deltatime, SIM_NOTHING ) end
local ForceAngle = Vector(0,0, math.deg( -phys:GetAngleVelocity().z ) * math.min( phys:GetVelocity():Length() / self.PhysicsDampingSpeed, 1 ) * self.ForceAngleMultiplier )
return self:PhysicsSimulateOverride( ForceAngle, phys, deltatime, SIM_GLOBAL_ACCELERATION )
end
if not self:AlignWheel( ent ) or self:IsDestroyed() then self:EnableHandbrake() return vector_origin, vector_origin, SIM_NOTHING end
local WheelTable = ent:GetTable()
local EntTable = self:GetTable()
if ent:IsHandbrakeActive() then
if WheelTable.SetRPM then
ent:SetRPM( 0 )
end
return vector_origin, vector_origin, SIM_NOTHING
end
local T = CurTime()
if (WheelTable._NextSimulate or 0) < T or not WheelTable.Simulate then
WheelTable._NextSimulate = T + ((self:PivotSteer() or self:GetBrake() > 0) and EntTable.WheelTickIntervalBraking or EntTable.WheelTickInterval)
WheelTable.Force, WheelTable.ForceAng, WheelTable.Simulate = self:SimulateRotatingWheel( ent, EntTable, WheelTable, phys, deltatime )
end
return WheelTable.Force, WheelTable.ForceAng, WheelTable.Simulate
end
function ENT:SimulateRotatingWheel( ent, EntTable, WheelTable, phys, deltatime )
local RotationAxis = ent:GetRotationAxis()
local curRPM = self:VectorSplitNormal( RotationAxis, phys:GetAngleVelocity() ) / 6
local Throttle = self:GetThrottle()
ent:SetRPM( curRPM )
local ForceAngle = vector_origin
local ForceLinear = Vector(0,0,0)
local TorqueFactor = ent:GetTorqueFactor()
local IsBraking = self:GetBrake() > 0
local IsBrakingWheel = (TorqueFactor * Throttle) <= 0.99
if IsBraking and IsBrakingWheel then
if ent:IsRotationLocked() then
ForceAngle = vector_origin
else
local ForwardVel = self:VectorSplitNormal( ent:GetDirectionAngle():Forward(), phys:GetVelocity() )
local targetRPM = ent:VelToRPM( ForwardVel ) * 0.5
if math.abs( curRPM ) < EntTable.WheelBrakeLockupRPM then
ent:LockRotation()
else
if (ForwardVel > 0 and targetRPM > 0) or (ForwardVel < 0 and targetRPM < 0) then
ForceAngle = RotationAxis * math.Clamp( (targetRPM - curRPM) / 100,-1,1) * math.deg( EntTable.WheelBrakeForce ) * ent:GetBrakeFactor() * self:GetBrake()
end
end
end
else
if math.abs( curRPM ) < EntTable.WheelBrakeLockupRPM and Throttle == 0 then
ent:LockRotation()
else
if ent:IsRotationLocked() then
ent:ReleaseRotation()
end
end
if TorqueFactor > 0 and Throttle > 0 then
local engineTorque = self:GetEngineTorque()
local targetVelocity = self:GetTargetVelocity()
local targetRPM = ent:VelToRPM( targetVelocity )
local targetRPMabs = math.abs( targetRPM )
local powerRPM = targetRPMabs * EntTable.EngineCurve
local powerCurve = (powerRPM + math.max( targetRPMabs - powerRPM,0) - math.max(math.abs(curRPM) - powerRPM,0)) / targetRPMabs * self:Sign( targetRPM - curRPM )
local Torque = powerCurve * engineTorque * TorqueFactor * Throttle
local BoostRPM = 0
if self:GetReverse() then
Torque = math.min( Torque, 0 )
BoostRPM = ent:VelToRPM( EntTable.MaxVelocityReverse / EntTable.TransGearsReverse ) * 0.5
else
Torque = math.max( Torque, 0 )
BoostRPM = ent:VelToRPM( EntTable.MaxVelocity / EntTable.TransGears ) * 0.5
end
local BoostMul = math.max( EntTable.EngineCurveBoostLow, 0 )
local BoostStart = 1 + BoostMul
local TorqueBoost = BoostStart - (math.min( math.max( math.abs( curRPM ) - BoostRPM, 0 ), BoostRPM) / BoostRPM) * BoostMul
local Forward = ent:GetDirectionAngle():Forward()
local curVelocity = self:VectorSplitNormal( Forward, phys:GetVelocity() )
if targetVelocity >= 0 then
if curVelocity < targetVelocity then
ForceAngle = RotationAxis * Torque * TorqueBoost
end
else
if curVelocity > targetVelocity then
ForceAngle = RotationAxis * Torque * TorqueBoost
end
end
if self:PivotSteer() then
local RotationDirection = ent:GetWheelType() * self:GetPivotSteer()
if EntTable.PivotSteerByBrake and RotationDirection < 0 then
ent:LockRotation( true )
return vector_origin, vector_origin, SIM_NOTHING
end
powerCurve = math.Clamp((EntTable.PivotSteerWheelRPM * RotationDirection - curRPM) / EntTable.PivotSteerWheelRPM,-1,1)
Torque = powerCurve * engineTorque * TorqueFactor * Throttle * 2 * EntTable.PivotSteerTorqueMul
ForceAngle = RotationAxis * Torque
end
end
end
if not self:StabilityAssist() or not self:WheelsOnGround() then return ForceAngle, ForceLinear, SIM_GLOBAL_ACCELERATION end
local Vel = phys:GetVelocity()
local ForwardAngle = ent:GetDirectionAngle()
local Forward = ForwardAngle:Forward()
local Right = ForwardAngle:Right()
local Fy = self:VectorSplitNormal( Right, Vel )
local Fx = self:VectorSplitNormal( Forward, Vel )
if TorqueFactor >= 1 then
local VelX = math.abs( Fx )
local VelY = math.abs( Fy )
if VelY > VelX * 0.1 then
if VelX > EntTable.FastSteerActiveVelocity then
if VelY < VelX * 0.6 then
return ForceAngle, ForceLinear, SIM_GLOBAL_ACCELERATION
end
else
return ForceAngle, ForceLinear, SIM_GLOBAL_ACCELERATION
end
end
end
if IsBraking and not IsBrakingWheel then
return ForceAngle, ForceLinear, SIM_GLOBAL_ACCELERATION
end
ForceLinear:Add( -self:GetUp() * EntTable.WheelDownForce * TorqueFactor )
if not self:GetRacingTires() then
ForceLinear:Add( -Right * math.Clamp(Fy * 5 * math.min( math.abs( Fx ) / 500, 1 ),-EntTable.WheelSideForce,EntTable.WheelSideForce) * EntTable.ForceLinearMultiplier )
end
return ForceAngle, ForceLinear, SIM_GLOBAL_ACCELERATION
end
function ENT:SteerTo( TargetValue, MaxSteer )
local Cur = self:GetSteer() / MaxSteer
local Diff = TargetValue - Cur
local Returning = (Diff > 0 and Cur < 0) or (Diff < 0 and Cur > 0)
local Rate = FrameTime() * (Returning and self.SteerReturnSpeed or self.SteerSpeed)
local New = (Cur + math.Clamp(Diff,-Rate,Rate))
self:SetSteer( New * MaxSteer )
if New == 0 or self:GetEngineActive() then return end
for _, wheel in pairs( self:GetWheels() ) do
if not IsValid( wheel ) then continue end
wheel:PhysWake()
end
end
function ENT:OnDriverEnterVehicle( ply )
end
function ENT:OnDriverExitVehicle( ply )
end
function ENT:OnDriverChanged( Old, New, VehicleIsActive )
self:OnPassengerChanged( Old, New, 1 )
if VehicleIsActive then
self:OnDriverEnterVehicle( New )
return
end
self:OnDriverExitVehicle( Old )
self:SetThrottle( 0 )
if self:GetBrake() > 0 then
self:SetBrake( 0 )
self:EnableHandbrake()
self:StopEngine()
self:SetTurnMode( 0 )
local LightsHandler = self:GetLightsHandler()
if IsValid( LightsHandler ) then
LightsHandler:SetActive( false )
LightsHandler:SetHighActive( false )
LightsHandler:SetFogActive( false )
end
else
if not self:GetEngineActive() then
self:SetBrake( 0 )
self:EnableHandbrake()
end
end
self:SetReverse( false )
self:StopSiren()
if self:GetSirenMode() > 0 then
self:SetSirenMode( 0 )
end
if IsValid( self.HornSND ) then
self.HornSND:Stop()
end
end
function ENT:OnRefueled()
local FuelTank = self:GetFuelTank()
if not IsValid( FuelTank ) then return end
FuelTank:EmitSound( "vehicles/jetski/jetski_no_gas_start.wav" )
end
function ENT:OnMaintenance()
local FuelTank = self:GetFuelTank()
local Engine = self:GetEngine()
if IsValid( Engine ) then
Engine:SetHP( Engine:GetMaxHP() )
Engine:SetDestroyed( false )
end
if IsValid( FuelTank ) then
FuelTank:ExtinguishAndRepair()
if FuelTank:GetFuel() ~= 1 then
FuelTank:SetFuel( 1 )
self:OnRefueled()
end
end
for _, wheel in pairs( self:GetWheels() ) do
if not IsValid( wheel ) then continue end
wheel:SetHP( wheel:GetMaxHP() )
end
end
function ENT:OnSuperCharged( enable )
end
function ENT:OnTurboCharged( enable )
end
function ENT:ApproachTargetAngle( TargetAngle )
local pod = self:GetDriverSeat()
if not IsValid( pod ) then return end
local ang = pod:GetAngles()
ang:RotateAroundAxis( self:GetUp(), 90 )
local Forward = ang:Right()
local View = pod:WorldToLocalAngles( TargetAngle ):Forward()
local Reversed = false
if self:AngleBetweenNormal( View, ang:Forward() ) < 90 then
Reversed = self:GetReverse()
end
local LocalAngSteer = (self:AngleBetweenNormal( View, ang:Right() ) - 90) / self.MouseSteerAngle
local Steer = (math.min( math.abs( LocalAngSteer ), 1 ) ^ self.MouseSteerExponent * self:Sign( LocalAngSteer ))
self:SteerTo( Reversed and Steer or -Steer, self:GetMaxSteerAngle() )
end

View File

@@ -0,0 +1,37 @@
function ENT:CalcMainActivityPassenger( ply )
end
function ENT:CalcMainActivity( ply )
if ply ~= self:GetDriver() then return self:CalcMainActivityPassenger( ply ) end
if ply.m_bWasNoclipping then
ply.m_bWasNoclipping = nil
ply:AnimResetGestureSlot( GESTURE_SLOT_CUSTOM )
if CLIENT then
ply:SetIK( true )
end
end
ply.CalcIdeal = ACT_STAND
ply.CalcSeqOverride = ply:LookupSequence( "drive_jeep" )
return ply.CalcIdeal, ply.CalcSeqOverride
end
function ENT:UpdateAnimation( ply, velocity, maxseqgroundspeed )
ply:SetPlaybackRate( 1 )
if CLIENT then
if ply == self:GetDriver() then
ply:SetPoseParameter( "vehicle_steer", self:GetSteer() / self:GetMaxSteerAngle() )
ply:InvalidateBoneCache()
end
GAMEMODE:GrabEarAnimation( ply )
GAMEMODE:MouthMoveAnimation( ply )
end
return false
end

View File

@@ -0,0 +1,50 @@
function ENT:GetEyeTrace( trace_forward )
local startpos = self:LocalToWorld( self:OBBCenter() )
local pod = self:GetDriverSeat()
if IsValid( pod ) then
startpos = pod:LocalToWorld( pod:OBBCenter() )
end
local AimVector = trace_forward and self:GetForward() or self:GetAimVector()
local data = {
start = startpos,
endpos = (startpos + AimVector * 50000),
filter = self:GetCrosshairFilterEnts(),
}
local trace = util.TraceLine( data )
return trace
end
function ENT:GetAimVector()
if self:GetAI() then
return self:GetAIAimVector()
end
local pod = self:GetDriverSeat()
if not IsValid( pod ) then return self:GetForward() end
local Driver = self:GetDriver()
if not IsValid( Driver ) then return pod:GetForward() end
if Driver:lvsMouseAim() then
if SERVER then
return pod:WorldToLocalAngles( Driver:EyeAngles() ):Forward()
else
return Driver:EyeAngles():Forward()
end
else
if SERVER then
return Driver:EyeAngles():Forward()
else
return pod:LocalToWorldAngles( Driver:EyeAngles() ):Forward()
end
end
end

View File

@@ -0,0 +1,345 @@
ENT.Base = "lvs_base"
ENT.PrintName = "[LVS] Wheeldrive Base"
ENT.Author = "Luna"
ENT.Information = "Luna's Vehicle Script"
ENT.Category = "[LVS] - Cars"
ENT.Spawnable = false
ENT.AdminSpawnable = false
ENT.MaxHealth = 600
ENT.MaxHealthEngine = 50
ENT.MaxHealthFuelTank = 10
ENT.MaxVelocity = 1400
ENT.MaxVelocityReverse = 700
ENT.EngineCurve = 0.65
ENT.EngineCurveBoostLow = 1
ENT.EngineTorque = 350
ENT.EngineIdleRPM = 1000
ENT.EngineMaxRPM = 6000
ENT.ThrottleRate = 3.5
ENT.BrakeRate = 3.5
ENT.ForceLinearMultiplier = 1
ENT.ForceLinearMultiplierRacingTires = 1.15
ENT.ForceAngleMultiplier = 0.5
ENT.TransGears = 4
ENT.TransGearsReverse = 1
ENT.TransMinGearHoldTime = 1
ENT.TransShiftSpeed = 0.3
ENT.TransShiftTorqueFactor = 0
ENT.TransWobble = 40
ENT.TransWobbleTime = 1.5
ENT.TransWobbleFrequencyMultiplier = 1
ENT.TransShiftSound = "lvs/vehicles/generic/gear_shift.wav"
ENT.SteerSpeed = 3
ENT.SteerReturnSpeed = 10
ENT.FastSteerActiveVelocity = 500
ENT.FastSteerAngleClamp = 10
ENT.FastSteerDeactivationDriftAngle = 7
ENT.SteerAssistDeadZoneAngle = 1
ENT.SteerAssistMaxAngle = 15
ENT.SteerAssistExponent = 1.5
ENT.SteerAssistMultiplier = 3
ENT.MouseSteerAngle = 20
ENT.MouseSteerExponent = 2
ENT.PhysicsWeightScale = 1
ENT.PhysicsMass = 1000
ENT.PhysicsInertia = Vector(1500,1500,750)
ENT.PhysicsDampingSpeed = 4000
ENT.PhysicsDampingForward = true
ENT.PhysicsDampingReverse = false
ENT.WheelTickInterval = 0.2
ENT.WheelTickIntervalBraking = 0.02
ENT.WheelPhysicsMass = 100
ENT.WheelPhysicsInertia = Vector(10,8,10)
ENT.WheelPhysicsTireHeight = 4
ENT.WheelPhysicsMaterials = {
[0] = "friction_00", -- 0
[1] = "friction_10", -- 0.1
[2] = "friction_25", -- 0.25
[3] = "rubber", -- 0.8
[4] = "rubber",
[5] = "rubber",
[6] = "rubber",
[7] = "rubber",
[8] = "rubber",
[9] = "rubber",
[10] = "jeeptire", -- 1.337 -- i don't believe friction in havok can go above 1, however other settings such as bouncyness and elasticity are affected by it as it seems. We use jeeptire as default even tho it technically isn't the "best" choice, but rather the most common one
[11] = "jalopytire", -- 1.337
[12] = "phx_tire_normal", -- 3
}
ENT.AutoReverseVelocity = 50
ENT.WheelBrakeLockupRPM = 20
ENT.WheelBrakeForce = 400
ENT.WheelSideForce = 800
ENT.WheelDownForce = 500
ENT.AllowSuperCharger = true
ENT.SuperChargerVolume = 0.6
ENT.SuperChargerSound = "lvs/vehicles/generic/supercharger_loop.wav"
ENT.SuperChargerVisible = true
ENT.AllowTurbo = true
ENT.TurboVolume = 0.6
ENT.TurboSound = "lvs/vehicles/generic/turbo_loop.wav"
ENT.TurboBlowOff = {"lvs/vehicles/generic/turbo_blowoff1.wav","lvs/vehicles/generic/turbo_blowoff2.wav"}
ENT.DeleteOnExplode = false
ENT.lvsAllowEngineTool = true
ENT.lvsShowInSpawner = false
function ENT:SetupDataTables()
self:CreateBaseDT()
self:AddDT( "Float", "Steer" )
self:AddDT( "Float", "Throttle" )
self:AddDT( "Float", "MaxThrottle" )
self:AddDT( "Float", "Brake" )
self:AddDT( "Float", "NWMaxSteer" )
self:AddDT( "Float", "WheelVelocity" )
self:AddDT( "Int", "NWGear" )
self:AddDT( "Int", "TurnMode" )
self:AddDT( "Int", "SirenMode" )
self:AddDT( "Bool", "Reverse" )
self:AddDT( "Bool", "NWHandBrake" )
self:AddDT( "Bool", "RacingHud" )
self:AddDT( "Bool", "RacingTires" )
self:AddDT( "Bool", "Backfire" )
self:AddDT( "Entity", "Engine" )
self:AddDT( "Entity", "FuelTank" )
self:AddDT( "Entity", "LightsHandler" )
self:AddDT( "Entity", "Turbo" )
self:AddDT( "Entity", "Compressor" )
self:AddDT( "Vector", "AIAimVector" )
self:TrackSystemDT()
if SERVER then
self:SetMaxThrottle( 1 )
self:SetSirenMode( -1 )
end
end
function ENT:TrackSystemDT()
end
function ENT:StabilityAssist()
if self:GetReverse() then
return self.PhysicsDampingReverse
end
return self.PhysicsDampingForward
end
function ENT:GetMaxSteerAngle()
if CLIENT then return self:GetNWMaxSteer() end
local EntTable = self:GetTable()
if EntTable._WheelMaxSteerAngle then return EntTable._WheelMaxSteerAngle end
local Cur = 0
for _, Axle in pairs( EntTable._WheelAxleData ) do
if not Axle.SteerAngle then continue end
if Axle.SteerAngle > Cur then
Cur = Axle.SteerAngle
end
end
EntTable._WheelMaxSteerAngle = Cur
self:SetNWMaxSteer( Cur )
return Cur
end
function ENT:GetTargetVelocity()
local Reverse = self:GetReverse()
if self:IsManualTransmission() then
local Gear = self:GetGear()
local EntTable = self:GetTable()
local NumGears = Reverse and EntTable.TransGearsReverse or EntTable.TransGears
local MaxVelocity = Reverse and EntTable.MaxVelocityReverse or EntTable.MaxVelocity
local GearedVelocity = math.min( (MaxVelocity / NumGears) * (Gear + 1), MaxVelocity )
return GearedVelocity * (Reverse and -1 or 1)
end
if Reverse then
return -self.MaxVelocityReverse
end
return self.MaxVelocity
end
function ENT:HasHighBeams()
local EntTable = self:GetTable()
if isbool( EntTable._HasHighBeams ) then return EntTable._HasHighBeams end
if not istable( EntTable.Lights ) then return false end
local HasHigh = false
for _, data in pairs( EntTable.Lights ) do
if not istable( data ) then continue end
for id, typedata in pairs( data ) do
if id == "Trigger" and typedata == "high" then
HasHigh = true
break
end
end
end
EntTable._HasHighBeams = HasHigh
return HasHigh
end
function ENT:HasFogLights()
local EntTable = self:GetTable()
if isbool( EntTable._HasFogLights ) then return EntTable._HasFogLights end
if not istable( EntTable.Lights ) then return false end
local HasFog = false
for _, data in pairs( EntTable.Lights ) do
if not istable( data ) then continue end
for id, typedata in pairs( data ) do
if id == "Trigger" and typedata == "fog" then
HasFog = true
break
end
end
end
EntTable._HasFogLights = HasFog
return HasFog
end
function ENT:HasTurnSignals()
local EntTable = self:GetTable()
if isbool( EntTable._HasTurnSignals ) then return EntTable._HasTurnSignals end
if not istable( EntTable.Lights ) then return false end
local HasTurnSignals = false
for _, data in pairs( EntTable.Lights ) do
if not istable( data ) then continue end
for id, typedata in pairs( data ) do
if id == "Trigger" and (typedata == "turnleft" or typedata == "turnright" or typedata == "main+brake+turnleft" or typedata == "main+brake+turnright") then
HasTurnSignals = true
break
end
end
end
EntTable._HasTurnSignals = HasTurnSignals
return HasTurnSignals
end
function ENT:GetGear()
local Gear = self:GetNWGear()
if Gear <= 0 then
return -1
end
if self:GetReverse() then
return math.Clamp( Gear, 1, self.TransGearsReverse )
end
return math.Clamp( Gear, 1, self.TransGears )
end
function ENT:IsManualTransmission()
return self:GetNWGear() > 0
end
function ENT:BodygroupIsValid( name, groups )
if not name or not istable( groups ) then return false end
local EntTable = self:GetTable()
local id = -1
if EntTable._StoredBodyGroups then
if EntTable._StoredBodyGroups[ name ] then
id = EntTable._StoredBodyGroups[ name ]
end
else
EntTable._StoredBodyGroups = {}
end
if id == -1 then
for _, data in pairs( self:GetBodyGroups() ) do
if data.name == name then
id = data.id
break
end
end
end
if id == -1 then return false end
EntTable._StoredBodyGroups[ name ] = id
local cur = self:GetBodygroup( id )
for _, active in pairs( groups ) do
if cur == active then return true end
end
return false
end
function ENT:GetWheelUp()
return self:GetUp()
end
function ENT:GetVehicleType()
return "car"
end

Some files were not shown because too many files have changed in this diff Show More