This commit is contained in:
lifestorm
2024-08-05 18:40:29 +03:00
parent 9f505a0646
commit c6d9b6f580
8044 changed files with 1853472 additions and 21 deletions

331
lua/entities/arccw_ammo.lua Normal file
View File

@@ -0,0 +1,331 @@
--[[
| 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()
ENT.Type = "anim"
ENT.Base = "base_entity"
ENT.RenderGroup = RENDERGROUP_TRANSLUCENT
ENT.PrintName = "Base Ammo"
ENT.Category = "ArcCW - Ammo"
ENT.ArcCW_Ammo = true
ENT.Spawnable = false
ENT.Model = "models/items/sniper_round_box.mdl"
ENT.MaxHealth = 40
ENT.Scale = 1
ENT.AmmoType = "SniperPenetratedRound"
ENT.AmmoCount = 5
ENT.DetonationDamage = 10 -- Per-round damage
ENT.DetonationRadius = 256
ENT.DetonationSound = "weapons/arccw/glock18/glock18-1.wav" -- string or table
ENT.ShellModel = "models/shells/shell_9mm.mdl"
ENT.ShellScale = 1.5
ENT.ResistanceMult = {
[DMG_BURN] = 3,
[DMG_DIRECT] = 3, -- This is also fire
[DMG_BLAST] = 2,
[DMG_BULLET] = 0.5,
[DMG_BUCKSHOT] = 0.5,
[DMG_CLUB] = 0.25,
[DMG_SLASH] = 0.25,
[DMG_CRUSH] = 0.25,
[DMG_NERVEGAS] = 0,
[DMG_POISON] = 0
}
function ENT:Initialize()
self:SetModel(self.Model)
self:SetHealth(math.max(math.ceil(self.MaxHealth * ArcCW.ConVars["mult_ammohealth"]:GetFloat()), 1))
self.AmmoCount = math.max(math.ceil(self.AmmoCount * ArcCW.ConVars["mult_ammoamount"]:GetFloat(), 1))
self.MaxAmmoCount = self.AmmoCount
if engine.ActiveGamemode() == "terrortown" and ArcCW.TTTReplaceTable then
self.AmmoType = ArcCW.TTTReplaceTable[self.AmmoType] or self.AmmoType
end
if self.Scale != 1 then
self:SetModelScale(self.Scale)
end
if self:SkinCount() > 1 and math.random() <= ArcCW.ConVars["ammo_rareskin"]:GetFloat() then
self:SetSkin(math.random(1, self:SkinCount() - 1))
end
if SERVER then
self:PhysicsInit(SOLID_VPHYSICS)
self:SetMoveType(MOVETYPE_VPHYSICS)
self:SetSolid(SOLID_VPHYSICS)
self:SetCollisionGroup(COLLISION_GROUP_WEAPON)
self:SetUseType(SIMPLE_USE)
self:PhysWake()
self:SetTrigger(true) -- Enables Touch() to be called even when not colliding
if ArcCW.ConVars["ammo_largetrigger"]:GetBool() then
self:UseTriggerBounds(true, 24)
end
end
end
-- Adapted from TTT's ammo - we don't use it otherwise
function ENT:TTT_PlayerCanPickup(ply)
if ply == self:GetOwner() then return false end
local result = hook.Call("TTTCanPickupAmmo", nil, ply, self)
if result then
return result
end
local ent = self
local phys = ent:GetPhysicsObject()
local spos = phys:IsValid() and phys:GetPos() or ent:OBBCenter()
local epos = ply:GetShootPos()
local tr = util.TraceLine({start = spos, endpos = epos, filter = {ply, ent}, mask = MASK_SOLID})
-- can pickup if trace was not stopped
return tr.Fraction == 1.0
end
-- Ditto - unused outside of TTT
function ENT:TTT_CheckForWeapon(ply)
--[[]
if !self.CachedWeapons then
local tbl = {}
for k,v in pairs(weapons.GetList()) do
if v and v.Primary.Ammo == self.AmmoType then
tbl[v.ClassName] = true -- WEPS.GetClass(v)
end
end
self.CachedWeapons = tbl
end
]]
-- Why does TTT not iterate over the player's weapons? This is obviously faster
for _, wep in ipairs(ply:GetWeapons()) do
--if self.CachedWeapons[wep:GetClass()] then return true end
-- Perform check for overwritten ammo types (attachments) and UBGLs
if wep.ArcCW and
(wep:GetBuff_Override("UBGL_Ammo") == self.AmmoType
or wep:GetBuff_Override("Override_Ammo", wep.Primary.Ammo) == self.AmmoType
or wep:GetBuff_Override("Akimbo_Ammo") == self.AmmoType) then
return true
end
end
return false
end
function ENT:ApplyAmmo(ply)
if self.USED then return end
if engine.ActiveGamemode() == "terrortown" then
-- Stupid checks mate... but we'll play along unless an override exists
if !self.IgnoreTTTChecks and !self:TTT_PlayerCanPickup(ply) or !self:TTT_CheckForWeapon(ply) then return end
local giveCount = math.min(self.AmmoCount, ArcCW.TTTAmmoToClipMax[string.lower(self.AmmoType)] - ply:GetAmmoCount(self.AmmoType))
if giveCount <= 0 then return end
self.AmmoCount = self.AmmoCount - giveCount
ply:GiveAmmo(giveCount, self.AmmoType)
-- Ugly hack to let client update ammo count
-- Why not just use NWInts or NetworkVars to begin with? Good question!
self:SetNWInt("truecount", self.AmmoCount)
if self.AmmoCount <= 0 then
self.USED = true
self:Remove()
end
else
self.USED = true -- Prevent multiple uses
ply:GiveAmmo(self.AmmoCount, self.AmmoType)
self:Remove()
end
end
function ENT:DetonateRound()
local count = math.Clamp(math.random(1, self.MaxAmmoCount / 5), 1, self.AmmoCount)
-- Default function
self:FireBullets({
Attacker = self.Burner,
Damage = self.DetonationDamage,
Force = self.DetonationDamage / 5,
Num = count,
AmmoType = self.AmmoType,
Src = self:WorldSpaceCenter(),
Dir = self:GetUp(),
Spread = Vector(math.pi * 2, math.pi * 2, 0),
IgnoreEntity = self
})
self.AmmoCount = self.AmmoCount - count
self:GetPhysicsObject():AddVelocity(VectorRand() * math.random(30, 50) * self:GetPhysicsObject():GetMass())
self:GetPhysicsObject():AddAngleVelocity(VectorRand() * math.random(60, 300))
if self.DetonationSound then
self:EmitSound(istable(self.DetonationSound) and table.Random(self.DetonationSound) or self.DetonationSound)
end
end
function ENT:Detonate(wet, attacker)
if wet then
self:FireBullets({
Attacker = attacker,
Damage = self.DetonationDamage,
Force = self.DetonationDamage / 5,
Num = math.max(self.AmmoCount, 50),
AmmoType = self.AmmoType,
Src = self:WorldSpaceCenter(),
Dir = self:GetUp(),
Spread = Vector(math.pi * 2, math.pi * 2, 0),
IgnoreEntity = self
})
end
local e = EffectData()
e:SetOrigin(self:GetPos())
util.Effect("Explosion", e)
util.BlastDamage(self, attacker, self:GetPos(), self.DetonationRadius, self.DetonationDamage * (wet and 0.5 or 1))
self:Remove()
end
if SERVER then
function ENT:Use(ply)
if !ply:IsPlayer() then return end
self:ApplyAmmo(ply)
end
function ENT:Touch(ply)
if !ply:IsPlayer() or !ArcCW.ConVars["ammo_autopickup"]:GetBool() then return end
self:ApplyAmmo(ply)
end
function ENT:Burn(attacker)
self.Burning = true
self.Burner = attacker
self:Ignite(30)
self:SetHealth(-1)
end
function ENT:OnTakeDamage(dmginfo)
if self:Health() <= 0 or self.USED then return end
--self:TakePhysicsDamage(dmginfo)
self:SetHealth(self:Health() - dmginfo:GetDamage())
if self:Health() <= 0 then
self.USED = true
local cvar = ArcCW.ConVars["ammo_detonationmode"]:GetInt()
if cvar == -1 or (!ArcCW.ConVars["ammo_chaindet"]:GetBool() and dmginfo:GetInflictor().ArcCW_Ammo) or self.DetonationDamage <= 0 then
-- Go quietly
local e = EffectData()
e:SetOrigin(self:GetPos())
e:SetMagnitude(8)
e:SetScale(2)
util.Effect("Sparks", e)
self:EmitSound("physics/cardboard/cardboard_box_break2.wav", 80, 120)
self:Remove()
elseif cvar == 2 and (math.random() <= 0.25 or dmginfo:IsDamageType(DMG_BURN)) then
-- Fancy ammobox burning
self:Burn(dmginfo:GetAttacker())
else
-- Plain old explosion
self:Detonate(cvar >= 1, dmginfo:GetAttacker())
end
end
end
function ENT:Think()
if self.Burning then
if self.AmmoCount <= 0 then
self:Detonate(false, IsValid(self.Burner) and self.Burner or self)
else
self:DetonateRound()
end
self:NextThink(CurTime() + math.random() * 0.3 + 0.2)
return true
end
end
-- Do it during the hook so that hit damage numbers show up properly (yes, I am _that_ pedantic)
hook.Add("EntityTakeDamage", "ArcCW_Ammo", function(ent, dmginfo)
if ent.ArcCW_Ammo then
if ArcCW.ConVars["mult_ammohealth"]:GetFloat() < 0 then
dmginfo:ScaleDamage(0)
elseif ent.ResistanceMult then
-- Only apply one multiplier, and prioritize larger ones
for k, v in SortedPairsByValue(ent.ResistanceMult, true) do if dmginfo:IsDamageType(k) then dmginfo:ScaleDamage(v) break end end
end
end
end)
elseif CLIENT then
function ENT:DrawTranslucent()
self:Draw()
end
function ENT:Draw()
self:DrawModel()
local cvar2d3d = ArcCW.ConVars["2d3d"]:GetInt()
if cvar2d3d == 0 or (cvar2d3d == 1 and LocalPlayer():GetEyeTrace().Entity != self) then return end
if (EyePos() - self:GetPos()):LengthSqr() <= 262144 then -- 512^2
local ang = LocalPlayer():EyeAngles()
ang:RotateAroundAxis(ang:Forward(), 180)
ang:RotateAroundAxis(ang:Right(), 90)
ang:RotateAroundAxis(ang:Up(), 90)
cam.Start3D2D(self:WorldSpaceCenter() + Vector(0, 0, (self:OBBMaxs().z - self:OBBMins().z) * 0.5 + 8) , ang, 0.1)
surface.SetFont("ArcCW_32_Unscaled")
local w = surface.GetTextSize(self.PrintName)
surface.SetTextPos(-w / 2 + 2, 2)
surface.SetTextColor(0, 0, 0, 150)
surface.DrawText(self.PrintName)
surface.SetTextPos(-w / 2, 0)
surface.SetTextColor(255, 255, 255, 255)
surface.DrawText(self.PrintName)
local ammo = self:GetNWInt("truecount", -1) != -1 and self:GetNWInt("truecount", -1) or self.AmmoCount
if ammo then
w = surface.GetTextSize("×" .. ammo)
surface.SetTextColor(0, 0, 0, 150)
surface.SetTextPos(-w / 2 + 2, 27)
surface.DrawText("×" .. ammo)
surface.SetTextColor(255, 255, 255, 255)
surface.SetTextPos(-w / 2, 25)
surface.DrawText("×" .. ammo)
end
cam.End3D2D()
end
end
end

View File

@@ -0,0 +1,32 @@
--[[
| 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()
ENT.Base = "arccw_ammo"
ENT.RenderGroup = RENDERGROUP_TRANSLUCENT
ENT.PrintName = "Magnum Ammo"
ENT.Category = "ArcCW - Ammo"
ENT.Spawnable = true
ENT.Model = "models/items/arccw/magnum_ammo.mdl"
ENT.AmmoType = "357"
ENT.AmmoCount = 12
if engine.ActiveGamemode() == "terrortown" then
ENT.AmmoType = "AlyxGun"
ENT.AmmoCount = 18
end
ENT.DetonationDamage = 50
ENT.DetonationRadius = 128
ENT.DetonationSound = "weapons/357_fire2.wav"

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/
--]]
AddCSLuaFile()
ENT.Base = "arccw_ammo"
ENT.RenderGroup = RENDERGROUP_TRANSLUCENT
ENT.PrintName = "Magnum Ammo (Large)"
ENT.Category = "ArcCW - Ammo"
ENT.Spawnable = true
ENT.Model = "models/items/arccw/magnum_ammo_closed.mdl"
ENT.AmmoType = "357"
ENT.AmmoCount = 60
ENT.Scale = 1.5
ENT.DetonationDamage = 50
ENT.DetonationRadius = 128
ENT.DetonationSound = "weapons/357_fire2.wav"

View File

@@ -0,0 +1,27 @@
--[[
| 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()
ENT.Base = "arccw_ammo"
ENT.RenderGroup = RENDERGROUP_TRANSLUCENT
ENT.PrintName = "Rifle Ammo"
ENT.Category = "ArcCW - Ammo"
ENT.Spawnable = true
ENT.Model = "models/items/arccw/rifle_ammo.mdl"
ENT.AmmoType = "ar2"
ENT.AmmoCount = 30
ENT.DetonationDamage = 50
ENT.DetonationRadius = 256
ENT.DetonationSound = "weapons/ar1/ar1_dist2.wav"

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/
--]]
AddCSLuaFile()
ENT.Base = "arccw_ammo"
ENT.RenderGroup = RENDERGROUP_TRANSLUCENT
ENT.PrintName = "Rifle Ammo (Large)"
ENT.Category = "ArcCW - Ammo"
ENT.Spawnable = true
ENT.Model = "models/items/arccw/rifle_ammo.mdl"
ENT.Scale = 1.5
ENT.AmmoType = "ar2"
ENT.AmmoCount = 150
ENT.DetonationDamage = 50
ENT.DetonationRadius = 256
ENT.DetonationSound = "weapons/ar1/ar1_dist2.wav"

View File

@@ -0,0 +1,29 @@
--[[
| 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()
ENT.Base = "arccw_ammo"
ENT.PrintName = "Shotgun Ammo"
ENT.Category = "ArcCW - Ammo"
ENT.Spawnable = true
ENT.Model = "models/items/arccw/shotgun_ammo.mdl"
ENT.AmmoType = "buckshot"
ENT.AmmoCount = 20
if engine.ActiveGamemode() == "terrortown" then
ENT.AmmoCount = 12
end
ENT.DetonationDamage = 80
ENT.DetonationRadius = 128
ENT.DetonationSound = "weapons/shotgun/shotgun_fire6.wav"

View File

@@ -0,0 +1,27 @@
--[[
| 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()
ENT.Base = "arccw_ammo"
ENT.PrintName = "Shotgun Ammo (Large)"
ENT.Category = "ArcCW - Ammo"
ENT.Spawnable = true
ENT.Model = "models/items/arccw/shotgun_ammo_closed.mdl"
ENT.Scale = 1.5
ENT.AmmoType = "buckshot"
ENT.AmmoCount = 100
ENT.DetonationDamage = 80
ENT.DetonationRadius = 128
ENT.DetonationSound = "weapons/shotgun/shotgun_fire6.wav"

View File

@@ -0,0 +1,26 @@
--[[
| 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()
ENT.Base = "arccw_ammo"
ENT.PrintName = "Pistol Ammo"
ENT.Category = "ArcCW - Ammo"
ENT.Spawnable = true
ENT.Model = "models/items/arccw/pistol_ammo.mdl"
ENT.AmmoType = "pistol"
ENT.AmmoCount = 40
ENT.DetonationDamage = 10
ENT.DetonationRadius = 256
ENT.DetonationSound = "weapons/pistol/pistol_fire3.wav"

View File

@@ -0,0 +1,27 @@
--[[
| 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()
ENT.Base = "arccw_ammo"
ENT.PrintName = "Pistol Ammo (Large)"
ENT.Category = "ArcCW - Ammo"
ENT.Spawnable = true
ENT.Model = "models/items/arccw/pistol_ammo.mdl"
ENT.Scale = 1.5
ENT.AmmoType = "pistol"
ENT.AmmoCount = 200
ENT.DetonationDamage = 10
ENT.DetonationRadius = 256
ENT.DetonationSound = "weapons/pistol/pistol_fire3.wav"

View File

@@ -0,0 +1,33 @@
--[[
| 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()
ENT.Base = "arccw_ammo"
ENT.RenderGroup = RENDERGROUP_TRANSLUCENT
ENT.PrintName = "Plinking Ammo"
ENT.Category = "ArcCW - Ammo"
ENT.Spawnable = true
ENT.Model = "models/items/arccw/plinking_ammo.mdl"
ENT.AmmoType = "plinking"
ENT.AmmoCount = 100
ENT.DetonationDamage = 10
ENT.DetonationRadius = 128
ENT.DetonationSound = nil
DEFINE_BASECLASS(ENT.Base)
function ENT:DetonateRound()
BaseClass.DetonateRound(self)
self:EmitSound("weapons/pistol/pistol_fire2.wav", 70, 175, 0.8)
end

View File

@@ -0,0 +1,34 @@
--[[
| 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()
ENT.Base = "arccw_ammo"
ENT.RenderGroup = RENDERGROUP_TRANSLUCENT
ENT.PrintName = "Plinking Ammo (Large)"
ENT.Category = "ArcCW - Ammo"
ENT.Spawnable = true
ENT.Model = "models/items/arccw/plinking_ammo.mdl"
ENT.Scale = 1.5
ENT.AmmoType = "plinking"
ENT.AmmoCount = 500
ENT.DetonationDamage = 10
ENT.DetonationRadius = 128
ENT.DetonationSound = nil
DEFINE_BASECLASS(ENT.Base)
function ENT:DetonateRound()
BaseClass.DetonateRound(self)
self:EmitSound("weapons/pistol/pistol_fire2.wav", 70, 175, 0.8)
end

View File

@@ -0,0 +1,29 @@
--[[
| 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()
ENT.Base = "arccw_ammo"
ENT.PrintName = "Carbine Ammo"
ENT.Category = "ArcCW - Ammo"
ENT.Spawnable = true
ENT.Model = "models/items/arccw/smg_ammo.mdl"
ENT.AmmoType = "smg1"
ENT.AmmoCount = 60
if engine.ActiveGamemode() == "terrortown" then
ENT.AmmoCount = 30
end
ENT.DetonationDamage = 30
ENT.DetonationRadius = 256
ENT.DetonationSound = "weapons/smg1/npc_smg1_fire1.wav"

View File

@@ -0,0 +1,49 @@
--[[
| 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()
ENT.Base = "arccw_ammo"
ENT.PrintName = "Rifle Grenade"
ENT.Category = "ArcCW - Ammo"
ENT.Spawnable = true
ENT.Model = "models/Items/AR2_Grenade.mdl"
ENT.Health = 15
ENT.AmmoType = "smg1_grenade"
ENT.AmmoCount = 1
ENT.DetonationDamage = 50 -- Per-round damage
ENT.DetonationRadius = 300
function ENT:DetonateRound(attacker)
local nade = ents.Create("arccw_gl_ammodet")
nade:SetPos(self:GetPos())
nade:SetAngles(self:GetAngles() + AngleRand(-10, 10))
nade:Spawn()
nade:GetPhysicsObject():AddVelocity(self:GetVelocity() + self:GetForward() * math.random(500, 2000))
nade:SetOwner(attacker or self.Burner)
self:Remove()
end
function ENT:Detonate(wet, attacker)
if wet then
self:DetonateRound(attacker)
else
local e = EffectData()
e:SetOrigin(self:GetPos())
util.Effect("Explosion", e)
util.BlastDamage(self, attacker, self:GetPos(), self.DetonationRadius, self.DetonationDamage)
self:Remove()
end
end

View File

@@ -0,0 +1,58 @@
--[[
| 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()
ENT.Base = "arccw_ammo"
ENT.PrintName = "Rifle Grenade Box"
ENT.Category = "ArcCW - Ammo"
ENT.Spawnable = true
ENT.Model = "models/items/arccw/riflegrenade_ammo.mdl"
ENT.Health = 70
ENT.AmmoType = "smg1_grenade"
ENT.AmmoCount = 5
ENT.DetonationDamage = 100 -- Per-round damage
ENT.DetonationRadius = 300
function ENT:DetonateRound(attacker)
local nade = ents.Create("arccw_gl_ammodet")
nade:SetPos(self:GetPos())
local v = self:GetUp():Angle() + AngleRand(-60, 60)
nade:SetAngles(v)
nade:Spawn()
nade:GetPhysicsObject():AddVelocity(self:GetVelocity() + self:GetForward() * math.random(2000, 3000))
nade:SetOwner(attacker or self.Burner)
self.AmmoCount = self.AmmoCount - 1
self:GetPhysicsObject():AddVelocity(VectorRand() * math.random(5, 10) * self:GetPhysicsObject():GetMass())
self:GetPhysicsObject():AddAngleVelocity(VectorRand() * math.random(60, 300))
self:EmitSound("weapons/ar2/ar2_altfire.wav", 80, 150)
end
function ENT:Detonate(wet, attacker)
if wet then
for i = 1, math.random(1, 3) do
self:DetonateRound(attacker)
end
end
local e = EffectData()
e:SetOrigin(self:GetPos())
util.Effect("Explosion", e)
util.BlastDamage(self, attacker, self:GetPos(), self.DetonationRadius, self.DetonationDamage * (wet and 1 or 2))
self:Remove()
end

View File

@@ -0,0 +1,27 @@
--[[
| 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()
ENT.Base = "arccw_ammo"
ENT.PrintName = "Carbine Ammo (Large)"
ENT.Category = "ArcCW - Ammo"
ENT.Spawnable = true
ENT.Model = "models/items/arccw/smg_ammo.mdl"
ENT.Scale = 1.5
ENT.AmmoType = "smg1"
ENT.AmmoCount = 300
ENT.DetonationDamage = 30
ENT.DetonationRadius = 256
ENT.DetonationSound = "weapons/smg1/npc_smg1_fire1.wav"

View File

@@ -0,0 +1,30 @@
--[[
| 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()
ENT.Base = "arccw_ammo"
ENT.PrintName = "Sniper Ammo"
ENT.Category = "ArcCW - Ammo"
ENT.Spawnable = true
ENT.Model = "models/items/arccw/sniper_ammo.mdl"
ENT.AmmoType = "SniperPenetratedRound"
ENT.AmmoCount = 10
ENT.MaxHealth = 20
if engine.ActiveGamemode() == "terrortown" then
ENT.AmmoType = "357"
end
ENT.DetonationDamage = 80
ENT.DetonationRadius = 128
ENT.DetonationSound = "npc/sniper/echo1.wav"

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/
--]]
AddCSLuaFile()
ENT.Base = "arccw_ammo"
ENT.PrintName = "Sniper Ammo (Large)"
ENT.Category = "ArcCW - Ammo"
ENT.Spawnable = true
ENT.Model = "models/items/arccw/sniper_ammo.mdl"
ENT.Scale = 1.5
ENT.AmmoType = "SniperPenetratedRound"
ENT.AmmoCount = 50
ENT.MaxHealth = 20
ENT.DetonationDamage = 80
ENT.DetonationRadius = 128
ENT.DetonationSound = "npc/sniper/echo1.wav"

View File

@@ -0,0 +1,142 @@
--[[
| 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.Base = "base_entity"
ENT.PrintName = "Base Dropped Attachment"
ENT.Author = ""
ENT.Information = ""
ENT.Spawnable = false
ENT.RenderGroup = RENDERGROUP_TRANSLUCENT
ENT.Category = "ArcCW - Attachments"
AddCSLuaFile()
ENT.GiveAttachments = nil -- table of all the attachments to give, and in what quantity. {{["id"] = int quantity}}
ENT.SoundImpact = "weapon.ImpactSoft"
ENT.Model = ""
if SERVER then
function ENT:Initialize()
if !self.Model then
self:Remove()
return
end
self:SetModel(self.Model)
self:PhysicsInit(SOLID_VPHYSICS)
self:SetMoveType(MOVETYPE_VPHYSICS)
self:SetSolid(SOLID_VPHYSICS)
self:SetCollisionGroup(COLLISION_GROUP_WEAPON)
self:SetTrigger( true )
self:SetPos(self:GetPos() + Vector(0, 0, 4))
local phys = self:GetPhysicsObject()
if phys:IsValid() then
phys:Wake()
phys:SetBuoyancyRatio(0)
end
end
function ENT:PhysicsCollide(colData, collider)
if colData.DeltaTime < 0.25 then return end
self:EmitSound(self.SoundImpact)
end
function ENT:Use(activator, caller)
if !caller:IsPlayer() then return end
if ArcCW.ConVars["attinv_free"]:GetBool() then return end
local take = false
for i, k in pairs(self.GiveAttachments) do
if i == "BaseClass" then continue end
if ArcCW.ConVars["attinv_lockmode"]:GetBool() then
if ArcCW:PlayerGetAtts(caller, i) > 0 then
continue
end
end
if hook.Run("ArcCW_PickupAttEnt", caller, i, k) then continue end
ArcCW:PlayerGiveAtt(caller, i, k)
take = true
end
if take then
ArcCW:PlayerSendAttInv(caller)
self:EmitSound("weapons/arccw/useatt.wav")
self:Remove()
end
end
else
local defaulticon = Material("arccw/hud/atts/default.png")
local iw = 64
function ENT:DrawTranslucent()
self:Draw()
end
function ENT:Draw()
self:DrawModel()
local cvar2d3d = ArcCW.ConVars["2d3d"]:GetInt()
if cvar2d3d == 0 or (cvar2d3d == 1 and LocalPlayer():GetEyeTrace().Entity != self) then return end
if self.PrintName == "Base Dropped Attachment" and self:GetNWInt("attid", -1) != -1 then
local att = ArcCW.AttachmentIDTable[self:GetNWInt("attid", -1)]
if !att then return end
local atttbl = ArcCW.AttachmentTable[att]
if !atttbl then return end
self.PrintName = atttbl.PrintName or att
self.Icon = atttbl.Icon or defaulticon
end
if (EyePos() - self:WorldSpaceCenter()):LengthSqr() <= 262144 then -- 512^2
local ang = LocalPlayer():EyeAngles()
ang:RotateAroundAxis(ang:Forward(), 180)
ang:RotateAroundAxis(ang:Right(), 90)
ang:RotateAroundAxis(ang:Up(), 90)
cam.Start3D2D(self:WorldSpaceCenter() + Vector(0, 0, 16), ang, 0.1)
surface.SetFont("ArcCW_32_Unscaled")
local w = surface.GetTextSize(self.PrintName)
surface.SetTextPos(-w / 2 + 2, 2)
surface.SetTextColor(0, 0, 0, 150)
surface.DrawText(self.PrintName)
surface.SetTextPos(-w / 2, 0)
surface.SetTextColor(255, 255, 255, 255)
surface.DrawText(self.PrintName)
surface.SetDrawColor(255, 255, 255)
surface.SetMaterial(self.Icon or defaulticon)
surface.DrawTexturedRect(-iw / 2, iw / 2, iw, iw)
cam.End3D2D()
end
end
end

View File

@@ -0,0 +1,59 @@
--[[
| 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()
ENT.Base = "arccw_att_base"
ENT.RenderGroup = RENDERGROUP_TRANSLUCENT
ENT.Category = "ArcCW - Attachments"
ENT.PrintName = "Attachment Box"
ENT.Spawnable = false
ENT.Model = "models/Items/BoxMRounds.mdl"
function ENT:Draw()
self:DrawModel()
local cvar2d3d = ArcCW.ConVars["2d3d"]:GetInt()
if cvar2d3d == 0 or (cvar2d3d == 1 and LocalPlayer():GetEyeTrace().Entity != self) then return end
if (EyePos() - self:WorldSpaceCenter()):LengthSqr() <= 262144 then -- 512^2
local ang = LocalPlayer():EyeAngles()
local name = self:GetNWString("boxname", nil) or self.PrintName
ang:RotateAroundAxis(ang:Forward(), 180)
ang:RotateAroundAxis(ang:Right(), 90)
ang:RotateAroundAxis(ang:Up(), 90)
cam.Start3D2D(self:WorldSpaceCenter() + Vector(0, 0, 14), ang, 0.1)
surface.SetFont("ArcCW_32_Unscaled")
local w = surface.GetTextSize(name)
surface.SetTextPos(-w / 2 + 2, 2)
surface.SetTextColor(0, 0, 0, 150)
surface.DrawText(name)
surface.SetTextPos(-w / 2, 0)
surface.SetTextColor(255, 255, 255, 255)
surface.DrawText(name)
local count = self:GetNWInt("boxcount", 0)
local str = count .. " Attachment" .. (count != 1 and "s" or "")
local w2 = surface.GetTextSize(str)
surface.SetTextPos(-w2 / 2 + 2, 26)
surface.SetTextColor(0, 0, 0, 150)
surface.DrawText(str)
surface.SetTextPos(-w2 / 2, 24)
surface.SetTextColor(255, 255, 255, 255)
surface.DrawText(str)
cam.End3D2D()
end
end

View File

@@ -0,0 +1,126 @@
--[[
| 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.Base = "base_entity"
ENT.PrintName = "HE Round"
ENT.Author = ""
ENT.Information = ""
ENT.Spawnable = false
AddCSLuaFile()
ENT.Model = "models/items/ar2_grenade.mdl"
ENT.Ticks = 0
ENT.FuseTime = 10
function ENT:Draw()
self:DrawModel()
end
ENT.Ticks = 0
function ENT:Detonate()
if !self:IsValid() then return end
local effectdata = EffectData()
effectdata:SetOrigin( self:GetPos() )
if self:WaterLevel() >= 1 then
util.Effect( "WaterSurfaceExplosion", effectdata )
self:EmitSound("weapons/underwater_explode3.wav", 125, 100, 1, CHAN_AUTO)
else
util.Effect( "Explosion", effectdata)
self:EmitSound("phx/kaboom.wav", 125, 100, 1, CHAN_AUTO)
end
local attacker = self
if self:GetOwner():IsValid() then
attacker = self:GetOwner()
end
util.BlastDamage(self, attacker, self:GetPos(), 300, 50)
self:FireBullets({
Attacker = attacker,
Damage = 0,
Tracer = 0,
Distance = 20000,
Dir = self:GetVelocity(),
Src = self:GetPos(),
Callback = function(att, tr, dmg)
util.Decal("Scorch", tr.StartPos, tr.HitPos - (tr.HitNormal * 16), self)
end
})
self:Remove()
end
if CLIENT then
function ENT:Think()
if self.Ticks % 2 == 0 then
local emitter = ParticleEmitter(self:GetPos())
if !self:IsValid() or self:WaterLevel() > 2 then return end
if !IsValid(emitter) then return end
local smoke = emitter:Add("particle/particle_smokegrenade", self:GetPos())
smoke:SetVelocity( VectorRand() * 25 )
smoke:SetGravity( Vector(math.Rand(-5, 5), math.Rand(-5, 5), math.Rand(-20, -25)) )
smoke:SetDieTime( math.Rand(1.5, 2.0) )
smoke:SetStartAlpha( 255 )
smoke:SetEndAlpha( 0 )
smoke:SetStartSize( 0 )
smoke:SetEndSize( 100 )
smoke:SetRoll( math.Rand(-180, 180) )
smoke:SetRollDelta( math.Rand(-0.2,0.2) )
smoke:SetColor( 20, 20, 20 )
smoke:SetAirResistance( 5 )
smoke:SetPos( self:GetPos() )
smoke:SetLighting( false )
emitter:Finish()
end
self.Ticks = self.Ticks + 1
end
else
function ENT:Initialize()
local pb_vert = 1
local pb_hor = 1
self:SetModel(self.Model)
self:PhysicsInitBox( Vector(-pb_vert,-pb_hor,-pb_hor), Vector(pb_vert,pb_hor,pb_hor) )
local phys = self:GetPhysicsObject()
if phys:IsValid() then
phys:Wake()
end
self.SpawnTime = CurTime()
timer.Simple(0.1, function()
if !IsValid(self) then return end
self:SetCollisionGroup(COLLISION_GROUP_PROJECTILE)
end)
end
function ENT:Think()
if SERVER and CurTime() - self.SpawnTime >= self.FuseTime then
self:Detonate()
end
end
function ENT:PhysicsCollide(colData, collider)
self:Detonate()
end
end

View File

@@ -0,0 +1,131 @@
--[[
| 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.Base = "base_entity"
ENT.PrintName = "Smoke Cloud"
ENT.Author = ""
ENT.Information = ""
ENT.Spawnable = false
ENT.AdminSpawnable = false
local smokeimages = {"particle/smokesprites_0002", "particle/smokesprites_0003", "particle/smokesprites_0004", "particle/smokesprites_0005", "particle/smokesprites_0006", "particle/smokesprites_0007", "particle/smokesprites_0008", "particle/smokesprites_0009", "particle/smokesprites_0010", "particle/smokesprites_0011", "particle/smokesprites_0012", "particle/smokesprites_0013", "particle/smokesprites_0014", "particle/smokesprites_0015", "particle/smokesprites_0016"}
local function GetSmokeImage()
return smokeimages[math.random(#smokeimages)]
end
ENT.Particles = nil
ENT.SmokeRadius = 256
ENT.SmokeColor = Color(150, 150, 150)
ENT.BillowTime = 1
ENT.Life = 15
ENT.ArcCWSmoke = true
AddCSLuaFile()
function ENT:Initialize()
if SERVER then
self:SetModel( "models/weapons/w_eq_smokegrenade_thrown.mdl" )
self:SetMoveType( MOVETYPE_NONE )
self:SetSolid( SOLID_NONE )
self:DrawShadow( false )
else
local emitter = ParticleEmitter(self:GetPos())
self.Particles = {}
local amt = 20
for i = 1, amt do
local smoke = emitter:Add(GetSmokeImage(), self:GetPos())
smoke:SetVelocity( VectorRand() * 8 + (Angle(0, i * (360 / amt), 0):Forward() * 400) )
smoke:SetStartAlpha( 0 )
smoke:SetEndAlpha( 255 )
smoke:SetStartSize( 0 )
smoke:SetEndSize( self.SmokeRadius )
smoke:SetRoll( math.Rand(-180, 180) )
smoke:SetRollDelta( math.Rand(-0.2,0.2) )
smoke:SetColor( self.SmokeColor.r, self.SmokeColor.g, self.SmokeColor.b )
smoke:SetAirResistance( 75 )
smoke:SetPos( self:GetPos() )
smoke:SetCollide( true )
smoke:SetBounce( 0.2 )
smoke:SetLighting( false )
smoke:SetNextThink( CurTime() + FrameTime() )
smoke.bt = CurTime() + self.BillowTime
smoke.dt = CurTime() + self.BillowTime + self.Life
smoke.ft = CurTime() + self.BillowTime + self.Life + math.Rand(2.5, 5)
smoke:SetDieTime(smoke.ft)
smoke.life = self.Life
smoke.billowed = false
smoke.radius = self.SmokeRadius
smoke:SetThinkFunction( function(pa)
if !pa then return end
local prog = 1
local alph = 0
if pa.ft < CurTime() then
return
elseif pa.dt < CurTime() then
local d = (CurTime() - pa.dt) / (pa.ft - pa.dt)
alph = 1 - d
elseif pa.bt < CurTime() then
alph = 1
else
local d = math.Clamp(pa:GetLifeTime() / (pa.bt - CurTime()), 0, 1)
prog = (-d ^ 2) + (2 * d)
alph = d
end
pa:SetEndSize( pa.radius * prog )
pa:SetStartSize( pa.radius * prog )
pa:SetStartAlpha(255 * alph)
pa:SetEndAlpha(255 * alph)
pa:SetNextThink( CurTime() + FrameTime() )
end )
table.insert(self.Particles, smoke)
end
emitter:Finish()
end
self.dt = CurTime() + self.Life + self.BillowTime
end
function ENT:Think()
if SERVER then
local targets = ents.FindInSphere(self:GetPos(), 256)
for _, k in pairs(targets) do
if k:IsNPC() then
k:SetSchedule(SCHED_STANDOFF)
end
end
end
if self.dt < CurTime() then
if SERVER then
SafeRemoveEntity(self)
end
end
end
function ENT:Draw()
return false
end

View File

@@ -0,0 +1,143 @@
--[[
| 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()
ENT.Base = "arccw_uc_40mm_he"
ENT.PrintName = "40mm Airburst"
ENT.GrenadeDamage = 75
ENT.GrenadeRadius = 300
ENT.ExplosionEffect = false
ENT.Scorch = false
ENT.DragCoefficient = 0.75
ENT.DetonateOnImpact = false
ENT.NextTraceTime = 0
if SERVER then
function ENT:Think()
if SERVER and CurTime() - self.SpawnTime >= self.FuseTime then
self:Detonate()
end
if self.SpawnTime + 0.2 < CurTime() and self.NextTraceTime < CurTime() then
self.NextTraceTime = CurTime() + 0.1
local dir = self:GetVelocity():GetNormalized()
local deg = math.Clamp(1.5 - dir:Cross(Vector(0, 0, -1)):Length(), 0.5, 1)
local tr = util.TraceHull({
start = self:GetPos(),
endpos = self:GetPos() + dir * (1024 * deg),
filter = self,
mins = Vector(-16, -16, -8),
maxs = Vector(16, 16, 8)
})
if tr.Hit then
self:Detonate()
end
end
end
end
function ENT:DoDetonation()
local attacker = IsValid(self:GetOwner()) and self:GetOwner() or self
local dir = self:GetVelocity():GetNormalized()
local effectdata = EffectData()
effectdata:SetOrigin(self:GetPos())
if self:WaterLevel() >= 1 then
util.Effect("WaterSurfaceExplosion", effectdata)
self:EmitSound("weapons/underwater_explode3.wav", 125, 100, 1, CHAN_AUTO)
else
effectdata:SetMagnitude(4)
effectdata:SetScale(1)
effectdata:SetRadius(4)
effectdata:SetNormal(dir)
util.Effect("Sparks", effectdata)
self:EmitSound("physics/metal/metal_box_break1.wav", 100, 200)
end
-- The steeper the vertical angle, the higher the damage
local deg = math.Clamp(1.5 - dir:Cross(Vector(0, 0, -1)):Length(), 0.5, 1)
self:FireBullets({
Attacker = attacker,
Damage = 25,
Force = 5,
Distance = 2048,
HullSize = 16,
Num = 64,
Tracer = 1,
Src = self:GetPos(),
Dir = dir,
Spread = Vector(1, 1, 0),
IgnoreEntity = self,
})
local dmg = DamageInfo()
dmg:SetAttacker(IsValid(self:GetOwner()) and self:GetOwner() or self)
dmg:SetDamageType(DMG_BULLET)
dmg:SetInflictor(self)
dmg:SetDamageForce(self:GetVelocity() * 100)
for _, ent in pairs(ents.FindInCone(self:GetPos(), dir, 1024, 0.707)) do
local tr = util.QuickTrace(self:GetPos(), ent:WorldSpaceCenter() - self:GetPos(), self)
if tr.Entity == ent then
dmg:SetDamage(math.Rand(75, 150) * deg * math.Clamp(tr.Fraction, 0.5, 1))
ent:TakeDamageInfo(dmg)
end
end
util.BlastDamage(self, attacker, self:GetPos(), self.GrenadeRadius, self.GrenadeDamage or self.Damage or 0)
end
--[[]
function ENT:BurstEffect()
local dir = self:GetVelocity():GetNormalized()
local emitter = ParticleEmitter(self:GetPos())
if !self:IsValid() or self:WaterLevel() > 2 then return end
if !IsValid(emitter) then return end
for i = 1, 64 do
local fire = emitter:Add("particles/smokey", self:GetPos())
fire:SetVelocity( VectorRand() * 500 + dir * 1000 )
fire:SetGravity( Vector(0, 0, -90) )
fire:SetDieTime( math.Rand(0.25, 0.5) )
fire:SetStartAlpha( 200 )
fire:SetEndAlpha( 0 )
fire:SetStartSize( 10 )
fire:SetEndSize( 128 )
fire:SetRoll( math.Rand(-180, 180) )
fire:SetRollDelta( math.Rand(-0.2,0.2) )
fire:SetColor( 255, 255, 255 )
fire:SetAirResistance( 150 )
fire:SetPos( self:GetPos() )
fire:SetLighting( false )
fire:SetCollide(true)
fire:SetBounce(0.75)
fire:SetNextThink( CurTime() + FrameTime() )
fire:SetThinkFunction( function(pa)
if !pa then return end
local col1 = Color(150, 150, 150)
local col2 = Color(200, 200, 200)
local col3 = col1
local d = pa:GetLifeTime() / pa:GetDieTime()
col3.r = Lerp(d, col1.r, col2.r)
col3.g = Lerp(d, col1.g, col2.g)
col3.b = Lerp(d, col1.b, col2.b)
pa:SetColor(col3.r, col3.g, col3.b)
pa:SetNextThink( CurTime() + FrameTime() )
end )
end
emitter:Finish()
end
]]

View File

@@ -0,0 +1,85 @@
--[[
| 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()
ENT.Base = "arccw_uc_40mm_he"
ENT.PrintName = "40mm HE Dual Purpose Grenade"
ENT.GrenadeDamage = false
ENT.GrenadeRadius = 300
ENT.ExplosionEffect = false
ENT.Scorch = false
function ENT:DoDetonation()
local dir = self.GrenadeDir or self:GetVelocity():GetNormalized()
local attacker = IsValid(self:GetOwner()) and self:GetOwner() or self
local damage = self.GrenadeDamage or self.Damage or 0
local blastpos = self:GetPos()
local tr = util.TraceLine({
start = self:GetPos(),
endpos = self:GetPos() + dir * 64,
filter = self
})
if IsValid(tr.Entity) then
-- do impact damage
local dmg = DamageInfo()
dmg:SetAttacker(attacker)
dmg:SetInflictor(self)
dmg:SetDamage(damage * 10)
dmg:SetDamageForce(dir * 3000)
dmg:SetDamagePosition(tr.HitPos)
tr.Entity:TakeDamageInfo(dmg)
end
-- attempt to penetrate entity/world and place explosion behind
local tr2 = util.TraceLine({
start = tr.HitPos + dir * 69,
endpos = tr.HitPos,
filter = self,
})
if tr2.Hit and !tr2.StartSolid then
-- Produce a weaker blast on the pre-penetration side
util.BlastDamage(self, attacker, blastpos, self.GrenadeRadius, damage * 0.5)
blastpos = tr2.HitPos + dir * 16
self:EmitSound("physics/concrete/concrete_break2.wav", 100, 110)
local effectdata = EffectData()
effectdata:SetOrigin(self:GetPos())
effectdata:SetMagnitude(2)
effectdata:SetScale(1)
effectdata:SetRadius(2)
effectdata:SetNormal(-dir)
util.Effect("Sparks", effectdata)
effectdata:SetOrigin(blastpos)
effectdata:SetNormal(dir)
util.Effect("Sparks", effectdata)
end
local effectdata = EffectData()
effectdata:SetOrigin(blastpos)
if self:WaterLevel() >= 1 then
util.Effect("WaterSurfaceExplosion", effectdata)
self:EmitSound("weapons/underwater_explode3.wav", 125, 100, 1, CHAN_AUTO)
else
self:EmitSound(self.ExplosionSounds[math.random(1,#self.ExplosionSounds)], 125, 100, 1, CHAN_AUTO)
ParticleEffect("explosion_grenade_fas2", self:GetPos(), tr.HitNormal:Angle())
if tr2.Hit and !tr2.StartSolid then
ParticleEffect("explosion_he_m79_fas2", tr2.StartPos, tr.HitNormal:Angle() * -1)
end
if self.DebrisSounds then
self:EmitSound(self.DebrisSounds[math.random(1,#self.DebrisSounds)], 85, 100, 1, CHAN_AUTO)
end
end
util.ScreenShake(self:GetPos(),25,4,.75,self.GrenadeRadius * 4)
util.BlastDamage(self, attacker, blastpos, self.GrenadeRadius, damage)
end

View File

@@ -0,0 +1,87 @@
--[[
| 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()
ENT.Base = "arccw_uc_40mm_he"
ENT.PrintName = "40mm Dummy Grenade"
ENT.GrenadeDamage = 50
ENT.GrenadeRadius = 150
ENT.ExplosionEffect = false
ENT.Scorch = "PaintSplatBlue"
function ENT:DoDetonation()
--[[]
local effectdata = EffectData()
effectdata:SetOrigin(self:GetPos())
effectdata:SetMagnitude(4)
effectdata:SetScale(1)
effectdata:SetRadius(4)
effectdata:SetNormal(self:GetVelocity():GetNormalized())
util.Effect("Sparks", effectdata)
]]
self:EmitSound("physics/cardboard/cardboard_box_break2.wav", 80, 110)
end
function ENT:DoImpact(ent)
end
if CLIENT then
function ENT:Think()
self.NextSmoke = self.NextSmoke or CurTime()
if self.SmokeTrail and self.NextSmoke < CurTime() then
self.NextSmoke = CurTime() + 0.025 / math.Clamp(self:GetVelocity():Length() / 1000, 1, 5)
local emitter = ParticleEmitter(self:GetPos())
if not self:IsValid() or self:WaterLevel() > 2 then return end
if not IsValid(emitter) then return end
local smoke = emitter:Add("particle/smokestack", self:GetPos())
smoke:SetVelocity(VectorRand() * 2)
smoke:SetGravity(Vector(0, 0, -3))
smoke:SetDieTime(math.Rand(2, 3))
smoke:SetStartAlpha(150)
smoke:SetEndAlpha(0)
smoke:SetStartSize(math.Rand(3, 5))
smoke:SetEndSize(20)
smoke:SetRoll(math.Rand(-180, 180))
smoke:SetRollDelta(math.Rand(-0.1, 0.1))
smoke:SetColor(150, 150, math.Rand(220, 255))
smoke:SetAirResistance(5)
smoke:SetPos(self:GetPos())
smoke:SetLighting(false)
emitter:Finish()
end
end
function ENT:OnRemove()
local emitter = ParticleEmitter(self:GetPos())
if not self:IsValid() or self:WaterLevel() > 2 then return end
if not IsValid(emitter) then return end
for i = 1, 10 do
local smoke = emitter:Add("particle/smokestack", self:GetPos())
smoke:SetVelocity(VectorRand() * 100)
smoke:SetGravity(Vector(math.Rand(-5, 5), math.Rand(-5, 5), -25))
smoke:SetDieTime(math.Rand(5, 7))
smoke:SetStartAlpha(100)
smoke:SetEndAlpha(0)
smoke:SetStartSize(math.Rand(10, 15))
smoke:SetEndSize(75)
smoke:SetRoll(math.Rand(-180, 180))
smoke:SetRollDelta(math.Rand(-0.5, 0.5))
smoke:SetColor(150, 150, math.Rand(220, 255))
smoke:SetAirResistance(150)
smoke:SetPos(self:GetPos())
smoke:SetLighting(false)
smoke:SetBounce(0.5)
smoke:SetCollide(true)
end
emitter:Finish()
end
end

View File

@@ -0,0 +1,80 @@
--[[
| 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()
ENT.Base = "arccw_uc_40mm_he"
ENT.PrintName = "40mm Flash Grenade"
ENT.GrenadeDamage = 50
ENT.GrenadeRadius = 150
ENT.ExplosionEffect = false
ENT.Scorch = false
function ENT:DoDetonation()
local effectdata = EffectData()
effectdata:SetOrigin(self:GetPos())
if self:WaterLevel() >= 1 then
util.Effect("WaterSurfaceExplosion", effectdata)
self:EmitSound("weapons/underwater_explode3.wav", 125, 100, 1, CHAN_AUTO)
else
effectdata:SetMagnitude(4)
effectdata:SetScale(1)
effectdata:SetRadius(4)
effectdata:SetNormal(self:GetVelocity():GetNormalized())
util.Effect("Sparks", effectdata)
self:EmitSound("physics/metal/metal_box_break1.wav", 100, 200, 0.5)
end
-- TODO: these sounds need to be replaced (dependency)!
self:EmitSound("weapons/arccw/flashbang/flashbang_explode1.wav", 100, 100, 1, CHAN_AUTO)
self:EmitSound("weapons/arccw/flashbang/flashbang_explode1_distant.wav", 140, 100, 1, CHAN_AUTO)
util.Effect( "arccw_flashexplosion", effectdata)
local flashorigin = self:GetPos()
local flashpower = 512
local targets = ents.FindInSphere(flashorigin, flashpower)
for _, k in pairs(targets) do
if k:IsPlayer() then
local dist = k:EyePos():Distance(flashorigin)
local dp = (k:EyePos() - flashorigin):Dot(k:EyeAngles():Forward())
local time = Lerp( dp, 2.5, 0.25 )
time = Lerp( dist / flashpower, time, 0 )
if k:VisibleVec( flashorigin ) then
k:ScreenFade( SCREENFADE.IN, Color( 255, 255, 255, 255 ), 2.5, time )
end
k:SetDSP(37, false)
elseif k:IsNPC() then
k:SetNPCState(NPC_STATE_PLAYDEAD)
if timer.Exists( k:EntIndex() .. "_arccw_flashtimer" ) then
timer.Remove( k:EntIndex() .. "_arccw_flashtimer" )
end
timer.Create( k:EntIndex() .. "_arccw_flashtimer", 10, 1, function()
if !k:IsValid() then return end
k:SetNPCState(NPC_STATE_ALERT)
end)
end
end
end

View File

@@ -0,0 +1,20 @@
--[[
| 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()
ENT.Base = "arccw_uc_riflegrenade"
ENT.PrintName = "40mm HE"
ENT.GrenadeDamage = false
ENT.GrenadeRadius = 300
ENT.DragCoefficient = 0.25
ENT.Model = "models/items/ar2_grenade.mdl"

View File

@@ -0,0 +1,47 @@
--[[
| 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()
ENT.Base = "arccw_uc_40mm_he"
ENT.PrintName = "40mm High Velocity"
ENT.GrenadeDamage = false
ENT.GrenadeRadius = 200
ENT.DragCoefficient = 0
ENT.Model = "models/weapons/shell.mdl"
if CLIENT then
function ENT:Think()
if self.Ticks % 3 == 0 then
local emitter = ParticleEmitter(self:GetPos())
if not self:IsValid() or self:WaterLevel() > 2 then return end
if not IsValid(emitter) then return end
local smoke = emitter:Add("particle/particle_smokegrenade", self:GetPos())
smoke:SetVelocity(VectorRand() * 25)
smoke:SetGravity(Vector(math.Rand(-5, 5), math.Rand(-5, 5), math.Rand(-20, -25)))
smoke:SetDieTime(math.Rand(1.5, 2.0))
smoke:SetStartAlpha(255)
smoke:SetEndAlpha(0)
smoke:SetStartSize(0)
smoke:SetEndSize(60)
smoke:SetRoll(math.Rand(-180, 180))
smoke:SetRollDelta(math.Rand(-0.2, 0.2))
smoke:SetColor(100, 100, 100)
smoke:SetAirResistance(5)
smoke:SetPos(self:GetPos())
smoke:SetLighting(false)
emitter:Finish()
end
self.Ticks = self.Ticks + 1
end
end

View File

@@ -0,0 +1,42 @@
--[[
| 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()
ENT.Base = "arccw_uc_40mm_he"
ENT.PrintName = "40mm Incendiary"
ENT.GrenadeDamage = 50
ENT.GrenadeRadius = 150
function ENT:DoDetonation()
local attacker = IsValid(self:GetOwner()) and self:GetOwner() or self
util.BlastDamage(self, attacker, self:GetPos(), self.GrenadeRadius, self.GrenadeDamage)
self:EmitSound("ambient/fire/gascan_ignite1.wav", 100, 100, 1)
for i = 1, 5 do
local cloud = ents.Create("arccw_uc_fire")
cloud.FireTime = 20
if !IsValid(cloud) then return end
local vel = Vector(math.Rand(-1, 1), math.Rand(-1, 1), math.Rand(-1, 1)) * 1500
cloud.Order = i
cloud:SetPos(self:GetPos() - (self:GetVelocity() * FrameTime()) * 3 + VectorRand())
cloud:SetAbsVelocity(vel + self:GetVelocity())
cloud:SetOwner(self:GetOwner())
cloud:Spawn()
end
end
--[[att.Hook_FireBullets = function(wep, data) this thing doesn't actually call firebullets lol
wep:EmitSound("DB_ADD") -- lua/arccw/shared/sh_0_uc.lua
end]]

View File

@@ -0,0 +1,82 @@
--[[
| 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()
ENT.Base = "arccw_uc_40mm_he"
ENT.PrintName = "40mm Napalm"
ENT.GrenadeDamage = 50
ENT.GrenadeRadius = 150
ENT.ExplosionEffect = false
ENT.Scorch = false
ENT.DragCoefficient = 0.75
ENT.DetonateOnImpact = false
ENT.NextTraceTime = 0
if SERVER then
function ENT:Think()
if SERVER and CurTime() - self.SpawnTime >= self.FuseTime then
self:Detonate()
end
if self.SpawnTime + 0.2 < CurTime() and self.NextTraceTime < CurTime() then
self.NextTraceTime = CurTime() + 0.1
local dir = self:GetVelocity():GetNormalized()
local tr = util.TraceHull({
start = self:GetPos(),
endpos = self:GetPos() + dir * 512,
filter = self,
mins = Vector(-16, -16, -8),
maxs = Vector(16, 16, 8)
})
if tr.Hit then
self:Detonate()
end
end
end
end
function ENT:DoDetonation()
local effectdata = EffectData()
effectdata:SetOrigin(self:GetPos())
if self:WaterLevel() >= 1 then
util.Effect("WaterSurfaceExplosion", effectdata)
self:EmitSound("weapons/underwater_explode3.wav", 125, 100, 1, CHAN_AUTO)
else
effectdata:SetMagnitude(4)
effectdata:SetScale(1)
effectdata:SetRadius(4)
effectdata:SetNormal(self:GetVelocity():GetNormalized())
util.Effect("Sparks", effectdata)
self:EmitSound("physics/metal/metal_box_break1.wav", 100, 200)
self:EmitSound("ambient/fire/gascan_ignite1.wav", 100, 100, 0.75)
end
for i = 1, math.random(5, 7) do
local cloud = ents.Create("arccw_uc_napalm")
cloud.FireTime = math.Rand(20, 40)
if !IsValid(cloud) then return end
local vel = VectorRand() * 500
cloud.Order = i
cloud:SetPos(self:GetPos() - (self:GetVelocity() * FrameTime()) + VectorRand())
--cloud:SetAbsVelocity(vel + self:GetVelocity())
cloud:SetOwner(self:GetOwner())
cloud:Spawn()
cloud:GetPhysicsObject():SetVelocityInstantaneous(self:GetVelocity() + vel)
end
end

View File

@@ -0,0 +1,48 @@
--[[
| 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()
ENT.Base = "arccw_uc_40mm_he"
ENT.PrintName = "40mm Smoke Grenade"
ENT.GrenadeDamage = 50
ENT.GrenadeRadius = 150
ENT.ExplosionEffect = false
ENT.Scorch = false
function ENT:DoDetonation()
local effectdata = EffectData()
effectdata:SetOrigin(self:GetPos())
if self:WaterLevel() >= 1 then
util.Effect("WaterSurfaceExplosion", effectdata)
self:EmitSound("weapons/underwater_explode3.wav", 125, 100, 1, CHAN_AUTO)
else
effectdata:SetMagnitude(4)
effectdata:SetScale(1)
effectdata:SetRadius(4)
effectdata:SetNormal(self:GetVelocity():GetNormalized())
util.Effect("Sparks", effectdata)
self:EmitSound("physics/metal/metal_box_break1.wav", 100, 200, 0.5)
end
-- TODO: these sounds need to be replaced (dependency)!
self:EmitSound("weapons/arccw/smokegrenade/smoke_emit.wav", 90, 100, 1, CHAN_AUTO)
local cloud = ents.Create( "arccw_smoke" )
if !IsValid(cloud) then return end
cloud:SetPos(self:GetPos())
cloud:Spawn()
end

View File

@@ -0,0 +1,205 @@
--[[
| 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()
ENT.Type = "anim"
ENT.Base = "base_entity"
ENT.PrintName = "Fire Particle"
ENT.Author = ""
ENT.Information = ""
ENT.Spawnable = false
ENT.AdminSpawnable = false
ENT.Model = "models/Items/AR2_Grenade.mdl"
ENT.FireTime = 20
ENT.Armed = false
ENT.NextDamageTick = 0
ENT.Ticks = 0
ENT.ArcCW_Killable = false
function ENT:Initialize()
if SERVER then
self:SetModel( self.Model )
self:SetMoveType( MOVETYPE_VPHYSICS )
self:SetSolid( SOLID_VPHYSICS )
local maxs = Vector(1, 1, 1)
local mins = -maxs
self:PhysicsInitBox(mins, maxs)
self:DrawShadow( false )
local phys = self:GetPhysicsObject()
if phys:IsValid() then
phys:Wake()
phys:SetBuoyancyRatio(0)
end
self.SpawnTime = CurTime()
self:Detonate()
self.FireTime = math.Rand(self.FireTime - 1, self.FireTime + 1)
end
end
local fired = {
"sprites/flamelet1",
"sprites/flamelet2",
"sprites/flamelet3",
"sprites/flamelet4",
"sprites/flamelet5",
}
local function GetFireParticle()
return fired[math.random(#fired)]
end
function ENT:Think()
if !self.SpawnTime then self.SpawnTime = CurTime() end
if CLIENT then
local emitter = ParticleEmitter(self:GetPos())
if !self:IsValid() or self:WaterLevel() > 2 then return end
if !IsValid(emitter) then return end
if math.random(1, 100) < 10 then
local fire = emitter:Add(GetFireParticle(), self:GetPos() + (VectorRand() * 16))
fire:SetVelocity( VectorRand() * 500 * VectorRand() )
fire:SetGravity( Vector(0, 0, 100) )
fire:SetDieTime( math.Rand(0.5, 0.75) )
fire:SetStartAlpha( 255 )
fire:SetEndAlpha( 0 )
fire:SetStartSize( 25 )
fire:SetEndSize( 100 )
fire:SetRoll( math.Rand(-180, 180) )
fire:SetRollDelta( math.Rand(-0.2,0.2) )
fire:SetColor( 255, 255, 255 )
fire:SetAirResistance( 150 )
fire:SetPos( self:GetPos() )
fire:SetLighting( false )
fire:SetCollide(true)
fire:SetBounce(0.75)
fire:SetNextThink( CurTime() + FrameTime() )
fire:SetThinkFunction( function(pa)
if !pa then return end
local col1 = Color(255, 255, 255)
local col2 = Color(0, 0, 0)
local col3 = col1
local d = pa:GetLifeTime() / pa:GetDieTime()
col3.r = Lerp(d, col1.r, col2.r)
col3.g = Lerp(d, col1.g, col2.g)
col3.b = Lerp(d, col1.b, col2.b)
pa:SetColor(col3.r, col3.g, col3.b)
pa:SetNextThink( CurTime() + FrameTime() )
end )
end
if math.random(1, 100) < 5 then
local fire = emitter:Add("particles/smokey", self:GetPos())
fire:SetVelocity( VectorRand() * 25 )
fire:SetGravity( Vector(0, 0, 1500) )
fire:SetDieTime( math.Rand(0.25, 1) )
fire:SetStartAlpha( 255 )
fire:SetEndAlpha( 0 )
fire:SetStartSize( 10 )
fire:SetEndSize( 150 )
fire:SetRoll( math.Rand(-180, 180) )
fire:SetRollDelta( math.Rand(-0.2,0.2) )
fire:SetColor( 255, 255, 255 )
fire:SetAirResistance( 150 )
fire:SetPos( self:GetPos() )
fire:SetLighting( false )
fire:SetCollide(true)
fire:SetBounce(0.75)
fire:SetNextThink( CurTime() + FrameTime() )
fire:SetThinkFunction( function(pa)
if !pa then return end
local col1 = Color(255, 135, 0)
local col2 = Color(150, 150, 150)
local col3 = col1
local d = pa:GetLifeTime() / pa:GetDieTime()
col3.r = Lerp(d, col1.r, col2.r)
col3.g = Lerp(d, col1.g, col2.g)
col3.b = Lerp(d, col1.b, col2.b)
pa:SetColor(col3.r, col3.g, col3.b)
pa:SetNextThink( CurTime() + FrameTime() )
end )
end
emitter:Finish()
self.Ticks = self.Ticks + 1
else
if self:GetVelocity():LengthSqr() <= 32 then
self:SetMoveType( MOVETYPE_NONE )
end
if self.NextDamageTick > CurTime() then return end
if self:WaterLevel() > 2 then self:Remove() return end
local dmg = DamageInfo()
dmg:SetDamageType(DMG_BURN)
dmg:SetDamage(math.Round(math.random() * 2 + 3))
dmg:SetInflictor(self)
dmg:SetAttacker(self:GetOwner())
util.BlastDamageInfo(dmg, self:GetPos(), 200)
self.NextDamageTick = CurTime() + 0.15
if self.SpawnTime + self.FireTime <= CurTime() then self:Remove() return end
end
end
function ENT:OnRemove()
if !self.FireSound then return end
self.FireSound:Stop()
end
function ENT:Detonate()
if !self:IsValid() then return end
self.Armed = true
if self.Order and self.Order != 1 then return end
self.FireSound = CreateSound(self, "arccw_go/molotov/fire_loop_1.wav")
self.FireSound:Play()
self.FireSound:ChangePitch(80, self.FireTime)
timer.Simple(self.FireTime - 1, function()
if !IsValid(self) then return end
self.FireSound:ChangeVolume(0, 1)
end)
timer.Simple(self.FireTime, function()
if !IsValid(self) then return end
self:Remove()
end)
end
function ENT:Draw()
-- cam.Start3D() -- Start the 3D function so we can draw onto the screen.
-- render.SetMaterial( GetFireParticle() ) -- Tell render what material we want, in this case the flash from the gravgun
-- render.DrawSprite( self:GetPos(), math.random(200, 250), math.random(200, 250), Color(255, 255, 255) ) -- Draw the sprite in the middle of the map, at 16x16 in it's original colour with full alpha.
-- cam.End3D()
end

View File

@@ -0,0 +1,263 @@
--[[
| 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()
ENT.Type = "anim"
ENT.Base = "base_entity"
ENT.PrintName = "Fire Particle"
ENT.Author = ""
ENT.Information = ""
ENT.Spawnable = false
ENT.AdminSpawnable = false
ENT.Model = "models/Items/AR2_Grenade.mdl"
ENT.FireTime = 30
ENT.CollisionGroup = COLLISION_GROUP_PROJECTILE
ENT.Armed = false
ENT.NextDamageTick = 0
ENT.NextStickTick = 0
ENT.Ticks = 0
ENT.ArcCW_Killable = false
function ENT:Initialize()
if SERVER then
self:SetModel( self.Model )
self:SetMoveType( MOVETYPE_VPHYSICS )
self:SetSolid( SOLID_VPHYSICS )
local maxs = Vector(1, 1, 1)
local mins = -maxs
self:PhysicsInitBox(mins, maxs)
self:DrawShadow( false )
local phys = self:GetPhysicsObject()
if phys:IsValid() then
phys:Wake()
phys:SetBuoyancyRatio(0)
end
self.SpawnTime = CurTime()
self:Detonate()
self.FireTime = math.Rand(self.FireTime - 1, self.FireTime + 1)
end
end
local fired = {
"sprites/flamelet1",
"sprites/flamelet2",
"sprites/flamelet3",
"sprites/flamelet4",
"sprites/flamelet5",
}
local function GetFireParticle()
return fired[math.random(#fired)]
end
function ENT:Think()
if !self.SpawnTime then self.SpawnTime = CurTime() end
if CLIENT then
local emitter = ParticleEmitter(self:GetPos())
if !self:IsValid() or self:WaterLevel() > 2 then return end
if !IsValid(emitter) then return end
if math.random(1, 100) < 10 then
local fire = emitter:Add(GetFireParticle(), self:GetPos() + (VectorRand() * 16))
fire:SetVelocity( VectorRand() * 500 * VectorRand() )
fire:SetGravity( Vector(0, 0, 100) )
fire:SetDieTime( math.Rand(0.5, 0.75) )
fire:SetStartAlpha( 255 )
fire:SetEndAlpha( 0 )
fire:SetStartSize( 15 )
fire:SetEndSize( 100 )
fire:SetRoll( math.Rand(-180, 180) )
fire:SetRollDelta( math.Rand(-0.2,0.2) )
fire:SetColor( 255, 255, 255 )
fire:SetAirResistance( 150 )
fire:SetPos( self:GetPos() )
fire:SetLighting( false )
fire:SetCollide(true)
fire:SetBounce(0.75)
fire:SetNextThink( CurTime() + FrameTime() )
fire:SetThinkFunction( function(pa)
if !pa then return end
local col1 = Color(255, 255, 175)
local col2 = Color(0, 0, 0)
local col3 = col1
local d = pa:GetLifeTime() / pa:GetDieTime()
col3.r = Lerp(d, col1.r, col2.r)
col3.g = Lerp(d, col1.g, col2.g)
col3.b = Lerp(d, col1.b, col2.b)
pa:SetColor(col3.r, col3.g, col3.b)
pa:SetNextThink( CurTime() + FrameTime() )
end )
end
if math.random(1, 100) < 15 then
local fire = emitter:Add("particles/smokey", self:GetPos())
fire:SetVelocity( VectorRand() * 100 )
fire:SetGravity( Vector(0, 0, 1000) )
fire:SetDieTime( math.Rand(0.5, 2) )
fire:SetStartAlpha( 200 )
fire:SetEndAlpha( 0 )
fire:SetStartSize( 10 )
fire:SetEndSize( 128 )
fire:SetRoll( math.Rand(-180, 180) )
fire:SetRollDelta( math.Rand(-0.2,0.2) )
fire:SetColor( 255, 255, 255 )
fire:SetAirResistance( 150 )
fire:SetPos( self:GetPos() )
fire:SetLighting( false )
fire:SetCollide(true)
fire:SetBounce(0.75)
fire:SetNextThink( CurTime() + FrameTime() )
fire:SetThinkFunction( function(pa)
if !pa then return end
local col1 = Color(150, 75, 0)
local col2 = Color(50, 50, 50)
local col3 = col1
local d = pa:GetLifeTime() / pa:GetDieTime()
col3.r = Lerp(d, col1.r, col2.r)
col3.g = Lerp(d, col1.g, col2.g)
col3.b = Lerp(d, col1.b, col2.b)
pa:SetColor(col3.r, col3.g, col3.b)
pa:SetNextThink( CurTime() + FrameTime() )
end )
end
emitter:Finish()
self.Ticks = self.Ticks + 1
else
if self.NextDamageTick > CurTime() then return end
if self.Stuck and (!IsValid(self:GetParent()) or (self:GetParent():IsPlayer() and !self:GetParent():Alive())) then
self:SetParent(NULL)
self:SetMoveType( MOVETYPE_VPHYSICS )
self:SetSolid( SOLID_VPHYSICS )
self.Stuck = false
local maxs = Vector(1, 1, 1)
local mins = -maxs
self:PhysicsInitBox(mins, maxs)
local phys = self:GetPhysicsObject()
if phys:IsValid() then
phys:Wake()
end
return
end
local dmg = DamageInfo()
dmg:SetDamageType(DMG_BURN)
dmg:SetDamage(math.random() * 2 + 1)
dmg:SetInflictor(self)
dmg:SetAttacker(self:GetOwner())
if self:WaterLevel() > 2 then
if math.random() <= 0.075 then self:Remove() return end
dmg:SetDamage(1)
end
util.BlastDamageInfo(dmg, self:GetPos(), 150)
self.NextDamageTick = CurTime() + 0.15
if !self.Stuck and self.NextStickTick < CurTime() then
self.NextStickTick = CurTime() + 0.5
if math.random() <= 0.25 then
for _, e in pairs(ents.FindInSphere(self:GetPos(), 96)) do
if e:IsNPC() or e:IsNextBot() or e:IsVehicle() or (e:IsPlayer() and e:Alive()) or (simfphys and simfphys.IsCar(e)) then
self.Stuck = true
timer.Simple(0, function()
-- we commit a mild amount of war crimes
self:SetSolid(SOLID_NONE)
self:SetMoveType(MOVETYPE_NONE)
self:SetParent(e)
local min, max = e:WorldSpaceAABB()
self:SetPos(min + (max - min) * math.random())
end)
break
end
end
end
end
if self.SpawnTime + self.FireTime <= CurTime() then self:Remove() return end
end
end
function ENT:OnRemove()
if !self.FireSound then return end
self.FireSound:Stop()
end
function ENT:Detonate()
if !self:IsValid() then return end
self.Armed = true
if self.Order and self.Order != 1 then return end
self.FireSound = CreateSound(self, "arccw_go/molotov/fire_loop_1.wav")
self.FireSound:Play()
self.FireSound:ChangePitch(80, self.FireTime)
timer.Simple(self.FireTime - 1, function()
if !IsValid(self) then return end
self.FireSound:ChangeVolume(0, 1)
end)
timer.Simple(self.FireTime, function()
if !IsValid(self) then return end
self:Remove()
end)
end
function ENT:PhysicsCollide(data, physobj)
if self.Stuck then return end
local tgt = data.HitEntity
if !tgt:IsWorld() then
timer.Simple(0, function()
-- we commit a mild amount of war crimes
self:SetSolid(SOLID_NONE)
self:SetMoveType(MOVETYPE_NONE)
self:SetParent(tgt)
end)
self.Stuck = true
else
timer.Simple(0, function()
-- we commit a mild amount of war crimes
self:SetSolid(SOLID_NONE)
self:SetMoveType(MOVETYPE_NONE)
end)
end
end
function ENT:Draw()
-- cam.Start3D() -- Start the 3D function so we can draw onto the screen.
-- render.SetMaterial( GetFireParticle() ) -- Tell render what material we want, in this case the flash from the gravgun
-- render.DrawSprite( self:GetPos(), math.random(200, 250), math.random(200, 250), Color(255, 255, 255) ) -- Draw the sprite in the middle of the map, at 16x16 in it's original colour with full alpha.
-- cam.End3D()
end

View File

@@ -0,0 +1,188 @@
--[[
| 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()
ENT.Type = "anim"
ENT.Base = "base_entity"
ENT.PrintName = "Base Rifle Grenade"
ENT.Author = ""
ENT.Information = ""
ENT.Spawnable = false
ENT.Ticks = 0
ENT.CollisionGroup = COLLISION_GROUP_PROJECTILE
-- Intentionally not ENT.Damage since ArcCW base overwrites it with weapon damage (for some reason)
ENT.GrenadeDamage = false
ENT.GrenadeRadius = 0
ENT.FuseTime = 10
ENT.DragCoefficient = 1
ENT.DetonateOnImpact = true
ENT.Model = "models/items/ar2_grenade.mdl"
ENT.ExplosionEffect = true
ENT.Scorch = "Scorch"
ENT.SmokeTrail = true
local path = "arccw_uc/common/"
local path1 = "arccw_uc/common/"
ENT.ExplosionSounds = {path .. "explosion-close-01.ogg", path .. "explosion-close-02.ogg"}
ENT.DebrisSounds = {path1 .. "debris-01.ogg", path1 .. "debris-02.ogg", path1 .. "debris-03.ogg", path1 .. "debris-04.ogg", path1 .. "debris-05.ogg"}
if SERVER then
function ENT:Initialize()
local pb_vert = 1
local pb_hor = 1
self:SetModel(self.Model)
self:PhysicsInitBox(Vector(-pb_vert, -pb_hor, -pb_hor), Vector(pb_vert, pb_hor, pb_hor))
local phys = self:GetPhysicsObject()
if phys:IsValid() then
phys:Wake()
phys:SetDragCoefficient(self.DragCoefficient)
phys:SetBuoyancyRatio(0.1)
end
self.SpawnTime = CurTime()
end
function ENT:Think()
if SERVER and CurTime() - self.SpawnTime >= self.FuseTime then
self:Detonate()
end
end
else
function ENT:Think()
if self.SmokeTrail then
if self.Ticks % 5 == 0 then
local emitter = ParticleEmitter(self:GetPos())
if not self:IsValid() or self:WaterLevel() > 2 then return end
if not IsValid(emitter) then return end
local smoke = emitter:Add("particle/particle_smokegrenade", self:GetPos())
smoke:SetVelocity(VectorRand() * 25)
smoke:SetGravity(Vector(math.Rand(-5, 5), math.Rand(-5, 5), math.Rand(-20, -25)))
smoke:SetDieTime(math.Rand(1.5, 2.0))
smoke:SetStartAlpha(255)
smoke:SetEndAlpha(0)
smoke:SetStartSize(0)
smoke:SetEndSize(100)
smoke:SetRoll(math.Rand(-180, 180))
smoke:SetRollDelta(math.Rand(-0.2, 0.2))
smoke:SetColor(20, 20, 20)
smoke:SetAirResistance(5)
smoke:SetPos(self:GetPos())
smoke:SetLighting(false)
emitter:Finish()
end
self.Ticks = self.Ticks + 1
end
end
end
-- overwrite to do special explosion things
function ENT:DoDetonation()
local attacker = IsValid(self:GetOwner()) and self:GetOwner() or self
util.BlastDamage(self, attacker, self:GetPos(), self.GrenadeRadius, self.GrenadeDamage or self.Damage or 0)
end
function ENT:DoImpact(ent)
local attacker = IsValid(self:GetOwner()) and self:GetOwner() or self
local dmg = DamageInfo()
dmg:SetAttacker(attacker)
dmg:SetInflictor(self)
dmg:SetDamage(100)
dmg:SetDamageType(DMG_CRUSH)
dmg:SetDamageForce(self.GrenadeDir * 5000)
dmg:SetDamagePosition(self:GetPos())
ent:TakeDamageInfo(dmg)
end
function ENT:Detonate()
if not self:IsValid() or self.BOOM then return end
self.BOOM = true
if self.ExplosionEffect then
local effectdata = EffectData()
effectdata:SetOrigin(self:GetPos())
if self:WaterLevel() >= 1 then
util.Effect("WaterSurfaceExplosion", effectdata)
self:EmitSound("weapons/underwater_explode3.wav", 125, 100, 1, CHAN_AUTO)
else
-- util.Effect("Explosion", effectdata)
-- explosion_HE_m79_fas2
-- explosion_he_grenade_fas2
-- explosion_HE_claymore_fas2
-- explosion_grenade_fas2
self:EmitSound(self.ExplosionSounds[math.random(1,#self.ExplosionSounds)], 125, 100, 1, CHAN_AUTO)
ParticleEffect("explosion_HE_m79_fas2", self:GetPos(), Angle(-90, 0, 0))
--self:EmitSound("phx/kaboom.wav", 125, 100, 1, CHAN_AUTO)
-- Where is the sound zenith ? ???
end
util.ScreenShake(self:GetPos(), 25, 4, 0.75, self.GrenadeRadius * 4)
if self.GrenadePos == nil then
self.GrenadePos = self:GetPos()
end
if self.GrenadeDir == nil then
self.GrenadeDir = self:GetVelocity():GetNormalized()
end
local trace = util.TraceLine({
start = self.GrenadePos,
endpos = self.GrenadePos + self.GrenadeDir * 4,
mask = MASK_SOLID_BRUSHONLY
})
if trace.Hit then
self:EmitSound(self.DebrisSounds[math.random(1,#self.DebrisSounds)], 85, 100, 1, CHAN_AUTO)
end
end
self:DoDetonation()
if self.Scorch then
util.Decal(self.Scorch, self.GrenadePos, self.GrenadePos + self.GrenadeDir * 4, self)
end
self:Remove()
end
function ENT:PhysicsCollide(colData, collider)
self.GrenadeDir = colData.OurOldVelocity:GetNormalized()
self.GrenadePos = colData.HitPos
self:DoImpact(colData.HitEntity)
if self.DetonateOnImpact then
self:Detonate()
else
local effectdata = EffectData()
effectdata:SetOrigin(self:GetPos())
effectdata:SetMagnitude(2)
effectdata:SetScale(1)
effectdata:SetRadius(2)
effectdata:SetNormal(self.GrenadeDir)
util.Effect("Sparks", effectdata)
self:EmitSound("weapons/rpg/shotdown.wav", 100, 150)
self:Remove()
end
end
function ENT:Draw()
self:DrawModel()
end

121
lua/entities/combofunc.lua Normal file
View File

@@ -0,0 +1,121 @@
--[[
| 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.HasMeleeAttack = false -- Turned off so that we can use the combo system instead
ENT.AnimTbl_MeleeAttack = nil
ENT.TimeUntilMeleeAttackDamage = false
ENT.ComboStrings = { -- Combos will trigger in the order the animations are placed, 1>2>3 or 2>1>3
{
--"attack1",
--"attack2",
--"attack3",
},
{
--"attack5",
--"attack2",
},
{
--"attack1",
--"attack2",
--"attack6",
},
{
--"attack6",
--"attack7",
},
{
--"attack6",
--"attack8",
},
{
--"attack16",
--"attack15",
--"attack12",
},
}
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnInitialize()
self.CurrentStringTable = {}
self.StringCount = 0
self.CurrentStringNum = 0
self.CurrentStringAnim = nil
self.Attacking = false
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnAcceptInput(key,activator,caller,data)
if key == "JumpTable(24)" then -- Combo event, used to initiate a combo if we can
self:CheckCanContinueString()
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CustomOnThink()
local ent = self:GetEnemy()
local dist = self.NearestPointToEnemyDistance
local cont = self.VJ_TheController
local key_atk = IsValid(cont) && cont:KeyDown(IN_ATTACK)
if IsValid(ent) && !self:IsBusy() then
if key_atk or !IsValid(cont) && dist <= self.MeleeAttackDistance && !self.Attacking && self:CheckCanSee(ent,55) then
self:Attack()
end
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:Attack()
if !self.CanMeleeAttack then return end
if self.Attacking then return end
if self:IsBusy() then return end
for k,v in RandomPairs(self.ComboStrings) do
self:PlayString(true,v)
break
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:PlayString(init,tbl)
if !isstring(tbl[1]) then
tbl[1](self)
return
end
self.Attacking = true
if init then
self.CurrentStringTable = tbl
self.StringCount = #tbl
self.CurrentStringNum = 1
else
self.CurrentStringNum = self.CurrentStringNum +1
end
self.CurrentStringAnim = tbl[self.CurrentStringNum]
self:VJ_ACT_PLAYACTIVITY(self.CurrentStringAnim,true,false,false)
if self.CurrentStringNum == self.StringCount then
self.Attacking = false
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CheckContinueString()
if self.CurrentStringNum +1 <= self.StringCount then
self.vACT_StopAttacks = false
self:PlayString(false,self.CurrentStringTable)
else
self.Attacking = false
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CheckCanContinueString()
if (self.VJ_IsBeingControlled && self.VJ_TheController:KeyDown(IN_ATTACK)) or !self.VJ_IsBeingControlled && IsValid(self:GetEnemy()) && self:GetEnemy():GetPos():Distance(self:GetPos()) <= 240 && self:CheckCanSee(self:GetEnemy(),55) then
self:CheckContinueString()
else
self.Attacking = false
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CheckCanSee(ent,cone)
return (self:GetSightDirection():Dot((ent:GetPos() -self:GetPos()):GetNormalized()) > math.cos(math.rad(cone)))
end

View File

@@ -0,0 +1,99 @@
--[[
| 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.IsDodging = false
ENT.NextDodgeTime = 0
function ENT:DoDodge(dist, chance, dodgeleft, dodgeright, dodgeback, dodgeforward)
if !self.Dead && !self.MeleeAttacking && !self.ComboAttacking then
if self.VJ_IsBeingControlled then
self:StopAttacks(true)
if self.VJ_TheController:KeyDown(IN_MOVELEFT) && self.VJ_TheController:KeyDown(IN_JUMP) && CurTime() > self.NextDodgeTime then
self.IsDodging = true
self:VJ_ACT_PLAYACTIVITY(dodgeleft,true,false,true) -- Left dodge anim
self.NextDodgeTime = CurTime() + VJ_GetSequenceDuration(self,self:GetSequenceName(self:GetSequence()))+0.2
timer.Simple(VJ_GetSequenceDuration(self,self:GetSequenceName(self:GetSequence())), function() self.IsDodging = false end)
elseif self.VJ_TheController:KeyDown(IN_MOVERIGHT) && self.VJ_TheController:KeyDown(IN_JUMP) && CurTime() > self.NextDodgeTime then
self.IsDodging = true
self:VJ_ACT_PLAYACTIVITY(dodgeright,true,false,true) -- Left dodge anim
self.NextDodgeTime = CurTime() + VJ_GetSequenceDuration(self,self:GetSequenceName(self:GetSequence()))+0.2
timer.Simple(VJ_GetSequenceDuration(self,self:GetSequenceName(self:GetSequence())), function() self.IsDodging = false end)
elseif self.VJ_TheController:KeyDown(IN_BACK) && self.VJ_TheController:KeyDown(IN_JUMP) && CurTime() > self.NextDodgeTime then
self.IsDodging = true
self:VJ_ACT_PLAYACTIVITY(dodgeback,true,false,true) -- Left dodge anim
self.NextDodgeTime = CurTime() + VJ_GetSequenceDuration(self,self:GetSequenceName(self:GetSequence()))+0.2
timer.Simple(VJ_GetSequenceDuration(self,self:GetSequenceName(self:GetSequence())), function() self.IsDodging = false end)
elseif self.VJ_TheController:KeyDown(IN_FORWARD) && self.VJ_TheController:KeyDown(IN_JUMP) && CurTime() > self.NextDodgeTime then
self.IsDodging = true
self:VJ_ACT_PLAYACTIVITY(dodgeforward,true,false,true) -- Left dodge anim
self.NextDodgeTime = CurTime() + VJ_GetSequenceDuration(self,self:GetSequenceName(self:GetSequence()))+0.2
timer.Simple(VJ_GetSequenceDuration(self,self:GetSequenceName(self:GetSequence())), function() self.IsDodging = false end)
end
end
if self:GetEnemy() != nil && !self.VJ_IsBeingControlled && CurTime() > self.NextDodgeTime then
local EnemyDistance = self:VJ_GetNearestPointToEntityDistance(self:GetEnemy(),self:GetPos():Distance(self:GetEnemy():GetPos()))
if EnemyDistance <= dist && math.random(1,chance) == 1 && (self:CanDodge("normal") or self:CanDodge("player")) then -- Random movement
self:StopAttacks(true)
self.IsDodging = true
local dodge_close = math.random(1, 3)
if dodge_close == 1 then
self:VJ_ACT_PLAYACTIVITY(dodgeleft,true,false,true) -- Left dodge anim
elseif dodge_close == 2 then
self:VJ_ACT_PLAYACTIVITY(dodgeright,true,false,true) -- Right dodge anim
elseif dodge_close == 3 then
self:VJ_ACT_PLAYACTIVITY(dodgeback,true,false,true) -- Right dodge anim
end
self.NextDodgeTime = CurTime() + VJ_GetSequenceDuration(self,self:GetSequenceName(self:GetSequence()))+0.2
timer.Simple(VJ_GetSequenceDuration(self,self:GetSequenceName(self:GetSequence())), function() self.IsDodging = false end)
end
end
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:CanDodge(dodgetype)
if dodgetype == "normal" then
if !self.RangeAttacking && !self.MeleeAttacking && self:GetEnemy():IsNPC()
&& ((self:GetEnemy().MeleeAttacking && self:GetEnemy().MeleeAttacking)
or (self:GetEnemy().IsAttacking && self:GetEnemy().IsAttacking)) then
return true
else
return false
end
elseif dodgetype == "player" then
if !self.RangeAttacking && !self.MeleeAttacking && self:GetEnemy():IsPlayer()
&& self:GetEnemy():GetEyeTrace().Entity == self && self:GetEnemy():IsPlayer() && self:GetEnemy():GetActiveWeapon() != nil
&& self.AcceptableWeaponsTbl[self:GetEnemy():GetActiveWeapon():GetClass()]
&& (self:GetEnemy():KeyPressed(IN_ATTACK) or self:GetEnemy():KeyPressed(IN_ATTACK2)
or self:GetEnemy():KeyReleased(IN_ATTACK) or self:GetEnemy():KeyReleased(IN_ATTACK2)
or self:GetEnemy():KeyDown(IN_ATTACK) or self:GetEnemy():KeyDown(IN_ATTACK2)) then
return true
else
return false
end
end
end
---------------------------------------------------------------------------------------------------------------------------------------------
function ENT:FindSeq(seq)
return self:GetSequenceActivity(self:LookupSequence(seq))
end
---------------------------------------------------------------------------------------------------------------------------------------------
ENT.AcceptableWeaponsTbl = {
["gmod_camera"]=true,
["gmod_tool"]=true,
["weapon_physgun"]=true,
["weapon_physcannon"]=true
}

View File

@@ -0,0 +1,26 @@
--[[
| 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()
ENT.Base = "arccw_ammo"
ENT.PrintName = "5.56x45mm HP"
ENT.Category = "ArcCW - EFT Ammo"
ENT.Spawnable = true
ENT.Model = "models/entities/eft_ammo/eft_ammo_556_warmage.mdl"
ENT.AmmoType = "556_hp"
ENT.AmmoCount = 60
ENT.DetonationDamage = 10
ENT.DetonationRadius = 256
ENT.DetonationSound = "weapons/pistol/pistol_fire3.wav"

View File

@@ -0,0 +1,26 @@
--[[
| 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()
ENT.Base = "arccw_ammo"
ENT.PrintName = "5.56x45mm M855"
ENT.Category = "ArcCW - EFT Ammo"
ENT.Spawnable = true
ENT.Model = "models/entities/eft_ammo/eft_ammo_556_warmage.mdl"
ENT.AmmoType = "556_M855"
ENT.AmmoCount = 60
ENT.DetonationDamage = 10
ENT.DetonationRadius = 256
ENT.DetonationSound = "weapons/pistol/pistol_fire3.wav"

View File

@@ -0,0 +1,26 @@
--[[
| 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()
ENT.Base = "arccw_ammo"
ENT.PrintName = "5.56x45mm M995"
ENT.Category = "ArcCW - EFT Ammo"
ENT.Spawnable = true
ENT.Model = "models/entities/eft_ammo/eft_ammo_556_warmage.mdl"
ENT.AmmoType = "556_M995"
ENT.AmmoCount = 60
ENT.DetonationDamage = 10
ENT.DetonationRadius = 256
ENT.DetonationSound = "weapons/pistol/pistol_fire3.wav"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,684 @@
--[[
| 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/
--]]
if SERVER then
AddCSLuaFile()
end
ENT.Type = "anim"
ENT.Model = Model("models/weapons/w_slam.mdl")
ENT.Base = "ent_chess_board"
ENT.Models = {
["board"] = Model("models/props_phx/games/chess/board.mdl"),
["table"] = Model("models/props/de_tides/restaurant_table.mdl"),
["hl2table"] = Model( "models/props_c17/furnituretable001a.mdl" ),
["dama"] = Model("models/props_phx/games/chess/white_pawn.mdl"),
["WhiteMan"] = Model("models/props_phx/games/chess/white_dama.mdl"), ["BlackMan"] = Model("models/props_phx/games/chess/black_dama.mdl"),
["WhiteKing"] = Model("models/props_phx/games/chess/white_dama.mdl"), ["BlackKing"] = Model("models/props_phx/games/chess/black_dama.mdl"),
}
ENT.Characters = { -- Notepad's being weird and not showing these characters. If you can't see them, they'll still show up in-game.
["WhiteMan"] = Model(""), ["BlackMan"] = Model(""),
["WhiteKing"] = Model(""), ["BlackKing"] = Model(""),
}
ENT.DrawDouble = {
["King"] = true,
}
ENT.PrintName = "Draughts/Checkers"
ENT.Author = "my_hat_stinks"
ENT.Information = "A draughts (checkers) board"
ENT.Category = "Game boards"
ENT.Game = "Draughts"
ENT.Spawnable = true
ENT.AdminOnly = true
ENT.AdminSpawnable = true
--Status
local CHESS_INACTIVE = 0
local CHESS_WHITEMOVE = 1
local CHESS_BLACKMOVE = 2
local CHESS_WHITEPROMO = 3 local CHESS_WHITEJUMP = 3
local CHESS_BLACKPROMO = 4 local CHESS_BLACKJUMP = 4
local CHESS_WAGER = 5
--Captured piece squares
local CHESS_WCAP1 = 10
local CHESS_WCAP2 = 11
local CHESS_BCAP1 = 12
local CHESS_BCAP2 = 13
-- Draw Offer
local PLAYER_NONE = 0 -- Nobody offering to draw
local PLAYER_WHITE = 1 -- White offering to draw
local PLAYER_BLACK = 2 -- Black offering to draw
local NumToLetter = {"a", "b", "c", "d", "e", "f", "g", "h", ["a"]=1, ["b"]=2, ["c"]=3, ["d"]=4, ["e"]=5, ["f"]=6, ["g"]=7, ["h"]=8} --Used extensively for conversions
ENT.StartState = CHESS_BLACKMOVE
function ENT:SetupDataTables()
-- self:NetworkVar( "Int", 0, "BlackPassant" )
-- self:NetworkVar( "Int", 1, "WhitePassant" )
self:NetworkVar( "Int", 2, "ChessState" )
self:NetworkVar( "Bool", 0, "Playing" )
self:NetworkVar( "Int", 3, "DrawOffer" )
self:NetworkVar( "Float", 0, "WhiteWager" )
self:NetworkVar( "Float", 1, "BlackWager" )
self:NetworkVar( "Entity", 0, "WhitePlayer" )
self:NetworkVar( "Entity", 1, "BlackPlayer" )
self:NetworkVar( "Entity", 2, "TableEnt" )
-- self:NetworkVar( "Int", 3, "MoveCount" )
-- self:NetworkVar( "Bool", 1, "Repetition" )
self:NetworkVar( "Bool", 2, "PSWager" )
self:NetworkVar( "Float", 2, "WhiteTime" )
self:NetworkVar( "Float", 3, "BlackTime" )
--Draughts vars
self:NetworkVar( "Bool", 1, "JumpMove" )
self:NetworkVar( "Int", 0, "JumpLet" )
self:NetworkVar( "Int", 1, "JumpNum" )
end
function ENT:Initialize()
self.ChessDerived = true
self.IsDraughts = true
return self.BaseClass.Initialize( self )
end
function ENT:GetManMoves( tbl, GridLet, GridNum, IsWhite )
local CapMove = false
--Forward Right
local TargetRow = GridNum+ (IsWhite and 1 or (-1))
local TargetColumn = NumToLetter[GridLet]+1
if TargetRow<=8 and TargetRow>=1 and TargetColumn<=8 and TargetColumn>=1 then
local target = self:GetSquare( NumToLetter[TargetColumn], TargetRow )
if target then
if ((self:SquareTeam(target)=="White")~=IsWhite) then --Enemy piece
local CapRow = TargetRow+ (IsWhite and 1 or (-1))
local CapCol = TargetColumn+1
if CapRow<=8 and CapRow>=1 and CapCol<=8 and CapCol>=1 then --In range
local target = self:GetSquare( NumToLetter[CapCol], CapRow )
if not target then --Empty space
tbl[NumToLetter[CapCol]][CapRow] = {"CAPTURE", NumToLetter[TargetColumn], TargetRow} --Capture move
CapMove = true --Flag as capture move
end
end
end
else
tbl[NumToLetter[TargetColumn]][TargetRow] = true --Standard valid move
end
end
--Forward Left
local TargetRow = GridNum+ (IsWhite and 1 or (-1))
local TargetColumn = NumToLetter[GridLet]-1
if TargetRow<=8 and TargetRow>=1 and TargetColumn<=8 and TargetColumn>=1 then
local target = self:GetSquare( NumToLetter[TargetColumn], TargetRow )
if target then
if ((self:SquareTeam(target)=="White")~=IsWhite) then
local CapRow = TargetRow+ (IsWhite and 1 or (-1))
local CapCol = TargetColumn-1
if CapRow<=8 and CapRow>=1 and CapCol<=8 and CapCol>=1 then
local target = self:GetSquare( NumToLetter[CapCol], CapRow )
if not target then
tbl[NumToLetter[CapCol]][CapRow] = {"CAPTURE", NumToLetter[TargetColumn], TargetRow}
CapMove = true
end
end
end
else
tbl[NumToLetter[TargetColumn]][TargetRow] = true --Standard valid move
end
end
return CapMove
end
function ENT:GetKingMoves( tbl, GridLet, GridNum, IsWhite )
local CapMove = self:GetManMoves( tbl, GridLet, GridNum, IsWhite ) --Forward moves
--Back Right
local TargetRow = GridNum+ (IsWhite and (-1) or (1))
local TargetColumn = NumToLetter[GridLet]+1
if TargetRow<=8 and TargetRow>=1 and TargetColumn<=8 and TargetColumn>=1 then
local target = self:GetSquare( NumToLetter[TargetColumn], TargetRow )
if target then
if ((self:SquareTeam(target)=="White")~=IsWhite) then
local CapRow = TargetRow+ (IsWhite and (-1) or (1))
local CapCol = TargetColumn+1
if CapRow<=8 and CapRow>=1 and CapCol<=8 and CapCol>=1 then
local target = self:GetSquare( NumToLetter[CapCol], CapRow )
if not target then
tbl[NumToLetter[CapCol]][CapRow] = {"CAPTURE", NumToLetter[TargetColumn], TargetRow}
CapMove = true
end
end
end
else
tbl[NumToLetter[TargetColumn]][TargetRow] = true --Standard valid move
end
end
--Back Left
local TargetRow = GridNum+ (IsWhite and (-1) or (1))
local TargetColumn = NumToLetter[GridLet]-1
if TargetRow<=8 and TargetRow>=1 and TargetColumn<=8 and TargetColumn>=1 then
local target = self:GetSquare( NumToLetter[TargetColumn], TargetRow )
if target then
if ((self:SquareTeam(target)=="White")~=IsWhite) then
local CapRow = TargetRow+ (IsWhite and (-1) or (1))
local CapCol = TargetColumn-1
if CapRow<=8 and CapRow>=1 and CapCol<=8 and CapCol>=1 then
local target = self:GetSquare( NumToLetter[CapCol], CapRow )
if not target then
tbl[NumToLetter[CapCol]][CapRow] = {"CAPTURE", NumToLetter[TargetColumn], TargetRow}
CapMove = true
end
end
end
else
tbl[NumToLetter[TargetColumn]][TargetRow] = true --Standard valid move
end
end
return CapMove
end
function ENT:GetMove( GridLet, GridNum, IgnoreCap )
if not (GridLet and GridNum) then return {} end
if not NumToLetter[GridLet] then return {} end
if NumToLetter[GridLet]<1 or NumToLetter[GridLet]>8 then return {} end
if GridNum<1 or GridNum>8 then return {} end
local square = self:GetSquare( GridLet, GridNum )
if not square then return {} end
local class = square.Class or (IsValid(square.Ent) and square.Ent:GetRole())
if not class then return {} end
if self:GetJumpMove() and self:GetJumpLet()~=0 and self:GetJumpNum()~=0 and (NumToLetter[GridLet]~=self:GetJumpLet() or GridNum~=self:GetJumpNum()) then return {} end
local IsWhite = self:SquareTeam(square)=="White"
local Moved = self:SquareMoved(square)
local CanJump = IgnoreCap or self:CanCapture( IsWhite )
local tbl = { ["a"] = {}, ["b"] = {}, ["c"] = {}, ["d"] = {}, ["e"] = {}, ["f"] = {}, ["g"] = {}, ["h"] = {} }
if class=="King" then
self:GetKingMoves( tbl, GridLet, GridNum, IsWhite )
else
self:GetManMoves( tbl, GridLet, GridNum, IsWhite )
end
if CanJump then
for CheckLet,File in pairs(tbl) do
for CheckNum,v in pairs(File) do
if v==true then
tbl[CheckLet][CheckNum] = nil --We can capture, but this isn't a capture move
end
end
end
end
return tbl
end
function ENT:ResetBoard()
if SERVER then
self:SetDrawOffer( PLAYER_NONE )
self:SetWhiteWager( -1 )
self:SetBlackWager( -1 )
self:SetWhiteTime( 600 )
self:SetBlackTime( 600 )
self:SetJumpMove( false )
self:SetJumpLet( 0 )
self:SetJumpNum( 0 )
end
self:RefreshSquares()
if self.Pieces then
for _,File in pairs( self.Pieces ) do
for _,Square in pairs(File) do
if IsValid(Square.Ent) then Square.Ent:SetGridNum(-1) Square.Ent:Remove() end
end
end
end
self.Pieces = {
["a"] = {
[1] = {Team="White",Class="Man",Moved=false}, [3] = {Team="White",Class="Man",Moved=false}, [7] = {Team="Black",Class="Man",Moved=false},
},
["b"] = {
[2] = {Team="White",Class="Man",Moved=false}, [6] = {Team="Black",Class="Man",Moved=false}, [8] = {Team="Black",Class="Man",Moved=false},
},
["c"] = {
[1] = {Team="White",Class="Man",Moved=false}, [3] = {Team="White",Class="Man",Moved=false}, [7] = {Team="Black",Class="Man",Moved=false},
},
["d"] = {
[2] = {Team="White",Class="Man",Moved=false}, [6] = {Team="Black",Class="Man",Moved=false}, [8] = {Team="Black",Class="Man",Moved=false},
},
["e"] = {
[1] = {Team="White",Class="Man",Moved=false}, [3] = {Team="White",Class="Man",Moved=false}, [7] = {Team="Black",Class="Man",Moved=false},
},
["f"] = {
[2] = {Team="White",Class="Man",Moved=false}, [6] = {Team="Black",Class="Man",Moved=false}, [8] = {Team="Black",Class="Man",Moved=false},
},
["g"] = {
[1] = {Team="White",Class="Man",Moved=false}, [3] = {Team="White",Class="Man",Moved=false}, [7] = {Team="Black",Class="Man",Moved=false},
},
["h"] = {
[2] = {Team="White",Class="Man",Moved=false}, [6] = {Team="Black",Class="Man",Moved=false}, [8] = {Team="Black",Class="Man",Moved=false},
},
[CHESS_WCAP1] = {}, [CHESS_WCAP2] = {}, [CHESS_BCAP1] = {}, [CHESS_BCAP2] = {},
}
self:Update()
end
function ENT:CanCapture( White )
for Let,column in pairs( self.Pieces ) do
for Num,square in pairs( column ) do
if square.Team==(White and "White" or "Black") then
local moves = self:GetMove( Let, Num, true )
for _,column in pairs( moves ) do
for _,move in pairs( column ) do
if type(move)=="table" and move[1]=="CAPTURE" then return true end
end
end
end
end
end
return false
end
function ENT:CanMove( White )
for Let,column in pairs( self.Pieces ) do
for Num,square in pairs( column ) do
if square.Team==(White and "White" or "Black") then
local moves = self:GetMove( Let, Num )
for _,column in pairs( moves ) do
for _,move in pairs( column ) do
if move then return true end
end
end
end
end
end
return false
end
function ENT:NoMaterialCheck()
local BlackMat = {}
local WhiteMat = {}
for GridLet,File in pairs(self.Pieces) do
if GridLet==CHESS_WCAP1 or GridLet==CHESS_WCAP2 or GridLet==CHESS_BCAP1 or GridLet==CHESS_BCAP2 then continue end
for GridNum,square in pairs(File) do
if square then
local IsWhite = self:SquareTeam(square)=="White"
if IsWhite then
table.insert( WhiteMat, {Square=square, Class=Class, GridLet=GridLet, GridNum=GridNum} )
else
table.insert( BlackMat, {square=square, Class=Class, GridLet=GridLet, GridNum=GridNum} )
end
end
end
end
if (#BlackMat+#WhiteMat)==0 then self:EndGame() return false end
if #WhiteMat==0 then self:EndGame("Black") return false end
if #BlackMat==0 then self:EndGame("White") return false end
return true
end
function ENT:EndGame( winner, NoMsg )
self:SetChessState( CHESS_INACTIVE )
self:SetPlaying( false )
local White = self:GetPlayer( "White" )
local Black = self:GetPlayer( "Black" )
local WhiteName = IsValid(White) and White:Nick() or "[Anonymous White]"
local BlackName = IsValid(Black) and Black:Nick() or "[Anonymous Black]"
if not NoMsg then
net.Start( "Chess GameOver" )
if winner=="White" then
net.WriteTable( {" ", Color(255,255,255), WhiteName, Color(150,255,150), " has won against ", Color(100,100,100), BlackName, Color(150,255,150), "!"} )
else
net.WriteTable( {" ", Color(100,100,100), BlackName, Color(150,255,150), " has won against ", Color(255,255,255), WhiteName, Color(150,255,150), "!"} )
end
net.WriteString( "icon16/medal_gold_2.png" )
net.Broadcast()
end
timer.Simple( 0.5, function()
if not IsValid(self) then return end
if IsValid(Black) and Black:GetVehicle()==self.BlackSeat then Black:ExitVehicle() end
if IsValid(White) and White:GetVehicle()==self.WhiteSeat then White:ExitVehicle() end
end)
local winnings = (self.WagerValue or 0)*2
if IsValid( White ) then
if winner=="White" then
if IsValid(Black) then White:DraughtsWin( Black ) end
if self.WagerValue then
if self:GetPSWager() then
White:PS_GivePoints( winnings )
else
if White.addMoney then White:addMoney( winnings ) else White:SetDarkRPVar( "money", (White:getDarkRPVar( "money" ) or 0) + winnings ) end
end
end
elseif winner~="Black" then
if IsValid(Black) and winner~="Error" then White:DraughtsDraw( Black ) end
if self.WagerValue then
if self:GetPSWager() then
White:PS_GivePoints( self.WagerValue )
else
if White.addMoney then White:addMoney( self.WagerValue ) else White:SetDarkRPVar( "money", (White:getDarkRPVar( "money" ) or 0) + self.WagerValue ) end
end
end
end
end
if IsValid( Black ) then
if winner=="Black" then
if IsValid(White) then Black:DraughtsWin( White ) end
if self.WagerValue then
if self:GetPSWager() then
Black:PS_GivePoints( winnings )
else
if Black.addMoney then Black:addMoney( winnings ) else Black:SetDarkRPVar( "money", (Black:getDarkRPVar( "money" ) or 0) + winnings ) end
end
end
elseif winner~="White" then
if self.WagerValue then
if self:GetPSWager() then
White:PS_GivePoints( self.WagerValue )
else
if White.addMoney then White:addMoney( self.WagerValue ) else White:SetDarkRPVar( "money", (White:getDarkRPVar( "money" ) or 0) + self.WagerValue ) end
end
end
end
end
end
function ENT:DoCapture( square, EndLet, EndNum )
if not square then return end
local class = square.Class
local made = false
local CapLet,CapNum
if square.Team=="White" then --Black captured
for i=CHESS_BCAP1,CHESS_BCAP2 do
for n=1,8 do
local CapSq = self:GetSquare( i, n )
if not CapSq then
self.Pieces[i][n] = {Team="White", Class=class, Moved=false}
CapSq = self.Pieces[i][n]
made = true
CapLet,CapNum = i,n
break
end
end
if made then break end
end
else
for i=CHESS_WCAP1,CHESS_WCAP2 do
for n=1,8 do
local CapSq = self:GetSquare( i, n )
if not CapSq then
self.Pieces[i][n] = {Team="Black", Class=class, Moved=false}
CapSq = self.Pieces[i][n]
made = true
CapLet,CapNum = i,n
break
end
end
if made then break end
end
end
return {From={EndLet,EndNum}, To={CapLet,CapNum}}
end
function ENT:DoMove( StartLet, StartNum, EndLet, EndNum )
if CLIENT then return end
if not (StartLet and EndLet and StartNum and EndNum) then return end
if (StartLet==EndLet) and (StartNum==EndNum) then return end
local Start = self:GetSquare( StartLet, StartNum )
if not Start then return end
local Moves = self:GetMove( StartLet, StartNum )
if not Moves[EndLet][EndNum] then return end
local Move = Moves[EndLet][EndNum]
local CapMove
if type(Move)=="table" then
if Move[1]=="CAPTURE" then
local CapLet, CapNum = Move[2], Move[3]
local square = self:GetSquare( CapLet, CapNum )
if CapLet and CapNum then
CapMove = self:DoCapture( square, CapLet, CapNum )
self.Pieces[CapLet][CapNum] = nil
end
end
end
local End = self:GetSquare( EndLet, EndNum )
if not End then
self.Pieces[EndLet] = self.Pieces[EndLet] or {}
self.Pieces[EndLet][EndNum] = self.Pieces[EndLet][EndNum] or {}
End = self.Pieces[EndLet][EndNum]
end
End.Team=Start.Team
End.Class=Start.Class
End.Moved=true
self.Pieces[StartLet][StartNum] = nil
local ply = self:GetPlayer( End.Team )
if (EndNum==1 or EndNum==8) and End.Class=="Man" then --End of the board, promote
End.Class = "King"
// self:SetChessState( End.Team=="White" and CHESS_BLACKMOVE or CHESS_WHITEMOVE )
//
// self:SetJumpMove( false )
// self:SetJumpLet( 0 )
// self:SetJumpNum( 0 )
end
if type(Move)=="table" and Move[1]=="CAPTURE" then
self:SetJumpMove(false)
if self:CanCapture( End.Team=="White" ) then
local GetMoves = self:GetMove(EndLet, EndNum)
local Cap = false
for _,column in pairs( GetMoves ) do
for _,move in pairs(column) do
if move and move~=true then
Cap=true
end
end
end
if Cap then
self:SetJumpMove( true )
self:SetJumpLet( NumToLetter[EndLet] )
self:SetJumpNum( EndNum )
else
self:SetChessState( End.Team=="White" and CHESS_BLACKMOVE or CHESS_WHITEMOVE )
self:SetJumpMove( false )
self:SetJumpLet( 0 )
self:SetJumpNum( 0 )
end
else
self:SetChessState( End.Team=="White" and CHESS_BLACKMOVE or CHESS_WHITEMOVE )
self:SetJumpMove( false )
self:SetJumpLet( 0 )
self:SetJumpNum( 0 )
end
else --Standard move, other player's turn
self:SetChessState( End.Team=="White" and CHESS_BLACKMOVE or CHESS_WHITEMOVE )
self:SetJumpMove( false )
self:SetJumpLet( 0 )
self:SetJumpNum( 0 )
end
local move = {From={StartLet,StartNum},To={EndLet,EndNum}}
self:Update( move, CapMove )
self:NoMaterialCheck()
if self:GetChessState()==CHESS_BLACKMOVE and not self:CanMove( false ) then self:EndGame( "White" ) end
if self:GetChessState()==CHESS_WHITEMOVE and not self:CanMove( true ) then self:EndGame( "Black" ) end
return move
end
function ENT:GetElo( ply )
return IsValid(ply) and " ("..ply:GetDraughtsElo()..")" or ""
end
if CLIENT then
local PanelCol = {
Main = Color(0,0,0,200), ToMove = Color(200,200,200,20), Text = Color(180,180,180),
White = Color(255,255,255), Black = Color(20,20,20,255),
}
local StateToString = {[CHESS_INACTIVE] = "Waiting", [CHESS_WHITEMOVE] = "White", [CHESS_BLACKMOVE] = "Black", [CHESS_WHITEPROMO] = "White (jumping)", [CHESS_BLACKPROMO] = "Black (jumping)", [CHESS_WAGER] = "Wagers"}
function ENT:CreateChessPanel()
self:EndSpectating()
local frame = vgui.Create( "DFrame" )
frame:SetSize(400,115)
frame:SetPos( (ScrW()/2)-100, ScrH()-150 )
--frame:SetDraggable( false )
frame:SetTitle( "" )
frame:ShowCloseButton( false )
frame:SetDeleteOnClose( true )
frame.Paint = function( s,w,h )
if not IsValid(self) then
s:Remove()
gui.EnableScreenClicker( false )
return
end
draw.RoundedBox( 8, 0, 0, w, h, PanelCol.Main )
end
frame:DockMargin( 0,0,0,0 )
frame:DockPadding( 5,6,5,5 )
local TimePnl = vgui.Create( "DPanel", frame )
TimePnl:Dock( RIGHT )
TimePnl:SetWide( 100 )
TimePnl:DockMargin( 2,2,2,2 )
TimePnl.Paint = function(s,w,h)
if not IsValid(self) then return end
draw.RoundedBox( 16, 0, 0, w, (h/2)-1, PanelCol.ToMove )
draw.RoundedBox( 16, 0, (h/2)+1, w, (h/2)-1, PanelCol.ToMove )
draw.SimpleText( string.FormattedTime( math.Round(self:GetWhiteTime() or 300,1), "%02i:%02i" ), "ChessText", w/2, h/4, PanelCol.White, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER )
draw.SimpleText( string.FormattedTime( math.Round(self:GetBlackTime() or 300,1), "%02i:%02i" ), "ChessText", w/2, (h/4)+(h/2), PanelCol.Black, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER )
end
local ButtonPanel = vgui.Create( "DPanel", frame )
ButtonPanel:SetSize( 100, 20 )
ButtonPanel:Dock( LEFT )
ButtonPanel.Paint = function() end
local ToMove = vgui.Create( "DPanel", frame )
ToMove:SetSize(200,80)
ToMove:Dock( FILL )
ToMove.Paint = function( s,w,h )
draw.RoundedBox( 4, 0, 0, w, h, PanelCol.ToMove )
draw.SimpleText( "To move", "ChessTextSmall", 5, 0, PanelCol.Text )
local state = IsValid(self) and self:GetChessState()
if not (IsValid( self ) and state) then
draw.SimpleText( "[N/A]", "ChessTextSmall", w/2, h/2, PanelCol.Text, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER )
else
local str = (StateToString[state] or "N/A")..( (self:GetPlaying() and self:GetJumpMove() and " (jump)") or "" )
local col = ((state==CHESS_WHITEMOVE or state==CHESS_WHITEPROMO) and PanelCol.White) or ((state==CHESS_BLACKMOVE or state==CHESS_BLACKPROMO) and PanelCol.Black) or PanelCol.Text
draw.SimpleText( str, "ChessTextLarge", w/2, h/2, col, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER )
end
end
frame.OfferDraw = vgui.Create( "DButton", ButtonPanel)
frame.OfferDraw:SetSize(94,35)
frame.OfferDraw:Dock( TOP )
frame.OfferDraw:SetText( "Offer Draw" )
frame.OfferDraw.DoClick = function( s )
if (IsValid(self)) and not (self:GetPlaying()) then
chat.AddText( Color(150,255,150), "You can't offer a draw before the game starts!" )
return
end
net.Start( "Chess DrawOffer" ) net.SendToServer()
-- s:SetText( "Draw offered" )
end
frame.OfferDraw.Think = function(s)
if IsValid(self) and self:GetDrawOffer()~=PLAYER_NONE then
if s.TextChanged then return end
s.TextChanged = true
if LocalPlayer()==self:GetWhitePlayer() then
if self:GetDrawOffer()==PLAYER_WHITE then
s:SetText( "Draw Offered" )
elseif self:GetDrawOffer()==PLAYER_BLACK then
s:SetText( "Accept Draw Offer" )
end
elseif LocalPlayer()==self:GetBlackPlayer() then
if self:GetDrawOffer()==PLAYER_WHITE then
s:SetText( "Accept Draw Offer" )
elseif self:GetDrawOffer()==PLAYER_BLACK then
s:SetText( "Draw Offered" )
end
end
elseif s.TextChanged then
s.TextChanged = false
s:SetText( "Offer Draw" )
end
end
local Resign = vgui.Create( "DButton", ButtonPanel)
Resign:SetSize(94,35)
Resign:Dock( TOP )
Resign:SetText( "Resign" )
Resign.DoClick = function( s )
net.Start( "Chess ClientResign" ) net.SendToServer() --No client-side exit func :/
end
local DermaMode = vgui.Create( "DButton", ButtonPanel)
DermaMode:SetSize(94,35)
DermaMode:Dock( TOP )
DermaMode:SetText( "Toggle 2D Mode" )
DermaMode.DoClick = function( s )
if IsValid(Chess_2DDermaPanel) then
Chess_2DDermaPanel:Remove()
else
Chess_Open2DBoard( self )
end
end
return frame
end
end

View File

@@ -0,0 +1,761 @@
--[[
| 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()
ENT.Type = "anim"
ENT.PrintName = "Editable Emplacement"
ENT.Category = "Jakub Baku"
ENT.Spawnable = true
ENT.AdminOnly = false
ENT.RenderGroup = RENDERGROUP_TRANSLUCENT
ENT.Editable = true
if(CLIENT) then
ENT.Laser = Material("sprites/bluelaser1")
function ENT:DrawTranslucent()
self:DrawModel()
local gun = self:GetNWEntity("gun")
if(IsValid(gun)) then
if(self:GetLaser() && self:GetNWBool("manned", false)) then
local origin = gun:GetAttachment(self:GetNWInt("attachment")).Pos
local target = util.TraceLine({
start = origin,
endpos = gun:GetForward() * 1000000 * self:GetNWInt("mul", 1) + origin,
filter = function(e)
if(e == gun || e == self) then return false else return true end
end
}).HitPos
self:SetRenderBoundsWS(target, self:GetPos(), Vector(20, 20, 20))
render.SetMaterial(self.Laser)
render.DrawBeam(origin, target, 16, 0, 0, self:GetLaserColor():ToColor())
end
if(self:GetShowAmmo() && self:GetEnableAmmo()) then
local ang = gun:GetAngles()
ang:RotateAroundAxis(gun:GetRight(), 90)
ang:RotateAroundAxis(gun:GetForward(), 90)
if(self:GetNWInt("mul", 1) < 0) then
ang:RotateAroundAxis(gun:GetUp(), 180)
end
cam.Start3D2D(gun:GetPos() - gun:GetRight() * 13 * self:GetNWInt("mul", 1) + gun:GetUp() * 10, ang, 0.5)
draw.SimpleText(self:GetNWInt("ammo", 0))
cam.End3D2D()
end
end
end
end
function ENT:SetupDataTables()
/*self:NetworkVar("Int", 6, "Preset", {KeyName = "preset", Edit = { title = "Preset", category = "Preset Bank", type = "Combo", order = 10, values = {
["Airboat Gun"] = 1,
["Combine Bunker Gun"] = 2,
["SMG1"] = 3,
}}})*/
self:NetworkVar("Int", 0, "BulletsPerShot", {KeyName = "numbullets", Edit = { title = "Bullets per shot", category = "Basics", type = "Int", order = 20, min = 1, max = 10}})
self:NetworkVar("Int", 1, "BulletDamage", {KeyName = "dmgbullet", Edit = { title = "Bullet Damage", category = "Basics", type = "Int", order = 30, min = 0, max = 50}})
self:NetworkVar("Int", 6, "ProjType", {KeyName = "projtype", Edit = { title = "Projectile", category = "Basics", type = "Combo", order = 10, values = {
["Bullet"] = 1,
["Flechette"] = 2,
["SMG Grenade"] = 3,
["Rocket"] = 4,
["Homing Rocket"] = 5,
["Physics Prop"] = 6
}}})
self:NetworkVar("Int", 2, "DmgTypeBullet", {KeyName = "dmgbullettype", Edit = { title = "Damage type", category = "Advanced", type = "Combo", order = 80, values = {
["Airboat"] = DMG_AIRBOAT,
["Bullet"] = DMG_BULLET,
["Energy"] = DMG_ENERGYBEAM,
["Burn"] = DMG_BURN,
["Explosion"] = DMG_BLAST,
["Dissolve"] = DMG_DISSOLVE
}}})
self:NetworkVar("Int", 3, "MuzzleEffect", {KeyName = "muzzleeffect", Edit = { title = "Muzzle Effect", category = "Advanced", type = "Combo", order = 90, values = {
["Airboat Gun"] = 3,
["Chopper"] = 2,
["Gunship"] = 1,
["Counter Strike X"] = 6,
["HL2 Default"] = 4,
["Counter Strike"] = 5,
["Strider"] = 7
}}})
self:NetworkVar("Int", 4, "Tracer", {KeyName = "tracereffect", Edit = { title = "Tracer Effect", category = "Advanced", type = "Combo", order = 100, values = {
["AR2"] = 1,
["Chopper"] = 2,
["Airboat Gun"] = 3,
["Combine laser"] = 5,
["HL2 Default"] = 8,
["Toolgun"] = 9
}}})
self:NetworkVar("Int", 5, "Impact", {KeyName = "impacteffect", Edit = { title = "Impact Effect", category = "Advanced", type = "Combo", order = 110, values = {
["Stunstick"] = 1,
["Crossbow Bolt"] = 2,
["Manhack Sparks"] = 3,
["Helicopter Bomb Explosion"] = 4,
["AR2"] = 5,
["Strider Wormhole"] = 6,
["No effect"] = 0
}}})
self:NetworkVar("Int", 10, "GunModel", {KeyName = "gunmodel", Edit = { title = "Model", category = "Advanced", type = "Combo", order = 85, values = {
["Airboat gun"] = 1,
["Pistol"] = 2,
["SMG1"] = 3,
["AR2"] = 4,
[".357"] = 5,
["Shotgun"] = 6
}}})
self:NetworkVar("Float", 0, "BulletSpread", {KeyName = "sprdbullet", Edit = { title = "Spread", category = "Basics", type = "Float", order = 40, min = 0, max = 0.2}})
self:NetworkVar("Float", 1, "BulletForce", {KeyName = "frcbullet", Edit = { title = "Force", category = "Basics", type = "Float", order = 50, min = 0, max = 10}})
self:NetworkVar("Float", 6, "GunInertia", {KeyName = "inertia", Edit = { title = "Gun's Weight", category = "Basics", type = "Float", order = 51, min = 0.05, max = 1}})
self:NetworkVar("Float", 5, "GunKick", {KeyName = "kick", Edit = { title = "Gun's Recoil", category = "Basics", type = "Float", order = 52, min = 0.01, max = 5}})
self:NetworkVar("Float", 2, "ShootDelay", {KeyName = "shootdelay", Edit = { title = "Delay", category = "Basics", type = "Float", order = 60, min = 0.05, max = 2}})
self:NetworkVar("Float", 3, "PropVel", {KeyName = "propvelocity", Edit = { title = "Prop's Velocity", category = "Physics Prop", type = "Float", order = 79, min = 0, max = 100000}})
self:NetworkVar("Float", 4, "FlareScale", {KeyName = "flarescale", Edit = { title = "Flare size", category = "Misc", type = "Float", order = 160, min = 1, max = 40}})
self:NetworkVar("String", 0, "ShootSound", {KeyName = "shootsnd", Edit = { title = "Shoot Sound", category = "Basics", type = "Generic", order = 70}})
self:NetworkVar("String", 3, "LastSound", {KeyName = "lastsnd", Edit = { title = "Last shoot Sound", category = "Basics", type = "Generic", order = 72}})
self:NetworkVar("Bool", 1, "SndLoop", {KeyName = "sndloop", Edit = {title = "Loop the sound", category = "Basics", type = "Boolean", order = 71}})
self:NetworkVar("String", 1, "ImpactSound", {KeyName = "impactsnd", Edit = { title = "Impact Sound", category = "Basics", type = "Generic", order = 73}})
self:NetworkVar("String", 2, "PropModel", {KeyName = "propmodel", Edit = { title = "Prop Model", category = "Physics Prop", type = "Generic", order = 78}})
self:NetworkVar("Bool", 7, "EmitShells", {KeyName = "shlemit", Edit = {title = "Emit shells", category = "Basics", type = "Boolean", order = 74}})
self:NetworkVar("Bool", 0, "Immolate", {KeyName = "shouldimmolate", Edit = {title = "Immolate on Kill", category = "Advanced", type = "Boolean", order = 120}})
self:NetworkVar("Bool", 2, "Laser", {KeyName = "laserenable", Edit = {title = "Laser sight", category = "Misc", type = "Boolean", order = 130}})
self:NetworkVar("Bool", 3, "Flare", {KeyName = "flareenable", Edit = {title = "Enable Flare (Right Click)", category = "Misc", type = "Boolean", order = 150}})
self:NetworkVar("Vector", 0, "LaserColor", {KeyName = "lasercolor", Edit = {title = "Laser color", category = "Misc", type = "VectorColor", order = 140}})
self:NetworkVar("Bool", 4, "EnableAmmo", {KeyName = "enableammo", Edit = {title = "Enable ammo", category = "Ammo & Regen", type = "Boolean", order = 170}})
self:NetworkVar("Bool", 6, "RegenAmmo", {KeyName = "rgnammo", Edit = {title = "Regenerate ammo", category = "Ammo & Regen", type = "Boolean", order = 171}})
self:NetworkVar("Bool", 5, "ShowAmmo", {KeyName = "showammo", Edit = {title = "Show Ammo", category = "Ammo & Regen", type = "Boolean", order = 172}})
self:NetworkVar("Int", 9, "AmmoEnt", {KeyName = "ammoent", Edit = {title = "Ammo Entity", category = "Ammo & Regen", type = "Combo", order = 173, values = {
["AR2 Ammo"] = 1,
[".357 Ammo"] = 2,
["Pistol Ammo"] = 3,
["Shotgun Ammo"] = 4,
["SMG1 Ammo"] = 5,
["Crossbow Ammo"] = 7,
["SMG Grenade"] = 6,
["RPG Rocket"] = 8,
["Suit Battery"] = 9
}}})
self:NetworkVar("Int", 11, "ShellType", {KeyName = "shelltype", Edit = {title = "Shell Type", category = "Basics", type = "Combo", order = 75, values = {
["Default"] = 1,
["Rifle"] = 2,
["Buckshot"] = 3
}}})
self:NetworkVar("Int", 7, "AmmoCapacity", {KeyName = "ammocap", Edit = { title = "Ammo Capacity", category = "Ammo & Regen", type = "Int", order = 175, min = 1, max = 100}})
self:NetworkVar("Int", 8, "AmmoPerShot", {KeyName = "ammopershot", Edit = { title = "Take Ammo per Shot", category = "Ammo & Regen", type = "Int", order = 180, min = 1, max = 10}})
self:NetworkVar("Float", 7, "RegenDelay", {KeyName = "rgndelay", Edit = { title = "Regen Delay", category = "Ammo & Regen", type = "Float", order = 185, min = 0.01, max = 1}})
self:NetworkVar("Float", 8, "PostShootDelay", {KeyName = "psrgndelay", Edit = { title = "Post-shoot Regen Delay", category = "Ammo & Regen", type = "Float", order = 190, min = 0, max = 1}})
self:NetworkVar("Int", 12, "Burst", {KeyName = "burst", Edit = { title = "Rounds per burst", category = "Burst mode", type = "Int", order = 200, min = 0, max = 10}})
self:NetworkVar("Float", 9, "BurstDelay", {KeyName = "burstdelay", Edit = { title = "Delay between bursts", category = "Burst mode", type = "Float", order = 210, min = 0, max = 4}})
end
if(SERVER) then
function ENT:Initialize()
self.Presets = {
{
["numbullets"] = 2,
["dmgbullet"] = 6,
["dmgbullettype"] = DMG_AIRBOAT,
["muzzleeffect"] = 3,
["tracereffect"] = 3,
["impacteffect"] = 5,
["sprdbullet"] = 0.00,
["frcbullet"] = 3,
["shootdelay"] = 0.07,
["shootsnd"] = "Weapon_AR2.NPC_Single"
},
{
["numbullets"] = 1,
["dmgbullet"] = 6,
["dmgbullettype"] = DMG_BULLET,
["muzzleeffect"] = 3,
["tracereffect"] = 1,
["impacteffect"] = 5,
["sprdbullet"] = 0.03,
["frcbullet"] = 3,
["shootdelay"] = 0.07,
["shootsnd"] = "Weapon_AR2.NPC_Single"
}
}
self.Shells = {
"ShellEject",
"RifleShellEject",
"ShotgunShellEject"
}
self.Models = {
{"models/airboatgun.mdl", false},
{"models/weapons/w_pistol.mdl", true},
{"models/weapons/w_smg1.mdl",false},
{"models/weapons/w_irifle.mdl",true},
{"models/weapons/w_357.mdl",false},
{"models/weapons/w_shotgun.mdl", true}
}
self.AmmoEntities = {
{"item_ammo_ar2", 30, 1, "models/items/combine_rifle_cartridge01.mdl"}, {"item_ammo_357", 8, 1, "models/items/357ammo.mdl"}, {"item_ammo_pistol", 15, 1, "models/items/boxsrounds.mdl"}, {"item_box_buckshot", 12, 1, "models/items/boxbuckshot.mdl"}, {"item_ammo_smg1", 35, 1, "models/items/boxmrounds.mdl"}, {"item_ammo_smg1_grenade", 1, 1, "models/items/ar2_grenade.mdl"}, {"item_ammo_crossbow", 3, 1, "models/items/crossbowrounds.mdl"},
{"item_rpg_round", 1, 1, "models/weapons/w_missile_closed.mdl"}, {"item_battery", 40, 2, "models/items/battery.mdl"}
}
self.PickupSounds = {"items/ammo_pickup.wav", "items/battery_pickup.wav"}
util.PrecacheModel("models/airboatgun.mdl")
self:SetModel("models/airboatgun_stand.mdl")
local min = Vector(-1, -1, 0) * 8
local max = Vector(1, 1, 1) * 8
self:PhysicsInitBox(min, max)
self.UseTimer = 0
self.ShootTimer = 0
self.FlareTimer = 0
self.RegenTimer = 0
self.BurstTimer = 0
self.Mul = 1
self.Burst = 0
self.IsBursting = false
self.LerpTime = 0
self.NewAng = Angle(0, 0, 0)
self.OldAng = Angle(0, 0, 0)
self.LastWeapon = nil
self.SndPlaying = false
self.LoopSound = nil
self.VehicleMode = false
self.Vehicle = nil
self:SetBulletsPerShot(2)
self:SetBulletDamage(3)
self:SetBulletSpread(0.03)
self:SetBulletForce(0.3)
self:SetShootDelay(0.075)
self:SetShootSound("Weapon_AR2.NPC_Single")
self:SetDmgTypeBullet(DMG_BULLET)
self:SetMuzzleEffect(3)
self:SetTracer(1)
self:SetImpact(5)
self:SetProjType(1)
self:SetPropModel("models/props_c17/oildrum001_explosive.mdl")
self:SetPropVel(1000)
self:SetSndLoop(false)
self:SetLastSound("Airboat.FireGunRevDown")
self:SetLaserColor(Vector(255, 0, 0))
self:SetFlareScale(10)
self:SetGunInertia(0.1)
self:SetEnableAmmo(false)
self:SetRegenDelay(0.05)
self:SetAmmoPerShot(1)
self:SetAmmoCapacity(100)
self:SetAmmoEnt(1)
self:SetGunModel(1)
self:SetShellType(1)
self.Gun = ents.Create("prop_dynamic")
self.Gun:SetModel(self.Models[1][1])
self.Gun:SetPos(self:GetPos() + self:GetUp() * 10)
self.Gun:SetAngles(self:GetAngles())
self.Gun:Spawn()
self.Gun:SetParent(self)
self.Attachment = self.Gun:LookupAttachment("muzzle")
self:SetNWInt("attachment",self.Attachment)
self:SetNWEntity("gun", self.Gun)
self.Ammo = 100
self:SetNWInt("ammo", self.Ammo)
self.InfoTarget = ents.Create("info_target")
self.InfoTarget:SetPos(self:GetPos())
self.Impacts = {
"StunstickImpact",
"BoltImpact",
"ManhackSparks",
"HelicopterMegaBomb",
"AR2Impact",
"effect_combine_destruction"
}
self.Muzzles = {
"GunshipMuzzleFlash",
"ChopperMuzzleFlash",
"AirboatMuzzleFlash",
"MuzzleEffect",
"CS_MuzzleFlash",
"CS_MuzzleFlash_X",
"StriderMuzzleFlash",
"effect_combine_muzzle"
}
self.Tracers = {
"AR2Tracer",
"HelicopterTracer",
"AirboatGunTracer",
"GaussTracer",
"effect_combine_tracer",
"effect_combine_tracker",
"GunshipTracer",
"Tracer",
"ToolTracer"
}
self:NetworkVarNotify("SndLoop", self.NotifyOnVar)
self:NetworkVarNotify("ShootSound", self.NotifyOnVar)
self:NetworkVarNotify("AmmoCapacity", self.NotifyOnVar)
self:NetworkVarNotify("EnableAmmo", self.NotifyOnVar)
self:NetworkVarNotify("GunModel", self.NotifyOnVar)
end
function ENT:NotifyOnVar(name, old, new)
if(name == "SndLoop") then
if(!old && new) then
self.LoopSound = CreateSound(self, self:GetShootSound())
self.SndPlaying = false
elseif(old && !new) then
self.LoopSound:Stop()
self.SndPlaying = false
end
elseif(name == "ShootSound" && self:GetSndLoop()) then
self.LoopSound:Stop()
self.LoopSound = CreateSound(self, new)
self.SndPlaying = false
elseif(name == "AmmoCapacity") then
self.Ammo = math.min(self.Ammo, new)
self:SetNWInt("ammo", self.Ammo)
elseif(name == "EnableAmmo") then
self.Ammo = self:GetAmmoCapacity()
self:SetNWInt("ammo", self.Ammo)
elseif(name == "GunModel") then
self.Gun:SetModel(self.Models[new][1])
self.Attachment = self.Gun:LookupAttachment("muzzle")
self:SetNWInt("attachment",self.Attachment)
if(self.Models[new][2]) then
self.Mul = -1
self:SetNWInt("mul", -1)
else
self.Mul = 1
self:SetNWInt("mul", 1)
end
end
end
function ENT:StopTheSound()
if(!self:GetSndLoop()) then return end
self.LoopSound:Stop()
self.SndPlaying = false
end
function ENT:Think()
if(IsValid(self.User)) then
if(IsValid(self.User:GetActiveWeapon())) then
self.LastWeapon = self.User:GetActiveWeapon()
self.User:SetActiveWeapon(nil)
end
//self.Gun:SetPos(self:GetPos() + self:GetUp() * 10)
local topoint
local trace
if(self.VehicleMode) then
trace = util.TraceLine({
start = self.User:EyePos(),
endpos = self.User:GetAimVector() * 1000000,
filter = function(ent)
if(ent == self.User:GetVehicle()) then return false else return true end
end
})
topoint = trace.HitPos - self.Gun:GetAttachment(self.Attachment).Pos
topoint = topoint * self.Mul
else
trace = self.User:GetEyeTrace()
topoint = trace.HitPos - self.Gun:GetAttachment(self.Attachment).Pos
topoint = topoint * self.Mul
end
self.InfoTarget:SetPos(trace.HitPos)
if(self.LerpTime < CurTime()) then
self.OldAng = self.NewAng
self.NewAng = topoint:Angle()
self.LerpTime = CurTime() + math.max(self:GetGunInertia(), 0.05)
end
if(topoint:DistToSqr(Vector(0, 0, 0)) > 1900) then
local lerped = LerpAngle((self.LerpTime - CurTime()) / math.max(self:GetGunInertia(), 0.05), self.NewAng, self.OldAng)
self.Gun:SetAngles( lerped )
end
if(self.ShootTimer < CurTime() && (self.User:KeyDown(IN_ATTACK) || self.IsBursting) && (self.BurstTimer < CurTime() || self:GetBurst() < 1)) then
if(!(self:GetEnableAmmo() && self.Ammo < self:GetAmmoPerShot())) then
if(!self:GetSndLoop()) then
self:EmitSound(self:GetShootSound())
elseif(!self.SndPlaying) then
self.SndPlaying = true
self.LoopSound:Play()
end
if(self.Burst + 1 >= self:GetBurst()) then
self.Burst = 0
self.BurstTimer = CurTime() + self:GetBurstDelay()
self.IsBursting = false
else
self.Burst = self.Burst + 1
self.IsBursting = true
end
if(self:GetEmitShells()) then
local shang = self.Gun:GetAngles()
shang:RotateAroundAxis(self.Gun:GetUp(), -90)
local eff = EffectData()
eff:SetOrigin(self.Gun:GetPos())
eff:SetAngles(shang)
util.Effect(self.Shells[self:GetShellType()], eff)
end
self.Ammo = self.Ammo - self:GetAmmoPerShot()
self:SetNWInt("ammo", self.Ammo)
self.RegenTimer = CurTime() + self:GetPostShootDelay()
self.NewAng.x = self.NewAng.x - self:GetGunKick()//self:GetGunInertia()
local bullet = {}
bullet.Num = math.min(self:GetBulletsPerShot(), 10)
bullet.Src = self.Gun:GetAttachment(1).Pos + self.Gun:GetForward() * 20 * self.Mul * self:GetVelocity():Length() * 0.01
bullet.Dir = self.Gun:GetForward() * self.Mul
bullet.Spread = Vector(1, 1, 1) * self:GetBulletSpread()
bullet.Tracer = 1
bullet.Force = self:GetBulletForce()
bullet.Damage = math.min(self:GetBulletDamage(), 50)
bullet.AmmoType = "Pistol"
bullet.TracerName = self.Tracers[self:GetTracer()]
bullet.Attacker = self.User
bullet.Callback = function ( attacker, tr, dmginfo )
dmginfo:SetDamageType(self:GetDmgTypeBullet())
if(self:GetImpact() != 0) then
local eff = EffectData()
eff:SetOrigin(tr.HitPos)
eff:SetNormal(tr.HitNormal)
//eff:SetScale( 1 )
util.Effect( self.Impacts[self:GetImpact()], eff)
end
if(self:GetImmolate()) then
for k, v in pairs(ents.FindInSphere(tr.HitPos, 10 )) do
if((v:IsNPC() || v:IsPlayer()) && v:Health() <= self:GetBulletDamage()) then
v:SetName("to_dissolve")
local dis = ents.Create("env_entity_dissolver")
dis:Spawn()
dis:SetKeyValue("target", "to_dissolve")
dis:SetKeyValue("dissolvetype", "2")
dis:Fire("Dissolve", "", "")
dis:Remove()
end
end
end
sound.Play(self:GetImpactSound(), tr.HitPos, 100)
end
if(self:GetProjType() == 1) then
self.Gun:FireBullets(bullet)
else
local spread = (bullet.Dir + VectorRand() * self:GetBulletSpread())
if(self:GetProjType() == 2) then
local flech = ents.Create("hunter_flechette")
flech:SetPos(bullet.Src)
flech:SetAngles(spread:Angle())
flech:SetOwner(self.User)
flech:Spawn()
flech:SetVelocity(spread * 3000)
elseif(self:GetProjType() == 3) then
local ssbolt = ents.Create("grenade_ar2")
ssbolt:SetPos(bullet.Src)
ssbolt:SetAngles(spread:Angle())
ssbolt:SetOwner(self.User)
ssbolt:Spawn()
ssbolt:SetVelocity(spread * 3000)
elseif(self:GetProjType() == 4) then
local ssbolt = ents.Create("entity_tank_rocket")
ssbolt:SetPos(bullet.Src)
ssbolt:SetAngles(spread:Angle())
ssbolt:Spawn()
ssbolt:GetPhysicsObject():SetVelocity(spread * 300)
ssbolt.Owner = self.User
ssbolt.TargetAng = (self.Gun:GetForward() * self.Mul):Angle()
ssbolt.GraceTime = 0.1
elseif(self:GetProjType() == 5) then
local ssbolt = ents.Create("entity_tank_rocket")
ssbolt:SetPos(bullet.Src)
ssbolt:SetAngles(spread:Angle())
ssbolt:Spawn()
ssbolt:GetPhysicsObject():SetVelocity(spread * 300)
ssbolt.Owner = self.User
if(false && (trace.Entity:IsNPC() || trace.Entity:IsPlayer())) then
ssbolt.Target = trace.Entity
local min, max = trace.Entity:GetCollisionBounds()
ssbolt.Offset = max / 2
else
ssbolt.Target = self.InfoTarget
end
else
local prop = ents.Create("prop_physics")
prop:SetModel(self:GetPropModel())
prop:SetPos(bullet.Src + spread * 10)
prop:SetAngles(spread:Angle())
prop:SetOwner(self.User)
prop:Spawn()
prop:Fire("Kill", "", 10)
prop:GetPhysicsObject():SetVelocity(spread * self:GetPropVel())
end
end
local muzzle = EffectData()
muzzle:SetOrigin(self.Gun:GetAttachment(1).Pos)
muzzle:SetEntity( self.Gun )
muzzle:SetAngles( self.Gun:GetAttachment(1).Ang )
muzzle:SetAttachment( 1 )
muzzle:SetScale( 1 )
util.Effect( self.Muzzles[self:GetMuzzleEffect()], muzzle)
self.ShootTimer = CurTime() + math.max(0.05, math.min(self:GetShootDelay(), 10))
else
self:EmitSound("Weapon_Pistol.Empty")
self.ShootTimer = CurTime() + 0.3
if(self.SndPlaying) then
self:StopTheSound()
self:EmitSound(self:GetLastSound())
end
end
elseif(!self.User:KeyDown(IN_ATTACK)) then
if(self.SndPlaying) then
self:StopTheSound()
self:EmitSound(self:GetLastSound())
end
if(self.User:KeyDown(IN_ATTACK2) && self:GetFlare()) then
if(!self.FlareShot && self.FlareTimer < CurTime()) then
self.FlareShot = true
self.FlareTimer = CurTime() + 1
local flech = ents.Create("env_flare")
flech:SetPos(self.Gun:GetAttachment(1).Pos)
flech:SetAngles(self.Gun:GetAttachment(1).Ang)
flech:SetOwner(self.User)
flech:SetKeyValue( "scale", self:GetFlareScale() )
flech:Spawn()
flech:Activate()
self:EmitSound("Weapon_IRifle.Single")
flech:Fire("Launch", "3000", 0)
flech:Fire("Start", "30", 0.3)
end
else
self.FlareShot = false
end
if(self.RegenTimer < CurTime() && self.Ammo < self:GetAmmoCapacity() && self:GetRegenAmmo()) then
self.Ammo = self.Ammo + 1
self:SetNWInt("ammo", self.Ammo)
self.RegenTimer = CurTime() + self:GetRegenDelay()
end
end
local tobarrel = self.Gun:GetAttachment(1).Pos - self.User:EyePos()
tobarrel:Normalize()
if((self.User:GetPos():DistToSqr(self:GetPos()) > 10000 || !self.User:Alive() || (tobarrel:Dot(self.User:GetAimVector()) < 0.5 && false)) && !self.VehicleMode) then
self:EmitSound("weapons/shotgun/shotgun_cock.wav")
self.User:SetActiveWeapon(self.LastWeapon)
self.User.IsManningTheGun = false
self:SetNWBool("manned", false)
self.User = nil
self.UseTimer = CurTime() + 0.5
self:StopTheSound()
end
self:NextThink(CurTime())
return true
end
end
function ENT:Use(ply)
if(self.UseTimer < CurTime() && ply:GetPos():DistToSqr(self:GetPos()) < 7000 && !self.VehicleMode) then
if(self.User == nil && !ply.IsManningTheGun) then
self:EmitSound("weapons/shotgun/shotgun_cock.wav")
self.User = ply
self.LastWeapon = self.User:GetActiveWeapon()
self.User:SetActiveWeapon(nil)
self:SetNWBool("manned", true)
self.UseTimer = CurTime() + 1
self.User.IsManningTheGun = true
elseif(ply == self.User) then
self:EmitSound("weapons/shotgun/shotgun_cock.wav")
self.User:SelectWeapon(self.LastWeapon:GetClass() or "weapon_crowbar")
self.User.IsManningTheGun = false
self.User = nil
self:SetNWBool("manned", false)
self.UseTimer = CurTime() + 1
end
end
end
function ENT:OnRemove()
if(IsValid(self.LastWeapon) && IsValid(self.User)) then
self.User:SetActiveWeapon(self.LastWeapon)
self.User.IsManningTheGun = false
end
if(self.SndPlaying) then
self.LoopSound:Stop()
end
self.InfoTarget:Remove()
end
function ENT:PhysicsCollide(data, col)
if(string.find(data.HitEntity:GetClass(), self.AmmoEntities[self:GetAmmoEnt()][1]) != nil || (data.HitEntity:GetClass() == "prop_physics" && data.HitEntity:GetModel() == self.AmmoEntities[self:GetAmmoEnt()][4])) then
local bonus = 0
if(string.find(data.HitEntity:GetClass(), "_large") != nil) then
bonus = math.Round(self.AmmoEntities[self:GetAmmoEnt()][2] * 0.25)
end
if(self.Ammo < self:GetAmmoCapacity()) then
self.Ammo = math.min(self.Ammo + self.AmmoEntities[self:GetAmmoEnt()][2] + bonus, self:GetAmmoCapacity())
self:SetNWInt("ammo", self.Ammo)
self:EmitSound(self.PickupSounds[self.AmmoEntities[self:GetAmmoEnt()][3]])
data.HitEntity:Remove()
end
end
end
hook.Add("PlayerEnteredVehicle", "entity_tank_editable", function(ply, veh, role)
for k, v in pairs(constraint.FindConstraints(veh, "Weld")) do
local ent
if(v.Ent1:GetClass() == "entity_tank_editable") then
ent = v.Ent1
elseif(v.Ent2:GetClass() == "entity_tank_editable") then
ent = v.Ent2
else
continue
end
if(ent.User != nil) then
ent.User:SetActiveWeapon(ent.LastWeapon)
ent.User.IsManningTheGun = false
if(ent:GetSndLoop()) then
ent.LoopSound:Stop()
end
end
ent:EmitSound("weapons/shotgun/shotgun_cock.wav")
ent:SetNWBool("manned", true)
ent.User = ply
ent.VehicleMode = true
veh.Turret = ent
ent.LastWeapon = ply:GetActiveWeapon()
ent.Vehicle = veh
break
end
end)
hook.Add("PlayerLeaveVehicle", "entity_tank_editable", function(ply, veh)
if(veh.Turret != nil) then
local ent = veh.Turret
ent:EmitSound("weapons/shotgun/shotgun_cock.wav")
ent.User.IsManningTheGun = false
ent.User = nil
veh.Turret = nil
ent:SetNWBool("manned", false)
ent.VehicleMode = false
if(ent:GetSndLoop()) then
ent.LoopSound:Stop()
end
ent.Vehicle = nil
ply:SetActiveWeapon(ent.LastWeapon)
end
end)
duplicator.RegisterEntityClass("entity_tank_editable", function(ply, data)
data.Gun = nil
data.User = nil
data.VehicleMode = false
data.UseTimer = 0
data.ShootTimer = 0
data.FlareTimer = 0
data.RegenTimer = 0
data.LerpTime = 0
data.BurstTimer = 0
data.Burst = 0
data.SndPlaying = false
data.LoopSound = nil
data.Vehicle = nil
data.InfoTarget = nil
return duplicator.GenericDuplicatorFunction(ply, data)
end, "Data")
end

View File

@@ -0,0 +1,110 @@
--[[
| 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()
ENT.Type = "anim"
ENT.PrintName = "entity_tank_rocket"
ENT.Spawnable = false
ENT.AdminOnly = false
if(SERVER) then
function ENT:Initialize()
self.Target = nil
self.Owner = nil
self.TargetDir = nil
self.GraceTime = 0.3
self.GraceTimer = 0
self.OldAng = self:GetAngles()
self.NewAng = self:GetAngles()
self.Snd = CreateSound(self, "weapons/rpg/rocket1.wav")
self.Snd:Play()
self.Offset = Vector(0, 0, 0)
self:SetModel("models/weapons/w_missile_closed.mdl")
self:PhysicsInit(SOLID_VPHYSICS)
self:SetMoveType(MOVETYPE_VPHYSICS)
self:SetSolid(SOLID_VPHYSICS)
local phys = self:GetPhysicsObject()
if phys:IsValid() then
phys:Wake()
end
self.Trail = ents.Create("env_rockettrail")
self.Trail:SetPos(self:GetPos())
self.Trail:SetAngles(self:GetAngles())
self.Trail:SetParent(self)
self.Trail:Spawn()
end
function ENT:Think()
if(self.TargetAng == nil && !IsValid(self.Target)) then self:Remove() return false end
local ang
if(IsValid(self.Target)) then
ang = ( (self.Target:GetPos() + self.Offset) - self:GetPos()):Angle()
else
ang = self.TargetAng
end
if(self.GraceTimer < CurTime()) then
self.OldAng = self.NewAng
self.NewAng = ang
self.GraceTimer = CurTime() + self.GraceTime
end
self.NewAng = self.NewAng + AngleRand() * 0.01
self:GetPhysicsObject():SetVelocity(self:GetForward() * 1500)
self:SetAngles(LerpAngle((self.GraceTimer - CurTime()) / self.GraceTime, self.NewAng, self.OldAng))
self:NextThink(CurTime() + 0.05)
return true
end
function ENT:PhysicsCollide(data, collider)
if(data.HitNormal:Dot(self:GetForward()) > 0.2 && data.HitEntity:GetClass() != self:GetClass()) then
if(data.HitEntity != self.Owner) then
self:Explode(data.HitNormal)
end
end
end
function ENT:Explode(hitnormal)
local ent = ents.Create("env_explosion")
ent:SetPos(self:GetPos())
ent:SetKeyValue("iMagnitude", 70)
ent:Spawn()
ent:SetOwner(self.Owner)
ent:Fire("Explode", "", 0)
/*for i = 1, math.random(1, 1) do
local gib = ents.Create("prop_physics")
gib:SetModel("models/gibs/metal_gib" .. math.random(1, 5) .. ".mdl")
gib:SetPos(self:GetPos() + hitnormal * 100 + VectorRand())
gib:Spawn()
gib:GetPhysicsObject():ApplyForceCenter(VectorRand() * 0.1)
//gib:SetLocalAngularVelocity(AngleRand())
gib:Ignite(1, 2)
gib:Fire("Kill", "", 5)
end*/
self:Remove()
end
function ENT:OnRemove()
self.Snd:Stop()
end
end

View File

@@ -0,0 +1,154 @@
--[[
| 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/
--]]
--[[-------------------------------------------------------------------------
Changes the weather for players near it.
---------------------------------------------------------------------------]]
AddCSLuaFile()
DEFINE_BASECLASS( "base_anim" )
ENT.PrintName = "SF Atmo-Sphere"
ENT.Author = "Nak"
ENT.Information = "Changes the weather for players near it."
ENT.Category = "StormFox2"
ENT.Editable = true
ENT.Spawnable = true
ENT.AdminOnly = true
ENT.RenderGroup = RENDERGROUP_TRANSLUCENT
function ENT:Initialize()
if SERVER then
self:SetModel( "models/maxofs2d/hover_basic.mdl" )
self:PhysicsInit( SOLID_VPHYSICS )
self:SetMoveType( MOVETYPE_NONE )
self:SetSolid( SOLID_VPHYSICS )
self:SetCollisionGroup(COLLISION_GROUP_DEBRIS)
self:SetUseType( ONOFF_USE )
end
end
function ENT:SetupDataTables()
local weathers = {}
if CLIENT then
local s = language.GetPhrase("#none")
weathers[string.upper(s[1]) .. string.sub(s, 2)] = "none"
else
weathers["None"] = "one"
end
for _, str in ipairs( StormFox2.Weather.GetAll() ) do
if str == "BlueMoon" then continue end -- Shhh
weathers[StormFox2.Weather.Get(str).Name] = str
end
self:NetworkVar( "String", 0, "WeatherName", { KeyName = "Weather", Edit = { type = "Combo", order = 1, values = weathers } } )
self:NetworkVar( "Float", 0, "Percent", { KeyName = "Percent", Edit = { type = "Float", order = 2, min = 0, max = 1 } } )
self:NetworkVar( "Float", 1, "Temperature", { KeyName = "Temperature(C)", Edit = { type = "Int", order = 3, min = -20, max = 30 } } )
self:NetworkVar( "Int", 0, "Range", { KeyName = "Range", Edit = { type = "Int", order = 4, min = 250, max = 5000 } } )
if SERVER then
self:SetRange( 250 )
self:SetPercent( 0.5 )
end
end
function ENT:CanProperty(_, str)
if str == "skin" then return false
elseif str == "drive" then return false
elseif str == "collision" then return false
elseif str == "persist" then return true
end
return true
end
function ENT:UpdateTransmitState()
return TRANSMIT_ALWAYS
end
if SERVER then
function ENT:Think()
self:AddEFlags( EFL_FORCE_CHECK_TRANSMIT )
end
function ENT:SetWeather( str, amount, range, temp )
self:SetWeatherName( str )
self:SetPercent( amount )
if range then
self:SetRange( range )
end
if temp then
self:SetTemperature( temp )
end
end
else
local function HasToolgun()
local wep = LocalPlayer():GetActiveWeapon()
if not wep or not IsValid(wep) then return false end
if wep:GetClass() ~= "sf2_tool" then return false end
return true
end
local hasTool = false
local v1 = Vector(1,1,1)
local col1, col2 = Color(255,0,0,55),Color(0,255,0,255)
function ENT:Draw()
local w,p = self:GetWeatherName(),self:GetPercent()
local r = self:GetRange()
local we = StormFox2.Weather.Get(w)
if IsValid( we ) then
local c = CurTime()
local p = self:GetPos()
local np = p + Vector(0,0,math.sin( 3 * c))
local in_v = StormFox2.util.RenderPos():Distance( p ) < r
self:SetRenderBounds( v1 * -r, v1 )
self:SetRenderOrigin( np )
render.MaterialOverrideByIndex(0,Material("stormfox2/entities/env_weatherball_on"))
render.MaterialOverrideByIndex(1,Material("stormfox2/entities/env_weatherball_sphere"))
self:DrawModel()
render.MaterialOverrideByIndex()
self:SetRenderOrigin( )
-- (nTime, nTemp, nWind, bThunder,nFraction)
local symbol = we.GetIcon( StormFox2.Time.Get(), self:GetTemperature() or StormFox2.Temperature.Get(), StormFox2.Wind.GetForce(), StormFox2.Thunder.IsThundering(), self:GetPercent() )
render.SetMaterial( symbol )
render.DrawSprite( np , 8, 8, color_white)
self:SetRenderAngles( Angle(0,c * 40 % 360,0) )
else
self:DrawModel()
end
end
hook.Add("Think", "StormFox2.Weather.EController", function()
if not StormFox2 or StormFox2.Version < 2 then return end
if not StormFox2.Weather or not StormFox2.Weather.RemoveLocal then return end
hasTool = HasToolgun()
local t = {}
for _, ent in ipairs( ents.FindByClass("env_atmosphere") ) do
local p = ent:GetPos()
local r = ent:GetRange()
local dis = StormFox2.util.RenderPos():Distance( p )
local in_v = StormFox2.util.RenderPos():Distance( p ) < r
if in_v and IsValid(StormFox2.Weather.Get(ent:GetWeatherName() or "")) then table.insert(t, {ent, dis}) end
end
if #t < 1 then
StormFox2.Weather.RemoveLocal()
return
end
table.sort(t,function(a,b) return a[2] < b[2] end)
local ent = t[1][1]
if ent:GetPercent() <= 0 then
StormFox2.Weather.RemoveLocal()
else
StormFox2.Weather.SetLocal( ent:GetWeatherName(), ent:GetPercent(), 4, ent:GetTemperature())
end
end)
hook.Add("PreDrawHalos", "StormFox2.Atmosphere.Halo", function()
if not hasTool then return end
local d_tab = ents.FindByClass("env_atmosphere")
halo.Add( d_tab, color_white, 2, 2, 1, true,true )
end)
end

View File

@@ -0,0 +1,31 @@
--[[
| 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/
--]]
--[[-------------------------------------------------------------------------
A Material List
---------------------------------------------------------------------------]]
ENT.Type = "point"
ENT.Base = "base_point"
ENT.PrintName = "env_stormfox2_materials"
ENT.Author = "Nak"
ENT.Information = "Holds default material-types for the map"
ENT.Category = "StormFox2"
ENT.Editable = false
ENT.Spawnable = false
ENT.AdminOnly = false
function ENT:Initialize()
end
--[[
We handle the listing in the mapscanner instead, since we search for this type of entity, and generate the textree there.
]]

View File

@@ -0,0 +1,27 @@
--[[
| 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/
--]]
--[[-------------------------------------------------------------------------
A Setting Entity
---------------------------------------------------------------------------]]
ENT.Type = "point"
ENT.Base = "base_point"
ENT.PrintName = "env_stormfox2_settings"
ENT.Author = "Nak"
ENT.Information = "Holds default settings for the map"
ENT.Category = "StormFox2"
ENT.Editable = false
ENT.Spawnable = false
ENT.AdminOnly = false
function ENT:Initialize()
end

View File

@@ -0,0 +1,16 @@
--[[
| 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" )
function ENT:Draw()
self.BaseClass.Draw(self)
self.Entity:DrawModel()
end

View File

@@ -0,0 +1,310 @@
--[[
| 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/
--]]
--[[
Title: Adv. Dupe 2 Contraption Spawner
Desc: A mobile duplicator
Author: TB
Version: 1.0
]]
AddCSLuaFile( "cl_init.lua" )
AddCSLuaFile( "shared.lua" )
if(WireLib)then
include( "entities/base_wire_entity.lua" )
end
include( "shared.lua" )
function ENT:Initialize()
self.Entity:SetMoveType( MOVETYPE_NONE )
self.Entity:PhysicsInit( SOLID_VPHYSICS )
self.Entity:SetCollisionGroup( COLLISION_GROUP_WORLD )
self.Entity:DrawShadow( false )
local phys = self.Entity:GetPhysicsObject()
if phys:IsValid() then
phys:Wake()
end
self.UndoList = {}
self.Ghosts = {}
self.SpawnLastValue = 0
self.UndoLastValue = 0
self.LastSpawnTime = 0
self.CurrentPropCount = 0
if WireLib then
self.Inputs = Wire_CreateInputs(self.Entity, {"Spawn", "Undo"})
self.Outputs = WireLib.CreateSpecialOutputs(self.Entity, {"Out"}, { "NORMAL" })
end
end
/*-----------------------------------------------------------------------*
* Sets options for this spawner
*-----------------------------------------------------------------------*/
function ENT:SetOptions(ply, delay, undo_delay, key, undo_key, disgrav, disdrag, addvel, hideprops )
self.delay = delay
self.undo_delay = undo_delay
--Key bindings
self.key = key
self.undo_key = undo_key
numpad.Remove( self.CreateKey )
numpad.Remove( self.UndoKey )
self.CreateKey = numpad.OnDown( ply, self.key, "ContrSpawnerCreate", self.Entity, true )
self.UndoKey = numpad.OnDown( ply, self.undo_key, "ContrSpawnerUndo", self.Entity, true )
self.DisableGravity = disgrav
self.DisableDrag = disdrag
self.AddVelocity = addvel
self.HideProps = hideprops
self:ShowOutput()
end
function ENT:UpdateOptions( options )
self:SetOptions( options["delay"], options["undo_delay"], options["key"], options["undo_key"])
end
function ENT:AddGhosts()
if self.HideProps then return end
local moveable = self:GetPhysicsObject():IsMoveable()
self:GetPhysicsObject():EnableMotion(false)
local EntTable
local GhostEntity
local Offset = self.DupeAngle - self.EntAngle
local Phys
for EntIndex,v in pairs(self.EntityTable)do
if(EntIndex!=self.HeadEnt)then
if(self.EntityTable[EntIndex].Class=="gmod_contr_spawner")then self.EntityTable[EntIndex] = nil continue end
EntTable = table.Copy(self.EntityTable[EntIndex])
if(EntTable.BuildDupeInfo && EntTable.BuildDupeInfo.PhysicsObjects)then
Phys = EntTable.BuildDupeInfo.PhysicsObjects[0]
else
if(!v.BuildDupeInfo)then v.BuildDupeInfo = {} end
v.BuildDupeInfo.PhysicsObjects = table.Copy(v.PhysicsObjects)
Phys = EntTable.PhysicsObjects[0]
end
GhostEntity = nil
if(EntTable.Model==nil || !util.IsValidModel(EntTable.Model)) then EntTable.Model="models/error.mdl" end
if ( EntTable.Model:sub( 1, 1 ) == "*" ) then
GhostEntity = ents.Create( "func_physbox" )
else
GhostEntity = ents.Create( "gmod_ghost" )
end
// If there are too many entities we might not spawn..
if ( !GhostEntity || GhostEntity == NULL ) then return end
duplicator.DoGeneric( GhostEntity, EntTable )
GhostEntity:Spawn()
GhostEntity:DrawShadow( false )
GhostEntity:SetMoveType( MOVETYPE_NONE )
GhostEntity:SetSolid( SOLID_VPHYSICS );
GhostEntity:SetNotSolid( true )
GhostEntity:SetRenderMode( RENDERMODE_TRANSALPHA )
GhostEntity:SetColor( Color(255, 255, 255, 150) )
GhostEntity:SetAngles(Phys.Angle)
GhostEntity:SetPos(self:GetPos() + Phys.Pos - self.Offset)
self:SetAngles(self.EntAngle)
GhostEntity:SetParent( self )
self:SetAngles(self.DupeAngle)
self.Ghosts[EntIndex] = GhostEntity
end
end
self:SetAngles(self.DupeAngle)
self:GetPhysicsObject():EnableMotion(moveable)
end
function ENT:GetCreationDelay() return self.delay end
function ENT:GetDeletionDelay() return self.undo_delay end
function ENT:OnTakeDamage( dmginfo ) self.Entity:TakePhysicsDamage( dmginfo ) end
function ENT:SetDupeInfo( HeadEnt, EntityTable, ConstraintTable )
self.HeadEnt = HeadEnt
self.EntityTable = EntityTable
self.ConstraintTable = ConstraintTable
if(!self.DupeAngle)then self.DupeAngle = self:GetAngles() end
if(!self.EntAngle)then self.EntAngle = EntityTable[HeadEnt].PhysicsObjects[0].Angle end
if(!self.Offset)then self.Offset = self.EntityTable[HeadEnt].PhysicsObjects[0].Pos end
local headpos, headang = EntityTable[HeadEnt].PhysicsObjects[0].Pos, EntityTable[HeadEnt].PhysicsObjects[0].Angle
for k, v in pairs(EntityTable) do
for o, p in pairs(v.PhysicsObjects) do
p.LPos, p.LAngle = WorldToLocal(p.Pos, p.Angle, headpos, headang)
end
end
end
function ENT:DoSpawn( ply )
-- Explicitly allow spawning if no player is provided, but an invalid player gets denied. This can happen when a player leaves the server.
if not (ply and ply:IsValid()) then return end
for k, v in pairs(self.EntityTable) do
for o, p in pairs(v.PhysicsObjects) do
p.Pos, p.Angle = self:LocalToWorld(p.LPos), self:LocalToWorldAngles(p.LAngle)
end
end
/*local AngleOffset = self.EntAngle
AngleOffset = self:GetAngles() - AngleOffset
local AngleOffset2 = Angle(0,0,0)
//AngleOffset2.y = AngleOffset.y
AngleOffset2:RotateAroundAxis(self:GetUp(), AngleOffset.y)
AngleOffset2:RotateAroundAxis(self:GetRight(),AngleOffset.p)
AngleOffset2:RotateAroundAxis(self:GetForward(),AngleOffset.r)*/
local Ents, Constrs = AdvDupe2.duplicator.Paste(ply, self.EntityTable, self.ConstraintTable, nil, nil, Vector(0,0,0), true)
local i = #self.UndoList+1
self.UndoList[i] = Ents
undo.Create("contraption_spawns")
local phys
for k,ent in pairs(Ents)do
phys = ent:GetPhysicsObject()
if IsValid(phys) then
phys:Wake()
if(self.DisableGravity==1)then phys:EnableGravity(false) end
if(self.DisableDrag==1)then phys:EnableDrag(false) end
phys:EnableMotion(true)
if(ent.SetForce)then ent.SetForce(ent, ent.force, ent.mul) end
if(self.AddVelocity==1)then
phys:SetVelocity( self:GetVelocity() )
phys:AddAngleVelocity( self:GetPhysicsObject():GetAngleVelocity() )
end
end
undo.AddEntity(ent)
end
undo.SetPlayer(ply)
undo.Finish()
if(self.undo_delay>0)then
timer.Simple(self.undo_delay, function()
if(self.UndoList && self.UndoList[i])then
for k,ent in pairs(self.UndoList[i]) do
if(IsValid(ent)) then
ent:Remove()
end
end
end
end)
end
end
function ENT:DoUndo( ply )
if(!self.UndoList || #self.UndoList == 0)then return end
local entities = self.UndoList[ #self.UndoList ]
self.UndoList[ #self.UndoList ] = nil
for _,ent in pairs(entities) do
if (IsValid(ent)) then
ent:Remove()
end
end
end
function ENT:TriggerInput(iname, value)
local ply = self:GetPlayer()
if(iname == "Spawn")then
if ((value > 0) == self.SpawnLastValue) then return end
self.SpawnLastValue = (value > 0)
if(self.SpawnLastValue)then
local delay = self:GetCreationDelay()
if (delay == 0) then self:DoSpawn( ply ) return end
if(CurTime() < self.LastSpawnTime)then return end
self:DoSpawn( ply )
self.LastSpawnTime=CurTime()+delay
end
elseif (iname == "Undo") then
// Same here
if((value > 0) == self.UndoLastValue)then return end
self.UndoLastValue = (value > 0)
if(self.UndoLastValue)then self:DoUndo(ply) end
end
end
local text2 = {"Enabled", "Disabled"}
function ENT:ShowOutput()
local text = "\nGravity: "
if(self.DisableGravity==1)then text=text.."Enabled" else text=text.."Disabled" end
text=text.."\nDrag: "
if(self.DisableDrag==1)then text=text.."Enabled" else text=text.."Disabled" end
text=text.."\nVelocity: "
if(self.AddVelocity==1)then text=text.."Enabled" else text=text.."Disabled" end
self.Entity:SetOverlayText(
"Spawn Delay: " .. tostring(self:GetCreationDelay()) ..
"\nUndo Delay: ".. tostring(self:GetDeletionDelay()) ..
text
)
end
/*-----------------------------------------------------------------------*
* Handler for spawn keypad input
*-----------------------------------------------------------------------*/
function SpawnContrSpawner( ply, ent )
if (!ent || !ent:IsValid()) then return end
local delay = ent:GetTable():GetCreationDelay()
if(delay == 0) then
ent:DoSpawn( ply )
return
end
if(CurTime() < ent.LastSpawnTime)then return end
ent:DoSpawn( ply )
ent.LastSpawnTime=CurTime()+delay
end
/*-----------------------------------------------------------------------*
* Handler for undo keypad input
*-----------------------------------------------------------------------*/
function UndoContrSpawner( ply, ent )
if (!ent || !ent:IsValid()) then return end
ent:DoUndo( ply, true )
end
numpad.Register( "ContrSpawnerCreate", SpawnContrSpawner )
numpad.Register( "ContrSpawnerUndo", UndoContrSpawner )

View File

@@ -0,0 +1,20 @@
--[[
| 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.Base = WireLib and "base_wire_entity" or "base_gmodentity"
ENT.PrintName = "Contraption Spawner"
ENT.Author = "TB"
ENT.Contact = ""
ENT.Purpose = ""
ENT.Instructions = ""
ENT.Spawnable = false
ENT.AdminSpawnable = false

View File

@@ -0,0 +1,75 @@
--[[
| 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()
ENT.Type = "anim"
ENT.PrintName = "Tires"
ENT.Author = "Luna"
ENT.Information = "Fixes FakePhysics Wheels"
ENT.Category = "[LVS]"
ENT.Spawnable = true
ENT.AdminOnly = false
if SERVER then
function ENT:SpawnFunction( ply, tr, ClassName )
if not tr.Hit then return end
local ent = ents.Create( ClassName )
ent:SetPos( tr.HitPos + tr.HitNormal * 10 )
ent:SetAngles( Angle(90,0,0) )
ent:Spawn()
ent:Activate()
return ent
end
function ENT:OnTakeDamage( dmginfo )
end
function ENT:Initialize()
self:SetModel( "models/props_vehicles/tire001c_car.mdl" )
self:PhysicsInit( SOLID_VPHYSICS )
self:PhysWake()
end
function ENT:Fix( entity )
if self.AlreadyUsed or not IsValid( entity ) then return end
if entity:GetClass() ~= "gmod_sent_vehicle_fphysics_wheel" then return end
local Damaged = entity:GetDamaged()
if not Damaged then return end
entity:FixTire()
entity:EmitSound("npc/dog/dog_servo1.wav")
self.AlreadyUsed = true
SafeRemoveEntityDelayed( self, 0 )
end
function ENT:PhysicsCollide( data, physobj )
self:Fix( data.HitEntity )
end
function ENT:Think()
return false
end
return
end
function ENT:Draw( flags )
self:DrawModel( flags )
end

View File

@@ -0,0 +1,43 @@
--[[
| 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()
ENT.Type = "anim"
ENT.Spawnable = false
ENT.AdminSpawnable = false
ENT.RenderGroup = RENDERGROUP_BOTH
function ENT:Think()
return false
end
if SERVER then
function ENT:Initialize()
self:PhysicsInit( SOLID_VPHYSICS )
self:SetMoveType( MOVETYPE_VPHYSICS )
self:SetSolid( SOLID_VPHYSICS )
self:SetRenderMode( RENDERMODE_TRANSALPHA )
self:SetNotSolid( true )
self.DoNotDuplicate = true
end
end
if CLIENT then
function ENT:Draw( flags )
self:DrawModel( flags )
end
function ENT:DrawTranslucent( flags )
self:DrawModel( flags )
end
end

View File

@@ -0,0 +1,677 @@
--[[
| 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("cl_tiresounds.lua")
function ENT:LVSHudPaint( X, Y, ply )
local Pos2D = {
visible = true,
x = ScrW() * 0.5,
y = ScrH() * 0.5,
}
self:LVSPaintHitMarker( Pos2D )
end
function ENT:GetViewOverride()
return self.customview or vector_origin
end
function ENT:LVSCalcView( ply, pos, angles, fov, pod )
if pod:GetNWBool( "simfphys_SpecialCam" ) then return end
local IsDriverSeat = ply == self:GetDriver()
if IsDriverSeat and ply:lvsMouseAim() then
angles = ply:EyeAngles()
end
self.ZoomFov = fov
local view_origin = pos
if not pod:GetThirdPersonMode() then
local viewoverride = self:GetViewOverride()
local X = viewoverride.X
local Y = viewoverride.Y
local Z = viewoverride.Z
view_origin = view_origin + (IsDriverSeat and pod:GetForward() * X + pod:GetRight() * Y + pod:GetUp() * Z or pod:GetUp() * 5)
end
if not IsDriverSeat then return LVS:CalcView( self, ply, view_origin, angles, fov, pod ) end
return LVS:CalcView( self, ply, view_origin, angles, fov, pod )
end
function ENT:Initialize()
self.SmoothRPM = 0
self.OldDist = 0
self.PitchOffset = 0
self.OldActive = false
self.OldGear = 0
self.OldThrottle = 0
self.FadeThrottle = 0
self.SoundMode = 0
self.DamageSnd = CreateSound(self, "simulated_vehicles/engine_damaged.wav")
self.EngineSounds = {}
end
function ENT:Think()
local T = CurTime()
local Active = self:GetActive()
local Throttle = self:GetThrottle()
local LimitRPM = self:GetLimitRPM()
self:ManageSounds( Active, Throttle, LimitRPM )
self.RunNext = self.RunNext or 0
if self.RunNext < T then
self:ManageEffects( Active, Throttle, LimitRPM )
self:CalcFlasher()
self.RunNext = T + 0.06
end
self:SetPoseParameters( T )
self:TireSoundThink()
end
function ENT:CalcFlasher()
self.Flasher = self.Flasher or 0
local flashspeed = self.turnsignals_damaged and 0.06 or 0.0375
self.Flasher = self.Flasher and self.Flasher + flashspeed or 0
if self.Flasher >= 1 then
self.Flasher = self.Flasher - 1
end
self.flashnum = math.min( math.abs( math.cos( math.rad( self.Flasher * 360 ) ) ^ 2 * 1.5 ) , 1)
if not self.signal_left and not self.signal_right then return end
if LocalPlayer() == self:GetDriver() then
local fl_snd = self.flashnum > 0.9
if fl_snd ~= self.fl_snd then
self.fl_snd = fl_snd
if fl_snd then
self:EmitSound( "simulated_vehicles/sfx/flasher_on.ogg" )
else
self:EmitSound( "simulated_vehicles/sfx/flasher_off.ogg" )
end
end
end
end
function ENT:GetFlasher()
self.flashnum = self.flashnum or 0
return self.flashnum
end
function ENT:SetPoseParameters( curtime )
self.sm_vSteer = self.sm_vSteer and self.sm_vSteer + (self:GetVehicleSteer() - self.sm_vSteer) * 0.3 or 0
self:SetPoseParameter("vehicle_steer", self.sm_vSteer )
if not istable( self.pp_data ) then
self.ppNextCheck = self.ppNextCheck or curtime + 0.5
if self.ppNextCheck < curtime then
self.ppNextCheck = curtime + 0.5
net.Start("simfphys_request_ppdata",true)
net.WriteEntity( self )
net.SendToServer()
end
else
if not self.CustomWheels then
for i = 1, table.Count( self.pp_data ) do
local Wheel = self.pp_data[i].entity
if IsValid( Wheel ) then
local addPos = Wheel:GetDamaged() and self.pp_data[i].dradius or 0
local Pose = (self.pp_data[i].pos - self:WorldToLocal( Wheel:GetPos()).z + addPos ) / self.pp_data[i].travel
self:SetPoseParameter( self.pp_data[i].name, Pose )
end
end
end
end
self:InvalidateBoneCache()
end
function ENT:GetEnginePos()
if isvector( self.EnginePos ) then return self:LocalToWorld( self.EnginePos ) end
local Attachment = self:GetAttachment( self:LookupAttachment( "vehicle_engine" ) )
if Attachment then return Attachment.Pos end
return self:GetPos()
end
function ENT:GetRPM()
local RPM = self.SmoothRPM and self.SmoothRPM or 0
return RPM
end
function ENT:DamageEffects()
local Pos = self:GetEnginePos()
local Scale = self:GetCurHealth() / self:GetMaxHealth()
local smoke = self:OnSmoke() and Scale <= 0.5
local fire = self:OnFire()
if self.wasSmoke ~= smoke then
self.wasSmoke = smoke
if smoke then
self.smokesnd = CreateSound(self, "ambient/gas/steam2.wav")
self.smokesnd:PlayEx(0.2,90)
else
if self.smokesnd then
self.smokesnd:Stop()
end
end
end
if self.wasFire ~= fire then
self.wasFire = fire
if fire then
self:EmitSound( "ambient/fire/mtov_flame2.wav" )
self.firesnd = CreateSound(self, "ambient/fire/fire_small1.wav")
self.firesnd:Play()
else
if self.firesnd then
self.firesnd:Stop()
end
end
end
if smoke then
if Scale <= 0.5 then
local HP = self:GetCurHealth()
local MaxHP = self:GetMaxHealth()
local effectdata = EffectData()
effectdata:SetOrigin( Pos )
effectdata:SetEntity( self )
effectdata:SetMagnitude( math.max(HP - MaxHP * 0.25,0) / (MaxHP * 0.25) )
util.Effect( "simfphys_engine_smoke", effectdata )
end
end
if fire then
local effectdata = EffectData()
effectdata:SetOrigin( Pos )
effectdata:SetEntity( self )
util.Effect( "simfphys_engine_fire", effectdata )
end
end
function ENT:ManageEffects( Active, fThrottle, LimitRPM )
self:DamageEffects()
Active = Active and (self:GetFlyWheelRPM() ~= 0)
if not Active then return end
if not self.ExhaustPositions then return end
local Scale = fThrottle * (0.2 + math.min(self:GetRPM() / LimitRPM,1) * 0.8) ^ 2
for i = 1, table.Count( self.ExhaustPositions ) do
if self.ExhaustPositions[i].OnBodyGroups then
if self:BodyGroupIsValid( self.ExhaustPositions[i].OnBodyGroups ) then
local effectdata = EffectData()
effectdata:SetOrigin( self.ExhaustPositions[i].pos )
effectdata:SetAngles( self.ExhaustPositions[i].ang )
effectdata:SetMagnitude( Scale )
effectdata:SetEntity( self )
util.Effect( "simfphys_exhaust", effectdata )
end
else
local effectdata = EffectData()
effectdata:SetOrigin( self.ExhaustPositions[i].pos )
effectdata:SetAngles( self.ExhaustPositions[i].ang )
effectdata:SetMagnitude( Scale )
effectdata:SetEntity( self )
util.Effect( "simfphys_exhaust", effectdata )
end
end
end
function ENT:ManageSounds( Active, fThrottle, LimitRPM )
local EngineVolume = LVS.EngineVolume
local FlyWheelRPM = self:GetFlyWheelRPM()
local Active = Active and (FlyWheelRPM ~= 0)
local IdleRPM = self:GetIdleRPM()
local IsCruise = self:GetIsCruiseModeOn()
local CurDist = (LocalPlayer():GetPos() - self:GetPos()):Length()
local Throttle = IsCruise and math.Clamp(self:GetThrottle() ^ 3,0.01,0.7) or fThrottle
local Gear = self:GetGear()
local Clutch = self:GetClutch()
local FadeRPM = LimitRPM * 0.5
local FT = FrameTime()
local Rate = 3.33 * FT
self.FadeThrottle = self.FadeThrottle + math.Clamp(Throttle - self.FadeThrottle,-Rate,Rate)
self.PitchOffset = self.PitchOffset + ((CurDist - self.OldDist) * 0.23 - self.PitchOffset) * 0.5
self.OldDist = CurDist
self.SmoothRPM = self.SmoothRPM + math.Clamp(FlyWheelRPM - self.SmoothRPM,-0.972 * FT * LimitRPM,1.66 * FT * LimitRPM)
self.OldThrottle2 = self.OldThrottle2 or 0
if Throttle ~= self.OldThrottle2 then
self.OldThrottle2 = Throttle
if Throttle == 0 then
if self.SmoothRPM > LimitRPM * 0.6 then
self:Backfire()
end
end
end
if self:GetRevlimiter() and LimitRPM > 2500 then
if (self.SmoothRPM >= LimitRPM - 200) and self.FadeThrottle > 0 then
self.SmoothRPM = self.SmoothRPM - 0.2 * LimitRPM
self.FadeThrottle = 0.2
self:Backfire()
end
end
if Active ~= self.OldActive then
local preset = self:GetEngineSoundPreset()
local UseGearResetter = self:SetSoundPreset( preset )
self.SoundMode = UseGearResetter and 2 or 1
self.OldActive = Active
if Active then
local MaxHealth = self:GetMaxHealth()
local Health = self:GetCurHealth()
if Health <= (MaxHealth * 0.6) then
self.DamageSnd:PlayEx(0,0)
end
if self.SoundMode == 2 then
self.HighRPM = CreateSound(self, self.EngineSounds[ "HighRPM" ] )
self.LowRPM = CreateSound(self, self.EngineSounds[ "LowRPM" ])
self.Idle = CreateSound(self, self.EngineSounds[ "Idle" ])
self.HighRPM:PlayEx(0,0)
self.LowRPM:PlayEx(0,0)
self.Idle:PlayEx(0,0)
else
local IdleSound = self.EngineSounds[ "IdleSound" ]
local LowSound = self.EngineSounds[ "LowSound" ]
local HighSound = self.EngineSounds[ "HighSound" ]
local ThrottleSound = self.EngineSounds[ "ThrottleSound" ]
if IdleSound then
self.Idle = CreateSound(self, IdleSound)
self.Idle:PlayEx(0,0)
end
if LowSound then
self.LowRPM = CreateSound(self, LowSound)
self.LowRPM:PlayEx(0,0)
end
if HighSound then
self.HighRPM = CreateSound(self, HighSound)
self.HighRPM:PlayEx(0,0)
end
if ThrottleSound then
self.Valves = CreateSound(self, ThrottleSound)
self.Valves:PlayEx(0,0)
end
end
else
self:SaveStopSounds()
end
end
if Active then
local Volume = 0.25 + 0.25 * ((self.SmoothRPM / LimitRPM) ^ 1.5) + self.FadeThrottle * 0.5
local Pitch = math.Clamp( (20 + self.SmoothRPM / 50 - self.PitchOffset) * self.PitchMulAll,0,255)
if self.DamageSnd then
self.DamageSnd:ChangeVolume( ((self.SmoothRPM / LimitRPM) * 0.6 ^ 1.5) * EngineVolume )
self.DamageSnd:ChangePitch( 100 )
end
if self.SoundMode == 2 then
if self.FadeThrottle ~= self.OldThrottle then
self.OldThrottle = self.FadeThrottle
if self.FadeThrottle == 0 and Clutch == 0 then
if self.SmoothRPM >= FadeRPM then
if IsCruise ~= true then
if self.LowRPM then
self.LowRPM:Stop()
end
self.LowRPM = CreateSound(self, self.EngineSounds[ "RevDown" ] )
self.LowRPM:PlayEx(0,0)
end
end
end
end
if Gear ~= self.OldGear then
if self.SmoothRPM >= FadeRPM and Gear > 3 then
if Clutch ~= 1 then
if self.OldGear < Gear then
if self.HighRPM then
self.HighRPM:Stop()
end
self.HighRPM = CreateSound(self, self.EngineSounds[ "ShiftUpToHigh" ] )
self.HighRPM:PlayEx(0,0)
if self.SmoothRPM > LimitRPM * 0.6 then
if math.random(0,4) >= 3 then
timer.Simple(0.4, function()
if not IsValid( self ) then return end
self:Backfire()
end)
end
end
else
if self.FadeThrottle > 0 then
if self.HighRPM then
self.HighRPM:Stop()
end
self.HighRPM = CreateSound(self, self.EngineSounds[ "ShiftDownToHigh" ] )
self.HighRPM:PlayEx(0,0)
end
end
end
else
if Clutch ~= 1 then
if self.OldGear > Gear and self.FadeThrottle > 0 and Gear >= 3 then
if self.HighRPM then
self.HighRPM:Stop()
end
self.HighRPM = CreateSound(self, self.EngineSounds[ "ShiftDownToHigh" ] )
self.HighRPM:PlayEx(0,0)
else
if self.HighRPM then
self.HighRPM:Stop()
end
if self.LowRPM then
self.LowRPM:Stop()
end
self.HighRPM = CreateSound(self, self.EngineSounds[ "HighRPM" ] )
self.LowRPM = CreateSound(self, self.EngineSounds[ "LowRPM" ])
self.HighRPM:PlayEx(0,0)
self.LowRPM:PlayEx(0,0)
end
end
end
self.OldGear = Gear
end
self.Idle:ChangeVolume( math.Clamp( math.min((self.SmoothRPM / IdleRPM) * 3,1.5 + self.FadeThrottle * 0.5) * 0.7 - self.SmoothRPM / 2000 ,0,1) * EngineVolume )
self.Idle:ChangePitch( math.Clamp( Pitch * 3,0,255) )
self.LowRPM:ChangeVolume( math.Clamp(Volume - (self.SmoothRPM - 2000) / 2000 * self.FadeThrottle,0,1) * EngineVolume )
self.LowRPM:ChangePitch( math.Clamp( Pitch * self.PitchMulLow,0,255) )
local hivol = math.max((self.SmoothRPM - 2000) / 2000,0) * Volume
self.HighRPM:ChangeVolume( (self.FadeThrottle < 0.4 and hivol * self.FadeThrottle or hivol * self.FadeThrottle * 2.5) * EngineVolume )
self.HighRPM:ChangePitch( math.Clamp( Pitch * self.PitchMulHigh,0,255) )
else
if Gear ~= self.OldGear then
if self.SmoothRPM >= FadeRPM and Gear > 3 then
if Clutch ~= 1 then
if self.OldGear < Gear then
if self.SmoothRPM > LimitRPM * 0.6 then
if math.random(0,4) >= 3 then
timer.Simple(0.4, function()
if not IsValid( self ) then return end
self:Backfire()
end)
end
end
end
end
end
self.OldGear = Gear
end
local IdlePitch = self.Idle_PitchMul
self.Idle:ChangeVolume( math.Clamp( math.min((self.SmoothRPM / IdleRPM) * 3,1.5 + self.FadeThrottle * 0.5) * 0.7 - self.SmoothRPM / 2000,0,1) * EngineVolume )
self.Idle:ChangePitch( math.Clamp( Pitch * 3 * IdlePitch,0,255) )
local LowPitch = self.Mid_PitchMul
local LowVolume = self.Mid_VolumeMul
local LowFadeOutRPM = LimitRPM * (self.Mid_FadeOutRPMpercent / 100)
local LowFadeOutRate = LimitRPM * self.Mid_FadeOutRate
self.LowRPM:ChangeVolume( math.Clamp( (Volume - math.Clamp((self.SmoothRPM - LowFadeOutRPM) / LowFadeOutRate,0,1)) * LowVolume,0,1) * EngineVolume )
self.LowRPM:ChangePitch( math.Clamp(Pitch * LowPitch,0,255) )
local HighPitch = self.High_PitchMul
local HighVolume = self.High_VolumeMul
local HighFadeInRPM = LimitRPM * (self.High_FadeInRPMpercent / 100)
local HighFadeInRate = LimitRPM * self.High_FadeInRate
self.HighRPM:ChangeVolume( math.Clamp( math.Clamp((self.SmoothRPM - HighFadeInRPM) / HighFadeInRate,0,Volume) * HighVolume,0,1) * EngineVolume)
self.HighRPM:ChangePitch( math.Clamp(Pitch * HighPitch,0,255) )
local ThrottlePitch = self.Throttle_PitchMul
local ThrottleVolume = self.Throttle_VolumeMul
self.Valves:ChangeVolume( math.Clamp((self.SmoothRPM - 2000) / 2000,0,Volume) * (0.2 + 0.15 * self.FadeThrottle) * ThrottleVolume * EngineVolume )
self.Valves:ChangePitch( math.Clamp(Pitch * ThrottlePitch,0,255) )
end
end
end
function ENT:Backfire( damaged )
if not self:GetBackFire() and not damaged then return end
if not self.ExhaustPositions then return end
local expos = self.ExhaustPositions
for i = 1, table.Count( expos ) do
if math.random(1,3) >= 2 or damaged then
local Pos = expos[i].pos
local Ang = expos[i].ang - Angle(90,0,0)
if expos[i].OnBodyGroups then
if self:BodyGroupIsValid( expos[i].OnBodyGroups ) then
local effectdata = EffectData()
effectdata:SetOrigin( Pos )
effectdata:SetAngles( Ang )
effectdata:SetEntity( self )
effectdata:SetFlags( damaged and 1 or 0 )
util.Effect( "simfphys_backfire", effectdata )
end
else
local effectdata = EffectData()
effectdata:SetOrigin( Pos )
effectdata:SetAngles( Ang )
effectdata:SetEntity( self )
effectdata:SetFlags( damaged and 1 or 0 )
util.Effect( "simfphys_backfire", effectdata )
end
end
end
end
function ENT:SetSoundPreset(index)
if index == -1 then
local soundoverride = self:GetSoundoverride()
local data = string.Explode( ",", soundoverride)
if soundoverride ~= "" and data[1] == "1" then
self.EngineSounds[ "Idle" ] = data[4]
self.EngineSounds[ "LowRPM" ] = data[6]
self.EngineSounds[ "HighRPM" ] = data[2]
self.EngineSounds[ "RevDown" ] = data[8]
self.EngineSounds[ "ShiftUpToHigh" ] = data[10]
self.EngineSounds[ "ShiftDownToHigh" ] = data[9]
self.PitchMulLow = data[7]
self.PitchMulHigh = data[3]
self.PitchMulAll = data[5]
else
local idle = self.snd_idle or ""
local low = self.snd_low or ""
local mid = self.snd_mid or ""
local revdown = self.snd_low_revdown or ""
local gearup = self.snd_mid_gearup or ""
local geardown = self.snd_mid_geardown or ""
self.EngineSounds[ "Idle" ] = idle ~= "" and idle or false
self.EngineSounds[ "LowRPM" ] = low ~= "" and low or false
self.EngineSounds[ "HighRPM" ] = mid ~= "" and mid or false
self.EngineSounds[ "RevDown" ] = revdown ~= "" and revdown or low
self.EngineSounds[ "ShiftUpToHigh" ] = gearup ~= "" and gearup or mid
self.EngineSounds[ "ShiftDownToHigh" ] = geardown ~= "" and geardown or gearup
self.PitchMulLow = self.snd_low_pitch or 1
self.PitchMulHigh = self.snd_mid_pitch or 1
self.PitchMulAll = self.snd_pitch or 1
end
if self.EngineSounds[ "Idle" ] ~= false and self.EngineSounds[ "LowRPM" ] ~= false and self.EngineSounds[ "HighRPM" ] ~= false then
return true
else
self:SetSoundPreset( 0 )
return false
end
end
if index == 0 then
local soundoverride = self:GetSoundoverride()
local data = string.Explode( ",", soundoverride)
if soundoverride ~= "" and data[1] ~= "1" then
self.EngineSounds[ "IdleSound" ] = data[1]
self.Idle_PitchMul = data[2]
self.EngineSounds[ "LowSound" ] = data[3]
self.Mid_PitchMul = data[4]
self.Mid_VolumeMul = data[5]
self.Mid_FadeOutRPMpercent = data[6]
self.Mid_FadeOutRate = data[7]
self.EngineSounds[ "HighSound" ] = data[8]
self.High_PitchMul = data[9]
self.High_VolumeMul = data[10]
self.High_FadeInRPMpercent = data[11]
self.High_FadeInRate = data[12]
self.EngineSounds[ "ThrottleSound" ] = data[13]
self.Throttle_PitchMul = data[14]
self.Throttle_VolumeMul = data[15]
else
self.EngineSounds[ "IdleSound" ] = self.Sound_Idle or "simulated_vehicles/misc/e49_idle.wav"
self.Idle_PitchMul = self.Sound_IdlePitch or 1
self.EngineSounds[ "LowSound" ] = self.Sound_Mid or "simulated_vehicles/misc/gto_onlow.wav"
self.Mid_PitchMul = self.Sound_MidPitch or 1
self.Mid_VolumeMul = self.Sound_MidVolume or 0.75
self.Mid_FadeOutRPMpercent = self.Sound_MidFadeOutRPMpercent or 68
self.Mid_FadeOutRate = self.Sound_MidFadeOutRate or 0.4
self.EngineSounds[ "HighSound" ] = self.Sound_High or "simulated_vehicles/misc/nv2_onlow_ex.wav"
self.High_PitchMul = self.Sound_HighPitch or 1
self.High_VolumeMul = self.Sound_HighVolume or 1
self.High_FadeInRPMpercent = self.Sound_HighFadeInRPMpercent or 26.6
self.High_FadeInRate = self.Sound_HighFadeInRate or 0.266
self.EngineSounds[ "ThrottleSound" ] = self.Sound_Throttle or "simulated_vehicles/valve_noise.wav"
self.Throttle_PitchMul = self.Sound_ThrottlePitch or 0.65
self.Throttle_VolumeMul = self.Sound_ThrottleVolume or 1
end
self.PitchMulLow = 1
self.PitchMulHigh = 1
self.PitchMulAll = 1
return false
end
if index > 0 then
local clampindex = math.Clamp(index,1,table.Count(simfphys.SoundPresets))
self.EngineSounds[ "Idle" ] = simfphys.SoundPresets[clampindex][1]
self.EngineSounds[ "LowRPM" ] = simfphys.SoundPresets[clampindex][2]
self.EngineSounds[ "HighRPM" ] = simfphys.SoundPresets[clampindex][3]
self.EngineSounds[ "RevDown" ] = simfphys.SoundPresets[clampindex][4]
self.EngineSounds[ "ShiftUpToHigh" ] = simfphys.SoundPresets[clampindex][5]
self.EngineSounds[ "ShiftDownToHigh" ] = simfphys.SoundPresets[clampindex][6]
self.PitchMulLow = simfphys.SoundPresets[clampindex][7]
self.PitchMulHigh = simfphys.SoundPresets[clampindex][8]
self.PitchMulAll = simfphys.SoundPresets[clampindex][9]
return true
end
return false
end
function ENT:GetVehicleInfo()
return self.VehicleInfo
end
function ENT:SaveStopSounds()
if self.HighRPM then
self.HighRPM:Stop()
end
if self.LowRPM then
self.LowRPM:Stop()
end
if self.Idle then
self.Idle:Stop()
end
if self.Valves then
self.Valves:Stop()
end
if self.DamageSnd then
self.DamageSnd:Stop()
end
end
function ENT:StopFireSound()
if self.smokesnd then
self.smokesnd:Stop()
end
if self.firesnd then
self.firesnd:Stop()
end
end
function ENT:OnRemove()
self:SaveStopSounds()
self:StopFireSound()
self:TireSoundRemove()
end

View File

@@ -0,0 +1,91 @@
--[[
| 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.TireSoundFade = 0.15
ENT.TireSoundTypes = {
["roll"] = "simulated_vehicles/sfx/wheel_roll.wav",
["roll_dirt"] = "simulated_vehicles/sfx/wheel_roll_dirt.wav",
["roll_wet"] = "simulated_vehicles/sfx/wheel_roll_wet.wav",
["roll_damaged"] = "simulated_vehicles/sfx/tire_damaged.wav",
["skid"] = "simulated_vehicles/sfx/wheel_skid.wav",
["skid_dirt"] = "simulated_vehicles/sfx/wheel_skid_dirt.wav",
["skid_wet"] = "simulated_vehicles/sfx/wheel_skid_wet.wav",
}
ENT.TireSoundLevelSkid = 85
ENT.TireSoundLevelRoll = 75
function ENT:TireSoundRemove()
for snd, _ in pairs( self.TireSoundTypes ) do
self:StopTireSound( snd )
end
end
function ENT:TireSoundThink()
for snd, _ in pairs( self.TireSoundTypes ) do
local T = self:GetTireSoundTime( snd )
if T > 0 then
local speed = self:GetVelocity():Length()
local sound = self:StartTireSound( snd )
if string.StartsWith( snd, "skid" ) then
local vel = speed
speed = math.max( math.abs( self:GetWheelVelocity() ) - vel, 0 ) * 5 + vel
end
local volume = math.min(speed / 1000,1) ^ 2 * T
local pitch = 100 + math.Clamp((speed - 400) / 200,0,155)
sound:ChangeVolume( volume, 0 )
sound:ChangePitch( pitch, 0.5 )
else
self:StopTireSound( snd )
end
end
end
function ENT:DoTireSound( snd )
if not istable( self._TireSounds ) then
self._TireSounds = {}
end
self._TireSounds[ snd ] = CurTime() + self.TireSoundFade
end
function ENT:GetTireSoundTime( snd )
if not istable( self._TireSounds ) or not self._TireSounds[ snd ] then return 0 end
return math.max(self._TireSounds[ snd ] - CurTime(),0) / self.TireSoundFade
end
function ENT:StartTireSound( snd )
if not self.TireSoundTypes[ snd ] or not istable( self._ActiveTireSounds ) then
self._ActiveTireSounds = {}
end
if self._ActiveTireSounds[ snd ] then return self._ActiveTireSounds[ snd ] end
local sound = CreateSound( self, self.TireSoundTypes[ snd ] )
sound:SetSoundLevel( string.StartsWith( snd, "skid" ) and self.TireSoundLevelSkid or self.TireSoundLevelRoll )
sound:PlayEx(0,100)
self._ActiveTireSounds[ snd ] = sound
return sound
end
function ENT:StopTireSound( snd )
if not istable( self._ActiveTireSounds ) or not self._ActiveTireSounds[ snd ] then return end
self._ActiveTireSounds[ snd ]:Stop()
self._ActiveTireSounds[ snd ] = nil
end

View File

@@ -0,0 +1,314 @@
--[[
| 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.DSArmorDamageReduction = 1
ENT.DSArmorDamageReductionType = DMG_GENERIC
ENT.DSArmorIgnoreDamageType = DMG_GENERIC
function ENT:ApplyDamage( damage, type )
if type == DMG_BLAST then
damage = damage * 10
end
if type == DMG_BULLET then
damage = damage * 2
end
local MaxHealth = self:GetMaxHealth()
local CurHealth = self:GetCurHealth()
local NewHealth = math.max( math.Round(CurHealth - damage,0) , 0 )
if NewHealth <= (MaxHealth * 0.6) then
if NewHealth <= (MaxHealth * 0.3) then
self:SetOnFire( true )
self:SetOnSmoke( false )
else
self:SetOnSmoke( true )
end
end
if MaxHealth > 30 and NewHealth <= 31 then
if self:EngineActive() then
self:DamagedStall()
end
end
if NewHealth <= 0 then
self:ExplodeVehicle()
return
end
self:SetCurHealth( NewHealth )
end
function ENT:OnTakeDamage( dmginfo )
if not self:IsInitialized() then return end
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 dmgPartsTableOK = true
if istable( self._dmgParts ) then
for _, part in pairs( self._dmgParts ) do
if isvector( part.mins ) and isvector( part.maxs ) and isvector( part.pos ) and isangle( part.ang ) then continue end
dmgPartsTableOK = false
break
end
else
dmgPartsTableOK = false
end
local CriticalHit = false
if dmgPartsTableOK then
CriticalHit = self:CalcComponentDamage( dmginfo )
else
print("[LVS] - "..self:GetSpawn_List().." is doing something it shouldn't! DS part has been detected but was registered incorrectly!")
end
if hook.Run( "simfphysOnTakeDamage", self, dmginfo ) then return end
local Damage = dmginfo:GetDamage()
local DamagePos = dmginfo:GetDamagePosition()
local Type = dmginfo:GetDamageType()
local Driver = self:GetDriver()
self.LastAttacker = dmginfo:GetAttacker()
self.LastInflictor = dmginfo:GetInflictor()
if simfphys.DamageEnabled then
net.Start( "simfphys_spritedamage" )
net.WriteEntity( self )
net.WriteVector( self:WorldToLocal( DamagePos ) )
net.WriteBool( false )
net.Broadcast()
if Type == DMG_AIRBOAT then
Type = DMG_DIRECT
Damage = Damage * 6
end
local oldHP = self:GetCurHealth()
self:ApplyDamage( Damage, Type )
local newHP = self:GetCurHealth()
if oldHP ~= newHP then
local IsFireDamage = dmginfo:IsDamageType( DMG_BURN )
if IsValid( self.LastAttacker ) and self.LastAttacker:IsPlayer() and not IsFireDamage then
net.Start( "lvs_hitmarker" )
net.WriteBool( CriticalHit )
net.Send( self.LastAttacker )
end
if Damage > 1 and not IsFireDamage then
net.Start( "lvs_hurtmarker" )
net.WriteFloat( math.min( Damage / 50, 1 ) )
net.Send( self:GetEveryone() )
end
end
end
end
function ENT:ExplodeVehicle()
if not IsValid( self ) then return end
if self.destroyed then return end
self.destroyed = true
local Attacker = self.LastAttacker
if IsValid( Attacker ) and Attacker:IsPlayer() then
net.Start( "lvs_killmarker" )
net.Send( Attacker )
end
local GibMDL = self.GibModels
self.GibModels = nil
self:OnFinishExplosion()
self.GibModels = GibMDL
GibMDL = nil
local ply = self.EntityOwner
local skin = self:GetSkin()
local Col = self:GetColor()
Col.r = Col.r * 0.8
Col.g = Col.g * 0.8
Col.b = Col.b * 0.8
local Driver = self:GetDriver()
if IsValid( Driver ) then
if self.RemoteDriver ~= Driver then
local dmginfo = DamageInfo()
dmginfo:SetDamage( Driver:Health() + Driver:Armor() )
dmginfo:SetAttacker( self.LastAttacker or game.GetWorld() )
dmginfo:SetInflictor( self.LastInflictor or game.GetWorld() )
dmginfo:SetDamageType( DMG_DIRECT )
Driver:TakeDamageInfo( dmginfo )
end
end
if self.PassengerSeats then
for i = 1, table.Count( self.PassengerSeats ) do
local Passenger = self.pSeat[i]:GetDriver()
if IsValid( Passenger ) then
local dmginfo = DamageInfo()
dmginfo:SetDamage( Passenger:Health() + Passenger:Armor() )
dmginfo:SetAttacker( self.LastAttacker or game.GetWorld() )
dmginfo:SetInflictor( self.LastInflictor or game.GetWorld() )
dmginfo:SetDamageType( DMG_DIRECT )
Passenger:TakeDamageInfo( dmginfo )
end
end
end
if self.GibModels then
local bprop = ents.Create( "gmod_sent_vehicle_fphysics_gib" )
bprop:SetModel( self.GibModels[1] )
bprop:SetPos( self:GetPos() )
bprop:SetAngles( self:GetAngles() )
bprop.MakeSound = true
bprop:Spawn()
bprop:Activate()
bprop:GetPhysicsObject():SetVelocity( self:GetVelocity() + Vector(math.random(-5,5),math.random(-5,5),math.random(150,250)) )
bprop:GetPhysicsObject():SetMass( self.Mass * 0.75 )
bprop.DoNotDuplicate = true
bprop:SetColor( Col )
bprop:SetSkin( skin )
self.Gib = bprop
simfphys.SetOwner( ply , bprop )
if IsValid( ply ) then
undo.Create( "Gib" )
undo.SetPlayer( ply )
undo.AddEntity( bprop )
undo.SetCustomUndoText( "Undone Gib" )
undo.Finish( "Gib" )
ply:AddCleanup( "Gibs", bprop )
end
bprop.Gibs = {}
for i = 2, table.Count( self.GibModels ) do
local prop = ents.Create( "gmod_sent_vehicle_fphysics_gib" )
prop:SetModel( self.GibModels[i] )
prop:SetPos( self:GetPos() )
prop:SetAngles( self:GetAngles() )
prop:SetOwner( bprop )
prop:Spawn()
prop:Activate()
prop.DoNotDuplicate = true
bprop:DeleteOnRemove( prop )
bprop.Gibs[i-1] = prop
local PhysObj = prop:GetPhysicsObject()
if IsValid( PhysObj ) then
PhysObj:SetVelocityInstantaneous( VectorRand() * 500 + self:GetVelocity() + Vector(0,0,math.random(150,250)) )
PhysObj:AddAngleVelocity( VectorRand() )
end
simfphys.SetOwner( ply , prop )
end
else
local bprop = ents.Create( "gmod_sent_vehicle_fphysics_gib" )
bprop:SetModel( self:GetModel() )
bprop:SetPos( self:GetPos() )
bprop:SetAngles( self:GetAngles() )
bprop.MakeSound = true
bprop:Spawn()
bprop:Activate()
bprop:GetPhysicsObject():SetVelocity( self:GetVelocity() + Vector(math.random(-5,5),math.random(-5,5),math.random(150,250)) )
bprop:GetPhysicsObject():SetMass( self.Mass * 0.75 )
bprop.DoNotDuplicate = true
bprop:SetColor( Col )
bprop:SetSkin( skin )
for i = 0, self:GetNumBodyGroups() do
bprop:SetBodygroup(i, self:GetBodygroup(i))
end
self.Gib = bprop
simfphys.SetOwner( ply , bprop )
if IsValid( ply ) then
undo.Create( "Gib" )
undo.SetPlayer( ply )
undo.AddEntity( bprop )
undo.SetCustomUndoText( "Undone Gib" )
undo.Finish( "Gib" )
ply:AddCleanup( "Gibs", bprop )
end
if self.CustomWheels == true and not self.NoWheelGibs then
bprop.Wheels = {}
for i = 1, table.Count( self.GhostWheels ) do
local Wheel = self.GhostWheels[i]
if IsValid(Wheel) then
local prop = ents.Create( "gmod_sent_vehicle_fphysics_gib" )
prop:SetModel( Wheel:GetModel() )
prop:SetPos( Wheel:LocalToWorld( Vector(0,0,0) ) )
prop:SetAngles( Wheel:LocalToWorldAngles( Angle(0,0,0) ) )
prop:SetOwner( bprop )
prop:Spawn()
prop:Activate()
prop:GetPhysicsObject():SetVelocity( self:GetVelocity() + Vector(math.random(-5,5),math.random(-5,5),math.random(0,25)) )
prop:GetPhysicsObject():SetMass( 20 )
prop.DoNotDuplicate = true
bprop:DeleteOnRemove( prop )
bprop.Wheels[i] = prop
simfphys.SetOwner( ply , prop )
end
end
end
end
self:Extinguish()
self:OnDestroyed()
hook.Run( "simfphysOnDestroyed", self, self.Gib )
self:Remove()
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,317 @@
--[[
| 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/
--]]
numpad.Register( "k_forward", function( pl, ent, keydown )
if not IsValid(pl) or not IsValid(ent) then return false end
if ent.PressedKeys then
ent.PressedKeys["W"] = keydown
end
if keydown and ent:GetIsCruiseModeOn() then
ent:SetIsCruiseModeOn( false )
end
end )
numpad.Register( "k_reverse", function( pl, ent, keydown )
if not IsValid(pl) or not IsValid(ent) then return false end
if ent.PressedKeys then
ent.PressedKeys["S"] = keydown
end
if keydown and ent:GetIsCruiseModeOn() then
ent:SetIsCruiseModeOn( false )
end
end )
numpad.Register( "k_left", function( pl, ent, keydown )
if not IsValid(pl) or not IsValid(ent) then return false end
if ent.PressedKeys then
ent.PressedKeys["A"] = keydown
end
end )
numpad.Register( "k_right", function( pl, ent, keydown )
if not IsValid(pl) or not IsValid(ent) then return false end
if ent.PressedKeys then
ent.PressedKeys["D"] = keydown
end
end )
numpad.Register( "k_a_forward", function( pl, ent, keydown )
if not IsValid(pl) or not IsValid(ent) then return false end
if ent.PressedKeys then
ent.PressedKeys["aW"] = keydown
end
end )
numpad.Register( "k_a_reverse", function( pl, ent, keydown )
if not IsValid(pl) or not IsValid(ent) then return false end
if ent.PressedKeys then
ent.PressedKeys["aS"] = keydown
end
end )
numpad.Register( "k_a_left", function( pl, ent, keydown )
if not IsValid(pl) or not IsValid(ent) then return false end
if (ent.PressedKeys) then
ent.PressedKeys["aA"] = keydown
end
end )
numpad.Register( "k_a_right", function( pl, ent, keydown )
if not IsValid(pl) or not IsValid(ent) then return false end
if ent.PressedKeys then
ent.PressedKeys["aD"] = keydown
end
end )
numpad.Register( "k_gup", function( pl, ent, keydown )
if not IsValid(pl) or not IsValid(ent) then return false end
if not pl:lvsGetInputEnabled() then keydown = false end
if ent.PressedKeys then
ent.PressedKeys["M1"] = keydown
end
if keydown and ent:GetIsCruiseModeOn() then
ent:SetIsCruiseModeOn( false )
end
end )
numpad.Register( "k_gdn", function( pl, ent, keydown )
if not IsValid(pl) or not IsValid(ent) then return false end
if not pl:lvsGetInputEnabled() then keydown = false end
if ent.PressedKeys then
ent.PressedKeys["M2"] = keydown
end
if keydown and ent:GetIsCruiseModeOn() then
ent:SetIsCruiseModeOn( false )
end
end )
numpad.Register( "k_wot", function( pl, ent, keydown )
if not IsValid(pl) or not IsValid(ent) then return false end
if ent.PressedKeys then
ent.PressedKeys["Shift"] = keydown
end
end )
numpad.Register( "k_clutch", function( pl, ent, keydown )
if not IsValid(pl) or not IsValid(ent) then return false end
if ent.PressedKeys then
ent.PressedKeys["Alt"] = keydown
end
end )
numpad.Register( "k_hbrk", function( pl, ent, keydown )
if not IsValid(pl) or not IsValid(ent) then return false end
if ent.PressedKeys then
ent.PressedKeys["Space"] = keydown
end
if keydown and ent:GetIsCruiseModeOn() then
ent:SetIsCruiseModeOn( false )
end
end )
numpad.Register( "k_ccon", function( pl, ent, keydown )
if not IsValid(pl) or not IsValid(ent) then return false end
if keydown then
if ent:GetIsCruiseModeOn() then
ent:SetIsCruiseModeOn( false )
else
ent:SetIsCruiseModeOn( true )
ent.cc_speed = math.Round(ent:GetVelocity():Length(),0)
end
end
end )
numpad.Register( "k_hrn", function( pl, ent, keydown )
if not IsValid(pl) or not IsValid(ent) then return false end
ent.KeyPressedTime = isnumber( ent.KeyPressedTime ) and ent.KeyPressedTime or 0
local v_list = list.Get( "simfphys_lights" )[ent.LightsTable] or false
if keydown then
ent.HornKeyIsDown = true
if v_list and v_list.ems_sounds then
if not ent.emson then
timer.Simple( 0.1, function()
if not IsValid(ent) or not ent.HornKeyIsDown then return end
if not ent.horn then
ent.horn = CreateSound(ent, ent.snd_horn or "simulated_vehicles/horn_1.wav")
ent.horn:PlayEx(0,100)
end
end)
end
else
if not ent.horn then
ent.horn = CreateSound(ent, ent.snd_horn or "simulated_vehicles/horn_1.wav")
ent.horn:PlayEx(0,100)
end
end
else
ent.HornKeyIsDown = false
end
if not v_list then return end
if v_list.ems_sounds then
local Time = CurTime()
if keydown then
ent.KeyPressedTime = Time
else
if (Time - ent.KeyPressedTime) < 0.15 then
if not ent.emson then
ent.emson = true
ent.cursound = 0
end
end
if (Time - ent.KeyPressedTime) >= 0.22 then
if ent.emson then
ent.emson = false
if ent.ems then
ent.ems:Stop()
end
end
else
if ent.emson then
if ent.ems then ent.ems:Stop() end
local sounds = v_list.ems_sounds
local numsounds = table.Count( sounds )
if numsounds <= 1 and ent.ems then
ent.emson = false
ent.ems = nil
ent:SetEMSEnabled( false )
return
end
ent.cursound = ent.cursound + 1
if ent.cursound > table.Count( sounds ) then
ent.cursound = 1
end
ent.ems = CreateSound(ent, sounds[ent.cursound])
ent.ems:Play()
end
end
ent:SetEMSEnabled( ent.emson )
end
end
end)
numpad.Register( "k_eng", function( pl, ent, keydown )
if not IsValid(pl) or not IsValid(ent) then return false end
if keydown then
if ent:EngineActive() then
ent:StopEngine()
else
ent:StartEngine( true )
end
end
end)
numpad.Register( "k_flgts", function( pl, ent, keydown )
if not IsValid(pl) or not IsValid(ent) or not ent.LightsTable then return false end
if keydown then
ent:EmitSound( "buttons/lightswitch2.wav" )
if ent:GetFogLightsEnabled() then
ent:SetFogLightsEnabled( false )
else
ent:SetFogLightsEnabled( true )
end
end
end)
numpad.Register( "k_lgts", function( pl, ent, keydown )
if not IsValid(pl) or not IsValid(ent) or not ent.LightsTable then return false end
local Time = CurTime()
if keydown then
ent.KeyPressedTime = Time
else
if ent.KeyPressedTime and (Time - ent.KeyPressedTime) >= (ent.LightsActivated and 0.22 or 0) then
if (ent.NextLightCheck or 0) > Time then return end
local vehiclelist = list.Get( "simfphys_lights" )[ent.LightsTable] or false
if not vehiclelist then return end
if ent.LightsActivated then
ent.NextLightCheck = Time + (vehiclelist.DelayOff or 0)
ent.LightsActivated = false
ent:SetLightsEnabled(false)
ent:EmitSound( "buttons/lightswitch2.wav" )
ent.LampsActivated = false
ent:SetLampsEnabled( ent.LampsActivated )
else
ent.NextLightCheck = Time + (vehiclelist.DelayOn or 0)
ent.LightsActivated = true
ent:EmitSound( "buttons/lightswitch2.wav" )
end
if ent.LightsActivated then
if vehiclelist.BodyGroups then
ent:SetBodygroup(vehiclelist.BodyGroups.On[1], vehiclelist.BodyGroups.On[2] )
end
if vehiclelist.Animation then
ent:PlayAnimation( vehiclelist.Animation.On )
end
if ent.LightsPP then
ent:PlayPP(ent.LightsActivated)
end
else
if vehiclelist.BodyGroups then
ent:SetBodygroup(vehiclelist.BodyGroups.Off[1], vehiclelist.BodyGroups.Off[2] )
end
if vehiclelist.Animation then
ent:PlayAnimation( vehiclelist.Animation.Off )
end
if ent.LightsPP then
ent:PlayPP(ent.LightsActivated)
end
end
else
if (ent.NextLightCheck or 0) > Time then return end
if ent.LampsActivated then
ent.LampsActivated = false
else
ent.LampsActivated = true
end
ent:SetLampsEnabled( ent.LampsActivated )
ent:EmitSound( "items/flashlight1.wav" )
end
end
end )

View File

@@ -0,0 +1,314 @@
--[[
| 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.Base = "lvs_base"
ENT.PrintName = "Comedy Effect"
ENT.Author = "Blu"
ENT.Information = ""
ENT.Category = "Fun + Games"
ENT.Spawnable = false
ENT.AdminSpawnable = false
ENT.AutomaticFrameAdvance = true
ENT.RenderGroup = RENDERGROUP_BOTH
ENT.Editable = true
-- this needs to be here for some addons
ENT.IsSimfphyscar = true
ENT.LVSsimfphys = true
ENT.MaxHealth = -1
ENT.MouseSteerAngle = 20
ENT.MouseSteerExponent = 2
ENT.lvsDisableZoom = false
ENT.DoNotDuplicate = true
function ENT:SetupDataTables()
self:AddDT( "Int", "AITEAM", { KeyName = "aiteam", Edit = { type = "Int", order = 0,min = 0, max = 3, category = "AI"} } )
self:AddDT( "Float", "SteerSpeed", { KeyName = "steerspeed", Edit = { type = "Float", order = 1,min = 1, max = 16, category = "Steering"} } )
self:AddDT( "Float", "FastSteerConeFadeSpeed", { KeyName = "faststeerconefadespeed", Edit = { type = "Float", order = 2,min = 1, max = 5000, category = "Steering"} } )
self:AddDT( "Float", "FastSteerAngle", { KeyName = "faststeerangle", Edit = { type = "Float", order = 3,min = 0, max = 1, category = "Steering"} } )
self:AddDT( "Float", "FrontSuspensionHeight", { KeyName = "frontsuspensionheight", Edit = { type = "Float", order = 4,min = -1, max = 1, category = "Suspension" } } )
self:AddDT( "Float", "RearSuspensionHeight", { KeyName = "rearsuspensionheight", Edit = { type = "Float", order = 5,min = -1, max = 1, category = "Suspension" } } )
self:AddDT( "Int", "EngineSoundPreset", { KeyName = "enginesoundpreset", Edit = { type = "Int", order = 6,min = -1, max = 23, category = "Engine"} } )
self:AddDT( "Int", "IdleRPM", { KeyName = "idlerpm", Edit = { type = "Int", order = 7,min = 1, max = 25000, category = "Engine"} } )
self:AddDT( "Int", "LimitRPM", { KeyName = "limitrpm", Edit = { type = "Int", order = 8,min = 4, max = 25000, category = "Engine"} } )
self:AddDT( "Int", "PowerBandStart", { KeyName = "powerbandstart", Edit = { type = "Int", order = 9,min = 2, max = 25000, category = "Engine"} } )
self:AddDT( "Int", "PowerBandEnd", { KeyName = "powerbandend", Edit = { type = "Int", order = 10,min = 3, max = 25000, category = "Engine"} } )
self:AddDT( "Float", "MaxTorque", { KeyName = "maxtorque", Edit = { type = "Float", order = 11,min = 20, max = 1000, category = "Engine"} } )
self:AddDT( "Bool", "Revlimiter", { KeyName = "revlimiter", Edit = { type = "Boolean", order = 12, category = "Engine"} } )
self:AddDT( "Bool", "TurboCharged", { KeyName = "turbocharged", Edit = { type = "Boolean", order = 13, category = "Engine"} } )
self:AddDT( "Bool", "SuperCharged", { KeyName = "supercharged", Edit = { type = "Boolean", order = 14, category = "Engine"} } )
self:AddDT( "Bool", "BackFire", { KeyName = "backfire", Edit = { type = "Boolean", order = 15, category = "Engine"} } )
self:AddDT( "Bool", "DoNotStall", { KeyName = "donotstall", Edit = { type = "Boolean", order = 16, category = "Engine"} } )
self:AddDT( "Float", "DifferentialGear", { KeyName = "differentialgear", Edit = { type = "Float", order = 17,min = 0.2, max = 6, category = "Transmission"} } )
self:AddDT( "Float", "BrakePower", { KeyName = "brakepower", Edit = { type = "Float", order = 18,min = 0.1, max = 500, category = "Wheels"} } )
self:AddDT( "Float", "PowerDistribution", { KeyName = "powerdistribution", Edit = { type = "Float", order = 19,min = -1, max = 1, category = "Wheels"} } )
self:AddDT( "Float", "Efficiency", { KeyName = "efficiency", Edit = { type = "Float", order = 20,min = 0.2, max = 4, category = "Wheels"} } )
self:AddDT( "Float", "MaxTraction", { KeyName = "maxtraction", Edit = { type = "Float", order = 21,min = 5, max = 1000, category = "Wheels"} } )
self:AddDT( "Float", "TractionBias", { KeyName = "tractionbias", Edit = { type = "Float", order = 22,min = -0.99, max = 0.99, category = "Wheels"} } )
self:AddDT( "Bool", "BulletProofTires", { KeyName = "bulletprooftires", Edit = { type = "Boolean", order = 23, category = "Wheels"} } )
self:AddDT( "Vector", "TireSmokeColor", { KeyName = "tiresmokecolor", Edit = { type = "VectorColor", order = 24, category = "Wheels"} } )
self:AddDT( "Float", "FlyWheelRPM" )
self:AddDT( "Float", "Throttle" )
self:AddDT( "Float", "WheelVelocity" )
self:AddDT( "Int", "Gear" )
self:AddDT( "Int", "Clutch" )
self:AddDT( "Bool", "IsCruiseModeOn" )
self:AddDT( "Bool", "IsBraking" )
self:AddDT( "Bool", "LightsEnabled" )
self:AddDT( "Bool", "LampsEnabled" )
self:AddDT( "Bool", "EMSEnabled" )
self:AddDT( "Bool", "FogLightsEnabled" )
self:AddDT( "Bool", "HandBrakeEnabled" )
self:AddDT( "Bool", "lvsLockedStatus" )
self:AddDT( "Bool", "lvsReady" )
self:AddDT( "Float", "VehicleSteer" )
self:AddDT( "Float", "CurHealth" )
self:AddDT( "Entity", "Driver" )
self:AddDT( "Entity", "DriverSeat" )
self:AddDT( "Entity", "FuelTank" )
self:AddDT( "Bool", "Active" )
self:AddDT( "String", "Spawn_List")
self:AddDT( "String", "Lights_List")
self:AddDT( "String", "Soundoverride")
self:AddDT( "Vector", "FuelPortPosition" )
if SERVER then
self:SetCurHealth( 1 )
self:NetworkVarNotify( "FrontSuspensionHeight", self.OnFrontSuspensionHeightChanged )
self:NetworkVarNotify( "RearSuspensionHeight", self.OnRearSuspensionHeightChanged )
self:NetworkVarNotify( "TurboCharged", self.OnTurboCharged )
self:NetworkVarNotify( "SuperCharged", self.OnSuperCharged )
self:NetworkVarNotify( "Active", self.OnActiveChanged )
self:NetworkVarNotify( "Throttle", self.OnThrottleChanged )
self:NetworkVarNotify( "CurHealth", self.OnHealthChanged )
end
self:AddDataTables()
end
function ENT:AddDataTables()
end
local VehicleMeta = FindMetaTable("Entity")
local OldIsVehicle = VehicleMeta.IsVehicle
function VehicleMeta:IsVehicle()
if self.LVSsimfphys then
return true
end
return OldIsVehicle( self )
end
function ENT:GetSelectedWeapon()
return 0
end
function ENT:GetAI()
return false
end
function ENT:SetHP( nHealth )
self:SetCurHealth( nHealth )
end
function ENT:GetShield()
return 0
end
function ENT:SetShield()
end
function ENT:GetHP()
return self:GetCurHealth()
end
function ENT:GetMaxHP()
return self:GetMaxHealth()
end
function ENT:GetMaxHealth()
return self:GetNWFloat( "MaxHealth", 2000 )
end
function ENT:GetMaxFuel()
return self:GetNWFloat( "MaxFuel", 60 )
end
function ENT:GetFuel()
return self:GetNWFloat( "Fuel", self:GetMaxFuel() )
end
function ENT:GetFuelUse()
return self:GetNWFloat( "FuelUse", 0 )
end
function ENT:GetFuelType()
return self:GetNWInt( "FuelType", 1 )
end
function ENT:GetFuelPos()
return self:LocalToWorld( self:GetFuelPortPosition() )
end
function ENT:OnSmoke()
return self:GetNWBool( "OnSmoke", false )
end
function ENT:OnFire()
return self:GetNWBool( "OnFire", false )
end
function ENT:GetIsVehicleLocked()
return self:GetlvsLockedStatus()
end
function ENT:SetIsVehicleLocked( lock )
self:SetlvsLockedStatus( lock )
end
function ENT:GetBackfireSound()
return self:GetNWString( "backfiresound" )
end
function ENT:SetBackfireSound( the_sound )
self:SetNWString( "backfiresound", the_sound )
end
function ENT:BodyGroupIsValid( bodygroups )
for index, groups in pairs( bodygroups ) do
local mygroup = self:GetBodygroup( index )
for g_index = 1, table.Count( groups ) do
if mygroup == groups[g_index] then return true end
end
end
return false
end
function ENT:GetVehicleClass()
return self:GetSpawn_List()
end
function ENT:CalcMainActivityPassenger( ply )
if not istable( self.PassengerSeats ) then
if not self.HasCheckedpSeats then
self.HasCheckedpSeats = true
self.PassengerSeats = list.Get( "simfphys_vehicles" )[ self:GetSpawn_List() ].Members.PassengerSeats
end
return
end
local Pod = ply:GetVehicle()
local pSeatTBL = self.PassengerSeats[ Pod:GetNWInt( "pPodIndex", -1 ) - 1 ]
if not istable( pSeatTBL ) then return end
local seq = pSeatTBL.anim
if not isstring( seq ) then return end
if ply.m_bWasNoclipping then
ply.m_bWasNoclipping = nil
ply:AnimResetGestureSlot( GESTURE_SLOT_CUSTOM )
if CLIENT then
ply:SetIK( true )
end
end
ply.CalcIdeal = ACT_STAND
ply.CalcSeqOverride = ply:LookupSequence( seq )
return ply.CalcIdeal, ply.CalcSeqOverride
end
function ENT:CalcMainActivity( ply )
if ply ~= self:GetDriver() then return self:CalcMainActivityPassenger( ply ) end
if ply.m_bWasNoclipping then
ply.m_bWasNoclipping = nil
ply:AnimResetGestureSlot( GESTURE_SLOT_CUSTOM )
if CLIENT then
ply:SetIK( true )
end
end
ply.CalcIdeal = ACT_STAND
if isstring( self.SeatAnim ) then
ply.CalcSeqOverride = ply:LookupSequence( self.SeatAnim )
else
if not self.HasCheckedSeat then
self.HasCheckedSeat = true
self.SeatAnim = list.Get( "simfphys_vehicles" )[ self:GetSpawn_List() ].Members.SeatAnim
end
ply.CalcSeqOverride = ply:LookupSequence( "drive_jeep" )
end
return ply.CalcIdeal, ply.CalcSeqOverride
end
function ENT:UpdateAnimation( ply, velocity, maxseqgroundspeed )
ply:SetPlaybackRate( 1 )
if CLIENT then
if ply == self:GetDriver() then
ply:SetPoseParameter( "vehicle_steer", self:GetVehicleSteer() )
ply:InvalidateBoneCache()
end
GAMEMODE:GrabEarAnimation( ply )
GAMEMODE:MouthMoveAnimation( ply )
end
return false
end
function ENT:StartCommand( ply, cmd )
if self:GetDriver() ~= ply then return end
if SERVER then
self:SetRoadkillAttacker( ply )
end
end
function ENT:GetVehicleType()
return "car"
end

View File

@@ -0,0 +1,522 @@
--[[
| 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:WheelOnGround()
self.FrontWheelPowered = self:GetPowerDistribution() ~= 1
self.RearWheelPowered = self:GetPowerDistribution() ~= -1
for i = 1, table.Count( self.Wheels ) do
local Wheel = self.Wheels[i]
if not IsValid( Wheel ) then continue end
local dmgMul = Wheel:GetDamaged() and 0.5 or 1
local surfacemul = simfphys.TractionData[Wheel:GetSurfaceMaterial():lower()]
self.VehicleData[ "SurfaceMul_" .. i ] = (surfacemul and math.max(surfacemul,0.001) or 1) * dmgMul
local WheelPos = self:LogicWheelPos( i )
local WheelRadius = WheelPos.IsFrontWheel and self.FrontWheelRadius or self.RearWheelRadius
local startpos = Wheel:GetPos()
local dir = -self.Up
local len = WheelRadius + math.Clamp(-self.Vel.z / 50,2.5,6)
local HullSize = Vector(WheelRadius,WheelRadius,0)
local tr = util.TraceHull( {
start = startpos,
endpos = startpos + dir * len,
maxs = HullSize,
mins = -HullSize,
filter = self.VehicleData["filter"]
} )
if tr.Hit then
self.VehicleData[ "onGround_" .. i ] = 1
Wheel:SetSpeed( Wheel.FX )
Wheel:SetSkidSound( Wheel.skid )
Wheel:SetSurfaceMaterial( util.GetSurfacePropName( tr.SurfaceProps ) )
Wheel:SetOnGround(1)
else
self.VehicleData[ "onGround_" .. i ] = 0
Wheel:SetOnGround(0)
end
end
local FrontOnGround = math.max(self.VehicleData[ "onGround_1" ],self.VehicleData[ "onGround_2" ])
local RearOnGround = math.max(self.VehicleData[ "onGround_3" ],self.VehicleData[ "onGround_4" ],self.VehicleData[ "onGround_5" ],self.VehicleData[ "onGround_6" ])
self.DriveWheelsOnGround = math.max(self.FrontWheelPowered and FrontOnGround or 0,self.RearWheelPowered and RearOnGround or 0)
end
function ENT:SimulateAirControls(tilt_forward,tilt_back,tilt_left,tilt_right)
if self:IsDriveWheelsOnGround() then return end
if hook.Run( "simfphysAirControl", self, tilt_forward, tilt_back, tilt_left, tilt_right) then return end
local PObj = self:GetPhysicsObject()
local TiltForce = ((self.Right * (tilt_right - tilt_left) * 1.8) + (self.Forward * (tilt_forward - tilt_back) * 6)) * math.acos( math.Clamp( self.Up:Dot(Vector(0,0,1)) ,-1,1) ) * (180 / math.pi) * self.Mass
PObj:ApplyForceOffset( TiltForce, PObj:GetMassCenter() + self.Up )
PObj:ApplyForceOffset( -TiltForce, PObj:GetMassCenter() - self.Up )
end
function ENT:SimulateEngine(IdleRPM,LimitRPM,Powerbandstart,Powerbandend,c_time)
local PObj = self:GetPhysicsObject()
local IsRunning = self:EngineActive()
local Throttle = self:GetThrottle()
if not self:IsDriveWheelsOnGround() then
self.Clutch = 1
end
if self.Gears[self.CurrentGear] == 0 then
self.GearRatio = 1
self.Clutch = 1
self.HandBrake = self.HandBrake + (self.HandBrakePower - self.HandBrake) * 0.2
else
self.GearRatio = self.Gears[self.CurrentGear] * self:GetDiffGear()
end
self:SetClutch( self.Clutch )
local InvClutch = 1 - self.Clutch
local GearedRPM = self.WheelRPM / math.abs(self.GearRatio)
local MaxTorque = self:GetMaxTorque()
local DesRPM = Lerp(InvClutch, math.max(IdleRPM + (LimitRPM - IdleRPM) * Throttle,0), GearedRPM )
local Drag = (MaxTorque * (math.max( self.EngineRPM - IdleRPM, 0) / Powerbandend) * ( 1 - Throttle) / 0.15) * InvClutch
local TurboCharged = self:GetTurboCharged()
local SuperCharged = self:GetSuperCharged()
local boost = (TurboCharged and self:SimulateTurbo(Powerbandend) or 0) * 0.3 + (SuperCharged and self:SimulateBlower(Powerbandend) or 0)
if self:GetCurHealth() <= self:GetMaxHealth() * 0.3 then
MaxTorque = MaxTorque * (self:GetCurHealth() / (self:GetMaxHealth() * 0.3))
end
self.EngineRPM = math.Clamp(self.EngineRPM + math.Clamp(DesRPM - self.EngineRPM,-math.max(self.EngineRPM / 15, 1 ),math.max(-self.RpmDiff / 1.5 * InvClutch + (self.Torque * 5) / 0.15 * self.Clutch, 1)) + self.RPM_DIFFERENCE * Throttle,0,LimitRPM) * self.EngineIsOn
self.Torque = (Throttle + boost) * math.max(MaxTorque * math.min(self.EngineRPM / Powerbandstart, (LimitRPM - self.EngineRPM) / (LimitRPM - Powerbandend),1), 0)
self:SetFlyWheelRPM( math.min(self.EngineRPM + self.exprpmdiff * 2 * InvClutch,LimitRPM) )
self.RpmDiff = self.EngineRPM - GearedRPM
local signGearRatio = ((self.GearRatio > 0) and 1 or 0) + ((self.GearRatio < 0) and -1 or 0)
local signThrottle = (Throttle > 0) and 1 or 0
local signSpeed = ((self.ForwardSpeed > 0) and 1 or 0) + ((self.ForwardSpeed < 0) and -1 or 0)
local TorqueDiff = (self.RpmDiff / LimitRPM) * 0.15 * self.Torque
local EngineBrake = (signThrottle == 0) and math.min( self.EngineRPM * (self.EngineRPM / LimitRPM) ^ 2 / 60 * signSpeed, 100 ) or 0
local GearedPower = ((self.ThrottleDelay <= c_time and (self.Torque + TorqueDiff) * signThrottle * signGearRatio or 0) - EngineBrake) / math.abs(self.GearRatio) / 50
self.EngineTorque = IsRunning and GearedPower * InvClutch or 0
if not self:GetDoNotStall() then
if IsRunning then
if self.EngineRPM <= IdleRPM * 0.2 then
self.CurrentGear = 2
self:StallAndRestart()
end
end
end
if simfphys.Fuel then
local FuelUse = (Throttle * 0.3 + 0.7) * ((self.EngineRPM / LimitRPM) * MaxTorque + self.Torque) / 1500000
local Fuel = self:GetFuel()
self:SetFuel( Fuel - FuelUse * (1 / simfphys.FuelMul) )
self.UsedFuel = self.UsedFuel and (self.UsedFuel + FuelUse) or 0
self.CheckUse = self.CheckUse or 0
if self.CheckUse < CurTime() then
self.CheckUse = CurTime() + 1
self:SetFuelUse( self.UsedFuel * 60 )
self.UsedFuel = 0
end
if Fuel <= 0 and IsRunning then
self:StopEngine()
end
else
self:SetFuelUse( -1 )
end
local ReactionForce = (self.EngineTorque * 2 - math.Clamp(self.ForwardSpeed,-self.Brake,self.Brake)) * self.DriveWheelsOnGround
local BaseMassCenter = PObj:GetMassCenter()
local dt_mul = math.max( math.min(self:GetPowerDistribution() + 0.5,1),0)
PObj:ApplyForceOffset( -self.Forward * self.Mass * ReactionForce, BaseMassCenter + self.Up * dt_mul )
PObj:ApplyForceOffset( self.Forward * self.Mass * ReactionForce, BaseMassCenter - self.Up * dt_mul )
end
function ENT:SimulateTransmission(k_throttle,k_brake,k_fullthrottle,k_clutch,k_handbrake,k_gearup,k_geardown,isauto,IdleRPM,Powerbandstart,Powerbandend,shiftmode,cruisecontrol,curtime)
local GearsCount = table.Count( self.Gears )
local cruiseThrottle = math.min( math.max(self.cc_speed - math.abs(self.ForwardSpeed),0) / 10 ^ 2, 1)
if isnumber(self.ForceTransmission) then
isauto = self.ForceTransmission <= 1
end
if not isauto then
self.Brake = self:GetBrakePower() * math.max( k_brake, self.PressedKeys["joystick_brake"] )
self.HandBrake = self.HandBrakePower * k_handbrake
self.Clutch = math.max( k_clutch, k_handbrake, self.PressedKeys["joystick_clutch"] )
local AutoThrottle = self:EngineActive() and ((self.EngineRPM < IdleRPM) and (IdleRPM - self.EngineRPM) / IdleRPM or 0) or 0
local Throttle = cruisecontrol and cruiseThrottle or ( math.max( (0.5 + 0.5 * k_fullthrottle) * k_throttle, self.PressedKeys["joystick_throttle"] ) + AutoThrottle)
self:SetThrottle( Throttle )
if k_gearup ~= self.GearUpPressed then
self.GearUpPressed = k_gearup
if k_gearup == 1 then
if self.CurrentGear ~= GearsCount then
self.ThrottleDelay = curtime + 0.4 - 0.4 * k_clutch
end
self.CurrentGear = math.Clamp(self.CurrentGear + 1,1,GearsCount)
end
end
if k_geardown ~= self.GearDownPressed then
self.GearDownPressed = k_geardown
if k_geardown == 1 then
self.CurrentGear = math.Clamp(self.CurrentGear - 1,1,GearsCount)
if self.CurrentGear == 1 then
self.ThrottleDelay = curtime + 0.25
end
end
end
else
local throttleMod = 0.5 + 0.5 * k_fullthrottle
local throttleForward = math.max( k_throttle * throttleMod, self.PressedKeys["joystick_throttle"] )
local throttleReverse = math.max( k_brake * throttleMod, self.PressedKeys["joystick_brake"] )
local throttleStanding = math.max( k_throttle * throttleMod, k_brake * throttleMod, self.PressedKeys["joystick_brake"], self.PressedKeys["joystick_throttle"] )
local inputThrottle = self.ForwardSpeed >= 50 and throttleForward or ((self.ForwardSpeed < 50 and self.ForwardSpeed > -350) and throttleStanding or throttleReverse)
local Throttle = cruisecontrol and cruiseThrottle or inputThrottle
local CalcRPM = self.EngineRPM - self.RPM_DIFFERENCE * Throttle
self:SetThrottle( Throttle )
if self.CurrentGear <= 3 and Throttle > 0 and self.CurrentGear ~= 2 then
if Throttle < 1 and not cruisecontrol then
local autoclutch = math.Clamp((Powerbandstart / self.EngineRPM) - 0.5,0,1)
self.sm_autoclutch = self.sm_autoclutch and (self.sm_autoclutch + math.Clamp(autoclutch - self.sm_autoclutch,-0.2,0.1) ) or 0
else
self.sm_autoclutch = (self.EngineRPM < IdleRPM + (Powerbandstart - IdleRPM)) and 1 or 0
end
else
self.sm_autoclutch = 0
end
self.Clutch = math.max(self.sm_autoclutch,k_handbrake)
self.HandBrake = self.HandBrakePower * k_handbrake
self.Brake = self:GetBrakePower() * (self.ForwardSpeed >= 0 and math.max(k_brake,self.PressedKeys["joystick_brake"]) or math.max(k_throttle,self.PressedKeys["joystick_throttle"]))
if self:IsDriveWheelsOnGround() then
if self.ForwardSpeed >= 50 then
if self.Clutch == 0 then
local NextGear = self.CurrentGear + 1 <= GearsCount and math.min(self.CurrentGear + 1,GearsCount) or self.CurrentGear
local NextGearRatio = self.Gears[NextGear] * self:GetDiffGear()
local NextGearRPM = self.WheelRPM / math.abs(NextGearRatio)
local PrevGear = self.CurrentGear - 1 <= GearsCount and math.max(self.CurrentGear - 1,3) or self.CurrentGear
local PrevGearRatio = self.Gears[PrevGear] * self:GetDiffGear()
local PrevGearRPM = self.WheelRPM / math.abs(PrevGearRatio)
local minThrottle = shiftmode == 1 and 1 or math.max(Throttle,0.5)
local ShiftUpRPM = Powerbandstart + (Powerbandend - Powerbandstart) * minThrottle
local ShiftDownRPM = IdleRPM + (Powerbandend - Powerbandstart) * minThrottle
local CanShiftUp = NextGearRPM > math.max(Powerbandstart * minThrottle,Powerbandstart - IdleRPM) and CalcRPM >= ShiftUpRPM and self.CurrentGear < GearsCount
local CanShiftDown = CalcRPM <= ShiftDownRPM and PrevGearRPM < ShiftDownRPM and self.CurrentGear > 3
if CanShiftUp and self.NextShift < curtime then
self.CurrentGear = self.CurrentGear + 1
self.NextShift = curtime + 0.5
self.ThrottleDelay = curtime + 0.25
end
if CanShiftDown and self.NextShift < curtime then
self.CurrentGear = self.CurrentGear - 1
self.NextShift = curtime + 0.35
end
self.CurrentGear = math.Clamp(self.CurrentGear,3,GearsCount)
end
elseif (self.ForwardSpeed < 50 and self.ForwardSpeed > -350) then
self.CurrentGear = (k_throttle == 1 and 3 or k_brake == 1 and 1 or self.PressedKeys["joystick_throttle"] > 0 and 3 or self.PressedKeys["joystick_brake"] > 0 and 1) or 3
self.Brake = self:GetBrakePower() * math.max(k_throttle * k_brake,self.PressedKeys["joystick_throttle"] * self.PressedKeys["joystick_brake"])
elseif (self.ForwardSpeed >= -350) then
if (Throttle > 0) then
self.Brake = 0
end
self.CurrentGear = 1
end
if (Throttle == 0 and math.abs(self.ForwardSpeed) <= 80) then
self.CurrentGear = 2
self.Brake = 0
end
end
end
self:SetIsBraking( self.Brake > 0 )
self:SetGear( self.CurrentGear )
self:SetHandBrakeEnabled( self.HandBrake > 0 or self.CurrentGear == 2 )
if self.Clutch == 1 or self.CurrentGear == 2 then
if math.abs(self.ForwardSpeed) <= 20 then
local PObj = self:GetPhysicsObject()
local TiltForce = self.Torque * (-1 + self:GetThrottle() * 2)
PObj:ApplyForceOffset( self.Up * TiltForce, PObj:GetMassCenter() + self.Right * 1000 )
PObj:ApplyForceOffset( -self.Up * TiltForce, PObj:GetMassCenter() - self.Right * 1000)
end
end
end
function ENT:GetTransformedDirection()
local SteerAngForward = self.Forward:Angle()
local SteerAngRight = self.Right:Angle()
local SteerAngForward2 = self.Forward:Angle()
local SteerAngRight2 = self.Right:Angle()
SteerAngForward:RotateAroundAxis(-self.Up, self.VehicleData[ "Steer" ])
SteerAngRight:RotateAroundAxis(-self.Up, self.VehicleData[ "Steer" ])
SteerAngForward2:RotateAroundAxis(-self.Up, -self.VehicleData[ "Steer" ])
SteerAngRight2:RotateAroundAxis(-self.Up, -self.VehicleData[ "Steer" ])
local SteerForward = SteerAngForward:Forward()
local SteerRight = SteerAngRight:Forward()
local SteerForward2 = SteerAngForward2:Forward()
local SteerRight2 = SteerAngRight2:Forward()
return {Forward = SteerForward,Right = SteerRight,Forward2 = SteerForward2, Right2 = SteerRight2}
end
function ENT:LogicWheelPos( index )
local IsFront = index == 1 or index == 2
local IsRight = index == 2 or index == 4 or index == 6
return {IsFrontWheel = IsFront, IsRightWheel = IsRight}
end
function ENT:SimulateWheels(k_clutch,LimitRPM)
local Steer = self:GetTransformedDirection()
local MaxGrip = self:GetMaxTraction()
local Efficiency = self:GetEfficiency()
local GripOffset = self:GetTractionBias() * MaxGrip
local wVel = 0
for i = 1, table.Count( self.Wheels ) do
local Wheel = self.Wheels[i]
if IsValid( Wheel ) then
local WheelPos = self:LogicWheelPos( i )
local WheelRadius = WheelPos.IsFrontWheel and self.FrontWheelRadius or self.RearWheelRadius
local WheelDiameter = WheelRadius * 2
local SurfaceMultiplicator = self.VehicleData[ "SurfaceMul_" .. i ]
local MaxTraction = (WheelPos.IsFrontWheel and (MaxGrip + GripOffset) or (MaxGrip - GripOffset)) * SurfaceMultiplicator
local IsPoweredWheel = (WheelPos.IsFrontWheel and self.FrontWheelPowered or not WheelPos.IsFrontWheel and self.RearWheelPowered) and 1 or 0
local Velocity = Wheel:GetVelocity()
local VelForward = Velocity:GetNormalized()
local OnGround = self.VehicleData[ "onGround_" .. i ]
local Forward = WheelPos.IsFrontWheel and Steer.Forward or self.Forward
local Right = WheelPos.IsFrontWheel and Steer.Right or self.Right
if self.CustomWheels then
if WheelPos.IsFrontWheel then
Forward = IsValid(self.SteerMaster) and Steer.Forward or self.Forward
Right = IsValid(self.SteerMaster) and Steer.Right or self.Right
else
if IsValid( self.SteerMaster2 ) then
Forward = Steer.Forward2
Right = Steer.Right2
end
end
end
local Ax = math.deg( math.acos( math.Clamp( Forward:Dot(VelForward) ,-1,1) ) )
local Ay = math.deg( math.asin( math.Clamp( Right:Dot(VelForward) ,-1,1) ) )
local Fx = math.cos( math.rad( Ax ) ) * Velocity:Length()
local Fy = math.sin( math.rad( Ay ) ) * Velocity:Length()
local absFy = math.abs(Fy)
local absFx = math.abs(Fx)
local PowerBiasMul = WheelPos.IsFrontWheel and (1 - self:GetPowerDistribution()) * 0.5 or (1 + self:GetPowerDistribution()) * 0.5
local BrakeForce = math.Clamp(-Fx,-self.Brake,self.Brake) * SurfaceMultiplicator
local TorqueConv = self.EngineTorque * PowerBiasMul * IsPoweredWheel
local ForwardForce = TorqueConv + (not WheelPos.IsFrontWheel and math.Clamp(-Fx,-self.HandBrake,self.HandBrake) or 0) + BrakeForce * 0.5
local TractionCycle = Vector(math.min(absFy,MaxTraction),ForwardForce,0):Length()
local GripLoss = math.max(TractionCycle - MaxTraction,0)
local GripRemaining = math.max(MaxTraction - GripLoss,math.min(absFy / 25,MaxTraction))
--local GripRemaining = math.max(MaxTraction - GripLoss,math.min(absFy / 25,MaxTraction / 2))
local signForwardForce = ((ForwardForce > 0) and 1 or 0) + ((ForwardForce < 0) and -1 or 0)
local signEngineTorque = ((self.EngineTorque > 0) and 1 or 0) + ((self.EngineTorque < 0) and -1 or 0)
local Power = ForwardForce * Efficiency - GripLoss * signForwardForce + math.Clamp(BrakeForce * 0.5,-MaxTraction,MaxTraction)
local Force = -Right * math.Clamp(Fy,-GripRemaining,GripRemaining) + Forward * Power
local wRad = Wheel:GetDamaged() and Wheel.dRadius or WheelRadius
local wFX = (Fx + GripLoss * 35 * signEngineTorque * IsPoweredWheel)
local TurnWheel = (wFX / wRad * 1.85) + self.EngineRPM / 80 * (1 - OnGround) * IsPoweredWheel * (1 - k_clutch)
Wheel.FX = Fx
Wheel.skid = ((MaxTraction - (MaxTraction - Vector(absFy,math.abs(ForwardForce * 10),0):Length())) / MaxTraction) - 10
local RPM = Wheel:VelToRPM( absFx ) * OnGround
local GripLossFaktor = math.Clamp(GripLoss,0,MaxTraction) / MaxTraction
local abswFX = math.abs( wFX )
Wheel:SetRPM( Wheel:VelToRPM( wFX ) )
if IsPoweredWheel then
if abswFX > wVel then
wVel = abswFX
end
end
self.VehicleData[ "WheelRPM_".. i ] = RPM
self.VehicleData[ "GripLossFaktor_".. i ] = GripLossFaktor
self.VehicleData[ "Exp_GLF_".. i ] = GripLossFaktor ^ 2
Wheel:SetGripLoss( GripLossFaktor )
local WheelOPow = math.abs( self.CurrentGear == 1 and math.min( TorqueConv, 0 ) or math.max( TorqueConv, 0 ) ) > 0
local FrontWheelCanTurn = (WheelOPow and 0 or self.Brake) < MaxTraction * 1.75
local RearWheelCanTurn = (self.HandBrake < MaxTraction) and (WheelOPow and 0 or self.Brake) < MaxTraction * 2
if WheelPos.IsFrontWheel then
if FrontWheelCanTurn then
self.VehicleData[ "spin_" .. i ] = self.VehicleData[ "spin_" .. i ] + TurnWheel
end
else
if RearWheelCanTurn then
self.VehicleData[ "spin_" .. i ] = self.VehicleData[ "spin_" .. i ] + TurnWheel
end
end
if self.CustomWheels then
local GhostEnt = self.GhostWheels[i]
if IsValid( GhostEnt ) then
local Angle = GhostEnt:GetAngles()
local offsetang = WheelPos.IsFrontWheel and self.CustomWheelAngleOffset or (self.CustomWheelAngleOffset_R or self.CustomWheelAngleOffset)
local Direction = GhostEnt:LocalToWorldAngles( offsetang ):Forward()
local TFront = FrontWheelCanTurn and TurnWheel or 0
local TBack = RearWheelCanTurn and TurnWheel or 0
local AngleStep = WheelPos.IsFrontWheel and TFront or TBack
Angle:RotateAroundAxis(Direction, WheelPos.IsRightWheel and AngleStep or -AngleStep)
self.GhostWheels[i]:SetAngles( Angle )
end
else
self:SetPoseParameter(self.VehicleData[ "pp_spin_" .. i ],self.VehicleData[ "spin_" .. i ])
end
if not self.PhysicsEnabled then
Wheel:GetPhysicsObject():ApplyForceCenter( Force * 185 * OnGround )
end
end
end
self:SetWheelVelocity( wVel )
local target_diff = math.max(LimitRPM * 0.95 - self.EngineRPM,0)
if self.FrontWheelPowered and self.RearWheelPowered then
self.WheelRPM = math.max(self.VehicleData[ "WheelRPM_1" ] or 0,self.VehicleData[ "WheelRPM_2" ] or 0,self.VehicleData[ "WheelRPM_3" ] or 0,self.VehicleData[ "WheelRPM_4" ] or 0)
self.RPM_DIFFERENCE = target_diff * math.max(self.VehicleData[ "GripLossFaktor_1" ] or 0,self.VehicleData[ "GripLossFaktor_2" ] or 0,self.VehicleData[ "GripLossFaktor_3" ] or 0,self.VehicleData[ "GripLossFaktor_4" ] or 0)
self.exprpmdiff = target_diff * math.max(self.VehicleData[ "Exp_GLF_1" ] or 0,self.VehicleData[ "Exp_GLF_2" ] or 0,self.VehicleData[ "Exp_GLF_3" ] or 0,self.VehicleData[ "Exp_GLF_4" ] or 0)
elseif not self.FrontWheelPowered and self.RearWheelPowered then
self.WheelRPM = math.max(self.VehicleData[ "WheelRPM_3" ] or 0,self.VehicleData[ "WheelRPM_4" ] or 0)
self.RPM_DIFFERENCE = target_diff * math.max(self.VehicleData[ "GripLossFaktor_3" ] or 0,self.VehicleData[ "GripLossFaktor_4" ] or 0)
self.exprpmdiff = target_diff * math.max(self.VehicleData[ "Exp_GLF_3" ] or 0,self.VehicleData[ "Exp_GLF_4" ] or 0)
elseif self.FrontWheelPowered and not self.RearWheelPowered then
self.WheelRPM = math.max(self.VehicleData[ "WheelRPM_1" ] or 0,self.VehicleData[ "WheelRPM_2" ] or 0)
self.RPM_DIFFERENCE = target_diff * math.max(self.VehicleData[ "GripLossFaktor_1" ] or 0,self.VehicleData[ "GripLossFaktor_2" ] or 0)
self.exprpmdiff = target_diff * math.max(self.VehicleData[ "Exp_GLF_1" ] or 0,self.VehicleData[ "Exp_GLF_2" ] or 0)
else
self.WheelRPM = 0
self.RPM_DIFFERENCE = 0
self.exprpmdiff = 0
end
end
function ENT:SimulateTurbo(LimitRPM)
if not self.Turbo then return end
local Throttle = self:GetThrottle()
self.SmoothTurbo = self.SmoothTurbo + math.Clamp(math.min(self.EngineRPM / LimitRPM,1) * 600 * (0.75 + 0.25 * Throttle) - self.SmoothTurbo,-15,15)
local Volume = math.Clamp( ((self.SmoothTurbo - 300) / 150) ,0, 1) * 0.5
local Pitch = math.Clamp( self.SmoothTurbo / 7 , 0 , 255)
local boost = math.Clamp( -0.25 + (self.SmoothTurbo / 500) ^ 5,0,1)
self.Turbo:ChangeVolume( Volume )
self.Turbo:ChangePitch( Pitch )
return boost
end
function ENT:SimulateBlower(LimitRPM)
if not self.Blower or not self.BlowerWhine then return end
local Throttle = self:GetThrottle()
self.SmoothBlower = self.SmoothBlower + math.Clamp(math.min(self.EngineRPM / LimitRPM,1) * 500 - self.SmoothBlower,-20,20)
local Volume1 = math.Clamp( self.SmoothBlower / 400 * (1 - 0.4 * Throttle) ,0, 1)
local Volume2 = math.Clamp( self.SmoothBlower / 400 * (0.10 + 0.4 * Throttle) ,0, 1)
local Pitch1 = 50 + math.Clamp( self.SmoothBlower / 4.5 , 0 , 205)
local Pitch2 = Pitch1 * 1.2
local boost = math.Clamp( (self.SmoothBlower / 600) ^ 4 ,0,1)
self.Blower:ChangeVolume( Volume1 )
self.Blower:ChangePitch( Pitch1 )
self.BlowerWhine:ChangeVolume( Volume2 )
self.BlowerWhine:ChangePitch( Pitch2 )
return boost
end

View File

@@ -0,0 +1,710 @@
--[[
| 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/
--]]
local function IsServerOK()
if GetConVar( "gmod_physiterations" ):GetInt() < 4 then
RunConsoleCommand("gmod_physiterations", "4")
return false
end
return true
end
function ENT:Initialize()
self:PhysicsInit( SOLID_VPHYSICS )
self:SetMoveType( MOVETYPE_VPHYSICS )
self:SetSolid( SOLID_VPHYSICS )
self:SetNotSolid( true )
self:SetUseType( SIMPLE_USE )
--self:SetRenderMode( RENDERMODE_TRANSALPHA ) -- fix broken decals
self:AddFlags( FL_OBJECT ) -- this allows npcs to see this entity
if not IsServerOK() then
self:Remove()
print("[SIMFPHYS] ERROR COULDN'T INITIALIZE VEHICLE!")
end
local PObj = self:GetPhysicsObject()
if not IsValid( PObj ) then print("[SIMFPHYS] ERROR COULDN'T INITIALIZE VEHICLE! '"..self:GetModel().."' has no physics model!") self:Remove() return end
PObj:EnableMotion( false )
self:SetValues()
timer.Simple( 0.1, function()
if not IsValid( self ) then return end
self:InitializeVehicle()
end)
end
function ENT:PostEntityPaste( ply , ent , createdEntities )
self:SetValues()
self:SetActive( false )
self:SetDriver( NULL )
self:SetLightsEnabled( false )
self:SetLampsEnabled( false )
self:SetFogLightsEnabled( false )
self:SetDriverSeat( NULL )
self:SetFlyWheelRPM( 0 )
self:SetThrottle( 0 )
end
function ENT:UpdateTransmitState()
return TRANSMIT_ALWAYS
end
function ENT:SetupView()
local AttachmentID = self:LookupAttachment( "vehicle_driver_eyes" )
local AttachmentID2 = self:LookupAttachment( "vehicle_passenger0_eyes" )
local a_data1 = self:GetAttachment( AttachmentID )
local a_data2 = self:GetAttachment( AttachmentID2 )
local ID
local ViewPos
if a_data1 then
ID = AttachmentID
ViewPos = a_data1
elseif a_data2 then
ID = AttachmentID2
ViewPos = a_data2
else
ID = false
ViewPos = {Ang = self:LocalToWorldAngles( Angle(0, 90,0) ),Pos = self:GetPos()}
end
local ViewAng = ViewPos.Ang - Angle(0,0,self.SeatPitch)
ViewAng:RotateAroundAxis(self:GetUp(), -90 - (self.SeatYaw or 0))
local data = {
ID = ID,
ViewPos = ViewPos.Pos,
ViewAng = ViewAng,
}
return data
end
function ENT:SetupEnteringAnims()
local attachments = self:GetAttachments()
self.Exitpoints = {}
self.Enterpoints = {}
for _,i in pairs(attachments) do
local curstring = string.lower( i.name )
if string.match( curstring, "exit", 1 ) then
table.insert(self.Exitpoints, curstring)
end
if string.match( curstring, "enter", 1 ) then
table.insert(self.Enterpoints, curstring)
end
end
if table.Count( self.Enterpoints ) < 1 then
self.Enterpoints = nil
end
if table.Count( self.Exitpoints ) < 1 then
self.Exitpoints = nil
end
end
function ENT:InitializeVehicle()
if not IsValid( self ) then return end
local physObj = self:GetPhysicsObject()
if not IsValid( physObj ) then return end
if self.LightsTable then
local vehiclelist = list.Get( "simfphys_lights" )[self.LightsTable] or false
if vehiclelist then
if vehiclelist.PoseParameters then
self.LightsPP = vehiclelist.PoseParameters
end
if vehiclelist.BodyGroups then
self:SetBodygroup(vehiclelist.BodyGroups.Off[1], vehiclelist.BodyGroups.Off[2] )
end
end
end
physObj:SetDragCoefficient( self.AirFriction or -250 )
physObj:SetMass( self.Mass * 0.75 )
if self.Inertia then
physObj:SetInertia( self.Inertia )
end
local tanksize = self.FuelTankSize and self.FuelTankSize or 65
local fueltype = self.FuelType and self.FuelType or FUELTYPE_PETROL
self:SetMaxFuel( tanksize )
self:SetFuel( self:GetMaxFuel() )
self:SetFuelType( fueltype )
self:SetFuelPos( self.FuelFillPos and self.FuelFillPos or Vector(0,0,0) )
if fueltype ~= FUELTYPE_NONE then
self:AddFuelTank()
end
local View = self:SetupView()
self.DriverSeat = self:AddDriverSeat( self:WorldToLocal( View.ViewPos ), self:WorldToLocalAngles( View.ViewAng ) )
self.DriverSeat:SetParent( NULL )
self.DriverSeat:SetMoveType( MOVETYPE_NONE )
self.DriverSeat:Spawn()
self.DriverSeat:Activate()
self.DriverSeat:SetPos( View.ViewPos + self.DriverSeat:GetUp() * (-34 + self.SeatOffset.z) + self.DriverSeat:GetRight() * (self.SeatOffset.y) + self.DriverSeat:GetForward() * (-6 + self.SeatOffset.x) )
if View.ID ~= false then
self:SetupEnteringAnims()
self.DriverSeat:SetParent( self , View.ID )
else
self.DriverSeat:SetParent( self )
end
self.DriverSeat.fphysSeat = true
self.DriverSeat.base = self
if self.PassengerSeats then
for i = 1, table.Count( self.PassengerSeats ) do
self.pSeat[i] = self:AddPassengerSeat( self.PassengerSeats[i].pos, self.PassengerSeats[i].ang )
self.pSeat[i].fphysSeat = true
self.pSeat[i].base = self
end
end
if istable(WireLib) then
local passengersSeats = istable( self.pSeat ) and self.pSeat or {}
WireLib.TriggerOutput(self, "PassengerSeats", passengersSeats )
WireLib.TriggerOutput(self, "DriverSeat", self.DriverSeat )
end
if self.Attachments then
for i = 1, table.Count( self.Attachments ) do
local prop = ents.Create( "gmod_sent_vehicle_fphysics_attachment" )
prop:SetModel( self.Attachments[i].model )
prop:SetMaterial( self.Attachments[i].material )
prop:SetRenderMode( RENDERMODE_TRANSALPHA )
prop:SetPos( self:LocalToWorld( self.Attachments[i].pos ) )
prop:SetAngles( self:LocalToWorldAngles( self.Attachments[i].ang ) )
prop:SetOwner( self )
prop:Spawn()
prop:Activate()
prop:DrawShadow( true )
prop:SetNotSolid( true )
prop:SetParent( self )
prop.DoNotDuplicate = true
simfphys.SetOwner( self.EntityOwner, prop )
if self.Attachments[i].skin then
prop:SetSkin( self.Attachments[i].skin )
end
if self.Attachments[i].bodygroups then
for b = 1, table.Count( self.Attachments[i].bodygroups ) do
prop:SetBodygroup(b, self.Attachments[i].bodygroups[b] )
end
end
if self.Attachments[i].useVehicleColor == true then
self.ColorableProps[i] = prop
prop:SetColor( self:GetColor() )
else
prop:SetColor( self.Attachments[i].color or Color(255,255,255,255) )
end
self:DeleteOnRemove( prop )
end
end
self:GetVehicleData()
end
function ENT:GetVehicleData()
self:SetPoseParameter("vehicle_steer",1)
self:SetPoseParameter("vehicle_wheel_fl_height",1)
self:SetPoseParameter("vehicle_wheel_fr_height",1)
self:SetPoseParameter("vehicle_wheel_rl_height",1)
self:SetPoseParameter("vehicle_wheel_rr_height",1)
timer.Simple( 0.15, function()
if not IsValid(self) then return end
self.posepositions["Pose0_Steerangle"] = self.CustomWheels and Angle(0,0,0) or self:GetAttachment( self:LookupAttachment( "wheel_fl" ) ).Ang
self.posepositions["Pose0_Pos_FL"] = self.CustomWheels and self:LocalToWorld( self.CustomWheelPosFL ) or self:GetAttachment( self:LookupAttachment( "wheel_fl" ) ).Pos
self.posepositions["Pose0_Pos_FR"] = self.CustomWheels and self:LocalToWorld( self.CustomWheelPosFR ) or self:GetAttachment( self:LookupAttachment( "wheel_fr" ) ).Pos
self.posepositions["Pose0_Pos_RL"] = self.CustomWheels and self:LocalToWorld( self.CustomWheelPosRL ) or self:GetAttachment( self:LookupAttachment( "wheel_rl" ) ).Pos
self.posepositions["Pose0_Pos_RR"] = self.CustomWheels and self:LocalToWorld( self.CustomWheelPosRR ) or self:GetAttachment( self:LookupAttachment( "wheel_rr" ) ).Pos
self:WriteVehicleDataTable()
end )
end
function ENT:ResetJoystick()
self.PressedKeys["joystick_steer_left"] = 0
self.PressedKeys["joystick_steer_right"] = 0
self.PressedKeys["joystick_brake"] = 0
self.PressedKeys["joystick_throttle"] = 0
self.PressedKeys["joystick_gearup"] = 0
self.PressedKeys["joystick_geardown"] = 0
self.PressedKeys["joystick_handbrake"] = 0
self.PressedKeys["joystick_clutch"] = 0
self.PressedKeys["joystick_air_w"] = 0
self.PressedKeys["joystick_air_a"] = 0
self.PressedKeys["joystick_air_s"] = 0
self.PressedKeys["joystick_air_d"] = 0
end
function ENT:SetValues()
if istable( WireLib ) then
self:createWireIO()
end
self:SetGear( 2 )
self.WheelOnGroundDelay = 0
self.SmoothAng = 0
self.Steer = 0
self.EngineIsOn = 0
self.EngineTorque = 0
self.pSeat = {}
self.exfx = {}
self.Wheels = {}
self.Elastics = {}
self.GhostWheels = {}
self.PressedKeys = {}
self:ResetJoystick()
self.ColorableProps = {}
self.posepositions = {}
self.HandBrakePower = 0
self.DriveWheelsOnGround = 0
self.WheelRPM = 0
self.EngineRPM = 0
self.RpmDiff = 0
self.Torque = 0
self.CurrentGear = 2
self.GearUpPressed = 0
self.GearDownPressed = 0
self.RPM_DIFFERENCE = 0
self.exprpmdiff = 0
self.OldLockBrakes = 0
self.ThrottleDelay = 0
self.Brake = 0
self.HandBrake = 0
self.AutoClutch = 0
self.NextShift = 0
self.ForwardSpeed = 0
self.EngineWasOn = 0
self.SmoothTurbo = 0
self.SmoothBlower = 0
self.cc_speed = 0
self.LightsActivated = false
self.VehicleData = {}
for i = 1, 6 do
self.VehicleData[ "spin_"..i ] = 0
self.VehicleData[ "SurfaceMul_"..i ] = 1
self.VehicleData[ "onGround_"..i ] = 0
end
self.VehicleData[ "Steer" ] = 0
end
function ENT:WriteVehicleDataTable()
self:SetPoseParameter("vehicle_steer",0)
self:SetPoseParameter("vehicle_wheel_fl_height",0)
self:SetPoseParameter("vehicle_wheel_fr_height",0)
self:SetPoseParameter("vehicle_wheel_rl_height",0)
self:SetPoseParameter("vehicle_wheel_rr_height",0)
timer.Simple( 0.15, function()
if not IsValid(self) then return end
self.posepositions["Pose1_Steerangle"] = self.CustomWheels and Angle(0,0,0) or self:GetAttachment( self:LookupAttachment( "wheel_fl" ) ).Ang
self.posepositions["Pose1_Pos_FL"] = self.CustomWheels and self:LocalToWorld( self.CustomWheelPosFL ) or self:GetAttachment( self:LookupAttachment( "wheel_fl" ) ).Pos
self.posepositions["Pose1_Pos_FR"] = self.CustomWheels and self:LocalToWorld( self.CustomWheelPosFR ) or self:GetAttachment( self:LookupAttachment( "wheel_fr" ) ).Pos
self.posepositions["Pose1_Pos_RL"] = self.CustomWheels and self:LocalToWorld( self.CustomWheelPosRL ) or self:GetAttachment( self:LookupAttachment( "wheel_rl" ) ).Pos
self.posepositions["Pose1_Pos_RR"] = self.CustomWheels and self:LocalToWorld( self.CustomWheelPosRR ) or self:GetAttachment( self:LookupAttachment( "wheel_rr" ) ).Pos
self.posepositions["PoseL_Pos_FL"] = self:WorldToLocal( self.posepositions.Pose1_Pos_FL )
self.posepositions["PoseL_Pos_FR"] = self:WorldToLocal( self.posepositions.Pose1_Pos_FR )
self.posepositions["PoseL_Pos_RL"] = self:WorldToLocal( self.posepositions.Pose1_Pos_RL )
self.posepositions["PoseL_Pos_RR"] = self:WorldToLocal( self.posepositions.Pose1_Pos_RR )
self.VehicleData["suspensiontravel_fl"] = self.CustomWheels and self.FrontHeight or math.Round( (self.posepositions.Pose0_Pos_FL - self.posepositions.Pose1_Pos_FL):Length() , 2)
self.VehicleData["suspensiontravel_fr"] = self.CustomWheels and self.FrontHeight or math.Round( (self.posepositions.Pose0_Pos_FR - self.posepositions.Pose1_Pos_FR):Length() , 2)
self.VehicleData["suspensiontravel_rl"] = self.CustomWheels and self.RearHeight or math.Round( (self.posepositions.Pose0_Pos_RL - self.posepositions.Pose1_Pos_RL):Length() , 2)
self.VehicleData["suspensiontravel_rr"] = self.CustomWheels and self.RearHeight or math.Round( (self.posepositions.Pose0_Pos_RR - self.posepositions.Pose1_Pos_RR):Length() , 2)
local Figure1 = math.Round( math.acos( math.Clamp(self.posepositions.Pose0_Steerangle:Up():Dot(self.posepositions.Pose1_Steerangle:Up()),-1,1) ) * (180 / math.pi) , 2)
local Figure2 = math.Round( math.acos( math.Clamp(self.posepositions.Pose0_Steerangle:Forward():Dot(self.posepositions.Pose1_Steerangle:Forward()),-1,1) ) * (180 / math.pi) , 2)
local Figure3 = math.Round( math.acos( math.Clamp(self.posepositions.Pose0_Steerangle:Right():Dot(self.posepositions.Pose1_Steerangle:Right()),-1,1) ) * (180 / math.pi) , 2)
self.VehicleData["steerangle"] = self.CustomWheels and self.CustomSteerAngle or math.max(Figure1,Figure2,Figure3)
local pFL = self.posepositions.Pose0_Pos_FL
local pFR = self.posepositions.Pose0_Pos_FR
local pRL = self.posepositions.Pose0_Pos_RL
local pRR = self.posepositions.Pose0_Pos_RR
local pAngL = self:WorldToLocalAngles( ((pFL + pFR) / 2 - (pRL + pRR) / 2):Angle() )
pAngL.r = 0
pAngL.p = 0
self.VehicleData["LocalAngForward"] = pAngL
local yAngL = self.VehicleData.LocalAngForward - Angle(0,90,0)
yAngL:Normalize()
self.VehicleData["LocalAngRight"] = yAngL
self.VehicleData[ "pp_spin_1" ] = "vehicle_wheel_fl_spin"
self.VehicleData[ "pp_spin_2" ] = "vehicle_wheel_fr_spin"
self.VehicleData[ "pp_spin_3" ] = "vehicle_wheel_rl_spin"
self.VehicleData[ "pp_spin_4" ] = "vehicle_wheel_rr_spin"
self.Turbo = CreateSound(self, "")
self.Blower = CreateSound(self, "")
self.BlowerWhine = CreateSound(self, "")
self.BlowOff = CreateSound(self, "")
local Health = math.floor(self.MaxHealth ~= -1 and self.MaxHealth or (1000 + self:GetPhysicsObject():GetMass() / 3))
self:SetMaxHealth( Health )
self:SetCurHealth( Health )
self:SetAITEAM( self.AITEAM )
self:SetFastSteerAngle(self.FastSteeringAngle / self.VehicleData["steerangle"])
self:SetNotSolid( false )
self:SetupVehicle()
end )
end
function ENT:SetupVehicle()
local BaseMass = self:GetPhysicsObject():GetMass()
local MassCenterOffset = self.CustomMassCenter or Vector(0,0,0)
local BaseMassCenter = self:LocalToWorld( self:GetPhysicsObject():GetMassCenter() - MassCenterOffset )
local OffsetMass = BaseMass * 0.25
local CenterWheels = (self.posepositions["Pose1_Pos_FL"] + self.posepositions["Pose1_Pos_FR"] + self.posepositions["Pose1_Pos_RL"] + self.posepositions["Pose1_Pos_RR"]) / 4
local Sub = CenterWheels - BaseMassCenter
local Dir = Sub:GetNormalized()
local Dist = Sub:Length()
local DistAdd = BaseMass * Dist / OffsetMass
local OffsetMassCenter = BaseMassCenter + Dir * (Dist + DistAdd)
self.MassOffset = ents.Create( "prop_physics" )
self.MassOffset:SetModel( "models/hunter/plates/plate.mdl" )
self.MassOffset:SetPos( OffsetMassCenter )
self.MassOffset:SetAngles( Angle(0,0,0) )
self.MassOffset:Spawn()
self.MassOffset:Activate()
self.MassOffset:GetPhysicsObject():EnableMotion(false)
self.MassOffset:GetPhysicsObject():SetMass( OffsetMass )
self.MassOffset:GetPhysicsObject():EnableDrag( false )
self.MassOffset:SetOwner( self )
self.MassOffset:DrawShadow( false )
self.MassOffset:SetNotSolid( true )
self.MassOffset:SetNoDraw( true )
self.MassOffset.DoNotDuplicate = true
simfphys.SetOwner( self.EntityOwner, self.MassOffset )
local weld = constraint.Weld(self.MassOffset,self, 0, 0, 0,true, true)
weld.DoNotDuplicate = true
local ballsack = constraint.AdvBallsocket(self.MassOffset, self,0,0,Vector(0,0,0),Vector(0,0,0),0,0, -0.01, -0.01, -0.01, 0.01, 0.01, 0.01, 0, 0, 0, 0, 1)
ballsack.DoNotDuplicate = true
if self.CustomWheels then
if self.CustomWheelModel then
if not file.Exists( self.CustomWheelModel, "GAME" ) then
if IsValid( self.EntityOwner ) then
self.EntityOwner:PrintMessage( HUD_PRINTTALK, "ERROR: \""..self.CustomWheelModel.."\" does not exist! Removing vehicle. (Class: "..self:GetSpawn_List()..")")
end
self:Remove()
return
end
if self.SteerFront ~= false then
self.SteerMaster = ents.Create( "prop_physics" )
self.SteerMaster:SetModel( self.CustomWheelModel )
self.SteerMaster:SetPos( self:GetPos() )
self.SteerMaster:SetAngles( self:GetAngles() )
self.SteerMaster:Spawn()
self.SteerMaster:Activate()
local pobj = self.SteerMaster:GetPhysicsObject()
if IsValid(pobj) then
pobj:EnableMotion(false)
else
if IsValid( self.EntityOwner ) then
self.EntityOwner:PrintMessage( HUD_PRINTTALK, "ERROR: \""..self.CustomWheelModel.."\" doesn't have an collision model! Removing vehicle. (Class: "..self:GetSpawn_List()..")")
end
self.SteerMaster:Remove()
self:Remove()
return
end
self.SteerMaster:SetOwner( self )
self.SteerMaster:DrawShadow( false )
self.SteerMaster:SetNotSolid( true )
self.SteerMaster:SetNoDraw( true )
self.SteerMaster.DoNotDuplicate = true
self:DeleteOnRemove( self.SteerMaster )
simfphys.SetOwner( self.EntityOwner, self.SteerMaster )
end
if self.SteerRear then
self.SteerMaster2 = ents.Create( "prop_physics" )
self.SteerMaster2:SetModel( self.CustomWheelModel )
self.SteerMaster2:SetPos( self:GetPos() )
self.SteerMaster2:SetAngles( self:GetAngles() )
self.SteerMaster2:Spawn()
self.SteerMaster2:Activate()
local pobj = self.SteerMaster2:GetPhysicsObject()
if IsValid(pobj) then
pobj:EnableMotion(false)
else
if IsValid( self.EntityOwner ) then
self.EntityOwner:PrintMessage( HUD_PRINTTALK, "ERROR: \""..self.CustomWheelModel.."\" doesn't have an collision model! Removing vehicle. (Class: "..self:GetSpawn_List()..")")
end
self.SteerMaster2:Remove()
self:Remove()
return
end
self.SteerMaster2:SetOwner( self )
self.SteerMaster2:DrawShadow( false )
self.SteerMaster2:SetNotSolid( true )
self.SteerMaster2:SetNoDraw( true )
self.SteerMaster2.DoNotDuplicate = true
self:DeleteOnRemove( self.SteerMaster2 )
simfphys.SetOwner( self.EntityOwner, self.SteerMaster2 )
end
local radius = IsValid(self.SteerMaster) and (self.SteerMaster:OBBMaxs() - self.SteerMaster:OBBMins()) or (self.SteerMaster2:OBBMaxs() - self.SteerMaster2:OBBMins())
self.FrontWheelRadius = self.FrontWheelRadius or math.max( radius.x, radius.y, radius.z ) * 0.5
self.RearWheelRadius = self.RearWheelRadius or self.FrontWheelRadius
self:CreateWheel(1, WheelFL, self:LocalToWorld( self.CustomWheelPosFL ), self.FrontHeight, self.FrontWheelRadius, false , self:LocalToWorld( self.CustomWheelPosFL + Vector(0,0,self.CustomSuspensionTravel * 0.5) ),self.CustomSuspensionTravel, self.FrontConstant, self.FrontDamping, self.FrontRelativeDamping)
self:CreateWheel(2, WheelFR, self:LocalToWorld( self.CustomWheelPosFR ), self.FrontHeight, self.FrontWheelRadius, true , self:LocalToWorld( self.CustomWheelPosFR + Vector(0,0,self.CustomSuspensionTravel * 0.5) ),self.CustomSuspensionTravel, self.FrontConstant, self.FrontDamping, self.FrontRelativeDamping)
self:CreateWheel(3, WheelRL, self:LocalToWorld( self.CustomWheelPosRL ), self.RearHeight, self.RearWheelRadius, false , self:LocalToWorld( self.CustomWheelPosRL + Vector(0,0,self.CustomSuspensionTravel * 0.5) ),self.CustomSuspensionTravel, self.RearConstant, self.RearDamping, self.RearRelativeDamping)
self:CreateWheel(4, WheelRR, self:LocalToWorld( self.CustomWheelPosRR ), self.RearHeight, self.RearWheelRadius, true , self:LocalToWorld( self.CustomWheelPosRR + Vector(0,0,self.CustomSuspensionTravel * 0.5) ), self.CustomSuspensionTravel, self.RearConstant, self.RearDamping, self.RearRelativeDamping)
if self.CustomWheelPosML then
self:CreateWheel(5, WheelML, self:LocalToWorld( self.CustomWheelPosML ), self.RearHeight, self.RearWheelRadius, false , self:LocalToWorld( self.CustomWheelPosML + Vector(0,0,self.CustomSuspensionTravel * 0.5) ),self.CustomSuspensionTravel, self.RearConstant, self.RearDamping, self.RearRelativeDamping)
end
if self.CustomWheelPosMR then
self:CreateWheel(6, WheelMR, self:LocalToWorld( self.CustomWheelPosMR ), self.RearHeight, self.RearWheelRadius, true , self:LocalToWorld( self.CustomWheelPosMR + Vector(0,0,self.CustomSuspensionTravel * 0.5) ), self.CustomSuspensionTravel, self.RearConstant, self.RearDamping, self.RearRelativeDamping)
end
else
if IsValid( self.EntityOwner ) then
self.EntityOwner:PrintMessage( HUD_PRINTTALK, "ERROR: no wheel model defined. Removing vehicle. (Class: "..self:GetSpawn_List()..")")
end
self:Remove()
end
else
self:CreateWheel(1, WheelFL, self:GetAttachment( self:LookupAttachment( "wheel_fl" ) ).Pos, self.FrontHeight, self.FrontWheelRadius, false , self.posepositions.Pose1_Pos_FL, self.VehicleData.suspensiontravel_fl, self.FrontConstant, self.FrontDamping, self.FrontRelativeDamping)
self:CreateWheel(2, WheelFR, self:GetAttachment( self:LookupAttachment( "wheel_fr" ) ).Pos, self.FrontHeight, self.FrontWheelRadius, true , self.posepositions.Pose1_Pos_FR, self.VehicleData.suspensiontravel_fr, self.FrontConstant, self.FrontDamping, self.FrontRelativeDamping)
self:CreateWheel(3, WheelRL, self:GetAttachment( self:LookupAttachment( "wheel_rl" ) ).Pos, self.RearHeight, self.RearWheelRadius, false , self.posepositions.Pose1_Pos_RL, self.VehicleData.suspensiontravel_rl, self.RearConstant, self.RearDamping, self.RearRelativeDamping)
self:CreateWheel(4, WheelRR, self:GetAttachment( self:LookupAttachment( "wheel_rr" ) ).Pos, self.RearHeight, self.RearWheelRadius, true , self.posepositions.Pose1_Pos_RR, self.VehicleData.suspensiontravel_rr, self.RearConstant, self.RearDamping, self.RearRelativeDamping)
end
timer.Simple( 0.01, function()
if not istable( self.Wheels ) then return end
for i = 1, table.Count( self.Wheels ) do
local Ent = self.Wheels[ i ]
local PhysObj = Ent:GetPhysicsObject()
if IsValid( PhysObj ) then
PhysObj:EnableMotion( true )
end
end
timer.Simple( 0.1, function()
if not IsValid( self ) then return end
self:GetPhysicsObject():EnableMotion(true)
local PhysObj = self.MassOffset:GetPhysicsObject()
if IsValid( PhysObj ) then
PhysObj:EnableMotion(true)
end
end )
end )
if istable( self.GibModels ) then
for _, modelName in ipairs( self.GibModels ) do
util.PrecacheModel( modelName )
end
end
self:OnSpawn()
hook.Run( "simfphysOnSpawn", self )
self:SetlvsReady( true )
self.VehicleData["filter"] = self:GetCrosshairFilterEnts()
end
function ENT:CreateWheel(index, name, attachmentpos, height, radius, swap_y , poseposition, suspensiontravel, constant, damping, rdamping)
local fAng = self:LocalToWorldAngles( self.VehicleData.LocalAngForward )
local rAng = self:LocalToWorldAngles( self.VehicleData.LocalAngRight )
local Forward = fAng:Forward()
local Right = swap_y and -rAng:Forward() or rAng:Forward()
local Up = self:GetUp()
local RopeLength = 150
local LimiterLength = 60
local LimiterRopeLength = math.sqrt( (suspensiontravel * 0.5) ^ 2 + LimiterLength ^ 2 )
local WheelMass = self.Mass / 32
if self.FrontWheelMass and (index == 1 or index == 2) then
WheelMass = self.FrontWheelMass
end
if self.RearWheelMass and (index == 3 or index == 4 or index == 5 or index == 6) then
WheelMass = self.RearWheelMass
end
self.name = ents.Create( "gmod_sent_vehicle_fphysics_wheel" )
self.name:SetPos( attachmentpos - Up * height)
self.name:SetAngles( fAng )
self.name:Spawn()
self.name:Activate()
self.name:PhysicsInitSphere( radius, "jeeptire" )
self.name:SetCollisionBounds( Vector(-radius,-radius,-radius), Vector(radius,radius,radius) )
self.name:GetPhysicsObject():EnableMotion(false)
self.name:GetPhysicsObject():SetMass( WheelMass )
self.name:SetBaseEnt( self )
simfphys.SetOwner( self.EntityOwner, self.name )
self.name.EntityOwner = self.EntityOwner
self.name.Index = index
self.name.Radius = radius
self.name:SetRadius( radius )
if self.CustomWheels then
local Model = (self.CustomWheelModel_R and (index == 3 or index == 4 or index == 5 or index == 6)) and self.CustomWheelModel_R or self.CustomWheelModel
local ghostAng = Right:Angle()
local mirAng = swap_y and 1 or -1
ghostAng:RotateAroundAxis(Forward,self.CustomWheelAngleOffset.p * mirAng)
ghostAng:RotateAroundAxis(Right,self.CustomWheelAngleOffset.r * mirAng)
ghostAng:RotateAroundAxis(Up,-self.CustomWheelAngleOffset.y)
local Camber = self.CustomWheelCamber or 0
ghostAng:RotateAroundAxis(Forward, Camber * mirAng)
self.GhostWheels[index] = ents.Create( "gmod_sent_vehicle_fphysics_attachment" )
self.GhostWheels[index]:SetModel( Model )
self.GhostWheels[index]:SetPos( self.name:GetPos() )
self.GhostWheels[index]:SetAngles( ghostAng )
self.GhostWheels[index]:SetOwner( self )
self.GhostWheels[index]:Spawn()
self.GhostWheels[index]:Activate()
self.GhostWheels[index]:SetNotSolid( true )
self.GhostWheels[index].DoNotDuplicate = true
self.GhostWheels[index]:SetParent( self.name )
self:DeleteOnRemove( self.GhostWheels[index] )
simfphys.SetOwner( self.EntityOwner, self.GhostWheels[index] )
self.GhostWheels[index]:SetRenderMode( RENDERMODE_TRANSALPHA )
if self.ModelInfo then
if self.ModelInfo.WheelColor then
self.GhostWheels[index]:SetColor( self.ModelInfo.WheelColor )
end
end
self.name.GhostEnt = self.GhostWheels[index]
local nocollide = constraint.NoCollide(self,self.name,0,0)
nocollide.DoNotDuplicate = true
end
local targetentity = self
if self.CustomWheels then
if index == 1 or index == 2 then
targetentity = self.SteerMaster or self
end
if index == 3 or index == 4 then
targetentity = self.SteerMaster2 or self
end
end
local Ballsocket = constraint.AdvBallsocket(targetentity,self.name,0,0,Vector(0,0,0),Vector(0,0,0),0,0, -0.01, -0.01, -0.01, 0.01, 0.01, 0.01, 0, 0, 0, 1, 1)
local Rope1 = constraint.Rope(self,self.name,0,0,self:WorldToLocal( self.name:GetPos() + Forward * RopeLength * 0.5 + Right * RopeLength), Vector(0,0,0), Vector(RopeLength * 0.5,RopeLength,0):Length(), 0, 0, 0,"cable/cable2", true )
local Rope2 = constraint.Rope(self,self.name,0,0,self:WorldToLocal( self.name:GetPos() - Forward * RopeLength * 0.5 + Right * RopeLength), Vector(0,0,0), Vector(RopeLength * 0.5,RopeLength,0):Length(), 0, 0, 0,"cable/cable2", true )
if self.StrengthenSuspension == true then
local Rope3 = constraint.Rope(self,self.name,0,0,self:WorldToLocal( poseposition - Up * suspensiontravel * 0.5 + Right * LimiterLength), Vector(0,0,0),LimiterRopeLength * 0.99, 0, 0, 0,"cable/cable2", false )
local Rope4 = constraint.Rope(self,self.name,0,0,self:WorldToLocal( poseposition - Up * suspensiontravel * 0.5 - Right * LimiterLength), Vector(0,0,0),LimiterRopeLength * 1, 0, 0, 0,"cable/cable2", false )
local elastic1 = constraint.Elastic(self.name, self, 0, 0, Vector(0,0,height), self:WorldToLocal( self.name:GetPos() ), constant * 0.5, damping * 0.5, rdamping * 0.5,"cable/cable2",0, false)
local elastic2 = constraint.Elastic(self.name, self, 0, 0, Vector(0,0,height), self:WorldToLocal( self.name:GetPos() ), constant * 0.5, damping * 0.5, rdamping * 0.5,"cable/cable2",0, false)
Rope3.DoNotDuplicate = true
Rope4.DoNotDuplicate = true
elastic1.DoNotDuplicate = true
elastic2.DoNotDuplicate = true
self.Elastics[index] = elastic1
self.Elastics[index * 10] = elastic2
else
local Rope3 = constraint.Rope(self,self.name,0,0,self:WorldToLocal( poseposition - Up * suspensiontravel * 0.5 + Right * LimiterLength), Vector(0,0,0),LimiterRopeLength, 0, 0, 0,"cable/cable2", false )
local elastic = constraint.Elastic(self.name, self, 0, 0, Vector(0,0,height), self:WorldToLocal( self.name:GetPos() ), constant, damping, rdamping,"cable/cable2",0, false)
Rope3.DoNotDuplicate = true
elastic.DoNotDuplicate = true
self.Elastics[index] = elastic
end
self.Wheels[index] = self.name
Ballsocket.DoNotDuplicate = true
Rope1.DoNotDuplicate = true
Rope2.DoNotDuplicate = true
if index == 2 then
if IsValid( self.Wheels[ 1 ] ) and IsValid( self.Wheels[ 2 ] ) then
local nocollide = constraint.NoCollide( self.Wheels[ 1 ], self.Wheels[ 2 ], 0, 0 )
nocollide.DoNotDuplicate = true
end
elseif index == 4 then
if IsValid( self.Wheels[ 3 ] ) and IsValid( self.Wheels[ 4 ] ) then
local nocollide = constraint.NoCollide( self.Wheels[ 3 ], self.Wheels[ 4 ], 0, 0 )
nocollide.DoNotDuplicate = true
end
elseif index == 6 then
if IsValid( self.Wheels[ 5 ] ) and IsValid( self.Wheels[ 6 ] ) then
local nocollide = constraint.NoCollide( self.Wheels[ 5 ], self.Wheels[ 6 ], 0, 0 )
nocollide.DoNotDuplicate = true
end
end
end

View File

@@ -0,0 +1,102 @@
--[[
| 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()
ENT.Type = "anim"
ENT.DoNotDuplicate = true
function ENT:SetupDataTables()
self:NetworkVar( "Entity",0, "Base" )
end
function ENT:GetFuel()
local veh = self:GetBase()
if not IsValid( veh ) then return 0 end
return veh:GetFuel() / veh:GetMaxFuel()
end
function ENT:SetFuel( new )
local veh = self:GetBase()
if not IsValid( veh ) then return end
veh:SetFuel( new * veh:GetMaxFuel() )
end
function ENT:GetSize()
local veh = self:GetBase()
if not IsValid( veh ) then return 0 end
return veh:GetMaxFuel()
end
function ENT:GetFuelType()
local veh = self:GetBase()
if not IsValid( veh ) then return -1 end
return veh:GetFuelType()
end
function ENT:GetDoorHandler()
return NULL
end
function ENT:GetHP()
return 100
end
function ENT:GetMaxHP()
return 100
end
function ENT:GetDestroyed()
return false
end
if SERVER then
function ENT:Initialize()
self:SetMoveType( MOVETYPE_NONE )
self:SetSolid( SOLID_NONE )
self:DrawShadow( false )
debugoverlay.Cross( self:GetPos(), 20, 5, Color( 255, 93, 0 ) )
end
function ENT:ExtinguishAndRepair()
end
function ENT:Think()
self:NextThink( CurTime() + 1 )
return true
end
function ENT:OnTakeDamage( dmginfo )
end
return
end
function ENT:Initialize()
end
function ENT:OnRemove()
end
function ENT:Draw()
end
function ENT:Think()
return false
end

View File

@@ -0,0 +1,103 @@
--[[
| 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()
ENT.Type = "anim"
ENT.Spawnable = false
ENT.AdminSpawnable = false
if CLIENT then
local Mat = CreateMaterial("simfphysdamage", "VertexLitGeneric", {["$basetexture"] = "models/player/player_chrome1"})
function ENT:Draw()
self:DrawModel()
render.ModelMaterialOverride( Mat )
render.SetBlend( 0.8 )
self:DrawModel()
render.ModelMaterialOverride()
render.SetBlend(1)
end
end
if SERVER then
function ENT:Initialize()
self:PhysicsInit( SOLID_VPHYSICS )
self:SetMoveType( MOVETYPE_VPHYSICS )
self:SetSolid( SOLID_VPHYSICS )
if not IsValid( self:GetPhysicsObject() ) then
self.RemoveTimer = 0
self:Remove()
return
end
local PhysObj = self:GetPhysicsObject()
PhysObj:EnableMotion(true)
PhysObj:Wake()
self:SetCollisionGroup( COLLISION_GROUP_DEBRIS )
self:SetRenderMode( RENDERMODE_TRANSALPHA )
local fxPos = self:LocalToWorld( self:OBBCenter() )
timer.Simple( 0.05, function()
if not IsValid( self ) then return end
if self.MakeSound == true then
self:Ignite( 30 )
else
local GibDir = Vector( math.Rand(-1,1), math.Rand(-1,1), 1.5 ):GetNormalized()
PhysObj:SetVelocityInstantaneous( GibDir * math.random(800,1300) )
local effectdata = EffectData()
effectdata:SetOrigin( fxPos )
effectdata:SetStart( PhysObj:GetMassCenter() )
effectdata:SetEntity( self )
effectdata:SetScale( math.Rand(0.3,0.7) )
effectdata:SetMagnitude( math.Rand(0.5,2.5) )
util.Effect( "lvs_firetrail", effectdata )
end
end)
self.RemoveDis = GetConVar("sv_simfphys_gib_lifetime"):GetFloat()
self.RemoveTimer = CurTime() + self.RemoveDis
end
function ENT:Think()
if self.RemoveTimer < CurTime() then
if self.RemoveDis > 0 then
self:Remove()
end
end
self:NextThink( CurTime() + 0.2 )
return true
end
function ENT:OnRemove()
if self.FireSound then
self.FireSound:Stop()
end
end
function ENT:OnTakeDamage( dmginfo )
self:TakePhysicsDamage( dmginfo )
end
function ENT:PhysicsCollide( data, physobj )
end
end

View File

@@ -0,0 +1,663 @@
--[[
| 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()
ENT.Type = "anim"
ENT.Spawnable = false
ENT.AdminSpawnable = false
ENT.DoNotDuplicate = true
ENT.RenderGroup = RENDERGROUP_BOTH
function ENT:SetupDataTables()
self:NetworkVar( "Float", 0, "Radius")
self:NetworkVar( "Float", 1, "OnGround" )
self:NetworkVar( "Float", 2, "RPM" )
self:NetworkVar( "Float", 3, "GripLoss" )
self:NetworkVar( "Float", 4, "Speed" )
self:NetworkVar( "Float", 5, "SkidSound" )
self:NetworkVar( "String", 2, "SurfaceMaterial" )
self:NetworkVar( "Bool", 1, "Damaged" )
self:NetworkVar( "Entity", 1, "BaseEnt" )
if SERVER then
self:NetworkVarNotify( "Damaged", self.OnDamaged )
end
end
function ENT:GetWidth()
return 3
end
function ENT:VelToRPM( speed )
if not speed then return 0 end
return speed * 60 / math.pi / (self:GetRadius() * 2)
end
function ENT:RPMToVel( rpm )
if not rpm then return 0 end
return (math.pi * rpm * self:GetRadius() * 2) / 60
end
if SERVER then
function ENT:Initialize()
self:SetModel( "models/props_vehicles/tire001c_car.mdl" )
self:PhysicsInit( SOLID_VPHYSICS )
self:SetMoveType( MOVETYPE_VPHYSICS )
self:SetSolid( SOLID_VPHYSICS )
self:SetCollisionGroup( COLLISION_GROUP_WEAPON )
self:SetUseType( SIMPLE_USE )
self:AddFlags( FL_OBJECT )
self:AddEFlags( EFL_NO_PHYSCANNON_INTERACTION )
self:DrawShadow( false )
self.OldMaterial = ""
self.OldMaterial2 = ""
self.OldVar = 0
self.OldVar2 = 0
local Color = self:GetColor()
local dot = Color.r * Color.g * Color.b * Color.a
self.OldColor = dot
end
function ENT:Use( ply )
local base = self:GetBaseEnt()
if not IsValid( base ) then return end
base:Use( ply )
end
function ENT:Think()
if self.GhostEnt then
local Color = self:GetColor()
local dot = Color.r * Color.g * Color.b * Color.a
if dot ~= self.OldColor then
if IsValid( self.GhostEnt ) then
self.GhostEnt:SetColor( Color )
self.GhostEnt:SetRenderMode( self:GetRenderMode() )
end
self.OldColor = dot
end
end
self:NextThink( CurTime() + 0.15 )
return true
end
function ENT:FixTire()
self:SetDamaged( false )
if not self.PreBreak then return end
self.PreBreak:Stop()
self.PreBreak = nil
end
function ENT:OnRemove()
if self.PreBreak then
self.PreBreak:Stop()
end
end
function ENT:PhysicsCollide( data, physobj )
if data.Speed > 100 and data.DeltaTime > 0.2 then
if data.Speed > 400 then
self:EmitSound( "Rubber_Tire.ImpactHard" )
self:EmitSound( "simulated_vehicles/suspension_creak_".. math.random(1,6) ..".ogg" )
else
self:EmitSound( "Rubber.ImpactSoft" )
end
end
end
function ENT:OnTakeDamage( dmginfo )
local BaseEnt = self:GetBaseEnt()
if IsValid( BaseEnt ) and dmginfo:IsDamageType( DMG_AIRBOAT + DMG_BULLET ) then
BaseEnt:OnTakeDamage( dmginfo )
end
if self:GetDamaged() or not simfphys.DamageEnabled then return end
local Damage = dmginfo:GetDamage()
local DamagePos = dmginfo:GetDamagePosition()
if not IsValid( BaseEnt ) or BaseEnt:GetBulletProofTires() or Damage <= 1 then return end
if not self.PreBreak then
self.PreBreak = CreateSound(self, "ambient/gas/cannister_loop.wav")
self.PreBreak:PlayEx(0.5,100)
timer.Simple(math.Rand(0.5,5), function()
if IsValid(self) and not self:GetDamaged() then
self:SetDamaged( true )
if self.PreBreak then
self.PreBreak:Stop()
self.PreBreak = nil
end
end
end)
else
self:SetDamaged( true )
self.PreBreak:Stop()
self.PreBreak = nil
end
end
function ENT:OnDamaged( name, old, new)
if new == old then return end
if new == true then
self.dRadius = self:BoundingRadius() * 0.28
self:EmitSound( "simulated_vehicles/sfx/tire_break.ogg" )
if IsValid( self.GhostEnt ) then
self.GhostEnt:SetParent( nil )
self.GhostEnt:GetPhysicsObject():EnableMotion( false )
self.GhostEnt:SetPos( self:LocalToWorld( Vector(0,0,-self.dRadius) ) )
self.GhostEnt:SetParent( self )
end
else
if IsValid( self.GhostEnt ) then
self.GhostEnt:SetParent( nil )
self.GhostEnt:GetPhysicsObject():EnableMotion( false )
self.GhostEnt:SetPos( self:LocalToWorld( Vector(0,0,0) ) )
self.GhostEnt:SetParent( self )
end
end
local BaseEnt = self:GetBaseEnt()
if not IsValid( BaseEnt ) then return end
BaseEnt:SetSuspension( self.Index , new )
end
return
end
ENT.SkidmarkTraceAdd = Vector(0,0,10)
ENT.SkidmarkDelay = 0.05
ENT.SkidmarkLifetime = 10
ENT.SkidmarkRed = 0
ENT.SkidmarkGreen = 0
ENT.SkidmarkBlue = 0
ENT.SkidmarkAlpha = 150
ENT.SkidmarkSurfaces = {
["concrete"] = true,
["tile"] = true,
["metal"] = true,
["boulder"] = true,
["default"] = true,
}
ENT.DustEffectSurfaces = {
["sand"] = true,
["dirt"] = true,
["grass"] = true,
["antlionsand"] = true,
}
function ENT:Initialize()
self.FadeHeat = 0
timer.Simple( 0.01, function()
if not IsValid( self ) then return end
self.Radius = self:BoundingRadius()
end)
end
function ENT:Think()
self:ManageSmoke()
local T = CurTime()
if (self.fxTimer or 0) < T then
self:CalcWheelSlip()
self.fxTimer = T + 0.1
end
self:SetNextClientThink( CurTime() + 0.005 )
return true
end
function ENT:ManageSmoke()
local BaseEnt = self:GetBaseEnt()
if not IsValid( BaseEnt ) then return end
if LocalPlayer():GetPos():DistToSqr(self:GetPos()) > 6000 * 6000 or not BaseEnt:GetActive() then return end
local WheelOnGround = self:GetOnGround()
local GripLoss = self:GetGripLoss()
local Material = self:GetSurfaceMaterial()
if WheelOnGround > 0 and (Material == "concrete" or Material == "rock" or Material == "tile") and GripLoss > 0 then
self.FadeHeat = math.Clamp( self.FadeHeat + GripLoss * 0.06,0,10)
else
self.FadeHeat = self.FadeHeat * 0.995
end
local Scale = self.FadeHeat ^ 3 * 0.001
local SmokeOn = (self.FadeHeat >= 7)
local DirtOn = GripLoss > 0.05
local lcolor = BaseEnt:GetTireSmokeColor() * 255
local Speed = self:GetVelocity():Length()
local OnRim = self:GetDamaged()
local Forward = self:GetForward()
local Dir = (BaseEnt:GetGear() < 2) and Forward or -Forward
local WheelSize = self.Radius or 0
local Pos = self:GetPos()
if SmokeOn and not OnRim then
local effectdata = EffectData()
effectdata:SetOrigin( Pos )
effectdata:SetNormal( Dir )
effectdata:SetMagnitude( Scale )
effectdata:SetRadius( WheelSize )
effectdata:SetStart( Vector( lcolor.r, lcolor.g, lcolor.b ) )
util.Effect( "simfphys_tiresmoke", effectdata )
end
if WheelOnGround == 0 then return end
if (Speed > 150 or DirtOn) and OnRim then
self:MakeSparks( GripLoss, Dir, Pos, WheelSize )
end
end
function ENT:MakeSparks( Scale, Dir, Pos, WheelSize )
self.NextSpark = self.NextSpark or 0
if self.NextSpark < CurTime() then
self.NextSpark = CurTime() + 0.1
local effectdata = EffectData()
effectdata:SetOrigin( Pos - Vector(0,0,WheelSize * 0.5) )
effectdata:SetNormal( (Dir + Vector(0,0,0.5)) * Scale * 0.5)
util.Effect( "manhacksparks", effectdata, true, true )
end
end
function ENT:Draw()
end
function ENT:DrawTranslucent()
self:CalcWheelEffects()
end
function ENT:OnRemove()
self:StopWheelEffects()
end
function ENT:CalcWheelSlip()
local Base = self:GetBaseEnt()
if not IsValid( Base ) then return end
local Vel = self:GetVelocity()
local VelLength = Vel:Length()
local rpmTheoretical = self:VelToRPM( VelLength )
local rpm = math.abs( self:GetRPM() )
self._WheelSlip = math.max( rpm - rpmTheoretical - 250, 0 ) ^ 2 + math.max( math.abs( Base:VectorSplitNormal( self:GetRight(), Vel * 4 ) ) - VelLength, 0 )
self._WheelSkid = VelLength + self._WheelSlip
end
function ENT:GetSlip()
return (self._WheelSlip or 0)
end
function ENT:GetSkid()
return (self._WheelSkid or 0)
end
function ENT:StopWheelEffects()
if not self._DoingWheelFx then return end
self._DoingWheelFx = nil
self:FinishSkidmark()
end
function ENT:StartWheelEffects( Base, trace, traceWater )
self:DoWheelEffects( Base, trace, traceWater )
if self._DoingWheelFx then return end
self._DoingWheelFx = true
end
function ENT:DoWheelEffects( Base, trace, traceWater )
if not trace.Hit then self:FinishSkidmark() return end
local SurfacePropName = util.GetSurfacePropName( trace.SurfaceProps )
local SkidValue = self:GetSkid()
if traceWater.Hit then
local Scale = math.min( 0.3 + (SkidValue - 100) / 4000, 1 ) ^ 2
local effectdata = EffectData()
effectdata:SetOrigin( trace.HitPos )
effectdata:SetEntity( Base )
effectdata:SetNormal( trace.HitNormal )
effectdata:SetMagnitude( Scale )
effectdata:SetFlags( 1 )
util.Effect( "simfphys_physics_wheeldust", effectdata, true, true )
self:FinishSkidmark()
return
end
if self.SkidmarkSurfaces[ SurfacePropName ] then
local Scale = math.min( 0.3 + SkidValue / 4000, 1 ) ^ 2
if Scale > 0.2 then
self:StartSkidmark( trace.HitPos )
self:CalcSkidmark( trace, Base:GetCrosshairFilterEnts() )
else
self:FinishSkidmark()
end
local effectdata = EffectData()
effectdata:SetOrigin( trace.HitPos )
effectdata:SetEntity( Base )
effectdata:SetNormal( trace.HitNormal )
util.Effect( "simfphys_physics_wheelsmoke", effectdata, true, true )
else
self:FinishSkidmark()
end
if not LVS.ShowEffects then return end
if self.DustEffectSurfaces[ SurfacePropName ] then
local Scale = math.min( 0.3 + (SkidValue - 100) / 4000, 1 ) ^ 2
local effectdata = EffectData()
effectdata:SetOrigin( trace.HitPos )
effectdata:SetEntity( Base )
effectdata:SetNormal( trace.HitNormal )
effectdata:SetMagnitude( Scale )
effectdata:SetFlags( 0 )
util.Effect( "simfphys_physics_wheeldust", effectdata, true, true )
end
end
function ENT:CalcWheelEffects()
local T = CurTime()
if (self._NextFx or 0) > T then return end
self._NextFx = T + 0.05
local Base = self:GetBaseEnt()
if not IsValid( Base ) then return end
local Radius = Base:GetUp() * (self:GetRadius() + 1)
local Pos = self:GetPos() + self:GetVelocity() * 0.025
local StartPos = Pos + Radius
local EndPos = Pos - Radius
local trace = util.TraceLine( {
start = StartPos,
endpos = EndPos,
filter = Base:GetCrosshairFilterEnts(),
} )
local traceWater = util.TraceLine( {
start = StartPos,
endpos = EndPos,
filter = Base:GetCrosshairFilterEnts(),
mask = MASK_WATER,
} )
self:CalcWheelSounds( Base, trace, traceWater )
if traceWater.Hit and trace.HitPos.z < traceWater.HitPos.z then
if math.abs( self:GetRPM() ) > 25 then
local effectdata = EffectData()
effectdata:SetOrigin( traceWater.Fraction > 0.5 and traceWater.HitPos or Pos )
effectdata:SetEntity( Base )
effectdata:SetMagnitude( self:BoundingRadius() )
effectdata:SetFlags( 0 )
util.Effect( "simfphys_physics_wheelwatersplash", effectdata )
end
end
if self:GetSlip() < 500 or self:GetDamaged() then self:StopWheelEffects() return end
self:StartWheelEffects( Base, trace, traceWater )
end
function ENT:CalcWheelSounds( Base, trace, traceWater )
if not trace.Hit then return end
-- TODO: fix reason for this workaround
if trace.Entity == self then
if istable( Base.CrosshairFilterEnts ) and #Base.CrosshairFilterEnts > 1 then
Base.CrosshairFilterEnts = nil
end
return
end
local RPM = math.abs( self:GetRPM() )
if RPM > 50 then
if traceWater.Hit then
Base:DoTireSound( "roll_wet" )
return
end
if self:GetDamaged() then
Base:DoTireSound( "roll_damaged" )
return
end
local surface = self.DustEffectSurfaces[ util.GetSurfacePropName( trace.SurfaceProps ) ] and "_dirt" or ""
local snd_type = (self:GetSlip() > 500) and "skid" or "roll"
if (istable( StormFox ) or istable( StormFox2 )) and surface ~= "_dirt" then
local Rain = false
if StormFox then
Rain = StormFox.IsRaining()
end
if StormFox2 then
Rain = StormFox2.Weather:IsRaining()
end
if Rain then
local effectdata = EffectData()
effectdata:SetOrigin( trace.HitPos )
effectdata:SetEntity( Base )
effectdata:SetMagnitude( self:BoundingRadius() )
effectdata:SetFlags( 1 )
util.Effect( "lvs_physics_wheelwatersplash", effectdata )
Base:DoTireSound( snd_type.."_wet" )
return
end
end
Base:DoTireSound( snd_type..surface )
end
end
function ENT:GetSkidMarks()
if not istable( self._activeSkidMarks ) then
self._activeSkidMarks = {}
end
return self._activeSkidMarks
end
function ENT:StartSkidmark( pos )
if self:GetWidth() <= 0 or self._SkidMarkID or not LVS.ShowTraileffects then return end
local ID = 1
for _,_ in ipairs( self:GetSkidMarks() ) do
ID = ID + 1
end
self._activeSkidMarks[ ID ] = {
active = true,
startpos = pos + self.SkidmarkTraceAdd,
delay = CurTime() + self.SkidmarkDelay,
positions = {},
}
self._SkidMarkID = ID
end
function ENT:FinishSkidmark()
if not self._SkidMarkID then return end
self._activeSkidMarks[ self._SkidMarkID ].active = false
self._SkidMarkID = nil
end
function ENT:RemoveSkidmark( id )
if not id then return end
self._activeSkidMarks[ id ] = nil
end
function ENT:CalcSkidmark( trace, Filter )
local T = CurTime()
local CurActive = self:GetSkidMarks()[ self._SkidMarkID ]
if not CurActive or not CurActive.active or CurActive.delay >= T then return end
CurActive.delay = T + self.SkidmarkDelay
local W = self:GetWidth()
local cur = trace.HitPos + self.SkidmarkTraceAdd * 0.5
local prev = CurActive.positions[ #CurActive.positions ]
if not prev then
local sub = cur - CurActive.startpos
local L = sub:Length() * 0.5
local C = (cur + CurActive.startpos) * 0.5
local Ang = sub:Angle()
local Forward = Ang:Right()
local Right = Ang:Forward()
local p1 = C + Forward * W + Right * L
local p2 = C - Forward * W + Right * L
local t1 = util.TraceLine( { start = p1, endpos = p1 - self.SkidmarkTraceAdd } )
local t2 = util.TraceLine( { start = p2, endpos = p2 - self.SkidmarkTraceAdd } )
prev = {
px = CurActive.startpos,
p1 = t1.HitPos + t1.HitNormal,
p2 = t2.HitPos + t2.HitNormal,
lifetime = T + self.SkidmarkLifetime - self.SkidmarkDelay,
alpha = 0,
}
end
local sub = cur - prev.px
local L = sub:Length() * 0.5
local C = (cur + prev.px) * 0.5
local Ang = sub:Angle()
local Forward = Ang:Right()
local Right = Ang:Forward()
local p1 = C + Forward * W + Right * L
local p2 = C - Forward * W + Right * L
local t1 = util.TraceLine( { start = p1, endpos = p1 - self.SkidmarkTraceAdd, filter = Filter, } )
local t2 = util.TraceLine( { start = p2, endpos = p2 - self.SkidmarkTraceAdd, filter = Filter, } )
local nextID = #CurActive.positions + 1
CurActive.positions[ nextID ] = {
px = cur,
p1 = t1.HitPos + t1.HitNormal,
p2 = t2.HitPos + t2.HitNormal,
lifetime = T + self.SkidmarkLifetime,
alpha = math.min( nextID / 10, 1 ),
}
end
function ENT:RenderSkidMarks()
local T = CurTime()
for id, skidmark in pairs( self:GetSkidMarks() ) do
local prev
local AmountDrawn = 0
for markID, data in pairs( skidmark.positions ) do
if not prev then
prev = data
continue
end
local Mul = math.max( data.lifetime - CurTime(), 0 ) / self.SkidmarkLifetime
if Mul > 0 then
AmountDrawn = AmountDrawn + 1
render.DrawQuad( data.p2, data.p1, prev.p1, prev.p2, Color( self.SkidmarkRed, self.SkidmarkGreen, self.SkidmarkBlue, math.min(255 * Mul * data.alpha,self.SkidmarkAlpha) ) )
end
prev = data
end
if not skidmark.active and AmountDrawn == 0 then
self:RemoveSkidmark( id )
end
end
end
hook.Add( "PreDrawTranslucentRenderables", "!!!!lvs_fakephysics_skidmarks", function( bDepth, bSkybox )
if bSkybox then return end
render.SetColorMaterial()
for _, wheel in ipairs( ents.FindByClass("gmod_sent_vehicle_fphysics_wheel") ) do
wheel:RenderSkidMarks()
end
end)

View File

@@ -0,0 +1,30 @@
--[[
| 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/
--]]
-- StormFox2 E2 extension
-- Created by Nak
-- Time
E2Helper.Descriptions["sfTime()"] = "Returns the time as a number between 0 and 1439"
E2Helper.Descriptions["isNight()"] = "Is it night?"
E2Helper.Descriptions["isDay()"] = "Is it day?"
E2Helper.Descriptions["sfTimeDisplay()"] = "Returns the time as a 24h string"
E2Helper.Descriptions["sfTimeDisplay12h()"] = "Returns the time as a 12h string"
-- Weather
E2Helper.Descriptions["isRaining()"] = "Is it raining?"
E2Helper.Descriptions["isSnowing()"] = "Is it snowing?"
E2Helper.Descriptions["isThundering()"] = "Is it thundering?"
E2Helper.Descriptions["getWeather()"] = "Returns the description of the current weahter"
E2Helper.Descriptions["getWeatherPercent()"] = "Returns the weather amount between [0-1]"
-- Wind
E2Helper.Descriptions["getWind()"] = "Returns the wind in m/s"
E2Helper.Descriptions["getWindBeaufort()"] = "Returns the wind in Beaufort scale"

View File

@@ -0,0 +1,79 @@
--[[
| 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/
--]]
-- StormFox2 E2 extension
-- By Nak
E2Lib.RegisterExtension("stormfox2", true, "Lets E2 chips use StormFox functions")
__e2setcost( 3 )
-- Time
e2function number sfTime()
return StormFox2.Time.Get()
end
e2function number isNight()
return StormFox2.Time.IsNight() and 1 or 0
end
e2function number isDay()
return StormFox2.Time.IsDay() and 1 or 0
end
__e2setcost( 15 )
e2function string sfTimeDisplay()
return StormFox2.Time.TimeToString(nil)
end
e2function string sfTimeDisplay12h()
return StormFox2.Time.TimeToString(nil,true)
end
-- Weather
__e2setcost( 7 )
local function isRaining()
local wD = StormFox2.Weather.GetCurrent()
return wD.Name == "Rain" or wD.Inherit == "Rain"
end
local function isCold()
return StormFox2.Temperature.Get() <= -2
end
e2function number isRaining()
if isCold() then return 0 end
return isRaining() and 1 or 0
end
e2function number isSnowing()
if not isCold() then return 0 end
return isRaining() and 1 or 0
end
e2function number isThundering()
return StormFox2.Thunder.IsThundering() and 1 or 0
end
__e2setcost( 10 )
e2function string getWeather()
return StormFox2.Weather.GetDescription()
end
e2function number getWeatherPercent()
return StormFox2.Weather.GetPercent()
end
-- Wind
__e2setcost( 3 )
e2function number getWind()
return StormFox2.Wind.GetForce()
end
__e2setcost( 10 )
e2function number getWindBeaufort()
return StormFox2.Wind.GetBeaufort()
end

View File

@@ -0,0 +1,102 @@
--[[
| 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/
--]]
if (SERVER) then
AddCSLuaFile();
end;
DEFINE_BASECLASS("base_entity");
ENT.PrintName = "Ladder (BASE)";
ENT.Category = "Ladders";
ENT.Spawnable = false;
ENT.AdminOnly = false;
ENT.Model = Model("models/props_c17/metalladder001.mdl");
ENT.RenderGroup = RENDERGROUP_BOTH;
if (SERVER) then
function ENT:Initialize()
self:SetModel(self.Model);
self:SetSolid(SOLID_VPHYSICS);
self:PhysicsInit(SOLID_VPHYSICS);
self:SetUseType(SIMPLE_USE);
self:SetCollisionGroup(COLLISION_GROUP_WEAPON);
local phys = self:GetPhysicsObject();
if (IsValid(phys)) then
phys:EnableMotion(false);
end;
self:UpdateLadder(true);
end;
function ENT:UpdateLadder(bCreate)
if (bCreate) then
local oldAngs = self:GetAngles();
self:SetAngles(Angle(0, 0, 0));
local pos = self:GetPos();
local dist = self:OBBMaxs().x + 17;
local dismountDist = self:OBBMaxs().x + 49;
local bottom = self:LocalToWorld(Vector(0, 0, self:OBBMins().z));
local top = self:LocalToWorld(Vector(0, 0, self:OBBMaxs().z));
for k, v in pairs(self:GetChildren()) do
SafeRemoveEntity(v);
end;
self.ladder = ents.Create("func_useableladder");
self.ladder:SetPos(pos + self:GetForward() * dist);
self.ladder:SetKeyValue("point0", tostring(bottom + self:GetForward() * dist));
self.ladder:SetKeyValue("point1", tostring(top + self:GetForward() * dist));
self.ladder:SetKeyValue("targetname", "zladder_" .. self:EntIndex());
self.ladder:SetParent(self);
self.ladder:Spawn();
self.bottomDismount = ents.Create("info_ladder_dismount");
self.bottomDismount:SetPos(bottom + self:GetForward() * dismountDist);
self.bottomDismount:SetKeyValue("laddername", "zladder_" .. self:EntIndex());
self.bottomDismount:SetParent(self);
self.bottomDismount:Spawn();
self.topDismount = ents.Create("info_ladder_dismount");
self.topDismount:SetPos(top - self:GetForward() * dist);
self.topDismount:SetKeyValue("laddername", "zladder_" .. self:EntIndex());
self.topDismount:SetParent(self);
self.topDismount:Spawn();
self.ladder:Activate();
self:SetAngles(oldAngs);
else
self.ladder:Activate();
end;
end;
function ENT:Think()
if (IsValid(self.ladder)) then
self:UpdateLadder();
self:NextThink(CurTime() + 1);
return true;
end;
end;
elseif (CLIENT) then
function ENT:Initialize()
self:SetSolid(SOLID_VPHYSICS);
end;
function ENT:Draw()
self:DrawModel();
end;
end;

View File

@@ -0,0 +1,22 @@
--[[
| 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/
--]]
if (SERVER) then
AddCSLuaFile();
end;
ENT.Type = "anim";
ENT.Base = "ladder_base";
ENT.PrintName = "Ladder (Small)"
ENT.Category = "Ladders"
ENT.Spawnable = true
ENT.AdminOnly = false
ENT.Model = Model("models/props_c17/metalladder001.mdl");
ENT.RenderGroup = RENDERGROUP_BOTH;

View File

@@ -0,0 +1,22 @@
--[[
| 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/
--]]
if (SERVER) then
AddCSLuaFile();
end;
ENT.Type = "anim";
ENT.Base = "ladder_base";
ENT.PrintName = "Ladder (Small - Attached)"
ENT.Category = "Ladders"
ENT.Spawnable = true
ENT.AdminOnly = false
ENT.Model = Model("models/props_c17/metalladder002.mdl");
ENT.RenderGroup = RENDERGROUP_BOTH;

View File

@@ -0,0 +1,22 @@
--[[
| 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/
--]]
if (SERVER) then
AddCSLuaFile();
end;
ENT.Type = "anim";
ENT.Base = "ladder_base";
ENT.PrintName = "Ladder (Small - Runged)"
ENT.Category = "Ladders"
ENT.Spawnable = true
ENT.AdminOnly = false
ENT.Model = Model("models/props/cs_militia/ladderrung.mdl");
ENT.RenderGroup = RENDERGROUP_BOTH;

View File

@@ -0,0 +1,22 @@
--[[
| 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/
--]]
if (SERVER) then
AddCSLuaFile();
end;
ENT.Type = "anim";
ENT.Base = "ladder_base";
ENT.PrintName = "Ladder (Small - Wood)"
ENT.Category = "Ladders"
ENT.Spawnable = true
ENT.AdminOnly = false
ENT.Model = Model("models/props/cs_militia/ladderwood.mdl");
ENT.RenderGroup = RENDERGROUP_BOTH;

View File

@@ -0,0 +1,46 @@
--[[
| 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/
--]]
--[[-------------------------------------------------------------------------
A day relay
---------------------------------------------------------------------------]]
ENT.Type = "point"
ENT.PrintName = "logic_day_relay"
ENT.Author = "Nak"
ENT.Information = "Gets fired when it is daytime"
ENT.Category = "StormFox2"
ENT.Editable = false
ENT.Spawnable = false
ENT.AdminOnly = false
function ENT:Initialize()
end
--[[
0 : "When lightlvl change."
1 : "When it turns day."
]]
function ENT:GetTriggerType()
return self.trigger_type or 0
end
function ENT:Trigger()
self:TriggerOutput("OnTrigger")
end
function ENT:KeyValue( k, v )
if k == "OnTrigger" then
self:StoreOutput( k, v )
elseif k == "trigger_type" then
self.trigger_type = tonumber(v)
end
end

View File

@@ -0,0 +1,46 @@
--[[
| 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/
--]]
--[[-------------------------------------------------------------------------
A night relay
---------------------------------------------------------------------------]]
ENT.Type = "point"
ENT.PrintName = "logic_night_relay"
ENT.Author = "Nak"
ENT.Information = "Gets fired when it is daytime"
ENT.Category = "StormFox2"
ENT.Editable = false
ENT.Spawnable = false
ENT.AdminOnly = false
function ENT:Initialize()
end
--[[
0 : "When lightlvl change."
1 : "When it turns night."
]]
function ENT:GetTriggerType()
return self.trigger_type or 0
end
function ENT:Trigger()
self:TriggerOutput("OnTrigger")
end
function ENT:KeyValue( k, v )
if k == "OnTrigger" then
self:StoreOutput( k, v )
elseif k == "trigger_type" then
self.trigger_type = tonumber(v)
end
end

View File

@@ -0,0 +1,88 @@
--[[
| 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/
--]]
--[[-------------------------------------------------------------------------
A temperature relay
---------------------------------------------------------------------------]]
ENT.Type = "point"
ENT.PrintName = "logic_temperature_relay"
ENT.Author = "Nak"
ENT.Information = "Gets fired when temperature is within"
ENT.Category = "StormFox2"
ENT.Editable = false
ENT.Spawnable = false
ENT.AdminOnly = false
function ENT:Initialize()
self.triggered = false
self.bake = false
end
--[[
0 : "When lightlvl change."
1 : "When it turns day."
]]
function ENT:GetTriggerType()
return self.trigger_type or 0
end
function ENT:Trigger()
self:TriggerOutput("OnTrigger")
end
function ENT:KeyValue( k, v )
if k == "OnTrigger" then
self:StoreOutput( k, v )
elseif k == "temperature_type" then
--[[[
0 : "Celsius"
1 : "Fahrenheit"
2 : "Kelvin"
]]
self.temperature_type = tonumber(v)
elseif k == "temperature_min" then
self.temperature_min = tonumber(v)
elseif k == "temperature_max" then
self.temperature_max = tonumber(v)
end
end
-- Converts the temperatures into celcius, so we don't have to keep converting.
function ENT:Bake()
if self.bake then return end
if self.temperature_type == 1 then
self.temperature_min = StormFox2.Temperature.Convert("fahrenheit","celsius",self.temperature_min)
self.temperature_max = StormFox2.Temperature.Convert("fahrenheit","celsius",self.temperature_max)
elseif temperature_type == 2 then
self.temperature_min = StormFox2.Temperature.Convert("kelvin","celsius",self.temperature_min)
self.temperature_max = StormFox2.Temperature.Convert("kelvin","celsius",self.temperature_max)
end
self.bake = true
end
function ENT:Think()
if not StormFox2 or not StormFox2.Loaded then return end
self:Bake() -- Convert variables
local c = StormFox2.Temperature.Get()
if not self.triggered then
if c >= self.temperature_min and c <= self.temperature_max then
self:Trigger()
self.triggered = true
end
else
if c < self.temperature_min or c > self.temperature_max then
self.triggered = false
end
end
self:NextThink( CurTime() + 3 )
return true
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/
--]]
--[[-------------------------------------------------------------------------
A thunder relay
---------------------------------------------------------------------------]]
ENT.Type = "point"
ENT.PrintName = "logic_thunder_relay"
ENT.Author = "Nak"
ENT.Information = "Gets fired when thunder gets turned on/off"
ENT.Category = "StormFox2"
ENT.Editable = false
ENT.Spawnable = false
ENT.AdminOnly = false
function ENT:Initialize()
self.triggered = false
end
--[[
0 : "Starts thundering."
1 : "Stops thundering."
]]
function ENT:GetTriggerType()
return self.trigger_type or 0
end
function ENT:Trigger()
self:TriggerOutput("OnTrigger")
end
function ENT:KeyValue( k, v )
if k == "OnTrigger" then
self:StoreOutput( k, v )
elseif k == "trigger_type" then
self.trigger_type = tonumber(v)
if self.trigger_type == 1 then
self.triggered = true
end
end
end
function ENT:Think()
if not StormFox2 or not StormFox2.Loaded then return end
local b = StormFox2.Thunder.IsThundering()
local c = self:GetTriggerType() == 0 -- True if we want to trigger doing thunder
local on = b == c
if not self.triggered then
if on then
self:Trigger()
self.triggered = true
end
else
if not on then
self.triggered = false
end
end
self:NextThink( CurTime() + 6 )
return true
end

View File

@@ -0,0 +1,58 @@
--[[
| 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/
--]]
--[[-------------------------------------------------------------------------
A temperature relay
---------------------------------------------------------------------------]]
ENT.Type = "point"
ENT.PrintName = "logic_time_relay"
ENT.Author = "Nak"
ENT.Information = "Gets fired when time is within settings"
ENT.Category = "StormFox2"
ENT.Editable = false
ENT.Spawnable = false
ENT.AdminOnly = false
function ENT:Initialize()
self.triggered = false
end
function ENT:Trigger()
self:TriggerOutput("OnTrigger")
end
function ENT:KeyValue( k, v )
if k == "OnTrigger" then
self:StoreOutput( k, v )
elseif k == "time_min" then
self.time_min = StormFox2.Time.StringToTime(v)
elseif k == "time_max" then
self.time_max = StormFox2.Time.StringToTime(v)
end
end
function ENT:Think()
if not StormFox2 or not StormFox2.Loaded then return end
local c = StormFox2.Time.Get()
if not self.triggered then
if StormFox2.Time.IsBetween(self.time_min, self.time_max, c) then
self:Trigger()
self.triggered = true
end
else
if not StormFox2.Time.IsBetween(self.time_min, self.time_max, c) then
self.triggered = false
end
end
self:NextThink( CurTime() + 3 )
return true
end

View File

@@ -0,0 +1,46 @@
--[[
| 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/
--]]
--[[-------------------------------------------------------------------------
A weather relay
---------------------------------------------------------------------------]]
ENT.Type = "point"
ENT.Base = "base_point"
ENT.PrintName = "logic_weather_off_relay"
ENT.Author = "Nak"
ENT.Information = "Gets fired when weather turned off"
ENT.Category = "StormFox2"
ENT.Editable = false
ENT.Spawnable = false
ENT.AdminOnly = false
function ENT:Initialize()
self.triggered = false
end
function ENT:GetRequiredWeather()
return self.weather_type
end
function ENT:Trigger()
self:TriggerOutput("OnTrigger")
end
function ENT:KeyValue( k, v )
if k == "OnTrigger" then
self:StoreOutput( k, v )
elseif k == "weather_type" then
self.weather_type = string.lower(v)
elseif k == "weather_amount" then
self.weather_amount = tonumber(v)
end
end

View File

@@ -0,0 +1,65 @@
--[[
| 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/
--]]
--[[-------------------------------------------------------------------------
A weather relay
---------------------------------------------------------------------------]]
ENT.Type = "point"
ENT.Base = "base_point"
ENT.PrintName = "logic_weather_relay"
ENT.Author = "Nak"
ENT.Information = "Gets fired when weather turned on"
ENT.Category = "StormFox2"
ENT.Editable = false
ENT.Spawnable = false
ENT.AdminOnly = false
function ENT:Initialize()
self.triggered = false
end
function ENT:HasRequredAmount()
if self.weather_type == "clear" then return true end -- Doesn't matter when it is 'Clear'.
local a = self.weather_amount or 1
local p = StormFox2.Data.GetFinal("w_Percentage") or 0
if a == 0 then
return p > 0
elseif a == 1 then
return p >= 0.1
elseif a == 2 then
return p >= 0.25
elseif a == 3 then
return p >= 0.50
elseif a == 4 then
return p >= 0.75
else
return p >= 1
end
end
function ENT:GetRequiredWeather()
return self.weather_type or "clear"
end
function ENT:Trigger()
self:TriggerOutput("OnTrigger")
end
function ENT:KeyValue( k, v )
if k == "OnTrigger" then
self:StoreOutput( k, v )
elseif k == "weather_type" then
self.weather_type = string.lower(v)
elseif k == "weather_amount" then
self.weather_amount = tonumber(v)
end
end

View File

@@ -0,0 +1,83 @@
--[[
| 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/
--]]
--[[-------------------------------------------------------------------------
A weather relay
---------------------------------------------------------------------------]]
ENT.Type = "point"
ENT.Base = "base_point"
ENT.PrintName = "logic_weather_set"
ENT.Author = "Nak"
ENT.Information = "Allows the map to set weather"
ENT.Category = "StormFox2"
ENT.Editable = false
ENT.Spawnable = false
ENT.AdminOnly = false
function ENT:Initialize()
end
function ENT:KeyValue( k, v )
if k == "weather_type" then
local n = string.lower(v)
n = n:sub(0, 1):upper() .. n:sub(2)
self.weather_type = n
elseif k == "weather_amount" then
self.weather_amount = math.Clamp(tonumber(v), 0, 1)
end
end
function ENT:GetWeather()
return self.weather_type or "Clear", self.weather_amount or 0.8
end
local delay = 0
function ENT:TriggerWeather()
if delay > CurTime() then return false end
local w, p = self:GetWeather()
StormFox2.Weather.Set(w, p)
delay = CurTime() + 1.5
return true
end
function ENT:AcceptInput( name, activator, caller, data )
if name == "SetWeather" then
self:TriggerWeather()
return true
elseif name == "SetTemperature" then
local num = tonumber(string.match(data or "0", "[-%d]+") or 0)
local t = string.match(data:lower(), "[fck]") or "c"
if t == 'k' then
num = StormFox2.Temperature.Convert("kelvin", "celsius")
elseif t == 'f' then
num = StormFox2.Temperature.Convert("fahrenheit", "celsius")
end
StormFox2.Temperature.Set(num, 2)
return true
elseif name == "EnableThunder" then
StormFox2.Thunder.SetEnabled(true)
return true
elseif name == "DisableThunder" then
StormFox2.Thunder.SetEnabled(false)
return true
elseif name == "ClearWeather" then
StormFox2.Weather.Set("Clear")
elseif name == "SetWind" then
StormFox2.Wind.SetForce( tonumber(data or "0") or 0, 2 )
elseif name == "SetWindYaw" then
local n = tonumber(data or "0") or 0
StormFox2.Wind.SetYaw( math.Clamp(n,0,360) )
elseif name == "ClearWeather" then
StormFox2.Weather.Set("Clear")
end
return false
end

239
lua/entities/lvs_armor.lua Normal file
View File

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

View File

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

View File

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

View File

@@ -0,0 +1,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

View File

@@ -0,0 +1,350 @@
--[[
| 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()
ENT.Type = "anim"
ENT.DoNotDuplicate = true
ENT.RenderGroup = RENDERGROUP_BOTH
ENT.UseRange = 75
function ENT:SetupDataTables()
self:NetworkVar( "Entity",0, "Base" )
self:NetworkVar( "Bool",0, "Active" )
self:NetworkVar( "String",0, "PoseName" )
self:NetworkVar( "Vector",0, "Mins" )
self:NetworkVar( "Vector",1, "Maxs" )
self:NetworkVar( "Float",0, "Rate" )
self:NetworkVar( "Float",1, "RateExponent" )
self:NetworkVar( "Float",2, "PoseMin" )
self:NetworkVar( "Float",3, "PoseMax" )
if SERVER then
self:SetRate( 10 )
self:SetRateExponent( 2 )
self:SetPoseMax( 1 )
end
end
function ENT:IsServerSide()
local EntTable = self:GetTable()
if isbool( EntTable._IsServerSide ) then return EntTable._IsServerSide end
local PoseName = self:GetPoseName()
if PoseName == "" then return false end
local IsServerSide = string.StartsWith( PoseName, "^" )
EntTable._IsServerSide = IsServerSide
return IsServerSide
end
function ENT:IsOpen()
return self:GetActive()
end
if SERVER then
AccessorFunc(ENT, "soundopen", "SoundOpen", FORCE_STRING)
AccessorFunc(ENT, "soundclose", "SoundClose", FORCE_STRING)
AccessorFunc(ENT, "maxsopen", "MaxsOpen", FORCE_VECTOR)
AccessorFunc(ENT, "minsopen", "MinsOpen", FORCE_VECTOR)
AccessorFunc(ENT, "maxsclosed", "MaxsClosed", FORCE_VECTOR)
AccessorFunc(ENT, "minsclosed", "MinsClosed", FORCE_VECTOR)
util.AddNetworkString( "lvs_doorhandler_interact" )
net.Receive( "lvs_doorhandler_interact", function( length, ply )
if not IsValid( ply ) then return end
local ent = net.ReadEntity()
if not IsValid( ent ) then return end
if (ply:GetPos() - ent:GetPos()):Length() > (ent.UseRange or 75) * 2 then return end
ent:Use( ply, ply )
end)
function ENT:LinkToSeat( ent )
if not IsValid( ent ) or not ent:IsVehicle() then
ErrorNoHalt( "[LVS] Couldn't link seat to doorsystem. Entity expected, got "..tostring( ent ).."\n" )
return
end
self._LinkedSeat = ent
end
function ENT:GetLinkedSeat()
return self._LinkedSeat
end
function ENT:Initialize()
self:SetMoveType( MOVETYPE_NONE )
self:SetSolid( SOLID_NONE )
self:SetUseType( SIMPLE_USE )
self:DrawShadow( false )
debugoverlay.Cross( self:GetPos(), 15, 5, Color( 255, 223, 127 ) )
end
function ENT:Use( ply )
if not IsValid( ply ) then return end
local Base = self:GetBase()
if not IsValid( Base ) then return end
if not Base:IsUseAllowed( ply ) then return end
if self:IsOpen() then
self:Close( ply )
else
self:Open( ply )
end
end
function ENT:OnOpen( ply )
end
function ENT:OnClosed( ply )
end
function ENT:OpenAndClose( ply )
self:Open( ply )
self._PreventClosing = true
timer.Simple(0.5, function()
if not IsValid( self ) then return end
self:Close( ply )
self._PreventClosing = false
end )
end
function ENT:DisableOnBodyGroup( group, subgroup )
self._BodyGroupDisable = group
self._BodySubGroupDisable = subgroup
end
function ENT:IsBodyGroupDisabled()
if not self._BodyGroupDisable or not self._BodySubGroupDisable then return false end
local base = self:GetBase()
if not IsValid( base ) then return false end
return base:GetBodygroup( self._BodyGroupDisable ) == self._BodySubGroupDisable
end
function ENT:Open( ply )
if self:IsOpen() then return end
self:SetActive( true )
self:SetMins( self:GetMinsOpen() )
self:SetMaxs( self:GetMaxsOpen() )
if self:IsBodyGroupDisabled() then return end
self:OnOpen( ply )
local snd = self:GetSoundOpen()
if not snd then return end
self:EmitSound( snd )
end
function ENT:Close( ply )
if not self:IsOpen() then
if self:IsBodyGroupDisabled() then
self:Open( ply )
end
return
end
if self:IsBodyGroupDisabled() then return end
self:SetActive( false )
self:SetMins( self:GetMinsClosed() )
self:SetMaxs( self:GetMaxsClosed() )
self:OnClosed( ply )
local snd = self:GetSoundClose()
if not snd then return end
self:EmitSound( snd )
end
function ENT:OnDriverChanged( oldDriver, newDriver, pod )
if self._PreventClosing then return end
if IsValid( newDriver ) then
if self:IsOpen() then
self:Close( newDriver )
end
else
timer.Simple( FrameTime() * 2, function()
if not IsValid( self ) or not IsValid( oldDriver ) or IsValid( self._Driver ) then return end
if oldDriver:lvsGetVehicle() == self:GetBase() then return end
if not self:IsOpen() then
self:OpenAndClose()
end
end )
end
end
function ENT:SetPoseParameterSV()
local Base = self:GetBase()
if not IsValid( Base ) then return end
local Target = self:GetActive() and self:GetPoseMax() or self:GetPoseMin()
local poseName = self:GetPoseName()
if poseName == "" then return end
local EntTable = self:GetTable()
EntTable.sm_pp = EntTable.sm_pp and EntTable.sm_pp + (Target - EntTable.sm_pp) * FrameTime() * self:GetRate() or 0
local value = EntTable.sm_pp ^ self:GetRateExponent()
Base:SetPoseParameter( string.Replace(poseName, "^", ""), value )
end
function ENT:Think()
if IsValid( self._LinkedSeat ) then
local Driver = self._LinkedSeat:GetDriver()
if self._Driver ~= Driver then
self:OnDriverChanged( self._Driver, Driver, self._LinkedSeat )
self._Driver = Driver
end
end
if self:IsServerSide() then
self:SetPoseParameterSV()
self:NextThink( CurTime() )
else
self:NextThink( CurTime() + 0.25 )
end
return true
end
function ENT:OnTakeDamage( dmginfo )
end
return
end
function ENT:Initialize()
end
function ENT:Think()
if self:IsServerSide() then return end
local Base = self:GetBase()
if not IsValid( Base ) then return end
local Target = self:GetActive() and self:GetPoseMax() or self:GetPoseMin()
local poseName = self:GetPoseName()
if poseName == "" then return end
local EntTable = self:GetTable()
EntTable.sm_pp = EntTable.sm_pp and EntTable.sm_pp + (Target - EntTable.sm_pp) * RealFrameTime() * self:GetRate() or 0
local value = EntTable.sm_pp ^ self:GetRateExponent()
if string.StartsWith( poseName, "!" ) then
Base:SetBonePoseParameter( poseName, value )
else
Base:SetPoseParameter( poseName, value )
end
end
function ENT:OnRemove()
end
function ENT:Draw()
end
local LVS = LVS
ENT.ColorSelect = Color(127,255,127,150)
ENT.ColorNormal = Color(255,0,0,150)
ENT.ColorTransBlack = Color(0,0,0,150)
ENT.OutlineThickness = Vector(0.5,0.5,0.5)
function ENT:DrawTranslucent()
local ply = LocalPlayer()
if not IsValid( ply ) or ply:InVehicle() or not ply:KeyDown( IN_SPEED ) then return end
local boxOrigin = self:GetPos()
local boxAngles = self:GetAngles()
local boxMins = self:GetMins()
local boxMaxs = self:GetMaxs()
local EntTable = self:GetTable()
local HitPos, _, _ = util.IntersectRayWithOBB( ply:GetShootPos(), ply:GetAimVector() * EntTable.UseRange, boxOrigin, boxAngles, boxMins, boxMaxs )
local InRange = isvector( HitPos )
if InRange then
local Use = ply:KeyDown( IN_USE )
if EntTable.old_Use ~= Use then
EntTable.old_Use = Use
if Use then
net.Start( "lvs_doorhandler_interact" )
net.WriteEntity( self )
net.SendToServer()
end
end
end
if not LVS.DeveloperEnabled then return end
local Col = InRange and EntTable.ColorSelect or EntTable.ColorNormal
render.SetColorMaterial()
render.DrawBox( boxOrigin, boxAngles, boxMins, boxMaxs, Col )
render.DrawBox( boxOrigin, boxAngles, boxMaxs + EntTable.OutlineThickness, boxMins - EntTable.OutlineThickness, EntTable.ColorTransBlack )
end

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