This commit is contained in:
lifestorm
2024-08-05 18:40:29 +03:00
parent c4d91bf369
commit 324f19217d
8040 changed files with 1853423 additions and 21 deletions

View 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

View 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

View 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

View 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 )

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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")

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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