mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 21:53:46 +03:00
Upload
This commit is contained in:
40
lua/entities/lvs_base/cl_boneposeparemeter.lua
Normal file
40
lua/entities/lvs_base/cl_boneposeparemeter.lua
Normal file
@@ -0,0 +1,40 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
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
|
||||
129
lua/entities/lvs_base/cl_effects.lua
Normal file
129
lua/entities/lvs_base/cl_effects.lua
Normal file
@@ -0,0 +1,129 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
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
|
||||
275
lua/entities/lvs_base/cl_hud.lua
Normal file
275
lua/entities/lvs_base/cl_hud.lua
Normal file
@@ -0,0 +1,275 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
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, target_ent )
|
||||
if not IsValid( target_ent ) then return end
|
||||
|
||||
local HP = target_ent:GetHP()
|
||||
|
||||
surface.SetDrawColor( In_Col.r, In_Col.g, In_Col.b, In_Col.a )
|
||||
LVS:DrawDiamond( X + 1, Y + 1, 20, HP / target_ent:GetMaxHP() )
|
||||
|
||||
if target_ent:GetMaxShield() > 0 and HP > 0 then
|
||||
surface.SetDrawColor( 200, 200, 255, In_Col.a )
|
||||
LVS:DrawDiamond( X + 1, Y + 1, 24, target_ent:GetShield() / target_ent:GetMaxShield() )
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:LVSHudPaint( 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
|
||||
|
||||
function ENT:PaintCrosshairCenter( Pos2D, Col )
|
||||
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 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
|
||||
|
||||
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
|
||||
|
||||
function ENT:PaintCrosshairSquare( Pos2D, Col )
|
||||
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
|
||||
226
lua/entities/lvs_base/cl_init.lua
Normal file
226
lua/entities/lvs_base/cl_init.lua
Normal file
@@ -0,0 +1,226 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
include("shared.lua")
|
||||
include( "sh_weapons.lua" )
|
||||
include( "cl_effects.lua" )
|
||||
include( "cl_hud.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: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() then return { self } end -- wait for the server to be ready
|
||||
|
||||
if not istable( self.CrosshairFilterEnts ) then
|
||||
self.CrosshairFilterEnts = {self}
|
||||
|
||||
-- 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
|
||||
|
||||
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 )
|
||||
150
lua/entities/lvs_base/cl_seatswitcher.lua
Normal file
150
lua/entities/lvs_base/cl_seatswitcher.lua
Normal file
@@ -0,0 +1,150 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT.IconVehicleLocked = Material( "lvs/locked.png" )
|
||||
|
||||
LVS:AddHudEditor( "SeatSwitcher", ScrW() - 360, 10, 350, 30, 350, 30, "SEAT SWITCHER",
|
||||
function( self, vehicle, X, Y, W, H, ScrX, ScrY, ply )
|
||||
if not vehicle.LVSHudPaintSeatSwitcher then return end
|
||||
vehicle:LVSHudPaintSeatSwitcher( X, Y, W, H, ScrX, ScrY, ply )
|
||||
end
|
||||
)
|
||||
|
||||
function ENT:LVSHudPaintSeatSwitcher( X, Y, w, h, ScrX, ScrY, ply )
|
||||
local pSeats = self:GetPassengerSeats()
|
||||
local SeatCount = table.Count( pSeats )
|
||||
|
||||
if SeatCount <= 0 then return end
|
||||
|
||||
pSeats[0] = self:GetDriverSeat()
|
||||
|
||||
draw.NoTexture()
|
||||
|
||||
local HasAI = self:GetAI()
|
||||
local MySeat = ply:GetVehicle():GetNWInt( "pPodIndex", -1 )
|
||||
|
||||
local Passengers = {}
|
||||
for _, player in pairs( player.GetAll() ) do
|
||||
if player:lvsGetVehicle() == self then
|
||||
local Pod = player:GetVehicle()
|
||||
Passengers[ Pod:GetNWInt( "pPodIndex", -1 ) ] = player:GetName()
|
||||
end
|
||||
end
|
||||
|
||||
if HasAI then
|
||||
Passengers[1] = "[AI] "..self.PrintName
|
||||
|
||||
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:GetNWInt( "pPodIndex", -1 )
|
||||
|
||||
if I <= 0 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
|
||||
157
lua/entities/lvs_base/cl_trailsystem.lua
Normal file
157
lua/entities/lvs_base/cl_trailsystem.lua
Normal file
@@ -0,0 +1,157 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
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
|
||||
393
lua/entities/lvs_base/init.lua
Normal file
393
lua/entities/lvs_base/init.lua
Normal file
@@ -0,0 +1,393 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile( "shared.lua" )
|
||||
AddCSLuaFile( "sh_weapons.lua" )
|
||||
AddCSLuaFile( "cl_init.lua" )
|
||||
AddCSLuaFile( "cl_effects.lua" )
|
||||
AddCSLuaFile( "cl_hud.lua" )
|
||||
AddCSLuaFile( "cl_trailsystem.lua" )
|
||||
AddCSLuaFile( "cl_seatswitcher.lua" )
|
||||
AddCSLuaFile( "cl_boneposeparemeter.lua" )
|
||||
include("shared.lua")
|
||||
include( "sh_weapons.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, 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, 0 ) )
|
||||
ent:Spawn()
|
||||
ent:Activate()
|
||||
|
||||
return ent
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Initialize()
|
||||
self:SetModel( self.MDL )
|
||||
|
||||
self:PhysicsInit( SOLID_VPHYSICS )
|
||||
self:SetMoveType( MOVETYPE_VPHYSICS )
|
||||
self:SetSolid( SOLID_VPHYSICS )
|
||||
self:SetUseType( SIMPLE_USE )
|
||||
self:SetRenderMode( RENDERMODE_NORMAL )
|
||||
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
|
||||
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: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: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 )
|
||||
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 istable( self.CrosshairFilterEnts ) or not self:IsInitialized() 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
|
||||
543
lua/entities/lvs_base/sh_weapons.lua
Normal file
543
lua/entities/lvs_base/sh_weapons.lua
Normal file
@@ -0,0 +1,543 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
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 0
|
||||
data.HeatRateUp = data.HeatRateUp or default.HeatRateUp
|
||||
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: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
|
||||
|
||||
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()
|
||||
local CurWeapon = self:GetActiveWeapon()
|
||||
|
||||
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
|
||||
|
||||
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( self.WEAPONS[1] ) do
|
||||
local IsActive = ID == SelectedID
|
||||
if Weapon.OnThink then Weapon.OnThink( self, IsActive ) end
|
||||
|
||||
if IsActive then continue 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
|
||||
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
|
||||
|
||||
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 )
|
||||
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 )
|
||||
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" )
|
||||
|
||||
function ENT:LVSHudPaintWeaponInfo( X, Y, w, h, ScrX, ScrY, ply )
|
||||
local Base = ply:lvsGetWeaponHandler()
|
||||
|
||||
if not IsValid( Base ) then return end
|
||||
|
||||
if not Base:HasWeapon( Base:GetSelectedWeapon() ) then return end
|
||||
|
||||
local Heat = Base:GetNWHeat()
|
||||
local hX = X + w - h * 0.5
|
||||
local hY = Y + h * 0.25 + h * 0.25
|
||||
local hAng = math.cos( CurTime() * 50 ) * 5 * 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 )
|
||||
surface.SetDrawColor( 255, 255 * (1 - Heat), 255 * math.max(1 - Heat * 1.5,0), 255 )
|
||||
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( Base:GetNWAmmo(), "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
|
||||
330
lua/entities/lvs_base/shared.lua
Normal file
330
lua/entities/lvs_base/shared.lua
Normal file
@@ -0,0 +1,330 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
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.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.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"} } )
|
||||
self:AddDT( "Bool", "lvsLockedStatus" )
|
||||
self:AddDT( "Bool", "lvsReady" )
|
||||
|
||||
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" )
|
||||
|
||||
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
|
||||
|
||||
self:OnSetupDataTables()
|
||||
end
|
||||
|
||||
function ENT:SetupDataTables()
|
||||
self:CreateBaseDT()
|
||||
end
|
||||
|
||||
function ENT:OnSetupDataTables()
|
||||
end
|
||||
|
||||
function ENT:CalcMainActivity( ply )
|
||||
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:GetNWInt( "pPodIndex", -1 )
|
||||
|
||||
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:GetNWInt( "pPodIndex", -1 )
|
||||
|
||||
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
|
||||
237
lua/entities/lvs_base/sv_ai.lua
Normal file
237
lua/entities/lvs_base/sv_ai.lua
Normal file
@@ -0,0 +1,237 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
28
lua/entities/lvs_base/sv_cppi.lua
Normal file
28
lua/entities/lvs_base/sv_cppi.lua
Normal file
@@ -0,0 +1,28 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
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
|
||||
411
lua/entities/lvs_base/sv_damagesystem.lua
Normal file
411
lua/entities/lvs_base/sv_damagesystem.lua
Normal file
@@ -0,0 +1,411 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
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.Callback = data.Callback or function( tbl, ent, dmginfo ) end
|
||||
|
||||
debugoverlay.BoxAngles( self:LocalToWorld( data.pos ), data.mins, data.maxs, self: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.Callback = data.Callback or function( tbl, ent, dmginfo ) end
|
||||
|
||||
debugoverlay.BoxAngles( self:LocalToWorld( data.pos ), data.mins, data.maxs, self: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 mins = part.mins
|
||||
local maxs = part.maxs
|
||||
local pos = self:LocalToWorld( part.pos )
|
||||
local ang = self: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 mins = part.mins
|
||||
local maxs = part.maxs
|
||||
local pos = self:LocalToWorld( part.pos )
|
||||
local ang = self: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 mins = part.mins
|
||||
local maxs = part.maxs
|
||||
local pos = self:LocalToWorld( part.pos )
|
||||
local ang = self: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 mins = part.mins
|
||||
local maxs = part.maxs
|
||||
local pos = self:LocalToWorld( part.pos )
|
||||
local ang = self: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 mins = lastPartDS.mins
|
||||
local maxs = lastPartDS.maxs
|
||||
local pos = self:LocalToWorld( lastPartDS.pos )
|
||||
local ang = self: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
|
||||
|
||||
if dmginfo:IsDamageType( DMG_BLAST ) then
|
||||
local Inflictor = dmginfo:GetInflictor()
|
||||
|
||||
if IsValid( Inflictor ) and isfunction( Inflictor.GetEntityFilter ) then
|
||||
for ents, _ in pairs( Inflictor:GetEntityFilter() ) do
|
||||
if ents == self then return end
|
||||
end
|
||||
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:SetDestroyed( IsCollisionDamage )
|
||||
|
||||
self:ClearPDS()
|
||||
|
||||
self.FinalAttacker = dmginfo:GetAttacker()
|
||||
self.FinalInflictor = dmginfo:GetInflictor()
|
||||
|
||||
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 mins = part.mins
|
||||
local maxs = part.maxs
|
||||
local pos = self:LocalToWorld( part.pos )
|
||||
local ang = self: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 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 closestPart
|
||||
end
|
||||
|
||||
function ENT:DamageThink()
|
||||
if self.MarkForDestruction then
|
||||
self:Explode()
|
||||
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.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")
|
||||
86
lua/entities/lvs_base/sv_damagesystem_armor.lua
Normal file
86
lua/entities/lvs_base/sv_damagesystem_armor.lua
Normal file
@@ -0,0 +1,86 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT.DSArmorBulletPenetrationType = DMG_AIRBOAT + DMG_SNIPER
|
||||
|
||||
function ENT:AddArmor( pos, ang, mins, maxs, health, minforce )
|
||||
local Armor = ents.Create( "lvs_armor" )
|
||||
|
||||
if not IsValid( Armor ) then return end
|
||||
|
||||
Armor:SetPos( self:LocalToWorld( pos ) )
|
||||
Armor:SetAngles( self:LocalToWorldAngles( ang ) )
|
||||
Armor:Spawn()
|
||||
Armor:Activate()
|
||||
Armor:SetParent( self )
|
||||
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,
|
||||
Callback = function( tbl, ent, dmginfo )
|
||||
if not IsValid( Armor ) or not dmginfo:IsDamageType( self.DSArmorBulletPenetrationType ) 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
|
||||
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
|
||||
118
lua/entities/lvs_base/sv_doorsystem.lua
Normal file
118
lua/entities/lvs_base/sv_doorsystem.lua
Normal file
@@ -0,0 +1,118 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
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
|
||||
69
lua/entities/lvs_base/sv_duping.lua
Normal file
69
lua/entities/lvs_base/sv_duping.lua
Normal file
@@ -0,0 +1,69 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
-- attempt at fixing dupe support
|
||||
|
||||
function ENT:PostEntityPaste(Player,Ent,CreatedEntities)
|
||||
if not self.SetAI then return end
|
||||
|
||||
if IsValid( Player ) and Player:IsAdmin() then return end
|
||||
|
||||
self:SetAI( false )
|
||||
end
|
||||
|
||||
local Active
|
||||
local EngineActive
|
||||
local CrosshairFilterEnts
|
||||
local DoorHandlers
|
||||
local pPodKeyIndex
|
||||
local pSeats
|
||||
|
||||
function ENT:PreEntityCopy()
|
||||
Active = self:GetActive()
|
||||
EngineActive = self:GetEngineActive()
|
||||
|
||||
self:SetlvsReady( false )
|
||||
self:SetActive( false )
|
||||
self:SetEngineActive( false )
|
||||
|
||||
CrosshairFilterEnts = self.CrosshairFilterEnts
|
||||
DoorHandlers = self._DoorHandlers
|
||||
pSeats = self.pSeats
|
||||
pPodKeyIndex = self.pPodKeyIndex
|
||||
|
||||
self.CrosshairFilterEnts = nil
|
||||
self._DoorHandlers = nil
|
||||
self.pPodKeyIndex = nil
|
||||
self.pSeats = nil
|
||||
end
|
||||
|
||||
function ENT:PostEntityCopy()
|
||||
timer.Simple(0, function()
|
||||
if not IsValid( self ) then return end
|
||||
|
||||
self:SetlvsReady( true )
|
||||
self:SetActive( Active )
|
||||
self:SetEngineActive( EngineActive )
|
||||
|
||||
Active = nil
|
||||
EngineActive = nil
|
||||
end)
|
||||
|
||||
self.CrosshairFilterEnts = CrosshairFilterEnts
|
||||
self._DoorHandlers = DoorHandlers
|
||||
self.pPodKeyIndex = pPodKeyIndex
|
||||
self.pSeats = pSeats
|
||||
|
||||
CrosshairFilterEnts = nil
|
||||
DoorHandlers = nil
|
||||
pSeats = nil
|
||||
pPodKeyIndex = nil
|
||||
end
|
||||
61
lua/entities/lvs_base/sv_engine.lua
Normal file
61
lua/entities/lvs_base/sv_engine.lua
Normal file
@@ -0,0 +1,61 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
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
|
||||
176
lua/entities/lvs_base/sv_physics.lua
Normal file
176
lua/entities/lvs_base/sv_physics.lua
Normal file
@@ -0,0 +1,176 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
self:CallOnRemove( "stop_scraping", function( self )
|
||||
self:PhysicsStopScape()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:PhysicsThink()
|
||||
if not self._lvsScrapeData then return end
|
||||
|
||||
local startpos = self:LocalToWorld( self._lvsScrapeData.pos )
|
||||
|
||||
local trace = util.TraceLine( {
|
||||
start = startpos - self._lvsScrapeData.dir,
|
||||
endpos = startpos + self._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 )
|
||||
|
||||
self._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
|
||||
|
||||
self:TakeCollisionDamage( data.OurOldVelocity:Length() - data.OurNewVelocity:Length(), HitEnt )
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if data.Speed > 60 and data.DeltaTime > 0.2 then
|
||||
local VelDif = data.OurOldVelocity:Length() - data.OurNewVelocity:Length()
|
||||
|
||||
self:CalcPDS( data )
|
||||
|
||||
local effectdata = EffectData()
|
||||
effectdata:SetOrigin( data.HitPos )
|
||||
util.Effect( "lvs_physics_impact", effectdata, true, true )
|
||||
|
||||
if VelDif > 700 then
|
||||
self:EmitSound( "lvs/physics/impact_hard.wav", 75, 95 + math.min(VelDif / 1000,1) * 10, math.min(VelDif / 800,1) )
|
||||
|
||||
if not self:IsPlayerHolding() then
|
||||
self:TakeCollisionDamage( VelDif, HitEnt )
|
||||
end
|
||||
else
|
||||
self:EmitSound( "lvs/physics/impact_soft"..math.random(1,5)..".wav", 75, 100, math.min(0.1 + VelDif / 700,1) )
|
||||
end
|
||||
end
|
||||
end
|
||||
250
lua/entities/lvs_base/sv_physics_damagesystem.lua
Normal file
250
lua/entities/lvs_base/sv_physics_damagesystem.lua
Normal file
@@ -0,0 +1,250 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
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
|
||||
|
||||
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 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 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
|
||||
|
||||
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 = math.abs( physdata.OurOldVelocity:Length() - 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
|
||||
210
lua/entities/lvs_base/sv_pod.lua
Normal file
210
lua/entities/lvs_base/sv_pod.lua
Normal file
@@ -0,0 +1,210 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
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
|
||||
else
|
||||
self:EmitSound( "doors/default_locked.wav" )
|
||||
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:SetColor( Color( 255, 255, 255, 0 ) )
|
||||
Pod:SetRenderMode( RENDERMODE_TRANSALPHA )
|
||||
Pod:DrawShadow( false )
|
||||
Pod.DoNotDuplicate = true
|
||||
Pod:SetNWInt( "pPodIndex", 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:SetColor( Color( 255, 255, 255, 0 ) )
|
||||
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:SetNWInt( "pPodIndex", 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
|
||||
89
lua/entities/lvs_base/sv_shieldsystem.lua
Normal file
89
lua/entities/lvs_base/sv_shieldsystem.lua
Normal file
@@ -0,0 +1,89 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT.ShieldRechargeDelay = 5
|
||||
ENT.ShieldRechargeRate = 1
|
||||
ENT.ShieldBlockableTypes = {
|
||||
[1] = DMG_BULLET,
|
||||
[2] = DMG_AIRBOAT,
|
||||
[3] = DMG_BUCKSHOT,
|
||||
[4] = DMG_SNIPER,
|
||||
}
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user