mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 13:53:45 +03:00
Upload
This commit is contained in:
331
lua/entities/arccw_ammo.lua
Normal file
331
lua/entities/arccw_ammo.lua
Normal 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
|
||||
32
lua/entities/arccw_ammo_357.lua
Normal file
32
lua/entities/arccw_ammo_357.lua
Normal 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"
|
||||
28
lua/entities/arccw_ammo_357_large.lua
Normal file
28
lua/entities/arccw_ammo_357_large.lua
Normal file
@@ -0,0 +1,28 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
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"
|
||||
27
lua/entities/arccw_ammo_ar2.lua
Normal file
27
lua/entities/arccw_ammo_ar2.lua
Normal 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"
|
||||
28
lua/entities/arccw_ammo_ar2_large.lua
Normal file
28
lua/entities/arccw_ammo_ar2_large.lua
Normal file
@@ -0,0 +1,28 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
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"
|
||||
29
lua/entities/arccw_ammo_buckshot.lua
Normal file
29
lua/entities/arccw_ammo_buckshot.lua
Normal 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"
|
||||
27
lua/entities/arccw_ammo_buckshot_large.lua
Normal file
27
lua/entities/arccw_ammo_buckshot_large.lua
Normal 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"
|
||||
26
lua/entities/arccw_ammo_pistol.lua
Normal file
26
lua/entities/arccw_ammo_pistol.lua
Normal 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"
|
||||
27
lua/entities/arccw_ammo_pistol_large.lua
Normal file
27
lua/entities/arccw_ammo_pistol_large.lua
Normal 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"
|
||||
33
lua/entities/arccw_ammo_plinking.lua
Normal file
33
lua/entities/arccw_ammo_plinking.lua
Normal 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
|
||||
34
lua/entities/arccw_ammo_plinking_large.lua
Normal file
34
lua/entities/arccw_ammo_plinking_large.lua
Normal 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
|
||||
29
lua/entities/arccw_ammo_smg1.lua
Normal file
29
lua/entities/arccw_ammo_smg1.lua
Normal 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"
|
||||
49
lua/entities/arccw_ammo_smg1_grenade.lua
Normal file
49
lua/entities/arccw_ammo_smg1_grenade.lua
Normal 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
|
||||
58
lua/entities/arccw_ammo_smg1_grenade_large.lua
Normal file
58
lua/entities/arccw_ammo_smg1_grenade_large.lua
Normal 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
|
||||
27
lua/entities/arccw_ammo_smg1_large.lua
Normal file
27
lua/entities/arccw_ammo_smg1_large.lua
Normal 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"
|
||||
30
lua/entities/arccw_ammo_sniper.lua
Normal file
30
lua/entities/arccw_ammo_sniper.lua
Normal 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"
|
||||
28
lua/entities/arccw_ammo_sniper_large.lua
Normal file
28
lua/entities/arccw_ammo_sniper_large.lua
Normal file
@@ -0,0 +1,28 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
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"
|
||||
142
lua/entities/arccw_att_base/shared.lua
Normal file
142
lua/entities/arccw_att_base/shared.lua
Normal 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
|
||||
59
lua/entities/arccw_att_dropped.lua
Normal file
59
lua/entities/arccw_att_dropped.lua
Normal 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
|
||||
126
lua/entities/arccw_gl_ammodet.lua
Normal file
126
lua/entities/arccw_gl_ammodet.lua
Normal 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
|
||||
131
lua/entities/arccw_smoke/shared.lua
Normal file
131
lua/entities/arccw_smoke/shared.lua
Normal 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
|
||||
143
lua/entities/arccw_uc_40mm_airburst.lua
Normal file
143
lua/entities/arccw_uc_40mm_airburst.lua
Normal 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
|
||||
]]
|
||||
85
lua/entities/arccw_uc_40mm_dp.lua
Normal file
85
lua/entities/arccw_uc_40mm_dp.lua
Normal 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
|
||||
87
lua/entities/arccw_uc_40mm_dummy.lua
Normal file
87
lua/entities/arccw_uc_40mm_dummy.lua
Normal 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
|
||||
80
lua/entities/arccw_uc_40mm_flash.lua
Normal file
80
lua/entities/arccw_uc_40mm_flash.lua
Normal 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
|
||||
20
lua/entities/arccw_uc_40mm_he.lua
Normal file
20
lua/entities/arccw_uc_40mm_he.lua
Normal 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"
|
||||
47
lua/entities/arccw_uc_40mm_hv.lua
Normal file
47
lua/entities/arccw_uc_40mm_hv.lua
Normal 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
|
||||
42
lua/entities/arccw_uc_40mm_incendiary.lua
Normal file
42
lua/entities/arccw_uc_40mm_incendiary.lua
Normal 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]]
|
||||
82
lua/entities/arccw_uc_40mm_napalm.lua
Normal file
82
lua/entities/arccw_uc_40mm_napalm.lua
Normal 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
|
||||
48
lua/entities/arccw_uc_40mm_smoke.lua
Normal file
48
lua/entities/arccw_uc_40mm_smoke.lua
Normal 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
|
||||
205
lua/entities/arccw_uc_fire.lua
Normal file
205
lua/entities/arccw_uc_fire.lua
Normal 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
|
||||
263
lua/entities/arccw_uc_napalm.lua
Normal file
263
lua/entities/arccw_uc_napalm.lua
Normal 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
|
||||
188
lua/entities/arccw_uc_riflegrenade.lua
Normal file
188
lua/entities/arccw_uc_riflegrenade.lua
Normal 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
121
lua/entities/combofunc.lua
Normal 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
|
||||
99
lua/entities/dodgefunc.lua
Normal file
99
lua/entities/dodgefunc.lua
Normal 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
|
||||
}
|
||||
26
lua/entities/eft_ammo_556_hp.lua
Normal file
26
lua/entities/eft_ammo_556_hp.lua
Normal 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"
|
||||
26
lua/entities/eft_ammo_556_m855.lua
Normal file
26
lua/entities/eft_ammo_556_m855.lua
Normal 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"
|
||||
26
lua/entities/eft_ammo_556_m995.lua
Normal file
26
lua/entities/eft_ammo_556_m995.lua
Normal 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"
|
||||
2950
lua/entities/ent_chess_board.lua
Normal file
2950
lua/entities/ent_chess_board.lua
Normal file
File diff suppressed because it is too large
Load Diff
684
lua/entities/ent_draughts_board.lua
Normal file
684
lua/entities/ent_draughts_board.lua
Normal 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
|
||||
761
lua/entities/entity_tank_editable.lua
Normal file
761
lua/entities/entity_tank_editable.lua
Normal 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
|
||||
110
lua/entities/entity_tank_rocket.lua
Normal file
110
lua/entities/entity_tank_rocket.lua
Normal 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
|
||||
154
lua/entities/env_atmosphere.lua
Normal file
154
lua/entities/env_atmosphere.lua
Normal 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
|
||||
31
lua/entities/env_stormfox2_materials.lua
Normal file
31
lua/entities/env_stormfox2_materials.lua
Normal 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.
|
||||
]]
|
||||
27
lua/entities/env_stormfox2_settings.lua
Normal file
27
lua/entities/env_stormfox2_settings.lua
Normal 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
|
||||
16
lua/entities/gmod_contr_spawner/cl_init.lua
Normal file
16
lua/entities/gmod_contr_spawner/cl_init.lua
Normal 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
|
||||
310
lua/entities/gmod_contr_spawner/init.lua
Normal file
310
lua/entities/gmod_contr_spawner/init.lua
Normal 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 )
|
||||
20
lua/entities/gmod_contr_spawner/shared.lua
Normal file
20
lua/entities/gmod_contr_spawner/shared.lua
Normal 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
|
||||
75
lua/entities/gmod_item_vehicle_fphysics_wheel.lua
Normal file
75
lua/entities/gmod_item_vehicle_fphysics_wheel.lua
Normal 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
|
||||
43
lua/entities/gmod_sent_vehicle_fphysics_attachment.lua
Normal file
43
lua/entities/gmod_sent_vehicle_fphysics_attachment.lua
Normal 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
|
||||
677
lua/entities/gmod_sent_vehicle_fphysics_base/cl_init.lua
Normal file
677
lua/entities/gmod_sent_vehicle_fphysics_base/cl_init.lua
Normal 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
|
||||
@@ -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
|
||||
314
lua/entities/gmod_sent_vehicle_fphysics_base/damage.lua
Normal file
314
lua/entities/gmod_sent_vehicle_fphysics_base/damage.lua
Normal 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
|
||||
1428
lua/entities/gmod_sent_vehicle_fphysics_base/init.lua
Normal file
1428
lua/entities/gmod_sent_vehicle_fphysics_base/init.lua
Normal file
File diff suppressed because it is too large
Load Diff
317
lua/entities/gmod_sent_vehicle_fphysics_base/numpads.lua
Normal file
317
lua/entities/gmod_sent_vehicle_fphysics_base/numpads.lua
Normal 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 )
|
||||
314
lua/entities/gmod_sent_vehicle_fphysics_base/shared.lua
Normal file
314
lua/entities/gmod_sent_vehicle_fphysics_base/shared.lua
Normal 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
|
||||
522
lua/entities/gmod_sent_vehicle_fphysics_base/simfunc.lua
Normal file
522
lua/entities/gmod_sent_vehicle_fphysics_base/simfunc.lua
Normal 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
|
||||
710
lua/entities/gmod_sent_vehicle_fphysics_base/spawn.lua
Normal file
710
lua/entities/gmod_sent_vehicle_fphysics_base/spawn.lua
Normal 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
|
||||
102
lua/entities/gmod_sent_vehicle_fphysics_fueltank.lua
Normal file
102
lua/entities/gmod_sent_vehicle_fphysics_fueltank.lua
Normal 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
|
||||
103
lua/entities/gmod_sent_vehicle_fphysics_gib.lua
Normal file
103
lua/entities/gmod_sent_vehicle_fphysics_gib.lua
Normal 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
|
||||
663
lua/entities/gmod_sent_vehicle_fphysics_wheel.lua
Normal file
663
lua/entities/gmod_sent_vehicle_fphysics_wheel.lua
Normal 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)
|
||||
@@ -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"
|
||||
79
lua/entities/gmod_wire_expression2/core/custom/stormfox2.lua
Normal file
79
lua/entities/gmod_wire_expression2/core/custom/stormfox2.lua
Normal 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
|
||||
102
lua/entities/ladder_base/shared.lua
Normal file
102
lua/entities/ladder_base/shared.lua
Normal 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;
|
||||
22
lua/entities/ladder_small/shared.lua
Normal file
22
lua/entities/ladder_small/shared.lua
Normal 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;
|
||||
22
lua/entities/ladder_small2/shared.lua
Normal file
22
lua/entities/ladder_small2/shared.lua
Normal 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;
|
||||
22
lua/entities/ladder_small3/shared.lua
Normal file
22
lua/entities/ladder_small3/shared.lua
Normal 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;
|
||||
22
lua/entities/ladder_small4/shared.lua
Normal file
22
lua/entities/ladder_small4/shared.lua
Normal 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;
|
||||
46
lua/entities/logic_day_relay.lua
Normal file
46
lua/entities/logic_day_relay.lua
Normal 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
|
||||
46
lua/entities/logic_night_relay.lua
Normal file
46
lua/entities/logic_night_relay.lua
Normal 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
|
||||
88
lua/entities/logic_temperature_relay.lua
Normal file
88
lua/entities/logic_temperature_relay.lua
Normal 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
|
||||
69
lua/entities/logic_thunder_relay.lua
Normal file
69
lua/entities/logic_thunder_relay.lua
Normal file
@@ -0,0 +1,69 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
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
|
||||
58
lua/entities/logic_time_relay.lua
Normal file
58
lua/entities/logic_time_relay.lua
Normal 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
|
||||
46
lua/entities/logic_weather_off_relay.lua
Normal file
46
lua/entities/logic_weather_off_relay.lua
Normal 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
|
||||
65
lua/entities/logic_weather_relay.lua
Normal file
65
lua/entities/logic_weather_relay.lua
Normal 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
|
||||
83
lua/entities/logic_weather_set.lua
Normal file
83
lua/entities/logic_weather_set.lua
Normal 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
239
lua/entities/lvs_armor.lua
Normal 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
|
||||
134
lua/entities/lvs_armor_bounce.lua
Normal file
134
lua/entities/lvs_armor_bounce.lua
Normal 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
|
||||
159
lua/entities/lvs_armor_penetrate.lua
Normal file
159
lua/entities/lvs_armor_penetrate.lua
Normal 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
|
||||
40
lua/entities/lvs_base/cl_boneposeparemeter.lua
Normal file
40
lua/entities/lvs_base/cl_boneposeparemeter.lua
Normal file
@@ -0,0 +1,40 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
function ENT:CreateBonePoseParameter( name, bone, ang_min, ang_max, pos_min, pos_max )
|
||||
if not istable( self._BonePoseParameters ) then self._BonePoseParameters = {} end
|
||||
|
||||
self._BonePoseParameters[ name ] = {
|
||||
bone = (bone or -1),
|
||||
ang_min = ang_min or angle_zero,
|
||||
ang_max = ang_max or angle_zero,
|
||||
pos_min = pos_min or vector_origin,
|
||||
pos_max = pos_max or vector_origin,
|
||||
}
|
||||
end
|
||||
|
||||
function ENT:SetBonePoseParameter( name, value )
|
||||
if name and string.StartsWith( name, "!" ) then
|
||||
name = string.Replace( name, "!", "" )
|
||||
end
|
||||
|
||||
local EntTable = self:GetTable()
|
||||
|
||||
if not istable( EntTable._BonePoseParameters ) or not EntTable._BonePoseParameters[ name ] then return end
|
||||
|
||||
local data = EntTable._BonePoseParameters[ name ]
|
||||
|
||||
local ang = LerpAngle( value, data.ang_min, data.ang_max )
|
||||
local pos = LerpVector( value, data.pos_min, data.pos_max )
|
||||
|
||||
self:ManipulateBoneAngles( data.bone, ang )
|
||||
self:ManipulateBonePosition( data.bone, pos )
|
||||
end
|
||||
129
lua/entities/lvs_base/cl_effects.lua
Normal file
129
lua/entities/lvs_base/cl_effects.lua
Normal file
@@ -0,0 +1,129 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
function ENT:StartWindSounds()
|
||||
if not LVS.ShowEffects then return end
|
||||
|
||||
self:StopWindSounds()
|
||||
|
||||
if LocalPlayer():lvsGetVehicle() ~= self then return end
|
||||
|
||||
local EntTable = self:GetTable()
|
||||
|
||||
EntTable._WindSFX = CreateSound( self, "LVS.Physics.Wind" )
|
||||
EntTable._WindSFX:PlayEx(0,100)
|
||||
|
||||
EntTable._WaterSFX = CreateSound( self, "LVS.Physics.Water" )
|
||||
EntTable._WaterSFX:PlayEx(0,100)
|
||||
end
|
||||
|
||||
function ENT:StopWindSounds()
|
||||
local EntTable = self:GetTable()
|
||||
|
||||
if EntTable._WindSFX then
|
||||
EntTable._WindSFX:Stop()
|
||||
EntTable._WindSFX = nil
|
||||
end
|
||||
|
||||
if EntTable._WaterSFX then
|
||||
EntTable._WaterSFX:Stop()
|
||||
EntTable._WaterSFX = nil
|
||||
end
|
||||
end
|
||||
|
||||
ENT.DustEffectSurfaces = {
|
||||
["sand"] = true,
|
||||
["dirt"] = true,
|
||||
["grass"] = true,
|
||||
}
|
||||
|
||||
ENT.GroundEffectsMultiplier = 1
|
||||
|
||||
function ENT:DoVehicleFX()
|
||||
local EntTable = self:GetTable()
|
||||
|
||||
if EntTable.GroundEffectsMultiplier <= 0 or not LVS.ShowEffects then self:StopWindSounds() return end
|
||||
|
||||
local Vel = self:GetVelocity():Length() * EntTable.GroundEffectsMultiplier
|
||||
|
||||
if EntTable._WindSFX then EntTable._WindSFX:ChangeVolume( math.Clamp( (Vel - 1200) / 2800,0,1 ), 0.25 ) end
|
||||
|
||||
if Vel < 1500 then
|
||||
if EntTable._WaterSFX then EntTable._WaterSFX:ChangeVolume( 0, 0.25 ) end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if (EntTable.nextFX or 0) < CurTime() then
|
||||
EntTable.nextFX = CurTime() + 0.05
|
||||
|
||||
local LCenter = self:OBBCenter()
|
||||
LCenter.z = self:OBBMins().z
|
||||
|
||||
local CenterPos = self:LocalToWorld( LCenter )
|
||||
|
||||
local trace = util.TraceLine( {
|
||||
start = CenterPos + Vector(0,0,25),
|
||||
endpos = CenterPos - Vector(0,0,450),
|
||||
filter = self:GetCrosshairFilterEnts(),
|
||||
} )
|
||||
|
||||
local traceWater = util.TraceLine( {
|
||||
start = CenterPos + Vector(0,0,25),
|
||||
endpos = CenterPos - Vector(0,0,450),
|
||||
filter = self:GetCrosshairFilterEnts(),
|
||||
mask = MASK_WATER,
|
||||
} )
|
||||
|
||||
if EntTable._WaterSFX then EntTable._WaterSFX:ChangePitch( math.Clamp((Vel / 1000) * 50,80,150), 0.5 ) end
|
||||
|
||||
if traceWater.Hit and trace.HitPos.z < traceWater.HitPos.z then
|
||||
local effectdata = EffectData()
|
||||
effectdata:SetOrigin( traceWater.HitPos )
|
||||
effectdata:SetEntity( self )
|
||||
util.Effect( "lvs_physics_water", effectdata )
|
||||
|
||||
if EntTable._WaterSFX then EntTable._WaterSFX:ChangeVolume( 1 - math.Clamp(traceWater.Fraction,0,1), 0.5 ) end
|
||||
else
|
||||
if EntTable._WaterSFX then EntTable._WaterSFX:ChangeVolume( 0, 0.25 ) end
|
||||
end
|
||||
|
||||
if trace.Hit and EntTable.DustEffectSurfaces[ util.GetSurfacePropName( trace.SurfaceProps ) ] then
|
||||
local effectdata = EffectData()
|
||||
effectdata:SetOrigin( trace.HitPos )
|
||||
effectdata:SetEntity( self )
|
||||
util.Effect( "lvs_physics_dust", effectdata )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:GetParticleEmitter( Pos )
|
||||
local EntTable = self:GetTable()
|
||||
|
||||
local T = CurTime()
|
||||
|
||||
if IsValid( EntTable.Emitter ) and (EntTable.EmitterTime or 0) > T then
|
||||
return EntTable.Emitter
|
||||
end
|
||||
|
||||
self:StopEmitter()
|
||||
|
||||
EntTable.Emitter = ParticleEmitter( Pos, false )
|
||||
EntTable.EmitterTime = T + 2
|
||||
|
||||
return EntTable.Emitter
|
||||
end
|
||||
|
||||
function ENT:StopEmitter()
|
||||
if IsValid( self.Emitter ) then
|
||||
self.Emitter:Finish()
|
||||
end
|
||||
end
|
||||
275
lua/entities/lvs_base/cl_hud.lua
Normal file
275
lua/entities/lvs_base/cl_hud.lua
Normal file
@@ -0,0 +1,275 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
LVS:AddHudEditor( "VehicleHealth", 10, ScrH() - 85, 220, 75, 220, 75, "VEHICLE HEALTH",
|
||||
function( self, vehicle, X, Y, W, H, ScrX, ScrY, ply )
|
||||
if not vehicle.LVSHudPaintVehicleHealth then return end
|
||||
|
||||
vehicle:LVSHudPaintVehicleHealth( X, Y, W, H, ScrX, ScrY, ply )
|
||||
end
|
||||
)
|
||||
|
||||
LVS:AddHudEditor( "VehicleInfo", ScrW() - 460, ScrH() - 85, 220, 75, 220, 75, "VEHICLE INFORMATION",
|
||||
function( self, vehicle, X, Y, W, H, ScrX, ScrY, ply )
|
||||
if not vehicle.LVSHudPaintInfoText then return end
|
||||
|
||||
vehicle:LVSHudPaintInfoText( X, Y, W, H, ScrX, ScrY, ply )
|
||||
end
|
||||
)
|
||||
|
||||
function ENT:LVSHudPaintVehicleHealth( X, Y, W, H, ScrX, ScrY, ply )
|
||||
draw.DrawText( "HEALTH ", "LVS_FONT", X + 102, Y + 35, color_white, TEXT_ALIGN_RIGHT )
|
||||
draw.DrawText( math.Round( self:GetHP(), 0 ), "LVS_FONT_HUD_LARGE", X + 102, Y + 20, color_white, TEXT_ALIGN_LEFT )
|
||||
end
|
||||
|
||||
ENT.VehicleIdentifierRange = 10000
|
||||
|
||||
function ENT:LVSHudPaintVehicleIdentifier( X, Y, In_Col, target_ent )
|
||||
if not IsValid( target_ent ) then return end
|
||||
|
||||
local HP = target_ent:GetHP()
|
||||
|
||||
surface.SetDrawColor( In_Col.r, In_Col.g, In_Col.b, In_Col.a )
|
||||
LVS:DrawDiamond( X + 1, Y + 1, 20, HP / target_ent:GetMaxHP() )
|
||||
|
||||
if target_ent:GetMaxShield() > 0 and HP > 0 then
|
||||
surface.SetDrawColor( 200, 200, 255, In_Col.a )
|
||||
LVS:DrawDiamond( X + 1, Y + 1, 24, target_ent:GetShield() / target_ent:GetMaxShield() )
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:LVSHudPaint( X, Y, ply )
|
||||
end
|
||||
|
||||
function ENT:HurtMarker( intensity )
|
||||
LocalPlayer():EmitSound( "lvs/hit_receive"..math.random(1,2)..".wav", 75, math.random(95,105), 0.25 + intensity * 0.75, CHAN_STATIC )
|
||||
util.ScreenShake( Vector(0, 0, 0), 25 * intensity, 25 * intensity, 0.5, 1 )
|
||||
end
|
||||
|
||||
function ENT:KillMarker()
|
||||
self.LastKillMarker = CurTime() + 0.5
|
||||
|
||||
LocalPlayer():EmitSound( "lvs/hit_kill.wav", 85, 100, 0.4, CHAN_VOICE )
|
||||
end
|
||||
|
||||
local LastMarker = 0
|
||||
function ENT:ArmorMarker( IsDamage )
|
||||
local T = CurTime()
|
||||
|
||||
local DontHurtEars = math.Clamp( T - LastMarker, 0, 1 ) ^ 2
|
||||
|
||||
LastMarker = T
|
||||
|
||||
local ArmorFailed = IsDamage and "takedamage" or "pen"
|
||||
local Volume = IsDamage and (0.3 * DontHurtEars) or 1
|
||||
|
||||
LocalPlayer():EmitSound( "lvs/armor_"..ArmorFailed.."_"..math.random(1,3)..".wav", 85, math.random(95,105), Volume, CHAN_ITEM2 )
|
||||
end
|
||||
|
||||
function ENT:HitMarker()
|
||||
self.LastHitMarker = CurTime() + 0.15
|
||||
|
||||
LocalPlayer():EmitSound( "lvs/hit.wav", 85, math.random(95,105), 0.4, CHAN_ITEM )
|
||||
end
|
||||
|
||||
function ENT:CritMarker()
|
||||
self.LastCritMarker = CurTime() + 0.15
|
||||
|
||||
LocalPlayer():EmitSound( "lvs/hit_crit.wav", 85, math.random(95,105), 0.4, CHAN_ITEM2 )
|
||||
end
|
||||
|
||||
function ENT:GetHitMarker()
|
||||
return self.LastHitMarker or 0
|
||||
end
|
||||
|
||||
function ENT:GetCritMarker()
|
||||
return self.LastCritMarker or 0
|
||||
end
|
||||
|
||||
function ENT:GetKillMarker()
|
||||
return self.LastKillMarker or 0
|
||||
end
|
||||
|
||||
function ENT:LVSPaintHitMarker( scr )
|
||||
local T = CurTime()
|
||||
|
||||
local aV = math.cos( math.rad( math.max(((self:GetHitMarker() - T) / 0.15) * 360,0) ) )
|
||||
if aV ~= 1 then
|
||||
local Start = 12 + (1 - aV) * 8
|
||||
local dst = 10
|
||||
|
||||
surface.SetDrawColor( 255, 255, 0, 255 )
|
||||
|
||||
surface.DrawLine( scr.x + Start, scr.y + Start, scr.x + Start, scr.y + Start - dst )
|
||||
surface.DrawLine( scr.x + Start, scr.y + Start, scr.x + Start - dst, scr.y + Start )
|
||||
|
||||
surface.DrawLine( scr.x + Start, scr.y - Start, scr.x + Start, scr.y - Start + dst )
|
||||
surface.DrawLine( scr.x + Start, scr.y - Start, scr.x + Start - dst, scr.y - Start )
|
||||
|
||||
surface.DrawLine( scr.x - Start, scr.y + Start, scr.x - Start, scr.y + Start - dst )
|
||||
surface.DrawLine( scr.x - Start, scr.y + Start, scr.x - Start + dst, scr.y + Start )
|
||||
|
||||
surface.DrawLine( scr.x - Start, scr.y - Start, scr.x - Start, scr.y - Start + dst )
|
||||
surface.DrawLine( scr.x - Start, scr.y - Start, scr.x - Start + dst, scr.y - Start )
|
||||
|
||||
scr.x = scr.x + 1
|
||||
scr.y = scr.y + 1
|
||||
|
||||
surface.SetDrawColor( 0, 0, 0, 80 )
|
||||
|
||||
surface.DrawLine( scr.x + Start, scr.y + Start, scr.x + Start, scr.y + Start - dst )
|
||||
surface.DrawLine( scr.x + Start, scr.y + Start, scr.x + Start - dst, scr.y + Start )
|
||||
|
||||
surface.DrawLine( scr.x + Start, scr.y - Start, scr.x + Start, scr.y - Start + dst )
|
||||
surface.DrawLine( scr.x + Start, scr.y - Start, scr.x + Start - dst, scr.y - Start )
|
||||
|
||||
surface.DrawLine( scr.x - Start, scr.y + Start, scr.x - Start, scr.y + Start - dst )
|
||||
surface.DrawLine( scr.x - Start, scr.y + Start, scr.x - Start + dst, scr.y + Start )
|
||||
|
||||
surface.DrawLine( scr.x - Start, scr.y - Start, scr.x - Start, scr.y - Start + dst )
|
||||
surface.DrawLine( scr.x - Start, scr.y - Start, scr.x - Start + dst, scr.y - Start )
|
||||
end
|
||||
|
||||
local aV = math.sin( math.rad( math.max(((self:GetCritMarker() - T) / 0.15) * 180,0) ) )
|
||||
if aV > 0.01 then
|
||||
local Start = 10 + aV * 40
|
||||
local End = 20 + aV * 45
|
||||
|
||||
surface.SetDrawColor( 255, 100, 0, 255 )
|
||||
surface.DrawLine( scr.x + Start, scr.y + Start, scr.x + End, scr.y + End )
|
||||
surface.DrawLine( scr.x - Start, scr.y + Start, scr.x - End, scr.y + End )
|
||||
surface.DrawLine( scr.x + Start, scr.y - Start, scr.x + End, scr.y - End )
|
||||
surface.DrawLine( scr.x - Start, scr.y - Start, scr.x - End, scr.y - End )
|
||||
|
||||
draw.NoTexture()
|
||||
surface.DrawTexturedRectRotated( scr.x + Start, scr.y + Start, 3, 20, 45 )
|
||||
surface.DrawTexturedRectRotated( scr.x - Start, scr.y + Start, 20, 3, 45 )
|
||||
surface.DrawTexturedRectRotated( scr.x + Start, scr.y - Start, 20, 3, 45 )
|
||||
surface.DrawTexturedRectRotated( scr.x - Start, scr.y - Start, 3, 20, 45 )
|
||||
end
|
||||
|
||||
local aV = math.sin( math.rad( math.sin( math.rad( math.max(((self:GetKillMarker() - T) / 0.2) * 90,0) ) ) * 90 ) )
|
||||
if aV > 0.01 then
|
||||
surface.SetDrawColor( 255, 255, 255, 15 * (aV ^ 4) )
|
||||
surface.DrawRect( 0, 0, ScrW(), ScrH() )
|
||||
|
||||
local Start = 10 + aV * 40
|
||||
local End = 20 + aV * 45
|
||||
surface.SetDrawColor( 255, 0, 0, 255 )
|
||||
surface.DrawLine( scr.x + Start, scr.y + Start, scr.x + End, scr.y + End )
|
||||
surface.DrawLine( scr.x - Start, scr.y + Start, scr.x - End, scr.y + End )
|
||||
surface.DrawLine( scr.x + Start, scr.y - Start, scr.x + End, scr.y - End )
|
||||
surface.DrawLine( scr.x - Start, scr.y - Start, scr.x - End, scr.y - End )
|
||||
|
||||
draw.NoTexture()
|
||||
surface.DrawTexturedRectRotated( scr.x + Start, scr.y + Start, 5, 20, 45 )
|
||||
surface.DrawTexturedRectRotated( scr.x - Start, scr.y + Start, 20, 5, 45 )
|
||||
surface.DrawTexturedRectRotated( scr.x + Start, scr.y - Start, 20, 5, 45 )
|
||||
surface.DrawTexturedRectRotated( scr.x - Start, scr.y - Start, 5, 20, 45 )
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:PaintCrosshairCenter( Pos2D, Col )
|
||||
if not Col then
|
||||
Col = Color( 255, 255, 255, 255 )
|
||||
end
|
||||
|
||||
local Alpha = Col.a / 255
|
||||
local Shadow = Color( 0, 0, 0, 80 * Alpha )
|
||||
|
||||
surface.DrawCircle( Pos2D.x, Pos2D.y, 4, Shadow )
|
||||
surface.DrawCircle( Pos2D.x, Pos2D.y, 5, Col )
|
||||
surface.DrawCircle( Pos2D.x, Pos2D.y, 6, Shadow )
|
||||
end
|
||||
|
||||
function ENT:PaintCrosshairOuter( Pos2D, Col )
|
||||
if not Col then
|
||||
Col = Color( 255, 255, 255, 255 )
|
||||
end
|
||||
|
||||
local Alpha = Col.a / 255
|
||||
local Shadow = Color( 0, 0, 0, 80 * Alpha )
|
||||
|
||||
surface.DrawCircle( Pos2D.x,Pos2D.y, 17, Shadow )
|
||||
surface.DrawCircle( Pos2D.x, Pos2D.y, 18, Col )
|
||||
|
||||
if LVS.AntiAliasingEnabled then
|
||||
surface.DrawCircle( Pos2D.x, Pos2D.y, 19, Color( Col.r, Col.g, Col.b, 150 * Alpha ) )
|
||||
surface.DrawCircle( Pos2D.x, Pos2D.y, 20, Shadow )
|
||||
else
|
||||
surface.DrawCircle( Pos2D.x, Pos2D.y, 19, Shadow )
|
||||
end
|
||||
end
|
||||
|
||||
local Circles = {
|
||||
[1] = {r = -1, col = Color(0,0,0,200)},
|
||||
[2] = {r = 0, col = Color(255,255,255,200)},
|
||||
[3] = {r = 1, col = Color(255,255,255,255)},
|
||||
[4] = {r = 2, col = Color(255,255,255,200)},
|
||||
[5] = {r = 3, col = Color(0,0,0,200)},
|
||||
}
|
||||
|
||||
function ENT:LVSDrawCircle( X, Y, target_radius, value )
|
||||
local endang = 360 * value
|
||||
|
||||
if endang == 0 then return end
|
||||
|
||||
for i = 1, #Circles do
|
||||
local data = Circles[ i ]
|
||||
local radius = target_radius + data.r
|
||||
local segmentdist = endang / ( math.pi * radius / 2 )
|
||||
|
||||
for a = 0, endang, segmentdist do
|
||||
surface.SetDrawColor( data.col )
|
||||
|
||||
surface.DrawLine( X - math.sin( math.rad( a ) ) * radius, Y + math.cos( math.rad( a ) ) * radius, X - math.sin( math.rad( a + segmentdist ) ) * radius, Y + math.cos( math.rad( a + segmentdist ) ) * radius )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:PaintCrosshairSquare( Pos2D, Col )
|
||||
if not Col then
|
||||
Col = Color( 255, 255, 255, 255 )
|
||||
end
|
||||
|
||||
local X = Pos2D.x + 1
|
||||
local Y = Pos2D.y + 1
|
||||
|
||||
local Size = 20
|
||||
|
||||
surface.SetDrawColor( 0, 0, 0, 80 )
|
||||
surface.DrawLine( X - Size, Y + Size, X - Size * 0.5, Y + Size )
|
||||
surface.DrawLine( X + Size, Y + Size, X + Size * 0.5, Y + Size )
|
||||
surface.DrawLine( X - Size, Y + Size, X - Size, Y + Size * 0.5 )
|
||||
surface.DrawLine( X - Size, Y - Size, X - Size, Y - Size * 0.5 )
|
||||
surface.DrawLine( X + Size, Y + Size, X + Size, Y + Size * 0.5 )
|
||||
surface.DrawLine( X + Size, Y - Size, X + Size, Y - Size * 0.5 )
|
||||
surface.DrawLine( X - Size, Y - Size, X - Size * 0.5, Y - Size )
|
||||
surface.DrawLine( X + Size, Y - Size, X + Size * 0.5, Y - Size )
|
||||
|
||||
if Col then
|
||||
surface.SetDrawColor( Col.r, Col.g, Col.b, Col.a )
|
||||
else
|
||||
surface.SetDrawColor( 255, 255, 255, 255 )
|
||||
end
|
||||
|
||||
X = Pos2D.x
|
||||
Y = Pos2D.y
|
||||
|
||||
surface.DrawLine( X - Size, Y + Size, X - Size * 0.5, Y + Size )
|
||||
surface.DrawLine( X + Size, Y + Size, X + Size * 0.5, Y + Size )
|
||||
surface.DrawLine( X - Size, Y + Size, X - Size, Y + Size * 0.5 )
|
||||
surface.DrawLine( X - Size, Y - Size, X - Size, Y - Size * 0.5 )
|
||||
surface.DrawLine( X + Size, Y + Size, X + Size, Y + Size * 0.5 )
|
||||
surface.DrawLine( X + Size, Y - Size, X + Size, Y - Size * 0.5 )
|
||||
surface.DrawLine( X - Size, Y - Size, X - Size * 0.5, Y - Size )
|
||||
surface.DrawLine( X + Size, Y - Size, X + Size * 0.5, Y - Size )
|
||||
end
|
||||
226
lua/entities/lvs_base/cl_init.lua
Normal file
226
lua/entities/lvs_base/cl_init.lua
Normal file
@@ -0,0 +1,226 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
include("shared.lua")
|
||||
include( "sh_weapons.lua" )
|
||||
include( "cl_effects.lua" )
|
||||
include( "cl_hud.lua" )
|
||||
include( "cl_seatswitcher.lua" )
|
||||
include( "cl_trailsystem.lua" )
|
||||
include( "cl_boneposeparemeter.lua" )
|
||||
|
||||
local Zoom = 0
|
||||
|
||||
function ENT:LVSCalcFov( fov, ply )
|
||||
|
||||
local TargetZoom = ply:lvsKeyDown( "ZOOM" ) and 0 or 1
|
||||
|
||||
Zoom = Zoom + (TargetZoom - Zoom) * RealFrameTime() * 10
|
||||
|
||||
local newfov = fov * Zoom + (self.ZoomFov or 40) * (1 - Zoom)
|
||||
|
||||
return newfov
|
||||
end
|
||||
|
||||
function ENT:LVSCalcView( ply, pos, angles, fov, pod )
|
||||
return LVS:CalcView( self, ply, pos, angles, fov, pod )
|
||||
end
|
||||
|
||||
function ENT:PreDraw( flags )
|
||||
return true
|
||||
end
|
||||
|
||||
function ENT:PreDrawTranslucent( flags )
|
||||
return true
|
||||
end
|
||||
|
||||
function ENT:PostDraw( flags )
|
||||
end
|
||||
|
||||
function ENT:PostDrawTranslucent( flags )
|
||||
end
|
||||
|
||||
function ENT:Draw( flags )
|
||||
if self:PreDraw( flags ) then
|
||||
if self.lvsLegacyDraw then
|
||||
self:DrawModel() -- ugly, but required in order to fix old addons. Refract wont work on these.
|
||||
else
|
||||
self:DrawModel( flags )
|
||||
end
|
||||
end
|
||||
|
||||
self:PostDraw( flags )
|
||||
end
|
||||
|
||||
function ENT:DrawTranslucent( flags )
|
||||
self:DrawTrail()
|
||||
|
||||
if self:PreDrawTranslucent( flags ) then
|
||||
self:DrawModel( flags )
|
||||
else
|
||||
self.lvsLegacyDraw = true -- insert puke simley
|
||||
end
|
||||
|
||||
self:PostDrawTranslucent( flags )
|
||||
end
|
||||
|
||||
function ENT:Initialize()
|
||||
self:OnSpawn()
|
||||
|
||||
if not istable( self.GibModels ) then return end
|
||||
|
||||
for _, modelName in ipairs( self.GibModels ) do
|
||||
util.PrecacheModel( modelName )
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:OnSpawn()
|
||||
end
|
||||
|
||||
function ENT:OnFrameActive()
|
||||
end
|
||||
|
||||
function ENT:OnFrame()
|
||||
end
|
||||
|
||||
function ENT:OnEngineActiveChanged( Active )
|
||||
end
|
||||
|
||||
function ENT:OnActiveChanged( Active )
|
||||
end
|
||||
|
||||
ENT._oldActive = false
|
||||
ENT._oldEnActive = false
|
||||
|
||||
function ENT:HandleActive()
|
||||
local EntTable = self:GetTable()
|
||||
|
||||
local Active = self:GetActive()
|
||||
local EngineActive = self:GetEngineActive()
|
||||
local ActiveChanged = false
|
||||
|
||||
if EntTable._oldActive ~= Active then
|
||||
EntTable._oldActive = Active
|
||||
EntTable:OnActiveChanged( Active )
|
||||
ActiveChanged = true
|
||||
end
|
||||
|
||||
if EntTable._oldEnActive ~= EngineActive then
|
||||
EntTable._oldEnActive = EngineActive
|
||||
self:OnEngineActiveChanged( EngineActive )
|
||||
ActiveChanged = true
|
||||
end
|
||||
|
||||
if ActiveChanged then
|
||||
if Active or EngineActive then
|
||||
self:StartWindSounds()
|
||||
else
|
||||
self:StopWindSounds()
|
||||
end
|
||||
end
|
||||
|
||||
if Active or EngineActive then
|
||||
self:DoVehicleFX()
|
||||
end
|
||||
|
||||
self:FlyByThink()
|
||||
|
||||
return EngineActive
|
||||
end
|
||||
|
||||
function ENT:Think()
|
||||
if not self:IsInitialized() then return end
|
||||
|
||||
if self:HandleActive() then
|
||||
self:OnFrameActive()
|
||||
end
|
||||
|
||||
self:HandleTrail()
|
||||
self:OnFrame()
|
||||
end
|
||||
|
||||
function ENT:OnRemove()
|
||||
self:StopEmitter()
|
||||
self:StopWindSounds()
|
||||
self:StopFlyBy()
|
||||
self:StopDeathSound()
|
||||
|
||||
self:OnRemoved()
|
||||
end
|
||||
|
||||
function ENT:OnRemoved()
|
||||
end
|
||||
|
||||
function ENT:CalcDoppler( Ent )
|
||||
if not IsValid( Ent ) then return 1 end
|
||||
|
||||
if Ent:IsPlayer() then
|
||||
local ViewEnt = Ent:GetViewEntity()
|
||||
local Vehicle = Ent:lvsGetVehicle()
|
||||
|
||||
if IsValid( Vehicle ) then
|
||||
if Ent == ViewEnt then
|
||||
Ent = Vehicle
|
||||
end
|
||||
else
|
||||
if IsValid( ViewEnt ) then
|
||||
Ent = ViewEnt
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local sVel = self:GetVelocity()
|
||||
local oVel = Ent:GetVelocity()
|
||||
|
||||
local SubVel = oVel - sVel
|
||||
local SubPos = self:GetPos() - Ent:GetPos()
|
||||
|
||||
local DirPos = SubPos:GetNormalized()
|
||||
local DirVel = SubVel:GetNormalized()
|
||||
|
||||
local A = math.acos( math.Clamp( DirVel:Dot( DirPos ) ,-1,1) )
|
||||
|
||||
return (1 + math.cos( A ) * SubVel:Length() / 13503.9)
|
||||
end
|
||||
|
||||
function ENT:GetCrosshairFilterEnts()
|
||||
if not self:IsInitialized() then return { self } end -- wait for the server to be ready
|
||||
|
||||
if not istable( self.CrosshairFilterEnts ) then
|
||||
self.CrosshairFilterEnts = {self}
|
||||
|
||||
-- lets ask the server to build the filter for us because it has access to constraint.GetAllConstrainedEntities()
|
||||
net.Start( "lvs_player_request_filter" )
|
||||
net.WriteEntity( self )
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
return self.CrosshairFilterEnts
|
||||
end
|
||||
|
||||
function ENT:FlyByThink()
|
||||
end
|
||||
|
||||
function ENT:StopFlyBy()
|
||||
end
|
||||
|
||||
function ENT:StopDeathSound()
|
||||
end
|
||||
|
||||
function ENT:OnDestroyed()
|
||||
end
|
||||
|
||||
net.Receive( "lvs_vehicle_destroy", function( len )
|
||||
local ent = net.ReadEntity()
|
||||
|
||||
if not IsValid( ent ) or not isfunction( ent.OnDestroyed ) then return end
|
||||
|
||||
ent:OnDestroyed()
|
||||
end )
|
||||
150
lua/entities/lvs_base/cl_seatswitcher.lua
Normal file
150
lua/entities/lvs_base/cl_seatswitcher.lua
Normal file
@@ -0,0 +1,150 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT.IconVehicleLocked = Material( "lvs/locked.png" )
|
||||
|
||||
LVS:AddHudEditor( "SeatSwitcher", ScrW() - 360, 10, 350, 30, 350, 30, "SEAT SWITCHER",
|
||||
function( self, vehicle, X, Y, W, H, ScrX, ScrY, ply )
|
||||
if not vehicle.LVSHudPaintSeatSwitcher then return end
|
||||
vehicle:LVSHudPaintSeatSwitcher( X, Y, W, H, ScrX, ScrY, ply )
|
||||
end
|
||||
)
|
||||
|
||||
function ENT:LVSHudPaintSeatSwitcher( X, Y, w, h, ScrX, ScrY, ply )
|
||||
local pSeats = self:GetPassengerSeats()
|
||||
local SeatCount = table.Count( pSeats )
|
||||
|
||||
if SeatCount <= 0 then return end
|
||||
|
||||
pSeats[0] = self:GetDriverSeat()
|
||||
|
||||
draw.NoTexture()
|
||||
|
||||
local HasAI = self:GetAI()
|
||||
local MySeat = ply:GetVehicle():GetNWInt( "pPodIndex", -1 )
|
||||
|
||||
local Passengers = {}
|
||||
for _, player in pairs( player.GetAll() ) do
|
||||
if player:lvsGetVehicle() == self then
|
||||
local Pod = player:GetVehicle()
|
||||
Passengers[ Pod:GetNWInt( "pPodIndex", -1 ) ] = player:GetName()
|
||||
end
|
||||
end
|
||||
|
||||
if HasAI then
|
||||
Passengers[1] = "[AI] "..self.PrintName
|
||||
|
||||
for _, Pod in pairs( self:GetPassengerSeats() ) do
|
||||
if IsValid( Pod:GetDriver() ) then continue end
|
||||
|
||||
local weapon = Pod:lvsGetWeapon()
|
||||
|
||||
if not IsValid( weapon ) then continue end
|
||||
|
||||
Passengers[ weapon:GetPodIndex() ] = "[AI] Gunner"
|
||||
end
|
||||
end
|
||||
|
||||
ply.SwitcherTime = ply.SwitcherTime or 0
|
||||
ply._lvsoldPassengers = ply._lvsoldPassengers or {}
|
||||
|
||||
local Time = CurTime()
|
||||
for k, v in pairs( Passengers ) do
|
||||
if ply._lvsoldPassengers[k] ~= v then
|
||||
ply._lvsoldPassengers[k] = v
|
||||
ply.SwitcherTime = Time + 2
|
||||
end
|
||||
end
|
||||
for k, v in pairs( ply._lvsoldPassengers ) do
|
||||
if not Passengers[k] then
|
||||
ply._lvsoldPassengers[k] = nil
|
||||
ply.SwitcherTime = Time + 2
|
||||
end
|
||||
end
|
||||
for _, v in pairs( LVS.pSwitchKeysInv ) do
|
||||
if input.IsKeyDown(v) then
|
||||
ply.SwitcherTime = Time + 2
|
||||
end
|
||||
end
|
||||
|
||||
local Hide = ply.SwitcherTime > Time
|
||||
|
||||
ply.smHider = ply.smHider and (ply.smHider + ((Hide and 1 or 0) - ply.smHider) * RealFrameTime() * 15) or 0
|
||||
|
||||
local Alpha1 = 135 + 110 * ply.smHider
|
||||
local HiderOffset = 270 * ply.smHider
|
||||
local xPos = w - 35
|
||||
local yPos = Y - (SeatCount + 1) * 30 + h + 5
|
||||
|
||||
local SwapY = false
|
||||
local SwapX = false
|
||||
|
||||
local xHider = HiderOffset
|
||||
|
||||
if X < (ScrX * 0.5 - w * 0.5) then
|
||||
SwapX = true
|
||||
xPos = 0
|
||||
xHider = 0
|
||||
end
|
||||
|
||||
if Y < (ScrY * 0.5 - h * 0.5) then
|
||||
SwapY = true
|
||||
yPos = Y - h
|
||||
end
|
||||
|
||||
for _, Pod in pairs( pSeats ) do
|
||||
if not IsValid( Pod ) then continue end
|
||||
|
||||
local I = Pod:GetNWInt( "pPodIndex", -1 )
|
||||
|
||||
if I <= 0 then continue end
|
||||
|
||||
if I == MySeat then
|
||||
draw.RoundedBox(5, X + xPos - xHider, yPos + I * 30, 35 + HiderOffset, 25, Color(LVS.ThemeColor.r, LVS.ThemeColor.g, LVS.ThemeColor.b,100 + 50 * ply.smHider) )
|
||||
else
|
||||
draw.RoundedBox(5, X + xPos - xHider, yPos + I * 30, 35 + HiderOffset, 25, Color(0,0,0,100 + 50 * ply.smHider) )
|
||||
end
|
||||
|
||||
if Hide then
|
||||
if Passengers[I] then
|
||||
draw.DrawText( Passengers[I], "LVS_FONT_SWITCHER", X + 40 + xPos - xHider, yPos + I * 30 + 2.5, Color( 255, 255, 255, Alpha1 ), TEXT_ALIGN_LEFT )
|
||||
else
|
||||
draw.DrawText( "-", "LVS_FONT_SWITCHER", X + 40 + xPos - xHider, yPos + I * 30 + 2.5, Color( 255, 255, 255, Alpha1 ), TEXT_ALIGN_LEFT )
|
||||
end
|
||||
|
||||
draw.DrawText( "["..I.."]", "LVS_FONT_SWITCHER", X + 17 + xPos - xHider, yPos + I * 30 + 2.5, Color( 255, 255, 255, Alpha1 ), TEXT_ALIGN_CENTER )
|
||||
else
|
||||
if Passengers[I] then
|
||||
draw.DrawText( "[^"..I.."]", "LVS_FONT_SWITCHER", X + 17 + xPos - xHider, yPos + I * 30 + 2.5, Color( 255, 255, 255, Alpha1 ), TEXT_ALIGN_CENTER )
|
||||
else
|
||||
draw.DrawText( "["..I.."]", "LVS_FONT_SWITCHER", X + 17 + xPos - xHider, yPos + I * 30 + 2.5, Color( 255, 255, 255, Alpha1 ), TEXT_ALIGN_CENTER )
|
||||
end
|
||||
end
|
||||
|
||||
if not self:GetlvsLockedStatus() then continue end
|
||||
|
||||
local xLocker = SwapX and 35 + HiderOffset or -25 - HiderOffset
|
||||
|
||||
if SwapY then
|
||||
if I == 1 then
|
||||
surface.SetDrawColor( 255, 255, 255, 255 )
|
||||
surface.SetMaterial( self.IconVehicleLocked )
|
||||
surface.DrawTexturedRect( X + xPos + xLocker, yPos + I * 30, 25, 25 )
|
||||
end
|
||||
else
|
||||
if I == SeatCount then
|
||||
surface.SetDrawColor( 255, 255, 255, 255 )
|
||||
surface.SetMaterial( self.IconVehicleLocked )
|
||||
surface.DrawTexturedRect( X + xPos + xLocker, yPos + I * 30, 25, 25 )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
157
lua/entities/lvs_base/cl_trailsystem.lua
Normal file
157
lua/entities/lvs_base/cl_trailsystem.lua
Normal file
@@ -0,0 +1,157 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT.TrailMaterial = Material( "trails/smoke" )
|
||||
ENT.TrailRed = 255
|
||||
ENT.TrailGreen = 255
|
||||
ENT.TrailBlue = 255
|
||||
ENT.TrailAlpha = 100
|
||||
|
||||
function ENT:OnTrail( active, id )
|
||||
end
|
||||
|
||||
function ENT:HandleTrail()
|
||||
if not self.RegisteredTrailPositions then return end
|
||||
|
||||
local FT = RealFrameTime()
|
||||
|
||||
local pos = self:GetPos()
|
||||
local vel = self:GetVelocity()
|
||||
local vel_length = vel:Length()
|
||||
|
||||
for id, data in pairs( self.RegisteredTrailPositions ) do
|
||||
local cur_pos = self:LocalToWorld( data.pos )
|
||||
local cur_vel = (cur_pos - data.oldpos) / FT
|
||||
|
||||
local cur_velL = math.abs( self:WorldToLocal( pos + cur_vel ).z )
|
||||
|
||||
if cur_velL > data.activation_speed and vel_length > data.min_flight_speed then
|
||||
if not data.id then
|
||||
data.id = self:StartTrail( data.pos, data.startsize, data.endsize, data.lifetime )
|
||||
self:OnTrail( true, data.id )
|
||||
end
|
||||
else
|
||||
if data.id then
|
||||
self:OnTrail( false, data.id )
|
||||
self:FinishTrail( data.id )
|
||||
data.id = nil
|
||||
end
|
||||
end
|
||||
|
||||
data.oldpos = cur_pos
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:RegisterTrail( Pos, StartSize, EndSize, LifeTime, min_flight_speed, activation_speed )
|
||||
if not istable( self.RegisteredTrailPositions ) then
|
||||
self.RegisteredTrailPositions = {}
|
||||
end
|
||||
|
||||
local data = {
|
||||
pos = Pos,
|
||||
oldpos = self:LocalToWorld( Pos ),
|
||||
startsize = StartSize,
|
||||
endsize = EndSize,
|
||||
lifetime = LifeTime,
|
||||
min_flight_speed = min_flight_speed,
|
||||
activation_speed = activation_speed,
|
||||
}
|
||||
|
||||
table.insert( self.RegisteredTrailPositions, data )
|
||||
end
|
||||
|
||||
function ENT:StartTrail( Pos, StartSize, EndSize, LifeTime )
|
||||
if not LVS.ShowTraileffects then return end
|
||||
|
||||
if not istable( self.TrailActive ) then
|
||||
self.TrailActive = {}
|
||||
end
|
||||
|
||||
local ID = 1
|
||||
for _,_ in ipairs( self.TrailActive ) do
|
||||
ID = ID + 1
|
||||
end
|
||||
|
||||
self.TrailActive[ ID ] = {
|
||||
lifetime = LifeTime,
|
||||
start_size = StartSize,
|
||||
end_size = EndSize,
|
||||
pos = Pos,
|
||||
active = true,
|
||||
positions = {},
|
||||
}
|
||||
|
||||
return ID
|
||||
end
|
||||
|
||||
function ENT:FinishTrail( ID )
|
||||
self.TrailActive[ ID ].active = false
|
||||
end
|
||||
|
||||
function ENT:DrawTrail()
|
||||
local EntTable = self:GetTable()
|
||||
|
||||
if not EntTable.TrailActive then return end
|
||||
|
||||
local Time = CurTime()
|
||||
|
||||
EntTable._NextTrail = EntTable._NextTrail or 0
|
||||
|
||||
local Set = EntTable._NextTrail < Time
|
||||
|
||||
render.SetMaterial( EntTable.TrailMaterial )
|
||||
|
||||
for ID, data in pairs( EntTable.TrailActive ) do
|
||||
|
||||
for pos_id, pos_data in pairs( data.positions ) do
|
||||
if Time - pos_data.time > data.lifetime then
|
||||
data.positions[ pos_id ] = nil
|
||||
end
|
||||
end
|
||||
|
||||
if Set then
|
||||
if data.active then
|
||||
local cur_pos = {
|
||||
time = Time,
|
||||
pos = self:LocalToWorld( data.pos ),
|
||||
}
|
||||
|
||||
table.insert( data.positions, cur_pos )
|
||||
table.sort( data.positions, function( a, b ) return a.time > b.time end )
|
||||
end
|
||||
end
|
||||
|
||||
local num = #data.positions
|
||||
|
||||
if num == 0 then
|
||||
if not data.active then
|
||||
EntTable.TrailActive[ ID ] = nil
|
||||
end
|
||||
|
||||
continue
|
||||
end
|
||||
|
||||
render.StartBeam( num )
|
||||
|
||||
for _, pos_data in ipairs( data.positions ) do
|
||||
local Scale = (pos_data.time + data.lifetime - Time) / data.lifetime
|
||||
local InvScale = 1 - Scale
|
||||
|
||||
render.AddBeam( pos_data.pos, data.start_size * Scale + data.end_size * InvScale, pos_data.time * 50, Color( EntTable.TrailRed, EntTable.TrailGreen, EntTable.TrailBlue, EntTable.TrailAlpha * Scale ^ 2 ) )
|
||||
end
|
||||
|
||||
render.EndBeam()
|
||||
end
|
||||
|
||||
if Set then
|
||||
EntTable._NextTrail = Time + 0.025
|
||||
end
|
||||
end
|
||||
393
lua/entities/lvs_base/init.lua
Normal file
393
lua/entities/lvs_base/init.lua
Normal file
@@ -0,0 +1,393 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile( "shared.lua" )
|
||||
AddCSLuaFile( "sh_weapons.lua" )
|
||||
AddCSLuaFile( "cl_init.lua" )
|
||||
AddCSLuaFile( "cl_effects.lua" )
|
||||
AddCSLuaFile( "cl_hud.lua" )
|
||||
AddCSLuaFile( "cl_trailsystem.lua" )
|
||||
AddCSLuaFile( "cl_seatswitcher.lua" )
|
||||
AddCSLuaFile( "cl_boneposeparemeter.lua" )
|
||||
include("shared.lua")
|
||||
include( "sh_weapons.lua" )
|
||||
include("sv_ai.lua")
|
||||
include("sv_cppi.lua")
|
||||
include("sv_duping.lua")
|
||||
include("sv_pod.lua")
|
||||
include("sv_engine.lua")
|
||||
include("sv_physics.lua")
|
||||
include("sv_physics_damagesystem.lua")
|
||||
include("sv_damagesystem.lua")
|
||||
include("sv_shieldsystem.lua")
|
||||
include("sv_doorsystem.lua")
|
||||
|
||||
ENT.WaterLevelPreventStart = 1
|
||||
ENT.WaterLevelAutoStop = 2
|
||||
ENT.WaterLevelDestroyAI = 2
|
||||
|
||||
function ENT:SpawnFunction( ply, tr, ClassName )
|
||||
|
||||
if ply:InVehicle() then
|
||||
local ent = ents.Create( ClassName )
|
||||
ent:StoreCPPI( ply )
|
||||
ent:SetPos( ply:GetPos() + Vector(0,0,100 + ent.SpawnNormalOffset) )
|
||||
ent:SetAngles( Angle(0, ply:EyeAngles().y, 0 ) )
|
||||
ent:Spawn()
|
||||
ent:Activate()
|
||||
|
||||
return ent
|
||||
else
|
||||
if not tr.Hit then return end
|
||||
|
||||
local ent = ents.Create( ClassName )
|
||||
ent:StoreCPPI( ply )
|
||||
ent:SetPos( tr.HitPos + tr.HitNormal * ent.SpawnNormalOffset )
|
||||
ent:SetAngles( Angle(0, ply:EyeAngles().y, 0 ) )
|
||||
ent:Spawn()
|
||||
ent:Activate()
|
||||
|
||||
return ent
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Initialize()
|
||||
self:SetModel( self.MDL )
|
||||
|
||||
self:PhysicsInit( SOLID_VPHYSICS )
|
||||
self:SetMoveType( MOVETYPE_VPHYSICS )
|
||||
self:SetSolid( SOLID_VPHYSICS )
|
||||
self:SetUseType( SIMPLE_USE )
|
||||
self:SetRenderMode( RENDERMODE_NORMAL )
|
||||
self:AddFlags( FL_OBJECT )
|
||||
|
||||
local PObj = self:GetPhysicsObject()
|
||||
|
||||
if not IsValid( PObj ) then
|
||||
self:Remove()
|
||||
|
||||
print("LVS: missing model. Vehicle terminated.")
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
PObj:SetMaterial( "default_silent" )
|
||||
PObj:EnableMotion( false )
|
||||
PObj:EnableDrag( false )
|
||||
|
||||
timer.Simple(0, function()
|
||||
if not IsValid( self ) or not IsValid( PObj ) then print("LVS: ERROR couldn't initialize vehicle.") return end
|
||||
|
||||
self:PostInitialize( PObj )
|
||||
end)
|
||||
|
||||
if not istable( self.GibModels ) then return end
|
||||
|
||||
for _, modelName in ipairs( self.GibModels ) do
|
||||
util.PrecacheModel( modelName )
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:PostInitialize( PObj )
|
||||
local SpawnSuccess, ErrorMsg = pcall( function() self:OnSpawn( PObj ) end )
|
||||
|
||||
if not SpawnSuccess then
|
||||
ErrorNoHalt( "\n[ERROR] "..ErrorMsg.."\n\n" )
|
||||
end
|
||||
|
||||
self:StartMotionController()
|
||||
|
||||
self:AutoAI()
|
||||
|
||||
self:CallOnRemove( "finish_weapons_on_delete", function( ent )
|
||||
ent:WeaponsFinish()
|
||||
|
||||
for _, pod in pairs( ent:GetPassengerSeats() ) do
|
||||
local weapon = pod:lvsGetWeapon()
|
||||
|
||||
if not IsValid( weapon ) or not weapon._activeWeapon then continue end
|
||||
|
||||
local CurWeapon = self.WEAPONS[ weapon:GetPodIndex() ][ weapon._activeWeapon ]
|
||||
|
||||
if not CurWeapon then continue end
|
||||
|
||||
if CurWeapon.FinishAttack then
|
||||
CurWeapon.FinishAttack( weapon )
|
||||
end
|
||||
end
|
||||
|
||||
ent:WeaponsOnRemove()
|
||||
end)
|
||||
|
||||
self:SetlvsReady( true )
|
||||
|
||||
self:OnSpawnFinish( PObj )
|
||||
end
|
||||
|
||||
function ENT:OnSpawnFinish( PObj )
|
||||
if GetConVar( "developer" ):GetInt() ~= 1 then
|
||||
PObj:EnableMotion( true )
|
||||
end
|
||||
|
||||
self:PhysWake()
|
||||
end
|
||||
|
||||
function ENT:OnSpawn( PObj )
|
||||
end
|
||||
|
||||
function ENT:Think()
|
||||
self:NextThink( CurTime() )
|
||||
|
||||
if not self:IsInitialized() then return true end
|
||||
|
||||
self:HandleActive()
|
||||
self:HandleStart()
|
||||
self:PhysicsThink()
|
||||
self:DamageThink()
|
||||
self:WeaponsThink()
|
||||
self:ShieldThink()
|
||||
|
||||
if self:GetAI() then self:RunAI() end
|
||||
|
||||
self:OnTick()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function ENT:OnDriverChanged( Old, New, VehicleIsActive )
|
||||
self:OnPassengerChanged( Old, New, 1 )
|
||||
end
|
||||
|
||||
function ENT:OnPassengerChanged( Old, New, PodIndex )
|
||||
end
|
||||
|
||||
function ENT:OnSwitchSeat( ply, oldpod, newpod )
|
||||
end
|
||||
|
||||
function ENT:OnTick()
|
||||
end
|
||||
|
||||
function ENT:OnRemoved()
|
||||
end
|
||||
|
||||
function ENT:OnRemove()
|
||||
self:OnRemoved()
|
||||
end
|
||||
|
||||
function ENT:Lock()
|
||||
for _, Handler in pairs( self:GetDoorHandlers() ) do
|
||||
if not IsValid( Handler ) then continue end
|
||||
|
||||
Handler:Close( ply )
|
||||
end
|
||||
|
||||
if self:GetlvsLockedStatus() then return end
|
||||
|
||||
self:SetlvsLockedStatus( true )
|
||||
self:EmitSound( "doors/latchlocked2.wav" )
|
||||
end
|
||||
|
||||
function ENT:UnLock()
|
||||
if not self:GetlvsLockedStatus() then return end
|
||||
|
||||
self:SetlvsLockedStatus( false )
|
||||
self:EmitSound( "doors/latchunlocked1.wav" )
|
||||
end
|
||||
|
||||
function ENT:IsUseAllowed( ply )
|
||||
if not IsValid( ply ) then return false end
|
||||
|
||||
if (ply._lvsNextUse or 0) > CurTime() then return false end
|
||||
|
||||
if self:GetlvsLockedStatus() or (LVS.TeamPassenger and ((self:GetAITEAM() ~= ply:lvsGetAITeam()) and ply:lvsGetAITeam() ~= 0 and self:GetAITEAM() ~= 0)) then
|
||||
self:EmitSound( "doors/default_locked.wav" )
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function ENT:Use( ply )
|
||||
if not self:IsUseAllowed( ply ) then return end
|
||||
|
||||
if not istable( self._DoorHandlers ) then
|
||||
self:SetPassenger( ply )
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if ply:KeyDown( IN_SPEED ) then return end
|
||||
|
||||
local Handler = self:GetDoorHandler( ply )
|
||||
|
||||
if not IsValid( Handler ) then
|
||||
if self:HasDoorSystem() and ply:GetMoveType() == MOVETYPE_WALK then
|
||||
return
|
||||
end
|
||||
|
||||
self:SetPassenger( ply )
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local Pod = Handler:GetLinkedSeat()
|
||||
|
||||
if not IsValid( Pod ) then Handler:Use( ply ) return end
|
||||
|
||||
if not Handler:IsOpen() then Handler:Open( ply ) return end
|
||||
|
||||
if Handler:IsOpen() then
|
||||
Handler:Close( ply )
|
||||
else
|
||||
Handler:OpenAndClose( ply )
|
||||
end
|
||||
|
||||
if ply:KeyDown( IN_WALK ) then
|
||||
|
||||
self:SetPassenger( ply )
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local DriverSeat = self:GetDriverSeat()
|
||||
|
||||
if Pod ~= self:GetDriverSeat() then
|
||||
if IsValid( Pod:GetDriver() ) then
|
||||
self:SetPassenger( ply )
|
||||
else
|
||||
ply:EnterVehicle( Pod )
|
||||
self:AlignView( ply )
|
||||
|
||||
hook.Run( "LVS.UpdateRelationship", self )
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if self:GetAI() then
|
||||
self:SetPassenger( ply )
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if IsValid( Pod:GetDriver() ) then
|
||||
self:SetPassenger( ply )
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if hook.Run( "LVS.CanPlayerDrive", ply, self ) ~= false then
|
||||
ply:EnterVehicle( Pod )
|
||||
self:AlignView( ply )
|
||||
|
||||
hook.Run( "LVS.UpdateRelationship", self )
|
||||
else
|
||||
hook.Run( "LVS.OnPlayerCannotDrive", ply, self )
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:OnTakeDamage( dmginfo )
|
||||
self:CalcShieldDamage( dmginfo )
|
||||
self:CalcDamage( dmginfo )
|
||||
self:TakePhysicsDamage( dmginfo )
|
||||
self:OnAITakeDamage( dmginfo )
|
||||
end
|
||||
|
||||
function ENT:OnMaintenance()
|
||||
end
|
||||
|
||||
function ENT:UpdateTransmitState()
|
||||
return TRANSMIT_ALWAYS
|
||||
end
|
||||
|
||||
function ENT:GetMissileOffset()
|
||||
return self:OBBCenter()
|
||||
end
|
||||
|
||||
function ENT:RebuildCrosshairFilterEnts()
|
||||
self.CrosshairFilterEnts = nil
|
||||
|
||||
local CrosshairFilterEnts = table.Copy( self:GetCrosshairFilterEnts() )
|
||||
|
||||
for id, entity in pairs( CrosshairFilterEnts ) do
|
||||
if not IsValid( entity ) or entity:GetNoDraw() then
|
||||
CrosshairFilterEnts[ id ] = nil
|
||||
end
|
||||
end
|
||||
|
||||
net.Start( "lvs_player_request_filter" )
|
||||
net.WriteEntity( self )
|
||||
net.WriteTable( CrosshairFilterEnts )
|
||||
net.Broadcast()
|
||||
end
|
||||
|
||||
function ENT:GetCrosshairFilterEnts()
|
||||
if not istable( self.CrosshairFilterEnts ) or not self:IsInitialized() then
|
||||
self.CrosshairFilterEnts = {}
|
||||
|
||||
for _, Entity in pairs( constraint.GetAllConstrainedEntities( self ) ) do
|
||||
if not IsValid( Entity ) then continue end
|
||||
|
||||
table.insert( self.CrosshairFilterEnts , Entity )
|
||||
end
|
||||
|
||||
for _, Parent in pairs( self.CrosshairFilterEnts ) do
|
||||
for _, Child in pairs( Parent:GetChildren() ) do
|
||||
if not IsValid( Child ) then continue end
|
||||
|
||||
table.insert( self.CrosshairFilterEnts , Child )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self.CrosshairFilterEnts
|
||||
end
|
||||
|
||||
function ENT:LVSFireBullet( data )
|
||||
data.Entity = self
|
||||
data.Velocity = data.Velocity + self:GetVelocity():Length()
|
||||
data.SrcEntity = self:WorldToLocal( data.Src )
|
||||
|
||||
LVS:FireBullet( data )
|
||||
end
|
||||
|
||||
function ENT:AddSoundEmitter( pos, snd, snd_interior )
|
||||
local Emitter = ents.Create( "lvs_soundemitter" )
|
||||
|
||||
if not IsValid( Emitter ) then
|
||||
self:Remove()
|
||||
|
||||
print("LVS: Failed to create sound emitter entity. Vehicle terminated.")
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
Emitter:SetPos( self:LocalToWorld( pos ) )
|
||||
Emitter:SetAngles( self:GetAngles() )
|
||||
Emitter:Spawn()
|
||||
Emitter:Activate()
|
||||
Emitter:SetParent( self )
|
||||
Emitter:SetBase( self )
|
||||
|
||||
if snd and not snd_interior then
|
||||
Emitter:SetSound( snd )
|
||||
Emitter:SetSoundInterior( snd )
|
||||
else
|
||||
Emitter:SetSound( snd or "" )
|
||||
Emitter:SetSoundInterior( snd_interior )
|
||||
end
|
||||
|
||||
self:DeleteOnRemove( Emitter )
|
||||
|
||||
self:TransferCPPI( Emitter )
|
||||
|
||||
return Emitter
|
||||
end
|
||||
543
lua/entities/lvs_base/sh_weapons.lua
Normal file
543
lua/entities/lvs_base/sh_weapons.lua
Normal file
@@ -0,0 +1,543 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT.WEAPONS = {
|
||||
[1] = {},
|
||||
}
|
||||
|
||||
function ENT:InitWeapons()
|
||||
end
|
||||
|
||||
function ENT:AddWeapon( weaponData, PodID )
|
||||
if not istable( weaponData ) then print("[LVS] couldn't register weapon") return end
|
||||
|
||||
local data = table.Copy( weaponData )
|
||||
|
||||
if not PodID or PodID <= 1 then
|
||||
PodID = 1
|
||||
end
|
||||
|
||||
if not self.WEAPONS[ PodID ] then
|
||||
self.WEAPONS[ PodID ] = {}
|
||||
end
|
||||
|
||||
local default = LVS:GetWeaponPreset( "DEFAULT" )
|
||||
|
||||
data.Icon = data.Icon or Material("lvs/weapons/bullet.png")
|
||||
data.Ammo = data.Ammo or -1
|
||||
data.Delay = data.Delay or 0
|
||||
data.HeatRateUp = data.HeatRateUp or default.HeatRateUp
|
||||
data.Attack = data.Attack or default.Attack
|
||||
data.StartAttack = data.StartAttack or default.StartAttack
|
||||
data.FinishAttack = data.FinishAttack or default.FinishAttack
|
||||
data.OnSelect = data.OnSelect or default.OnSelect
|
||||
data.OnDeselect = data.OnDeselect or default.OnDeselect
|
||||
data.OnThink = data.OnThink or default.OnThink
|
||||
data.OnOverheat = data.OnOverheat or default.OnOverheat
|
||||
data.OnRemove = data.OnRemove or default.OnRemove
|
||||
data.UseableByAI = data.UseableByAI ~= false
|
||||
|
||||
table.insert( self.WEAPONS[ PodID ], data )
|
||||
end
|
||||
|
||||
function ENT:HasWeapon( ID )
|
||||
return istable( self.WEAPONS[1][ ID ] )
|
||||
end
|
||||
|
||||
function ENT:AIHasWeapon( ID )
|
||||
local weapon = self.WEAPONS[1][ ID ]
|
||||
if not istable( weapon ) then return false end
|
||||
|
||||
return weapon.UseableByAI
|
||||
end
|
||||
|
||||
function ENT:GetActiveWeapon()
|
||||
local SelectedID = self:GetSelectedWeapon()
|
||||
local CurWeapon = self.WEAPONS[1][ SelectedID ]
|
||||
|
||||
return CurWeapon, SelectedID
|
||||
end
|
||||
|
||||
function ENT:GetMaxAmmo()
|
||||
local CurWeapon = self:GetActiveWeapon()
|
||||
|
||||
if not CurWeapon then return -1 end
|
||||
|
||||
return CurWeapon.Ammo or -1
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
function ENT:WeaponRestoreAmmo()
|
||||
local AmmoIsSet = false
|
||||
|
||||
for PodID, data in pairs( self.WEAPONS ) do
|
||||
for id, weapon in pairs( data ) do
|
||||
local MaxAmmo = weapon.Ammo or -1
|
||||
local CurAmmo = weapon._CurAmmo or -1
|
||||
|
||||
if CurAmmo == MaxAmmo then continue end
|
||||
|
||||
self.WEAPONS[PodID][ id ]._CurAmmo = MaxAmmo
|
||||
|
||||
AmmoIsSet = true
|
||||
end
|
||||
end
|
||||
|
||||
if AmmoIsSet then
|
||||
self:SetNWAmmo( self:GetAmmo() )
|
||||
|
||||
for _, pod in pairs( self:GetPassengerSeats() ) do
|
||||
local weapon = pod:lvsGetWeapon()
|
||||
|
||||
if not IsValid( weapon ) then continue end
|
||||
|
||||
weapon:SetNWAmmo( weapon:GetAmmo() )
|
||||
end
|
||||
end
|
||||
|
||||
return AmmoIsSet
|
||||
end
|
||||
|
||||
function ENT:WeaponsOnRemove()
|
||||
for _, data in pairs( self.WEAPONS ) do
|
||||
for ID, Weapon in pairs( data ) do
|
||||
if not Weapon.OnRemove then continue end
|
||||
|
||||
Weapon.OnRemove( self )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:WeaponsFinish()
|
||||
if not self._activeWeapon then return end
|
||||
|
||||
local CurWeapon = self.WEAPONS[1][ self._activeWeapon ]
|
||||
|
||||
if not CurWeapon then return end
|
||||
|
||||
if CurWeapon.FinishAttack then
|
||||
CurWeapon.FinishAttack( self )
|
||||
end
|
||||
|
||||
self._activeWeapon = nil
|
||||
self.OldAttack = false
|
||||
end
|
||||
|
||||
function ENT:GetAmmo()
|
||||
if self:GetAI() then return self:GetMaxAmmo() end
|
||||
|
||||
local CurWeapon = self:GetActiveWeapon()
|
||||
|
||||
if not CurWeapon then return -1 end
|
||||
|
||||
return CurWeapon._CurAmmo or self:GetMaxAmmo()
|
||||
end
|
||||
|
||||
function ENT:TakeAmmo( num )
|
||||
if self:GetMaxAmmo() <= 0 then return end
|
||||
|
||||
local CurWeapon = self:GetActiveWeapon()
|
||||
|
||||
CurWeapon._CurAmmo = math.max( self:GetAmmo() - (num or 1), 0 )
|
||||
|
||||
self:SetNWAmmo( CurWeapon._CurAmmo )
|
||||
end
|
||||
|
||||
function ENT:GetHeat()
|
||||
local CurWeapon = self:GetActiveWeapon()
|
||||
|
||||
if not CurWeapon then return 0 end
|
||||
|
||||
return (CurWeapon._CurHeat or 0)
|
||||
end
|
||||
|
||||
function ENT:GetOverheated()
|
||||
local CurWeapon = self:GetActiveWeapon()
|
||||
|
||||
if not CurWeapon then return false end
|
||||
|
||||
return CurWeapon.Overheated == true
|
||||
end
|
||||
|
||||
function ENT:SetOverheated( overheat )
|
||||
if self:GetOverheated() == overheat then return end
|
||||
|
||||
local CurWeapon = self:GetActiveWeapon()
|
||||
|
||||
if not CurWeapon then return end
|
||||
|
||||
CurWeapon.Overheated = overheat
|
||||
|
||||
if self:GetHeat() == 0 then return end
|
||||
|
||||
if CurWeapon.OnOverheat then
|
||||
CurWeapon.OnOverheat( self )
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:SetHeat( heat )
|
||||
local CurWeapon = self:GetActiveWeapon()
|
||||
|
||||
if not CurWeapon then return end
|
||||
|
||||
heat = math.Clamp( heat, 0, 1 )
|
||||
|
||||
CurWeapon._CurHeat = heat
|
||||
|
||||
if self:GetNWHeat() == heat then return end
|
||||
|
||||
self:SetNWHeat( heat )
|
||||
end
|
||||
|
||||
function ENT:CanAttack()
|
||||
local CurWeapon = self:GetActiveWeapon()
|
||||
|
||||
return (CurWeapon._NextFire or 0) < CurTime()
|
||||
end
|
||||
|
||||
function ENT:SetNextAttack( time )
|
||||
local CurWeapon = self:GetActiveWeapon()
|
||||
|
||||
CurWeapon._NextFire = time
|
||||
end
|
||||
|
||||
function ENT:WeaponsShouldFire()
|
||||
if self:GetAI() then return self._AIFireInput end
|
||||
|
||||
local ply = self:GetDriver()
|
||||
|
||||
if not IsValid( ply ) then return false end
|
||||
|
||||
return ply:lvsKeyDown( "ATTACK" )
|
||||
end
|
||||
|
||||
function ENT:WeaponsThink()
|
||||
local EntTable = self:GetTable()
|
||||
|
||||
local T = CurTime()
|
||||
local FT = FrameTime()
|
||||
local CurWeapon, SelectedID = self:GetActiveWeapon()
|
||||
|
||||
for ID, Weapon in pairs( self.WEAPONS[1] ) do
|
||||
local IsActive = ID == SelectedID
|
||||
if Weapon.OnThink then Weapon.OnThink( self, IsActive ) end
|
||||
|
||||
if IsActive then continue end
|
||||
|
||||
-- cool all inactive weapons down
|
||||
Weapon._CurHeat = Weapon._CurHeat and Weapon._CurHeat - math.min( Weapon._CurHeat, (Weapon.HeatRateDown or 0.25) * FT ) or 0
|
||||
end
|
||||
|
||||
if not CurWeapon then return end
|
||||
|
||||
local ShouldFire = self:WeaponsShouldFire()
|
||||
local CurHeat = self:GetHeat()
|
||||
|
||||
if self:GetOverheated() then
|
||||
if CurHeat <= 0 then
|
||||
self:SetOverheated( false )
|
||||
else
|
||||
ShouldFire = false
|
||||
end
|
||||
else
|
||||
if CurHeat >= 1 then
|
||||
self:SetOverheated( true )
|
||||
ShouldFire = false
|
||||
end
|
||||
end
|
||||
|
||||
if self:GetMaxAmmo() > 0 then
|
||||
if self:GetAmmo() <= 0 then
|
||||
ShouldFire = false
|
||||
end
|
||||
end
|
||||
|
||||
if ShouldFire ~= EntTable.OldAttack then
|
||||
EntTable.OldAttack = ShouldFire
|
||||
|
||||
if ShouldFire then
|
||||
if CurWeapon.StartAttack then
|
||||
CurWeapon.StartAttack( self )
|
||||
end
|
||||
EntTable._activeWeapon = SelectedID
|
||||
else
|
||||
self:WeaponsFinish()
|
||||
end
|
||||
end
|
||||
|
||||
if ShouldFire then
|
||||
if not self:CanAttack() then return end
|
||||
|
||||
local ShootDelay = (CurWeapon.Delay or 0)
|
||||
|
||||
self:SetNextAttack( T + ShootDelay )
|
||||
self:SetHeat( CurHeat + (CurWeapon.HeatRateUp or 0.2) * math.max(ShootDelay, FT) )
|
||||
|
||||
if not CurWeapon.Attack then return end
|
||||
|
||||
if CurWeapon.Attack( self ) then
|
||||
self:SetHeat( CurHeat - math.min( self:GetHeat(), (CurWeapon.HeatRateDown or 0.25) * FT ) )
|
||||
self:SetNextAttack( T )
|
||||
end
|
||||
|
||||
EntTable._lvsNextActiveWeaponCoolDown = T + 0.25
|
||||
else
|
||||
if (EntTable._lvsNextActiveWeaponCoolDown or 0) > T then return end
|
||||
|
||||
self:SetHeat( self:GetHeat() - math.min( self:GetHeat(), (CurWeapon.HeatRateDown or 0.25) * FT ) )
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:SelectWeapon( ID )
|
||||
if not isnumber( ID ) then return end
|
||||
|
||||
if self.WEAPONS[1][ ID ] then
|
||||
self:SetSelectedWeapon( ID )
|
||||
end
|
||||
|
||||
local ply = self:GetDriver()
|
||||
|
||||
if not IsValid( ply ) then return end
|
||||
|
||||
net.Start( "lvs_select_weapon" )
|
||||
net.Send( ply )
|
||||
end
|
||||
|
||||
function ENT:OnWeaponChanged( name, old, new)
|
||||
if new == old then return end
|
||||
|
||||
self:WeaponsFinish()
|
||||
|
||||
local PrevWeapon = self.WEAPONS[1][ old ]
|
||||
if PrevWeapon and PrevWeapon.OnDeselect then
|
||||
PrevWeapon.OnDeselect( self, new )
|
||||
end
|
||||
|
||||
local NextWeapon = self.WEAPONS[1][ new ]
|
||||
if NextWeapon and NextWeapon.OnSelect then
|
||||
NextWeapon.OnSelect( self, old )
|
||||
self:SetNWAmmo( NextWeapon._CurAmmo or NextWeapon.Ammo or -1 )
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
function ENT:DrawWeaponIcon( PodID, ID, x, y, width, height, IsSelected, IconColor )
|
||||
end
|
||||
|
||||
function ENT:SelectWeapon( ID )
|
||||
if not isnumber( ID ) then return end
|
||||
|
||||
net.Start( "lvs_select_weapon" )
|
||||
net.WriteInt( ID, 5 )
|
||||
net.WriteBool( false )
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
function ENT:NextWeapon()
|
||||
net.Start( "lvs_select_weapon" )
|
||||
net.WriteInt( 1, 5 )
|
||||
net.WriteBool( true )
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
function ENT:PrevWeapon()
|
||||
net.Start( "lvs_select_weapon" )
|
||||
net.WriteInt( -1, 5 )
|
||||
net.WriteBool( true )
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
LVS:AddHudEditor( "WeaponSwitcher", ScrW() - 210, ScrH() - 165, 200, 68, 200, 68, "WEAPON SELECTOR",
|
||||
function( self, vehicle, X, Y, W, H, ScrX, ScrY, ply )
|
||||
if not vehicle.LVSHudPaintWeapons then return end
|
||||
vehicle:LVSHudPaintWeapons( X, Y, W, H, ScrX, ScrY, ply )
|
||||
end
|
||||
)
|
||||
|
||||
LVS:AddHudEditor( "WeaponInfo", ScrW() - 230, ScrH() - 85, 220, 75, 220, 75, "WEAPON INFO",
|
||||
function( self, vehicle, X, Y, W, H, ScrX, ScrY, ply )
|
||||
if not vehicle.LVSHudPaintWeaponInfo then return end
|
||||
|
||||
vehicle:LVSHudPaintWeaponInfo( X, Y, W, H, ScrX, ScrY, ply )
|
||||
end
|
||||
)
|
||||
|
||||
function ENT:GetAmmoID( ID )
|
||||
local ply = LocalPlayer()
|
||||
|
||||
if not IsValid( ply ) then return end
|
||||
|
||||
local Base = ply:lvsGetWeaponHandler()
|
||||
|
||||
if not IsValid( Base ) then return -1 end
|
||||
|
||||
local selected = Base:GetSelectedWeapon()
|
||||
local weapon = self.WEAPONS[ Base:GetPodIndex() ][ ID ]
|
||||
|
||||
if ID == selected then
|
||||
weapon._CurAmmo = Base:GetNWAmmo()
|
||||
else
|
||||
weapon._CurAmmo = weapon._CurAmmo or weapon.Ammo or -1
|
||||
end
|
||||
|
||||
return weapon._CurAmmo
|
||||
end
|
||||
|
||||
|
||||
local Circles = {
|
||||
[1] = {r = -1, col = Color(0,0,0,200)},
|
||||
[2] = {r = 0, col = Color(255,255,255,200)},
|
||||
[3] = {r = 1, col = Color(255,255,255,255)},
|
||||
[4] = {r = 2, col = Color(255,255,255,200)},
|
||||
[5] = {r = 3, col = Color(0,0,0,200)},
|
||||
}
|
||||
|
||||
local function DrawCircle( X, Y, target_radius, heatvalue )
|
||||
local endang = 360 * heatvalue
|
||||
|
||||
if endang == 0 then return end
|
||||
|
||||
for i = 1, #Circles do
|
||||
local data = Circles[ i ]
|
||||
local radius = target_radius + data.r
|
||||
local segmentdist = endang / ( math.pi * radius / 2 )
|
||||
|
||||
for a = 0, endang, segmentdist do
|
||||
local r = data.col.r
|
||||
local g = data.col.g * (1 - math.min(a / 270,1))
|
||||
local b = data.col.b * (1 - math.min(a / 90,1))
|
||||
|
||||
surface.SetDrawColor( r, g, b, data.col.a )
|
||||
|
||||
surface.DrawLine( X - math.sin( math.rad( a ) ) * radius, Y + math.cos( math.rad( a ) ) * radius, X - math.sin( math.rad( a + segmentdist ) ) * radius, Y + math.cos( math.rad( a + segmentdist ) ) * radius )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ENT.HeatMat = Material( "lvs/heat.png" )
|
||||
|
||||
function ENT:LVSHudPaintWeaponInfo( X, Y, w, h, ScrX, ScrY, ply )
|
||||
local Base = ply:lvsGetWeaponHandler()
|
||||
|
||||
if not IsValid( Base ) then return end
|
||||
|
||||
if not Base:HasWeapon( Base:GetSelectedWeapon() ) then return end
|
||||
|
||||
local Heat = Base:GetNWHeat()
|
||||
local hX = X + w - h * 0.5
|
||||
local hY = Y + h * 0.25 + h * 0.25
|
||||
local hAng = math.cos( CurTime() * 50 ) * 5 * Heat ^ 2
|
||||
|
||||
surface.SetMaterial( self.HeatMat )
|
||||
surface.SetDrawColor( 0, 0, 0, 200 )
|
||||
surface.DrawTexturedRectRotated( hX + 4, hY + 1, h * 0.5, h * 0.5, hAng )
|
||||
surface.SetDrawColor( 255, 255 * (1 - Heat), 255 * math.max(1 - Heat * 1.5,0), 255 )
|
||||
surface.DrawTexturedRectRotated( hX + 2, hY - 1, h * 0.5, h * 0.5, hAng )
|
||||
|
||||
DrawCircle( hX, hY, h * 0.35, Heat )
|
||||
|
||||
if Base:GetMaxAmmo() <= 0 then return end
|
||||
|
||||
draw.DrawText( "AMMO ", "LVS_FONT", X + 72, Y + 35, color_white, TEXT_ALIGN_RIGHT )
|
||||
draw.DrawText( Base:GetNWAmmo(), "LVS_FONT_HUD_LARGE", X + 72, Y + 20, color_white, TEXT_ALIGN_LEFT )
|
||||
end
|
||||
|
||||
function ENT:LVSHudPaintWeapons( X, Y, w, h, ScrX, ScrY, ply )
|
||||
local EntTable = self:GetTable()
|
||||
|
||||
local Base = ply:lvsGetWeaponHandler()
|
||||
|
||||
if not IsValid( Base ) then return end
|
||||
|
||||
local Pod = ply:GetVehicle()
|
||||
|
||||
if not IsValid( Pod ) then return end
|
||||
|
||||
local PodID = Base:GetPodIndex()
|
||||
|
||||
local num = #self.WEAPONS[ PodID ]
|
||||
|
||||
if num <= 1 then return end
|
||||
|
||||
local CenterY = (Y + h * 0.5)
|
||||
local CenterX = (X + w * 0.5)
|
||||
|
||||
local FlatSelector = CenterX > ScrX * 0.333 and CenterX < ScrX * 0.666
|
||||
|
||||
local T = CurTime()
|
||||
local FT = RealFrameTime()
|
||||
|
||||
local gap = 4
|
||||
local SizeY = h - gap
|
||||
|
||||
local Selected = Base:GetSelectedWeapon()
|
||||
if Selected ~= EntTable._OldSelected then
|
||||
EntTable._OldSelected = Selected
|
||||
Pod._SelectActiveTime = T + 2
|
||||
end
|
||||
|
||||
local tAlpha = (Pod._SelectActiveTime or 0) > T and 1 or 0
|
||||
local tAlphaRate = FT * 15
|
||||
|
||||
EntTable.smAlphaSW = EntTable.smAlphaSW and (EntTable.smAlphaSW + math.Clamp(tAlpha - EntTable.smAlphaSW,-tAlphaRate,tAlphaRate)) or 0
|
||||
|
||||
if EntTable.smAlphaSW > 0.95 then
|
||||
EntTable._DisplaySelected = Selected
|
||||
else
|
||||
EntTable._DisplaySelected = EntTable._DisplaySelected or Selected
|
||||
end
|
||||
|
||||
local A255 = 255 * EntTable.smAlphaSW
|
||||
local A150 = 150 * EntTable.smAlphaSW
|
||||
|
||||
local Col = Color(0,0,0,A150)
|
||||
local ColSelect = Color(255,255,255,A150)
|
||||
|
||||
local SwapY = 0
|
||||
|
||||
if Y < (ScrY * 0.5 - h * 0.5) then
|
||||
SwapY = 1
|
||||
end
|
||||
|
||||
for ID = 1, num do
|
||||
local IsSelected = EntTable._DisplaySelected == ID
|
||||
local n = num - ID
|
||||
local xPos = FlatSelector and X + (w + gap) * (ID - 1) - ((w + gap) * 0.5 * num - w * 0.5) or X
|
||||
local yPos = FlatSelector and Y - h * math.min(SwapY,0) or Y - h * n + (num - 1) * h * SwapY
|
||||
|
||||
draw.RoundedBox(5, xPos, yPos, w, SizeY, IsSelected and ColSelect or Col )
|
||||
|
||||
if IsSelected then
|
||||
surface.SetDrawColor( 0, 0, 0, A255 )
|
||||
else
|
||||
surface.SetDrawColor( 255, 255, 255, A255 )
|
||||
end
|
||||
|
||||
if isbool( EntTable.WEAPONS[PodID][ID].Icon ) then
|
||||
local col = IsSelected and Color(255,255,255,A255) or Color(0,0,0,A255)
|
||||
self:DrawWeaponIcon( PodID, ID, xPos, yPos, SizeY * 2, SizeY, IsSelected, col )
|
||||
else
|
||||
surface.SetMaterial( self.WEAPONS[PodID][ID].Icon )
|
||||
surface.DrawTexturedRect( xPos, yPos, SizeY * 2, SizeY )
|
||||
end
|
||||
|
||||
local ammo = self:GetAmmoID( ID )
|
||||
|
||||
if ammo > -1 then
|
||||
draw.DrawText( ammo, "LVS_FONT_HUD", xPos + w - 10, yPos + SizeY * 0.5 - 10, IsSelected and Color(0,0,0,A255) or Color(255,255,255,A255), TEXT_ALIGN_RIGHT )
|
||||
else
|
||||
draw.DrawText( "O", "LVS_FONT_HUD", xPos + w - 19, yPos + SizeY * 0.5 - 10, IsSelected and Color(0,0,0,A255) or Color(255,255,255,A255), TEXT_ALIGN_RIGHT )
|
||||
draw.DrawText( "O", "LVS_FONT_HUD", xPos + w - 10, yPos + SizeY * 0.5 - 10, IsSelected and Color(0,0,0,A255) or Color(255,255,255,A255), TEXT_ALIGN_RIGHT )
|
||||
end
|
||||
end
|
||||
end
|
||||
330
lua/entities/lvs_base/shared.lua
Normal file
330
lua/entities/lvs_base/shared.lua
Normal file
@@ -0,0 +1,330 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
ENT.Type = "anim"
|
||||
|
||||
ENT.PrintName = "LBaseEntity"
|
||||
ENT.Author = "Luna"
|
||||
ENT.Information = "Luna's Vehicle Script"
|
||||
ENT.Category = "[LVS]"
|
||||
|
||||
ENT.Spawnable = false
|
||||
ENT.AdminSpawnable = false
|
||||
|
||||
ENT.AutomaticFrameAdvance = true
|
||||
ENT.RenderGroup = RENDERGROUP_BOTH
|
||||
|
||||
ENT.Editable = true
|
||||
|
||||
ENT.LVS = true
|
||||
|
||||
ENT.MDL = "models/props_c17/trappropeller_engine.mdl"
|
||||
|
||||
ENT.AITEAM = 0
|
||||
|
||||
ENT.MaxHealth = 100
|
||||
ENT.MaxShield = 0
|
||||
|
||||
ENT.SpawnNormalOffset = 15
|
||||
ENT.HitGroundLength = 10
|
||||
|
||||
ENT.lvsDisableZoom = true
|
||||
|
||||
function ENT:AddDT( type, name, data )
|
||||
if not self.DTlist then self.DTlist = {} end
|
||||
|
||||
if self.DTlist[ type ] then
|
||||
self.DTlist[ type ] = self.DTlist[ type ] + 1
|
||||
else
|
||||
self.DTlist[ type ] = 0
|
||||
end
|
||||
|
||||
self:NetworkVar( type, self.DTlist[ type ], name, data )
|
||||
end
|
||||
|
||||
function ENT:CreateBaseDT()
|
||||
local InitWeaponsSuccess, ErrorMsg = pcall( function() self:InitWeapons() end )
|
||||
|
||||
if not InitWeaponsSuccess then
|
||||
ErrorNoHalt( "\n[ERROR] "..ErrorMsg.."\n\n" )
|
||||
end
|
||||
|
||||
self:AddDT( "Entity", "Driver" )
|
||||
self:AddDT( "Entity", "DriverSeat" )
|
||||
|
||||
self:AddDT( "Bool", "Active" )
|
||||
self:AddDT( "Bool", "EngineActive" )
|
||||
self:AddDT( "Bool", "AI", { KeyName = "aicontrolled", Edit = { type = "Boolean", order = 1, category = "AI"} } )
|
||||
self:AddDT( "Bool", "lvsLockedStatus" )
|
||||
self:AddDT( "Bool", "lvsReady" )
|
||||
|
||||
self:AddDT( "Int", "AITEAM", { KeyName = "aiteam", Edit = { type = "Int", order = 2,min = 0, max = 3, category = "AI"} } )
|
||||
self:AddDT( "Int", "SelectedWeapon" )
|
||||
self:AddDT( "Int", "NWAmmo" )
|
||||
|
||||
self:AddDT( "Float", "HP", { KeyName = "health", Edit = { type = "Float", order = 2,min = 0, max = self.MaxHealth, category = "Misc"} } )
|
||||
self:AddDT( "Float", "Shield" )
|
||||
self:AddDT( "Float", "NWHeat" )
|
||||
|
||||
if SERVER then
|
||||
self:NetworkVarNotify( "AI", self.OnToggleAI )
|
||||
self:NetworkVarNotify( "HP", self.PDSHealthValueChanged )
|
||||
self:NetworkVarNotify( "SelectedWeapon", self.OnWeaponChanged )
|
||||
|
||||
self:SetAITEAM( self.AITEAM )
|
||||
self:SetHP( self.MaxHealth )
|
||||
self:SetShield( self.MaxShield )
|
||||
self:SetSelectedWeapon( 1 )
|
||||
end
|
||||
|
||||
self:OnSetupDataTables()
|
||||
end
|
||||
|
||||
function ENT:SetupDataTables()
|
||||
self:CreateBaseDT()
|
||||
end
|
||||
|
||||
function ENT:OnSetupDataTables()
|
||||
end
|
||||
|
||||
function ENT:CalcMainActivity( ply )
|
||||
end
|
||||
|
||||
function ENT:UpdateAnimation( ply, velocity, maxseqgroundspeed )
|
||||
ply:SetPlaybackRate( 1 )
|
||||
|
||||
if CLIENT then
|
||||
GAMEMODE:GrabEarAnimation( ply )
|
||||
GAMEMODE:MouthMoveAnimation( ply )
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function ENT:StartCommand( ply, cmd )
|
||||
end
|
||||
|
||||
function ENT:HitGround()
|
||||
local trace = util.TraceLine( {
|
||||
start = self:LocalToWorld( self:OBBCenter() ),
|
||||
endpos = self:LocalToWorld( Vector(0,0,self:OBBMins().z - self.HitGroundLength) ),
|
||||
filter = self:GetCrosshairFilterEnts()
|
||||
} )
|
||||
|
||||
return trace.Hit
|
||||
end
|
||||
|
||||
function ENT:Sign( n )
|
||||
if n > 0 then return 1 end
|
||||
|
||||
if n < 0 then return -1 end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function ENT:VectorSubtractNormal( Normal, Velocity )
|
||||
local VelForward = Velocity:GetNormalized()
|
||||
|
||||
local Ax = math.acos( math.Clamp( Normal:Dot( VelForward ) ,-1,1) )
|
||||
|
||||
local Fx = math.cos( Ax ) * Velocity:Length()
|
||||
|
||||
local NewVelocity = Velocity - Normal * math.abs( Fx )
|
||||
|
||||
return NewVelocity
|
||||
end
|
||||
|
||||
function ENT:VectorSplitNormal( Normal, Velocity )
|
||||
return math.cos( math.acos( math.Clamp( Normal:Dot( Velocity:GetNormalized() ) ,-1,1) ) ) * Velocity:Length()
|
||||
end
|
||||
|
||||
function ENT:AngleBetweenNormal( Dir1, Dir2 )
|
||||
return math.deg( math.acos( math.Clamp( Dir1:Dot( Dir2 ) ,-1,1) ) )
|
||||
end
|
||||
|
||||
function ENT:GetMaxShield()
|
||||
return self.MaxShield
|
||||
end
|
||||
|
||||
function ENT:GetShieldPercent()
|
||||
return self:GetShield() / self:GetMaxShield()
|
||||
end
|
||||
|
||||
function ENT:GetMaxHP()
|
||||
return self.MaxHealth
|
||||
end
|
||||
|
||||
function ENT:IsInitialized()
|
||||
if not self.GetlvsReady then return false end -- in case this is called BEFORE setupdatatables
|
||||
|
||||
return self:GetlvsReady()
|
||||
end
|
||||
|
||||
function ENT:GetWeaponHandler( num )
|
||||
if num == 1 then return self end
|
||||
|
||||
local pod = self:GetPassengerSeat( num )
|
||||
|
||||
if not IsValid( pod ) then return NULL end
|
||||
|
||||
return pod:lvsGetWeapon()
|
||||
end
|
||||
|
||||
function ENT:GetPassengerSeat( num )
|
||||
if num == 1 then
|
||||
return self:GetDriverSeat()
|
||||
else
|
||||
for _, Pod in pairs( self:GetPassengerSeats() ) do
|
||||
|
||||
if not IsValid( Pod ) then continue end
|
||||
|
||||
local id = Pod:GetNWInt( "pPodIndex", -1 )
|
||||
|
||||
if id == -1 then continue end
|
||||
|
||||
if id == num then
|
||||
return Pod
|
||||
end
|
||||
end
|
||||
|
||||
return NULL
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:GetPassengerSeats()
|
||||
if not self:IsInitialized() then return {} end
|
||||
|
||||
if not istable( self.pSeats ) then
|
||||
self.pSeats = {}
|
||||
|
||||
local DriverSeat = self:GetDriverSeat()
|
||||
|
||||
for _, v in pairs( self:GetChildren() ) do
|
||||
if v ~= DriverSeat and v:GetClass():lower() == "prop_vehicle_prisoner_pod" then
|
||||
table.insert( self.pSeats, v )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self.pSeats
|
||||
end
|
||||
|
||||
function ENT:HasActiveSoundEmitters()
|
||||
local active = false
|
||||
|
||||
for _, emitter in ipairs( self:GetChildren() ) do
|
||||
if emitter:GetClass() ~= "lvs_soundemitter" then continue end
|
||||
|
||||
if not IsValid( emitter ) or not emitter.GetActive or not emitter.GetActiveVisible then continue end
|
||||
|
||||
if emitter:GetActive() and emitter:GetActiveVisible() then
|
||||
active = true
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return active
|
||||
end
|
||||
|
||||
function ENT:GetPassenger( num )
|
||||
if num == 1 then
|
||||
return self:GetDriver()
|
||||
else
|
||||
for _, Pod in pairs( self:GetPassengerSeats() ) do
|
||||
|
||||
if not IsValid( Pod ) then
|
||||
return NULL
|
||||
end
|
||||
|
||||
local id = Pod:GetNWInt( "pPodIndex", -1 )
|
||||
|
||||
if id == -1 then continue end
|
||||
|
||||
if id == num then
|
||||
return Pod:GetDriver()
|
||||
end
|
||||
end
|
||||
|
||||
return NULL
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:GetEveryone()
|
||||
local plys = {}
|
||||
|
||||
local Pilot = self:GetDriver()
|
||||
if IsValid( Pilot ) then
|
||||
table.insert( plys, Pilot )
|
||||
end
|
||||
|
||||
for _, Pod in pairs( self:GetPassengerSeats() ) do
|
||||
if not IsValid( Pod ) then continue end
|
||||
|
||||
local ply = Pod:GetDriver()
|
||||
|
||||
if not IsValid( ply ) then continue end
|
||||
|
||||
table.insert( plys, ply )
|
||||
end
|
||||
|
||||
return plys
|
||||
end
|
||||
|
||||
function ENT:GetPodIndex()
|
||||
return 1
|
||||
end
|
||||
|
||||
function ENT:PlayAnimation( animation, playbackrate )
|
||||
playbackrate = playbackrate or 1
|
||||
|
||||
local sequence = self:LookupSequence( animation )
|
||||
|
||||
self:ResetSequence( sequence )
|
||||
self:SetPlaybackRate( playbackrate )
|
||||
self:SetSequence( sequence )
|
||||
end
|
||||
|
||||
function ENT:GetVehicle()
|
||||
return self
|
||||
end
|
||||
|
||||
function ENT:GetVehicleType()
|
||||
return "LBaseEntity"
|
||||
end
|
||||
|
||||
function ENT:GetBoneInfo( BoneName )
|
||||
local BoneID = self:LookupBone( BoneName )
|
||||
local numHitBoxSets = self:GetHitboxSetCount()
|
||||
|
||||
if not BoneID then
|
||||
goto SkipLoop
|
||||
end
|
||||
|
||||
for hboxset = 0, numHitBoxSets - 1 do
|
||||
local numHitBoxes = self:GetHitBoxCount( hboxset )
|
||||
|
||||
for hitbox=0, numHitBoxes - 1 do
|
||||
local bone = self:GetHitBoxBone( hitbox, hboxset )
|
||||
local name = self:GetBoneName( bone )
|
||||
|
||||
if BoneName ~= name then continue end
|
||||
|
||||
local mins, maxs = self:GetHitBoxBounds( hitbox, hboxset )
|
||||
local pos, ang = self:GetBonePosition( BoneID )
|
||||
|
||||
return self:WorldToLocal( pos ), self:WorldToLocalAngles( ang ), mins, maxs
|
||||
end
|
||||
end
|
||||
|
||||
:: SkipLoop ::
|
||||
|
||||
return vector_origin, angle_zero, vector_origin, vector_origin
|
||||
end
|
||||
237
lua/entities/lvs_base/sv_ai.lua
Normal file
237
lua/entities/lvs_base/sv_ai.lua
Normal file
@@ -0,0 +1,237 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
function ENT:RunAI()
|
||||
end
|
||||
|
||||
function ENT:AutoAI()
|
||||
if not IsValid( self._OwnerEntLVS ) then return end
|
||||
|
||||
if self._OwnerEntLVS:InVehicle() then
|
||||
if self._OwnerEntLVS:IsAdmin() then
|
||||
self:SetAI( true )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:OnCreateAI()
|
||||
end
|
||||
|
||||
function ENT:OnRemoveAI()
|
||||
end
|
||||
|
||||
function ENT:OnToggleAI( name, old, new )
|
||||
if new == old then return end
|
||||
|
||||
if not self:IsInitialized() then
|
||||
timer.Simple( FrameTime(), function()
|
||||
if not IsValid( self ) then return end
|
||||
|
||||
self:OnToggleAI( name, old, new )
|
||||
end )
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if new == true then
|
||||
local Driver = self:GetDriver()
|
||||
|
||||
if IsValid( Driver ) then
|
||||
Driver:ExitVehicle()
|
||||
end
|
||||
|
||||
self:SetActive( true )
|
||||
self:OnCreateAI()
|
||||
|
||||
hook.Run( "LVS.UpdateRelationship", self )
|
||||
else
|
||||
self:SetActive( false )
|
||||
self:OnRemoveAI()
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:OnAITakeDamage( dmginfo )
|
||||
end
|
||||
|
||||
function ENT:AITargetInFront( ent, range )
|
||||
if not IsValid( ent ) then return false end
|
||||
if not range then range = 45 end
|
||||
|
||||
if range >= 180 then return true end
|
||||
|
||||
local DirToTarget = (ent:GetPos() - self:GetPos()):GetNormalized()
|
||||
|
||||
local InFront = math.deg( math.acos( math.Clamp( self:GetForward():Dot( DirToTarget ) ,-1,1) ) ) < range
|
||||
|
||||
return InFront
|
||||
end
|
||||
|
||||
function ENT:AICanSee( otherEnt )
|
||||
if not IsValid( otherEnt ) then return false end
|
||||
|
||||
local PhysObj = otherEnt:GetPhysicsObject()
|
||||
|
||||
if IsValid( PhysObj ) then
|
||||
local trace = {
|
||||
start = self:LocalToWorld( self:OBBCenter() ),
|
||||
endpos = otherEnt:LocalToWorld( PhysObj:GetMassCenter() ),
|
||||
filter = self:GetCrosshairFilterEnts(),
|
||||
}
|
||||
|
||||
return util.TraceLine( trace ).Entity == otherEnt
|
||||
end
|
||||
|
||||
local trace = {
|
||||
start = self:LocalToWorld( self:OBBCenter() ),
|
||||
endpos = otherEnt:LocalToWorld( otherEnt:OBBCenter() ),
|
||||
filter = self:GetCrosshairFilterEnts(),
|
||||
}
|
||||
|
||||
return util.TraceLine( trace ).Entity == otherEnt
|
||||
end
|
||||
|
||||
function ENT:AIGetTarget( viewcone )
|
||||
if (self._lvsNextAICheck or 0) > CurTime() then return self._LastAITarget end
|
||||
|
||||
self._lvsNextAICheck = CurTime() + 2
|
||||
|
||||
local MyPos = self:GetPos()
|
||||
local MyTeam = self:GetAITEAM()
|
||||
|
||||
if MyTeam == 0 then self._LastAITarget = NULL return NULL end
|
||||
|
||||
local ClosestTarget = NULL
|
||||
local TargetDistance = 60000
|
||||
|
||||
if not LVS.IgnorePlayers then
|
||||
for _, ply in pairs( player.GetAll() ) do
|
||||
if not ply:Alive() then continue end
|
||||
|
||||
if ply:IsFlagSet( FL_NOTARGET ) then continue end
|
||||
|
||||
local Dist = (ply:GetPos() - MyPos):Length()
|
||||
|
||||
if Dist > TargetDistance then continue end
|
||||
|
||||
local Veh = ply:lvsGetVehicle()
|
||||
|
||||
if IsValid( Veh ) then
|
||||
if self:AICanSee( Veh ) and Veh ~= self then
|
||||
local HisTeam = Veh:GetAITEAM()
|
||||
|
||||
if HisTeam == 0 then continue end
|
||||
|
||||
if self.AISearchCone then
|
||||
if not self:AITargetInFront( Veh, self.AISearchCone ) then continue end
|
||||
end
|
||||
|
||||
if HisTeam ~= MyTeam or HisTeam == 3 then
|
||||
ClosestTarget = Veh
|
||||
TargetDistance = Dist
|
||||
end
|
||||
end
|
||||
else
|
||||
local HisTeam = ply:lvsGetAITeam()
|
||||
if not ply:IsLineOfSightClear( self ) or HisTeam == 0 then continue end
|
||||
|
||||
if self.AISearchCone then
|
||||
if not self:AITargetInFront( ply, self.AISearchCone ) then continue end
|
||||
end
|
||||
|
||||
if HisTeam ~= MyTeam or HisTeam == 3 then
|
||||
ClosestTarget = ply
|
||||
TargetDistance = Dist
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not LVS.IgnoreNPCs then
|
||||
for _, npc in pairs( LVS:GetNPCs() ) do
|
||||
local HisTeam = LVS:GetNPCRelationship( npc:GetClass() )
|
||||
|
||||
if HisTeam == 0 or (HisTeam == MyTeam and HisTeam ~= 3) then continue end
|
||||
|
||||
local Dist = (npc:GetPos() - MyPos):Length()
|
||||
|
||||
if Dist > TargetDistance or not self:AICanSee( npc ) then continue end
|
||||
|
||||
if self.AISearchCone then
|
||||
if not self:AITargetInFront( npc, self.AISearchCone ) then continue end
|
||||
end
|
||||
|
||||
ClosestTarget = npc
|
||||
TargetDistance = Dist
|
||||
end
|
||||
end
|
||||
|
||||
for _, veh in pairs( LVS:GetVehicles() ) do
|
||||
if veh:IsDestroyed() then continue end
|
||||
|
||||
if veh == self then continue end
|
||||
|
||||
local Dist = (veh:GetPos() - MyPos):Length()
|
||||
|
||||
if Dist > TargetDistance or not self:AITargetInFront( veh, (viewcone or 100) ) then continue end
|
||||
|
||||
local HisTeam = veh:GetAITEAM()
|
||||
|
||||
if HisTeam == 0 then continue end
|
||||
|
||||
if HisTeam == self:GetAITEAM() then
|
||||
if HisTeam ~= 3 then continue end
|
||||
end
|
||||
|
||||
if self.AISearchCone then
|
||||
if not self:AITargetInFront( veh, self.AISearchCone ) then continue end
|
||||
end
|
||||
|
||||
if self:AICanSee( veh ) then
|
||||
ClosestTarget = veh
|
||||
TargetDistance = Dist
|
||||
end
|
||||
end
|
||||
|
||||
self._LastAITarget = ClosestTarget
|
||||
|
||||
return ClosestTarget
|
||||
end
|
||||
|
||||
function ENT:IsEnemy( ent )
|
||||
if not IsValid( ent ) then return false end
|
||||
|
||||
local HisTeam = 0
|
||||
|
||||
if ent:IsNPC() then
|
||||
HisTeam = LVS:GetNPCRelationship( ent:GetClass() )
|
||||
end
|
||||
|
||||
if ent:IsPlayer() then
|
||||
if ent:IsFlagSet( FL_NOTARGET ) then return false end
|
||||
|
||||
local veh = ent:lvsGetVehicle()
|
||||
if IsValid( veh ) then
|
||||
HisTeam = veh:GetAITEAM()
|
||||
else
|
||||
HisTeam = ent:lvsGetAITeam()
|
||||
end
|
||||
end
|
||||
|
||||
if ent.LVS and ent.GetAITEAM then
|
||||
HisTeam = ent:GetAITEAM()
|
||||
end
|
||||
|
||||
if HisTeam == 0 then return false end
|
||||
|
||||
if HisTeam == 3 then return true end
|
||||
|
||||
return HisTeam ~= self:GetAITEAM()
|
||||
end
|
||||
28
lua/entities/lvs_base/sv_cppi.lua
Normal file
28
lua/entities/lvs_base/sv_cppi.lua
Normal file
@@ -0,0 +1,28 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
function ENT:StoreCPPI( owner )
|
||||
self._OwnerEntLVS = owner
|
||||
end
|
||||
|
||||
function ENT:TransferCPPI( target )
|
||||
if not IsEntity( target ) or not IsValid( target ) then return end
|
||||
|
||||
if not CPPI then return end
|
||||
|
||||
local Owner = self._OwnerEntLVS
|
||||
|
||||
if not IsEntity( Owner ) then return end
|
||||
|
||||
if IsValid( Owner ) then
|
||||
target:CPPISetOwner( Owner )
|
||||
end
|
||||
end
|
||||
411
lua/entities/lvs_base/sv_damagesystem.lua
Normal file
411
lua/entities/lvs_base/sv_damagesystem.lua
Normal file
@@ -0,0 +1,411 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT._armorParts = {}
|
||||
ENT._dmgParts = {}
|
||||
|
||||
ENT.DSArmorDamageReduction = 0.1
|
||||
ENT.DSArmorDamageReductionType = DMG_BULLET + DMG_CLUB
|
||||
|
||||
ENT.DSArmorIgnoreDamageType = DMG_SONIC
|
||||
ENT.DSArmorIgnoreForce = 0
|
||||
|
||||
ENT.DSArmorBulletPenetrationAdd = 250
|
||||
|
||||
function ENT:AddDS( data )
|
||||
if not data then return end
|
||||
|
||||
data.pos = data.pos or Vector(0,0,0)
|
||||
data.ang = data.ang or Angle(0,0,0)
|
||||
data.mins = data.mins or Vector(-1,-1,-1)
|
||||
data.maxs = data.maxs or Vector(1,1,1)
|
||||
data.Callback = data.Callback or function( tbl, ent, dmginfo ) end
|
||||
|
||||
debugoverlay.BoxAngles( self:LocalToWorld( data.pos ), data.mins, data.maxs, self:LocalToWorldAngles( data.ang ), 5, Color( 50, 0, 50, 150 ) )
|
||||
|
||||
table.insert( self._dmgParts, data )
|
||||
end
|
||||
|
||||
function ENT:AddDSArmor( data )
|
||||
if not data then return end
|
||||
|
||||
data.pos = data.pos or Vector(0,0,0)
|
||||
data.ang = data.ang or Angle(0,0,0)
|
||||
data.mins = data.mins or Vector(-1,-1,-1)
|
||||
data.maxs = data.maxs or Vector(1,1,1)
|
||||
data.Callback = data.Callback or function( tbl, ent, dmginfo ) end
|
||||
|
||||
debugoverlay.BoxAngles( self:LocalToWorld( data.pos ), data.mins, data.maxs, self:LocalToWorldAngles( data.ang ), 5, Color( 0, 50, 50, 150 ) )
|
||||
|
||||
table.insert( self._armorParts, data )
|
||||
end
|
||||
|
||||
function ENT:CalcComponentDamage( dmginfo )
|
||||
local Len = self:BoundingRadius()
|
||||
local dmgPos = dmginfo:GetDamagePosition()
|
||||
local dmgDir = dmginfo:GetDamageForce():GetNormalized()
|
||||
local dmgPenetration = dmgDir * self.DSArmorBulletPenetrationAdd
|
||||
|
||||
debugoverlay.Line( dmgPos - dmgDir * self.DSArmorBulletPenetrationAdd, dmgPos + dmgPenetration, 4, Color( 0, 0, 255 ) )
|
||||
|
||||
local closestPart
|
||||
local closestDist = Len * 2
|
||||
local HitDistance
|
||||
|
||||
for index, part in ipairs( self._armorParts ) do
|
||||
local mins = part.mins
|
||||
local maxs = part.maxs
|
||||
local pos = self:LocalToWorld( part.pos )
|
||||
local ang = self:LocalToWorldAngles( part.ang )
|
||||
|
||||
local HitPos, HitNormal, Fraction = util.IntersectRayWithOBB( dmgPos, dmgPenetration, pos, ang, mins, maxs )
|
||||
|
||||
if HitPos then
|
||||
debugoverlay.Cross( HitPos, 50, 4, Color( 255, 0, 255 ) )
|
||||
|
||||
local dist = (HitPos - dmgPos):Length()
|
||||
|
||||
if closestDist > dist then
|
||||
closestPart = part
|
||||
closestDist = dist
|
||||
HitDistance = (HitPos - dmgPos):Length()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local lastPartDS
|
||||
local closestPartDS
|
||||
local closestDistDS = Len * 2
|
||||
for index, part in ipairs( self._dmgParts ) do
|
||||
local mins = part.mins
|
||||
local maxs = part.maxs
|
||||
local pos = self:LocalToWorld( part.pos )
|
||||
local ang = self:LocalToWorldAngles( part.ang )
|
||||
|
||||
local HitPos, HitNormal, Fraction = util.IntersectRayWithOBB( dmgPos, dmgPenetration, pos, ang, mins, maxs )
|
||||
|
||||
if HitPos and HitDistance then
|
||||
lastPartDS = part
|
||||
|
||||
if HitDistance < (HitPos - dmgPos):Length() then continue end
|
||||
|
||||
closestPart = nil
|
||||
closestDist = Len * 2
|
||||
end
|
||||
|
||||
if not HitPos then continue end
|
||||
|
||||
debugoverlay.Cross( HitPos, 50, 4, Color( 255, 0, 255 ) )
|
||||
|
||||
local dist = (HitPos - pos):Length()
|
||||
|
||||
if closestDistDS > dist then
|
||||
closestPartDS = part
|
||||
closestDistDS = dist
|
||||
end
|
||||
end
|
||||
|
||||
local Hit = false
|
||||
for index, part in pairs( self._dmgParts ) do
|
||||
local mins = part.mins
|
||||
local maxs = part.maxs
|
||||
local pos = self:LocalToWorld( part.pos )
|
||||
local ang = self:LocalToWorldAngles( part.ang )
|
||||
|
||||
if part == closestPartDS then
|
||||
Hit = true
|
||||
part:Callback( self, dmginfo )
|
||||
debugoverlay.BoxAngles( pos, mins, maxs, ang, 1, Color( 255, 0, 0, 150 ) )
|
||||
end
|
||||
end
|
||||
|
||||
for index, part in pairs( self._armorParts ) do
|
||||
local mins = part.mins
|
||||
local maxs = part.maxs
|
||||
local pos = self:LocalToWorld( part.pos )
|
||||
local ang = self:LocalToWorldAngles( part.ang )
|
||||
|
||||
if part == closestPart then
|
||||
if not part:Callback( self, dmginfo ) then
|
||||
lastPartDS = nil
|
||||
end
|
||||
|
||||
debugoverlay.BoxAngles( pos, mins, maxs, ang, 1, Color( 0, 150, 0, 150 ) )
|
||||
end
|
||||
end
|
||||
|
||||
if lastPartDS then
|
||||
lastPartDS:Callback( self, dmginfo )
|
||||
|
||||
local mins = lastPartDS.mins
|
||||
local maxs = lastPartDS.maxs
|
||||
local pos = self:LocalToWorld( lastPartDS.pos )
|
||||
local ang = self:LocalToWorldAngles( lastPartDS.ang )
|
||||
|
||||
debugoverlay.BoxAngles( pos, mins, maxs, ang, 1, Color( 255, 0, 0, 150 ) )
|
||||
|
||||
Hit = false
|
||||
end
|
||||
|
||||
return Hit
|
||||
end
|
||||
|
||||
function ENT:CalcDamage( dmginfo )
|
||||
if dmginfo:IsDamageType( self.DSArmorIgnoreDamageType ) then return end
|
||||
|
||||
if dmginfo:IsDamageType( self.DSArmorDamageReductionType ) then
|
||||
if dmginfo:GetDamage() ~= 0 then
|
||||
dmginfo:ScaleDamage( self.DSArmorDamageReduction )
|
||||
|
||||
dmginfo:SetDamage( math.max(dmginfo:GetDamage(),1) )
|
||||
end
|
||||
end
|
||||
|
||||
if dmginfo:IsDamageType( DMG_BLAST ) then
|
||||
local Inflictor = dmginfo:GetInflictor()
|
||||
|
||||
if IsValid( Inflictor ) and isfunction( Inflictor.GetEntityFilter ) then
|
||||
for ents, _ in pairs( Inflictor:GetEntityFilter() ) do
|
||||
if ents == self then return end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local IsFireDamage = dmginfo:IsDamageType( DMG_BURN )
|
||||
local IsCollisionDamage = dmginfo:GetDamageType() == (DMG_CRUSH + DMG_VEHICLE)
|
||||
local CriticalHit = false
|
||||
|
||||
if dmginfo:GetDamageForce():Length() < self.DSArmorIgnoreForce and not IsFireDamage then return end
|
||||
|
||||
if not IsCollisionDamage then
|
||||
CriticalHit = self:CalcComponentDamage( dmginfo )
|
||||
end
|
||||
|
||||
local Damage = dmginfo:GetDamage()
|
||||
|
||||
if Damage <= 0 then return end
|
||||
|
||||
local CurHealth = self:GetHP()
|
||||
|
||||
local NewHealth = math.Clamp( CurHealth - Damage, -self:GetMaxHP(), self:GetMaxHP() )
|
||||
|
||||
self:SetHP( NewHealth )
|
||||
|
||||
if self:IsDestroyed() then return end
|
||||
|
||||
local Attacker = dmginfo:GetAttacker()
|
||||
|
||||
if IsValid( Attacker ) and Attacker:IsPlayer() and not IsFireDamage then
|
||||
net.Start( "lvs_hitmarker" )
|
||||
net.WriteBool( CriticalHit )
|
||||
net.Send( Attacker )
|
||||
end
|
||||
|
||||
if Damage > 1 and not IsCollisionDamage and not IsFireDamage then
|
||||
net.Start( "lvs_hurtmarker" )
|
||||
net.WriteFloat( math.min( Damage / 50, 1 ) )
|
||||
net.Send( self:GetEveryone() )
|
||||
end
|
||||
|
||||
if NewHealth <= 0 then
|
||||
self:SetDestroyed( IsCollisionDamage )
|
||||
|
||||
self:ClearPDS()
|
||||
|
||||
self.FinalAttacker = dmginfo:GetAttacker()
|
||||
self.FinalInflictor = dmginfo:GetInflictor()
|
||||
|
||||
local Attacker = self.FinalAttacker
|
||||
if IsValid( Attacker ) and Attacker:IsPlayer() then
|
||||
net.Start( "lvs_killmarker" )
|
||||
net.Send( Attacker )
|
||||
end
|
||||
|
||||
local ExplodeTime = self:PreExplode( math.Clamp((self:GetVelocity():Length() - 200) / 200,1.5,16) )
|
||||
|
||||
timer.Simple( ExplodeTime, function()
|
||||
if not IsValid( self ) then return end
|
||||
self:Explode()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:PreExplode( ExplodeTime )
|
||||
self:OnStartExplosion()
|
||||
|
||||
local PhysObj = self:GetPhysicsObject()
|
||||
|
||||
if not IsValid( PhysObj ) then return 0 end
|
||||
|
||||
self:OnStartFireTrail( PhysObj, ExplodeTime )
|
||||
|
||||
return ExplodeTime
|
||||
end
|
||||
|
||||
function ENT:FindDS( PosToCheck, RadiusAdd )
|
||||
|
||||
if not isnumber( RadiusAdd ) then
|
||||
RadiusAdd = 1
|
||||
end
|
||||
|
||||
local closestPart
|
||||
local closestDist = 50000
|
||||
|
||||
local ToCenter = (self:LocalToWorld( self:OBBCenter() ) - PosToCheck):GetNormalized()
|
||||
|
||||
debugoverlay.Cross( PosToCheck, 50, 4, Color( 255, 255, 0 ) )
|
||||
|
||||
for _, tbl in ipairs( { self._armorParts, self._dmgParts } ) do
|
||||
for index, part in ipairs( tbl ) do
|
||||
local mins = part.mins
|
||||
local maxs = part.maxs
|
||||
local pos = self:LocalToWorld( part.pos )
|
||||
local ang = self:LocalToWorldAngles( part.ang )
|
||||
|
||||
local HitPos, HitNormal, Fraction = util.IntersectRayWithOBB( PosToCheck, ToCenter * RadiusAdd, pos, ang, mins, maxs )
|
||||
|
||||
if HitPos then
|
||||
local dist = (HitPos - PosToCheck):Length()
|
||||
|
||||
if closestDist > dist then
|
||||
closestPart = part
|
||||
closestDist = dist
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if closestPart then
|
||||
local mins = closestPart.mins
|
||||
local maxs = closestPart.maxs
|
||||
local pos = self:LocalToWorld( closestPart.pos )
|
||||
local ang = self:LocalToWorldAngles( closestPart.ang )
|
||||
debugoverlay.BoxAngles( pos, mins, maxs, ang, 1, Color( 255, 255, 0, 150 ) )
|
||||
end
|
||||
|
||||
return closestPart
|
||||
end
|
||||
|
||||
function ENT:DamageThink()
|
||||
if self.MarkForDestruction then
|
||||
self:Explode()
|
||||
end
|
||||
|
||||
if self:IsDestroyed() then
|
||||
if self:GetVelocity():Length() < 800 then
|
||||
self:Explode()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:HurtPlayer( ply, dmg, attacker, inflictor )
|
||||
if not IsValid( ply ) then return end
|
||||
|
||||
if not IsValid( attacker ) then
|
||||
attacker = game.GetWorld()
|
||||
end
|
||||
|
||||
if not IsValid( inflictor ) then
|
||||
inflictor = game.GetWorld()
|
||||
end
|
||||
|
||||
local dmginfo = DamageInfo()
|
||||
dmginfo:SetDamage( dmg )
|
||||
dmginfo:SetAttacker( attacker )
|
||||
dmginfo:SetInflictor( inflictor )
|
||||
dmginfo:SetDamageType( DMG_DIRECT )
|
||||
|
||||
ply:TakeDamageInfo( dmginfo )
|
||||
end
|
||||
|
||||
function ENT:Explode()
|
||||
if self.ExplodedAlready then return end
|
||||
|
||||
self.ExplodedAlready = true
|
||||
|
||||
local Driver = self:GetDriver()
|
||||
|
||||
if IsValid( Driver ) then
|
||||
self:HurtPlayer( Driver, Driver:Health() + Driver:Armor(), self.FinalAttacker, self.FinalInflictor )
|
||||
end
|
||||
|
||||
if istable( self.pSeats ) then
|
||||
for _, pSeat in pairs( self.pSeats ) do
|
||||
if not IsValid( pSeat ) then continue end
|
||||
|
||||
local psgr = pSeat:GetDriver()
|
||||
if not IsValid( psgr ) then continue end
|
||||
|
||||
self:HurtPlayer( psgr, psgr:Health() + psgr:Armor(), self.FinalAttacker, self.FinalInflictor )
|
||||
end
|
||||
end
|
||||
|
||||
self:OnFinishExplosion()
|
||||
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
function ENT:OnStartExplosion()
|
||||
local effectdata = EffectData()
|
||||
effectdata:SetOrigin( self:GetPos() )
|
||||
util.Effect( "lvs_explosion_nodebris", effectdata )
|
||||
end
|
||||
|
||||
function ENT:OnFinishExplosion()
|
||||
local ent = ents.Create( "lvs_destruction" )
|
||||
|
||||
if not IsValid( ent ) then return end
|
||||
|
||||
ent:SetModel( self:GetModel() )
|
||||
ent:SetPos( self:GetPos() )
|
||||
ent:SetAngles( self:GetAngles() )
|
||||
ent.GibModels = self.GibModels
|
||||
ent.Vel = self:GetVelocity()
|
||||
ent:Spawn()
|
||||
ent:Activate()
|
||||
end
|
||||
|
||||
function ENT:OnStartFireTrail( PhysObj, ExplodeTime )
|
||||
local effectdata = EffectData()
|
||||
effectdata:SetOrigin( self:GetPos() )
|
||||
effectdata:SetStart( PhysObj:GetMassCenter() )
|
||||
effectdata:SetEntity( self )
|
||||
effectdata:SetScale( (self.FireTrailScale or 1) )
|
||||
effectdata:SetMagnitude( ExplodeTime )
|
||||
util.Effect( "lvs_firetrail", effectdata )
|
||||
end
|
||||
|
||||
function ENT:IsDestroyed()
|
||||
return self.Destroyed == true
|
||||
end
|
||||
|
||||
function ENT:OnDestroyed()
|
||||
end
|
||||
|
||||
util.AddNetworkString( "lvs_vehicle_destroy" )
|
||||
|
||||
function ENT:SetDestroyed( SuppressOnDestroy )
|
||||
if self.Destroyed then return end
|
||||
|
||||
self.Destroyed = true
|
||||
|
||||
hook.Run( "LVS.UpdateRelationship", self )
|
||||
|
||||
if SuppressOnDestroy then return end
|
||||
|
||||
self:OnDestroyed()
|
||||
|
||||
net.Start("lvs_vehicle_destroy")
|
||||
net.WriteEntity( self )
|
||||
net.SendPAS( self:GetPos() )
|
||||
end
|
||||
|
||||
include("sv_damagesystem_armor.lua")
|
||||
86
lua/entities/lvs_base/sv_damagesystem_armor.lua
Normal file
86
lua/entities/lvs_base/sv_damagesystem_armor.lua
Normal file
@@ -0,0 +1,86 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT.DSArmorBulletPenetrationType = DMG_AIRBOAT + DMG_SNIPER
|
||||
|
||||
function ENT:AddArmor( pos, ang, mins, maxs, health, minforce )
|
||||
local Armor = ents.Create( "lvs_armor" )
|
||||
|
||||
if not IsValid( Armor ) then return end
|
||||
|
||||
Armor:SetPos( self:LocalToWorld( pos ) )
|
||||
Armor:SetAngles( self:LocalToWorldAngles( ang ) )
|
||||
Armor:Spawn()
|
||||
Armor:Activate()
|
||||
Armor:SetParent( self )
|
||||
Armor:SetBase( self )
|
||||
Armor:SetMaxHP( health )
|
||||
Armor:SetHP( health )
|
||||
Armor:SetMins( mins )
|
||||
Armor:SetMaxs( maxs )
|
||||
|
||||
if isnumber( minforce ) then
|
||||
Armor:SetIgnoreForce( minforce + self.DSArmorIgnoreForce )
|
||||
else
|
||||
Armor:SetIgnoreForce( self.DSArmorIgnoreForce )
|
||||
end
|
||||
|
||||
self:DeleteOnRemove( Armor )
|
||||
|
||||
self:TransferCPPI( Armor )
|
||||
|
||||
self:AddDSArmor( {
|
||||
pos = pos,
|
||||
ang = ang,
|
||||
mins = mins,
|
||||
maxs = maxs,
|
||||
Callback = function( tbl, ent, dmginfo )
|
||||
if not IsValid( Armor ) or not dmginfo:IsDamageType( self.DSArmorBulletPenetrationType ) then return true end
|
||||
|
||||
local MaxHealth = self:GetMaxHP()
|
||||
local MaxArmor = Armor:GetMaxHP()
|
||||
local Damage = dmginfo:GetDamage()
|
||||
|
||||
local ArmoredHealth = MaxHealth + MaxArmor
|
||||
local NumShotsToKill = ArmoredHealth / Damage
|
||||
|
||||
local ScaleDamage = math.Clamp( MaxHealth / (NumShotsToKill * Damage),0,1)
|
||||
|
||||
local DidDamage = Armor:TakeTransmittedDamage( dmginfo )
|
||||
|
||||
if DidDamage then
|
||||
local Attacker = dmginfo:GetAttacker()
|
||||
|
||||
if IsValid( Attacker ) and Attacker:IsPlayer() then
|
||||
local NonLethal = self:GetHP() > Damage * ScaleDamage
|
||||
|
||||
if not ent._preventArmorMarker then
|
||||
net.Start( "lvs_armormarker" )
|
||||
net.WriteBool( NonLethal )
|
||||
net.Send( Attacker )
|
||||
|
||||
if not NonLethal then
|
||||
ent._preventArmorMarker = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
dmginfo:ScaleDamage( ScaleDamage )
|
||||
else
|
||||
dmginfo:ScaleDamage( 0 )
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
} )
|
||||
|
||||
return Armor
|
||||
end
|
||||
118
lua/entities/lvs_base/sv_doorsystem.lua
Normal file
118
lua/entities/lvs_base/sv_doorsystem.lua
Normal file
@@ -0,0 +1,118 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
function ENT:AddDoorHandler( poseparameter, pos, ang, mins, maxs, openmins, openmaxs )
|
||||
if not isstring( poseparameter ) then return end
|
||||
|
||||
if not isvector( pos ) or not isangle( ang ) or not isvector( mins ) or not isvector( maxs ) then
|
||||
pos = vector_origin
|
||||
ang = angle_zero
|
||||
mins = self:OBBMins()
|
||||
maxs = self:OBBMaxs()
|
||||
end
|
||||
|
||||
if not isvector( openmins ) then
|
||||
openmins = mins
|
||||
end
|
||||
|
||||
if not isvector( openmaxs ) then
|
||||
openmaxs = maxs
|
||||
end
|
||||
|
||||
local Handler = ents.Create( "lvs_base_doorhandler" )
|
||||
|
||||
if not IsValid( Handler ) then
|
||||
return
|
||||
end
|
||||
|
||||
Handler:SetPos( self:LocalToWorld( pos ) )
|
||||
Handler:SetAngles( self:LocalToWorldAngles( ang ) )
|
||||
Handler:Spawn()
|
||||
Handler:Activate()
|
||||
Handler:SetParent( self )
|
||||
Handler:SetBase( self )
|
||||
Handler:SetMins( mins )
|
||||
Handler:SetMinsOpen( openmins )
|
||||
Handler:SetMinsClosed( mins )
|
||||
|
||||
Handler:SetMaxs( maxs )
|
||||
Handler:SetMaxsOpen( openmaxs )
|
||||
Handler:SetMaxsClosed( maxs )
|
||||
|
||||
Handler:SetPoseName( poseparameter )
|
||||
|
||||
self:DeleteOnRemove( Handler )
|
||||
|
||||
self:TransferCPPI( Handler )
|
||||
|
||||
if not istable( self._DoorHandlers ) then
|
||||
self._DoorHandlers = {}
|
||||
end
|
||||
|
||||
table.insert( self._DoorHandlers, Handler )
|
||||
|
||||
return Handler
|
||||
end
|
||||
|
||||
function ENT:GetDoorHandler( ply )
|
||||
if not IsValid( ply ) or not istable( self._DoorHandlers ) then return NULL end
|
||||
|
||||
local ShootPos = ply:GetShootPos()
|
||||
local AimVector = ply:GetAimVector()
|
||||
|
||||
local radius = 99999999999
|
||||
local target = NULL
|
||||
|
||||
for _, doorHandler in pairs( self._DoorHandlers ) do
|
||||
if not IsValid( doorHandler ) then continue end
|
||||
|
||||
local boxOrigin = doorHandler:GetPos()
|
||||
local boxAngles = doorHandler:GetAngles()
|
||||
local boxMins = doorHandler:GetMins()
|
||||
local boxMaxs = doorHandler:GetMaxs()
|
||||
|
||||
local HitPos, _, _ = util.IntersectRayWithOBB( ShootPos, AimVector * doorHandler.UseRange, boxOrigin, boxAngles, boxMins, boxMaxs )
|
||||
|
||||
local InRange = isvector( HitPos )
|
||||
|
||||
if not InRange then continue end
|
||||
|
||||
local dist = (ShootPos - HitPos):Length()
|
||||
|
||||
if dist < radius then
|
||||
target = doorHandler
|
||||
radius = dist
|
||||
end
|
||||
end
|
||||
|
||||
return target
|
||||
end
|
||||
|
||||
function ENT:GetDoorHandlers()
|
||||
if istable( self._DoorHandlers ) then return self._DoorHandlers end
|
||||
|
||||
return {}
|
||||
end
|
||||
|
||||
function ENT:HasDoorSystem()
|
||||
local HasDoorSystem = false
|
||||
|
||||
for _, Handler in pairs( self:GetDoorHandlers() ) do
|
||||
if not IsValid( Handler ) then continue end
|
||||
|
||||
if IsValid( Handler:GetLinkedSeat() ) then
|
||||
HasDoorSystem = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return HasDoorSystem
|
||||
end
|
||||
69
lua/entities/lvs_base/sv_duping.lua
Normal file
69
lua/entities/lvs_base/sv_duping.lua
Normal file
@@ -0,0 +1,69 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
-- attempt at fixing dupe support
|
||||
|
||||
function ENT:PostEntityPaste(Player,Ent,CreatedEntities)
|
||||
if not self.SetAI then return end
|
||||
|
||||
if IsValid( Player ) and Player:IsAdmin() then return end
|
||||
|
||||
self:SetAI( false )
|
||||
end
|
||||
|
||||
local Active
|
||||
local EngineActive
|
||||
local CrosshairFilterEnts
|
||||
local DoorHandlers
|
||||
local pPodKeyIndex
|
||||
local pSeats
|
||||
|
||||
function ENT:PreEntityCopy()
|
||||
Active = self:GetActive()
|
||||
EngineActive = self:GetEngineActive()
|
||||
|
||||
self:SetlvsReady( false )
|
||||
self:SetActive( false )
|
||||
self:SetEngineActive( false )
|
||||
|
||||
CrosshairFilterEnts = self.CrosshairFilterEnts
|
||||
DoorHandlers = self._DoorHandlers
|
||||
pSeats = self.pSeats
|
||||
pPodKeyIndex = self.pPodKeyIndex
|
||||
|
||||
self.CrosshairFilterEnts = nil
|
||||
self._DoorHandlers = nil
|
||||
self.pPodKeyIndex = nil
|
||||
self.pSeats = nil
|
||||
end
|
||||
|
||||
function ENT:PostEntityCopy()
|
||||
timer.Simple(0, function()
|
||||
if not IsValid( self ) then return end
|
||||
|
||||
self:SetlvsReady( true )
|
||||
self:SetActive( Active )
|
||||
self:SetEngineActive( EngineActive )
|
||||
|
||||
Active = nil
|
||||
EngineActive = nil
|
||||
end)
|
||||
|
||||
self.CrosshairFilterEnts = CrosshairFilterEnts
|
||||
self._DoorHandlers = DoorHandlers
|
||||
self.pPodKeyIndex = pPodKeyIndex
|
||||
self.pSeats = pSeats
|
||||
|
||||
CrosshairFilterEnts = nil
|
||||
DoorHandlers = nil
|
||||
pSeats = nil
|
||||
pPodKeyIndex = nil
|
||||
end
|
||||
61
lua/entities/lvs_base/sv_engine.lua
Normal file
61
lua/entities/lvs_base/sv_engine.lua
Normal file
@@ -0,0 +1,61 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
function ENT:HandleStart()
|
||||
local Driver = self:GetDriver()
|
||||
|
||||
if IsValid( Driver ) then
|
||||
local KeyReload = Driver:lvsKeyDown( "ENGINE" )
|
||||
|
||||
if self.OldKeyReload ~= KeyReload then
|
||||
self.OldKeyReload = KeyReload
|
||||
|
||||
if KeyReload then
|
||||
self:ToggleEngine()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:ToggleEngine()
|
||||
if self:GetEngineActive() then
|
||||
self:StopEngine()
|
||||
else
|
||||
self:StartEngine()
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:IsEngineStartAllowed()
|
||||
if hook.Run( "LVS.IsEngineStartAllowed", self ) == false then return false end
|
||||
|
||||
if self:WaterLevel() > self.WaterLevelPreventStart then return false end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function ENT:OnEngineActiveChanged( Active )
|
||||
end
|
||||
|
||||
function ENT:StartEngine()
|
||||
if self:GetEngineActive() or not self:IsEngineStartAllowed() then return end
|
||||
|
||||
self:PhysWake()
|
||||
|
||||
self:SetEngineActive( true )
|
||||
self:OnEngineActiveChanged( true )
|
||||
end
|
||||
|
||||
function ENT:StopEngine()
|
||||
if not self:GetEngineActive() then return end
|
||||
|
||||
self:SetEngineActive( false )
|
||||
self:OnEngineActiveChanged( false )
|
||||
end
|
||||
176
lua/entities/lvs_base/sv_physics.lua
Normal file
176
lua/entities/lvs_base/sv_physics.lua
Normal file
@@ -0,0 +1,176 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
function ENT:GetWorldGravity()
|
||||
local PhysObj = self:GetPhysicsObject()
|
||||
|
||||
if not IsValid( PhysObj ) or not PhysObj:IsGravityEnabled() then return 0 end
|
||||
|
||||
return physenv.GetGravity():Length()
|
||||
end
|
||||
|
||||
function ENT:GetWorldUp()
|
||||
local Gravity = physenv.GetGravity()
|
||||
|
||||
if Gravity:Length() > 0 then
|
||||
return -Gravity:GetNormalized()
|
||||
else
|
||||
return Vector(0,0,1)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:PhysicsSimulate( phys, deltatime )
|
||||
end
|
||||
|
||||
function ENT:PhysicsStopScape()
|
||||
if self._lvsScrapeData then
|
||||
if self._lvsScrapeData.sound then
|
||||
self._lvsScrapeData.sound:Stop()
|
||||
end
|
||||
end
|
||||
|
||||
self._lvsScrapeData = nil
|
||||
end
|
||||
|
||||
function ENT:PhysicsStartScrape( pos, dir )
|
||||
local startpos = self:LocalToWorld( pos )
|
||||
|
||||
local trace = util.TraceLine( {
|
||||
start = startpos - dir * 5,
|
||||
endpos = startpos + dir * 5,
|
||||
filter = self:GetCrosshairFilterEnts()
|
||||
} )
|
||||
|
||||
if trace.Hit then
|
||||
local sound
|
||||
|
||||
if self._lvsScrapeData and self._lvsScrapeData.sound then
|
||||
sound = self._lvsScrapeData.sound
|
||||
else
|
||||
sound = CreateSound( self, "LVS.Physics.Scrape" )
|
||||
sound:PlayEx( 0, 90 + math.min( (self:GetVelocity():Length() / 2000) * 10,10) )
|
||||
end
|
||||
|
||||
self._lvsScrapeData = {
|
||||
dir = dir,
|
||||
pos = pos,
|
||||
sound = sound,
|
||||
}
|
||||
|
||||
self:CallOnRemove( "stop_scraping", function( self )
|
||||
self:PhysicsStopScape()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:PhysicsThink()
|
||||
if not self._lvsScrapeData then return end
|
||||
|
||||
local startpos = self:LocalToWorld( self._lvsScrapeData.pos )
|
||||
|
||||
local trace = util.TraceLine( {
|
||||
start = startpos - self._lvsScrapeData.dir,
|
||||
endpos = startpos + self._lvsScrapeData.dir * 5,
|
||||
filter = self:GetCrosshairFilterEnts()
|
||||
} )
|
||||
|
||||
local Vel = self:GetVelocity():Length()
|
||||
|
||||
if trace.Hit and Vel > 25 then
|
||||
local vol = math.min(math.max(Vel - 50,0) / 1000,1)
|
||||
|
||||
local effectdata = EffectData()
|
||||
effectdata:SetOrigin( trace.HitPos + trace.HitNormal )
|
||||
effectdata:SetNormal( trace.HitNormal )
|
||||
effectdata:SetMagnitude( vol )
|
||||
util.Effect( "lvs_physics_scrape", effectdata, true, true )
|
||||
|
||||
self._lvsScrapeData.sound:ChangeVolume( vol, 0.1 )
|
||||
else
|
||||
self:PhysicsStopScape()
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:TakeCollisionDamage( damage, attacker )
|
||||
if not IsValid( attacker ) then
|
||||
attacker = game.GetWorld()
|
||||
end
|
||||
|
||||
local dmginfo = DamageInfo()
|
||||
dmginfo:SetDamage( damage )
|
||||
dmginfo:SetAttacker( attacker )
|
||||
dmginfo:SetInflictor( attacker )
|
||||
dmginfo:SetDamageType( DMG_CRUSH + DMG_VEHICLE ) -- this will communicate to the damage system to handle this kind of damage differently.
|
||||
self:TakeDamageInfo( dmginfo )
|
||||
end
|
||||
|
||||
function ENT:OnCollision( data, physobj )
|
||||
return false
|
||||
end
|
||||
|
||||
function ENT:OnSkyCollide( data, physobj )
|
||||
return true
|
||||
end
|
||||
|
||||
function ENT:PhysicsCollide( data, physobj )
|
||||
local HitEnt = data.HitEntity
|
||||
|
||||
if not IsValid( HitEnt ) and util.GetSurfacePropName( data.TheirSurfaceProps ) == "default_silent" then
|
||||
if self:OnSkyCollide( data, physobj ) then return end
|
||||
end
|
||||
|
||||
if self:IsDestroyed() then
|
||||
self.MarkForDestruction = true
|
||||
end
|
||||
|
||||
if self:OnCollision( data, physobj ) then return end
|
||||
|
||||
self:PhysicsStartScrape( self:WorldToLocal( data.HitPos ), data.HitNormal )
|
||||
|
||||
if IsValid( HitEnt ) then
|
||||
if HitEnt:IsPlayer() or HitEnt:IsNPC() then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if self:GetAI() and not self:IsPlayerHolding() then
|
||||
if self:WaterLevel() >= self.WaterLevelDestroyAI then
|
||||
self:SetDestroyed( true )
|
||||
self.MarkForDestruction = true
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
self:TakeCollisionDamage( data.OurOldVelocity:Length() - data.OurNewVelocity:Length(), HitEnt )
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if data.Speed > 60 and data.DeltaTime > 0.2 then
|
||||
local VelDif = data.OurOldVelocity:Length() - data.OurNewVelocity:Length()
|
||||
|
||||
self:CalcPDS( data )
|
||||
|
||||
local effectdata = EffectData()
|
||||
effectdata:SetOrigin( data.HitPos )
|
||||
util.Effect( "lvs_physics_impact", effectdata, true, true )
|
||||
|
||||
if VelDif > 700 then
|
||||
self:EmitSound( "lvs/physics/impact_hard.wav", 75, 95 + math.min(VelDif / 1000,1) * 10, math.min(VelDif / 800,1) )
|
||||
|
||||
if not self:IsPlayerHolding() then
|
||||
self:TakeCollisionDamage( VelDif, HitEnt )
|
||||
end
|
||||
else
|
||||
self:EmitSound( "lvs/physics/impact_soft"..math.random(1,5)..".wav", 75, 100, math.min(0.1 + VelDif / 700,1) )
|
||||
end
|
||||
end
|
||||
end
|
||||
250
lua/entities/lvs_base/sv_physics_damagesystem.lua
Normal file
250
lua/entities/lvs_base/sv_physics_damagesystem.lua
Normal file
@@ -0,0 +1,250 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT._pdsParts = {}
|
||||
|
||||
ENT.PDSDamageVelocity = 100
|
||||
ENT.PDSDamageMultiplier = 0.05
|
||||
|
||||
function ENT:ClearPDS()
|
||||
if not istable( self._pdsParts ) then return end
|
||||
|
||||
table.Empty( self._pdsParts )
|
||||
end
|
||||
|
||||
function ENT:PDSHealthValueChanged( name, old, new)
|
||||
if new == old then return end
|
||||
|
||||
if not self:IsInitialized() or not istable( self._pdsParts ) or new ~= self:GetMaxHP() then return end
|
||||
|
||||
for _, part in pairs( self._pdsParts ) do
|
||||
part:SetStage( 0 )
|
||||
|
||||
if not part._group or not part._subgroup then continue end
|
||||
|
||||
self:SetBodygroup( part._group, part._subgroup )
|
||||
|
||||
part._group = nil
|
||||
part._subgroup = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function DamagePart( ent, part, speed )
|
||||
if not speed then
|
||||
speed = 0
|
||||
end
|
||||
|
||||
local stage = part:GetStage() + 1
|
||||
|
||||
part:SetStage( stage )
|
||||
|
||||
local data = part:GetStageData()
|
||||
|
||||
if isfunction( data.Callback ) then
|
||||
data:Callback( ent, part, speed )
|
||||
end
|
||||
|
||||
if istable( data.bodygroup ) then
|
||||
for group, subgroup in pairs( data.bodygroup ) do
|
||||
if not part._group or not part._subgroup then
|
||||
part._group = group
|
||||
part._subgroup = ent:GetBodygroup( group )
|
||||
end
|
||||
|
||||
ent:SetBodygroup( group, subgroup )
|
||||
end
|
||||
end
|
||||
|
||||
if isstring( data.sound ) then
|
||||
ent:EmitSound( data.sound, 75, 100, math.min(0.1 + speed / 700,1) )
|
||||
end
|
||||
|
||||
if isstring( data.effect ) then
|
||||
local effectdata = EffectData()
|
||||
effectdata:SetOrigin( ent:LocalToWorld( part.pos ) )
|
||||
util.Effect( data.effect, effectdata, true, true )
|
||||
end
|
||||
|
||||
if not istable( data.gib ) or not data.gib.mdl then return end
|
||||
|
||||
timer.Simple(0, function()
|
||||
if not IsValid( ent ) then return end
|
||||
|
||||
local InvAttach = isstring( data.gib.target )
|
||||
|
||||
local pos
|
||||
local ang
|
||||
|
||||
if InvAttach then
|
||||
pos = vector_origin
|
||||
ang = angle_zero
|
||||
else
|
||||
if isvector( data.gib.pos ) and isangle( data.gib.ang ) then
|
||||
pos = ent:LocalToWorld( data.gib.pos )
|
||||
ang = ent:LocalToWorldAngles( data.gib.ang )
|
||||
end
|
||||
end
|
||||
|
||||
local gib = ents.Create( "prop_physics" )
|
||||
gib:SetModel( data.gib.mdl )
|
||||
gib:SetPos( pos )
|
||||
gib:SetAngles( ang )
|
||||
gib:Spawn()
|
||||
gib:Activate()
|
||||
gib:SetCollisionGroup( COLLISION_GROUP_DEBRIS )
|
||||
gib:SetSkin( ent:GetSkin() )
|
||||
|
||||
if InvAttach then
|
||||
local att = gib:GetAttachment( gib:LookupAttachment( data.gib.target ) )
|
||||
|
||||
if att then
|
||||
local newpos = ent:LocalToWorld( -att.Pos )
|
||||
local newang = ent:LocalToWorldAngles( -att.Ang )
|
||||
|
||||
gib:SetPos( newpos )
|
||||
gib:SetAngles( newang )
|
||||
end
|
||||
end
|
||||
|
||||
gib:SetOwner( ent )
|
||||
gib:SetColor( ent:GetColor() )
|
||||
gib:SetRenderMode( RENDERMODE_TRANSALPHA )
|
||||
|
||||
ent:DeleteOnRemove( gib )
|
||||
ent:TransferCPPI( gib )
|
||||
|
||||
timer.Simple( 59.5, function()
|
||||
if not IsValid( gib ) then return end
|
||||
gib:SetRenderFX( kRenderFxFadeFast )
|
||||
end)
|
||||
|
||||
timer.Simple( 60, function()
|
||||
if not IsValid( gib ) then return end
|
||||
gib:Remove()
|
||||
end)
|
||||
|
||||
local PhysObj = gib:GetPhysicsObject()
|
||||
|
||||
if not IsValid( PhysObj ) then return end
|
||||
|
||||
PhysObj:SetVelocityInstantaneous( ent:GetVelocity() + Vector(0,0,250) )
|
||||
PhysObj:AddAngleVelocity( VectorRand() * 500 )
|
||||
end)
|
||||
end
|
||||
|
||||
function ENT:AddPDS( data )
|
||||
if not data then return end
|
||||
|
||||
if self._pdsPartsID then
|
||||
self._pdsPartsID = self._pdsPartsID + 1
|
||||
else
|
||||
self._pdsPartsID = 1
|
||||
end
|
||||
|
||||
data.pos = data.pos or Vector(0,0,0)
|
||||
data.ang = data.ang or Angle(0,0,0)
|
||||
data.mins = data.mins or Vector(-1,-1,-1)
|
||||
data.maxs = data.maxs or Vector(1,1,1)
|
||||
data.stages = data.stages or {}
|
||||
data.GetStage = function( self )
|
||||
if not self._curstage then
|
||||
self._curstage = 0
|
||||
end
|
||||
|
||||
return self._curstage
|
||||
end
|
||||
data.SetStage = function( self, stage )
|
||||
self._curstage = stage
|
||||
end
|
||||
|
||||
data.GetStageData = function( self, stage )
|
||||
return self.stages[ self:GetStage() ] or {}
|
||||
end
|
||||
|
||||
debugoverlay.BoxAngles( self:LocalToWorld( data.pos ), data.mins, data.maxs, self:LocalToWorldAngles( data.ang ), 8, Color( 50, 50, 0, 150 ) )
|
||||
|
||||
self._pdsParts[ self._pdsPartsID ] = data
|
||||
|
||||
if data.allow_damage then
|
||||
local id = self._pdsPartsID
|
||||
|
||||
self:AddDS( {
|
||||
pos = data.pos,
|
||||
ang = data.ang,
|
||||
mins = data.mins,
|
||||
maxs = data.maxs,
|
||||
Callback = function( tbl, ent, dmginfo )
|
||||
if not IsValid( ent ) then return end
|
||||
|
||||
local part = ent._pdsParts[ id ]
|
||||
|
||||
if not part then return end
|
||||
|
||||
DamagePart( ent, part, 1000 )
|
||||
end
|
||||
} )
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:FindPDS( PosToCheck, RadiusAdd )
|
||||
if not isnumber( RadiusAdd ) then
|
||||
RadiusAdd = 1
|
||||
end
|
||||
|
||||
local Parts = {}
|
||||
|
||||
debugoverlay.Cross( PosToCheck, 50, 4, Color( 255, 255, 0 ) )
|
||||
|
||||
for index, part in ipairs( self._pdsParts ) do
|
||||
local mins = part.mins
|
||||
local maxs = part.maxs
|
||||
local pos = self:LocalToWorld( part.pos )
|
||||
local ang = self:LocalToWorldAngles( part.ang )
|
||||
local dir = (pos - PosToCheck):GetNormalized()
|
||||
|
||||
local HitPos, HitNormal, Fraction = util.IntersectRayWithOBB( PosToCheck, dir * RadiusAdd, pos, ang, mins, maxs )
|
||||
|
||||
if HitPos then
|
||||
table.insert( Parts, part )
|
||||
end
|
||||
end
|
||||
|
||||
for _, closestPart in ipairs( Parts ) do
|
||||
local mins = closestPart.mins
|
||||
local maxs = closestPart.maxs
|
||||
local pos = self:LocalToWorld( closestPart.pos )
|
||||
local ang = self:LocalToWorldAngles( closestPart.ang )
|
||||
debugoverlay.BoxAngles( pos, mins, maxs, ang, 1, Color( 255, 255, 0, 150 ) )
|
||||
end
|
||||
|
||||
return Parts
|
||||
end
|
||||
|
||||
function ENT:CalcPDS( physdata )
|
||||
local VelDif = math.abs( physdata.OurOldVelocity:Length() - physdata.OurNewVelocity:Length() )
|
||||
|
||||
if VelDif < self.PDSDamageVelocity then return end
|
||||
|
||||
local parts = self:FindPDS( physdata.HitPos, (VelDif - self.PDSDamageVelocity) * self.PDSDamageMultiplier )
|
||||
|
||||
if #parts == 0 then return end
|
||||
|
||||
local HP = self:GetHP()
|
||||
local MaxHP = self:GetMaxHP()
|
||||
|
||||
if HP == MaxHP then
|
||||
self:SetHP( math.max( MaxHP - 0.1, 1 ) )
|
||||
end
|
||||
|
||||
for _, part in pairs( parts ) do
|
||||
DamagePart( self, part, VelDif )
|
||||
end
|
||||
end
|
||||
210
lua/entities/lvs_base/sv_pod.lua
Normal file
210
lua/entities/lvs_base/sv_pod.lua
Normal file
@@ -0,0 +1,210 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT.DriverActiveSound = "vehicles/atv_ammo_close.wav"
|
||||
ENT.DriverInActiveSound = "vehicles/atv_ammo_open.wav"
|
||||
|
||||
function ENT:AlignView( ply, SetZero )
|
||||
if not IsValid( ply ) then return end
|
||||
|
||||
timer.Simple( 0, function()
|
||||
if not IsValid( ply ) or not IsValid( self ) then return end
|
||||
local Ang = Angle(0,90,0)
|
||||
|
||||
if not SetZero then
|
||||
Ang = self:GetAngles()
|
||||
Ang.r = 0
|
||||
end
|
||||
|
||||
ply:SetEyeAngles( Ang )
|
||||
end)
|
||||
end
|
||||
|
||||
function ENT:HandleActive()
|
||||
local Pod = self:GetDriverSeat()
|
||||
|
||||
if not IsValid( Pod ) then
|
||||
self:SetActive( false )
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local Driver = Pod:GetDriver()
|
||||
local Active = self:GetActive()
|
||||
|
||||
if Driver ~= self:GetDriver() then
|
||||
local NewDriver = Driver
|
||||
local OldDriver = self:GetDriver()
|
||||
local IsActive = IsValid( Driver )
|
||||
|
||||
self:SetDriver( Driver )
|
||||
self:SetActive( IsActive )
|
||||
|
||||
self:OnDriverChanged( OldDriver, NewDriver, IsActive )
|
||||
|
||||
if IsActive then
|
||||
Driver:lvsBuildControls()
|
||||
self:AlignView( Driver )
|
||||
|
||||
if self.DriverActiveSound then self:EmitSound( self.DriverActiveSound ) end
|
||||
else
|
||||
self:WeaponsFinish()
|
||||
|
||||
if self.DriverInActiveSound then self:EmitSound( self.DriverInActiveSound ) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:SetPassenger( ply )
|
||||
if not IsValid( ply ) then return end
|
||||
|
||||
local AI = self:GetAI()
|
||||
local DriverSeat = self:GetDriverSeat()
|
||||
local AllowedToBeDriver = hook.Run( "LVS.CanPlayerDrive", ply, self ) ~= false
|
||||
|
||||
if IsValid( DriverSeat ) and not IsValid( DriverSeat:GetDriver() ) and not ply:KeyDown( IN_WALK ) and not AI and AllowedToBeDriver then
|
||||
ply:EnterVehicle( DriverSeat )
|
||||
self:AlignView( ply )
|
||||
|
||||
hook.Run( "LVS.UpdateRelationship", self )
|
||||
else
|
||||
local Seat = NULL
|
||||
local Dist = 500000
|
||||
|
||||
for _, v in pairs( self:GetPassengerSeats() ) do
|
||||
if not IsValid( v ) or IsValid( v:GetDriver() ) then continue end
|
||||
if v:GetNWInt( "pPodIndex" ) == -1 then continue end
|
||||
|
||||
local cDist = (v:GetPos() - ply:GetPos()):Length()
|
||||
|
||||
if cDist < Dist then
|
||||
Seat = v
|
||||
Dist = cDist
|
||||
end
|
||||
end
|
||||
|
||||
if IsValid( Seat ) then
|
||||
ply:EnterVehicle( Seat )
|
||||
self:AlignView( ply, true )
|
||||
|
||||
hook.Run( "LVS.UpdateRelationship", self )
|
||||
else
|
||||
if IsValid( DriverSeat ) then
|
||||
if not IsValid( self:GetDriver() ) and not AI then
|
||||
if AllowedToBeDriver then
|
||||
ply:EnterVehicle( DriverSeat )
|
||||
self:AlignView( ply )
|
||||
|
||||
hook.Run( "LVS.UpdateRelationship", self )
|
||||
else
|
||||
hook.Run( "LVS.OnPlayerCannotDrive", ply, self )
|
||||
end
|
||||
end
|
||||
else
|
||||
self:EmitSound( "doors/default_locked.wav" )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:AddDriverSeat( Pos, Ang )
|
||||
if IsValid( self:GetDriverSeat() ) then return self:GetDriverSeat() end
|
||||
|
||||
local Pod = ents.Create( "prop_vehicle_prisoner_pod" )
|
||||
|
||||
if not IsValid( Pod ) then
|
||||
self:Remove()
|
||||
|
||||
print("LVS: Failed to create driverseat. Vehicle terminated.")
|
||||
|
||||
return
|
||||
else
|
||||
self:SetDriverSeat( Pod )
|
||||
|
||||
local DSPhys = Pod:GetPhysicsObject()
|
||||
|
||||
Pod:SetMoveType( MOVETYPE_NONE )
|
||||
Pod:SetModel( "models/nova/airboat_seat.mdl" )
|
||||
Pod:SetKeyValue( "vehiclescript","scripts/vehicles/prisoner_pod.txt" )
|
||||
Pod:SetKeyValue( "limitview", 0 )
|
||||
Pod:SetPos( self:LocalToWorld( Pos ) )
|
||||
Pod:SetAngles( self:LocalToWorldAngles( Ang ) )
|
||||
Pod:SetOwner( self )
|
||||
Pod:Spawn()
|
||||
Pod:Activate()
|
||||
Pod:SetParent( self )
|
||||
Pod:SetNotSolid( true )
|
||||
Pod:SetColor( Color( 255, 255, 255, 0 ) )
|
||||
Pod:SetRenderMode( RENDERMODE_TRANSALPHA )
|
||||
Pod:DrawShadow( false )
|
||||
Pod.DoNotDuplicate = true
|
||||
Pod:SetNWInt( "pPodIndex", 1 )
|
||||
Pod:PhysicsDestroy()
|
||||
|
||||
debugoverlay.BoxAngles( Pod:GetPos(), Pod:OBBMins(), Pod:OBBMaxs(), Pod:GetAngles(), 5, Color( 255, 93, 0, 200 ) )
|
||||
|
||||
self:DeleteOnRemove( Pod )
|
||||
|
||||
self:TransferCPPI( Pod )
|
||||
end
|
||||
|
||||
return Pod
|
||||
end
|
||||
|
||||
function ENT:AddPassengerSeat( Pos, Ang )
|
||||
if not isvector( Pos ) or not isangle( Ang ) then return NULL end
|
||||
|
||||
local Pod = ents.Create( "prop_vehicle_prisoner_pod" )
|
||||
|
||||
if not IsValid( Pod ) then return NULL end
|
||||
|
||||
Pod:SetMoveType( MOVETYPE_NONE )
|
||||
Pod:SetModel( "models/nova/airboat_seat.mdl" )
|
||||
Pod:SetKeyValue( "vehiclescript","scripts/vehicles/prisoner_pod.txt" )
|
||||
Pod:SetKeyValue( "limitview", 0 )
|
||||
Pod:SetPos( self:LocalToWorld( Pos ) )
|
||||
Pod:SetAngles( self:LocalToWorldAngles( Ang ) )
|
||||
Pod:SetOwner( self )
|
||||
Pod:Spawn()
|
||||
Pod:Activate()
|
||||
Pod:SetParent( self )
|
||||
Pod:SetNotSolid( true )
|
||||
Pod:SetColor( Color( 255, 255, 255, 0 ) )
|
||||
Pod:SetRenderMode( RENDERMODE_TRANSALPHA )
|
||||
|
||||
debugoverlay.BoxAngles( Pod:GetPos(), Pod:OBBMins(), Pod:OBBMaxs(), Pod:GetAngles(), 5, Color( 100, 65, 127, 200 ) )
|
||||
|
||||
Pod:DrawShadow( false )
|
||||
Pod.DoNotDuplicate = true
|
||||
|
||||
self.pPodKeyIndex = self.pPodKeyIndex and self.pPodKeyIndex + 1 or 2
|
||||
|
||||
if self.WEAPONS[ self.pPodKeyIndex ] then
|
||||
local weapon = Pod:lvsAddWeapon( self.pPodKeyIndex )
|
||||
|
||||
if IsValid( weapon ) then
|
||||
self:TransferCPPI( weapon )
|
||||
self:DeleteOnRemove( weapon )
|
||||
end
|
||||
end
|
||||
|
||||
Pod:SetNWInt( "pPodIndex", self.pPodKeyIndex )
|
||||
Pod:PhysicsDestroy()
|
||||
|
||||
self:DeleteOnRemove( Pod )
|
||||
self:TransferCPPI( Pod )
|
||||
|
||||
if not istable( self.pSeats ) then self.pSeats = {} end
|
||||
|
||||
table.insert( self.pSeats, Pod )
|
||||
|
||||
return Pod
|
||||
end
|
||||
89
lua/entities/lvs_base/sv_shieldsystem.lua
Normal file
89
lua/entities/lvs_base/sv_shieldsystem.lua
Normal file
@@ -0,0 +1,89 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT.ShieldRechargeDelay = 5
|
||||
ENT.ShieldRechargeRate = 1
|
||||
ENT.ShieldBlockableTypes = {
|
||||
[1] = DMG_BULLET,
|
||||
[2] = DMG_AIRBOAT,
|
||||
[3] = DMG_BUCKSHOT,
|
||||
[4] = DMG_SNIPER,
|
||||
}
|
||||
|
||||
function ENT:CalcShieldDamage( dmginfo )
|
||||
local MaxShield = self:GetMaxShield()
|
||||
|
||||
if MaxShield <= 0 then return end
|
||||
|
||||
local DMG_ENUM = DMG_GENERIC
|
||||
for _, ENUM in ipairs( self.ShieldBlockableTypes ) do
|
||||
DMG_ENUM = DMG_ENUM + ENUM
|
||||
end
|
||||
|
||||
if not dmginfo:IsDamageType( DMG_ENUM ) then return end
|
||||
|
||||
self:DelayNextShieldRecharge( self.ShieldRechargeDelay )
|
||||
|
||||
local DamageRemaining = self:TakeShieldDamage( dmginfo:GetDamage() )
|
||||
|
||||
dmginfo:SetDamage( DamageRemaining )
|
||||
|
||||
self:OnTakeShieldDamage( dmginfo )
|
||||
end
|
||||
|
||||
function ENT:CanShieldRecharge()
|
||||
return (self.NextShieldRecharge or 0) < CurTime()
|
||||
end
|
||||
|
||||
function ENT:DelayNextShieldRecharge( delay )
|
||||
self.NextShieldRecharge = CurTime() + delay
|
||||
end
|
||||
|
||||
function ENT:ShieldThink()
|
||||
local MaxShield = self:GetMaxShield()
|
||||
|
||||
if MaxShield <= 0 or self:GetShieldPercent() == 1 then return end
|
||||
|
||||
if not self:CanShieldRecharge() then return end
|
||||
|
||||
local Cur = self:GetShield()
|
||||
local Rate = FrameTime() * 20 * self.ShieldRechargeRate
|
||||
|
||||
self:SetShield( Cur + math.Clamp(MaxShield - Cur,-Rate,Rate) )
|
||||
end
|
||||
|
||||
function ENT:TakeShieldDamage( damage )
|
||||
local cur = self:GetShield()
|
||||
local sub = cur - damage
|
||||
local new = math.Clamp( sub , 0, self:GetMaxShield() )
|
||||
|
||||
self:SetShield( new )
|
||||
|
||||
if sub < 0 then
|
||||
return math.abs( sub )
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:OnTakeShieldDamage( dmginfo )
|
||||
if dmginfo:GetDamage() ~= 0 then return end
|
||||
|
||||
local dmgNormal = -dmginfo:GetDamageForce():GetNormalized()
|
||||
local dmgPos = dmginfo:GetDamagePosition()
|
||||
|
||||
dmginfo:SetDamagePosition( dmgPos + dmgNormal * 250 * self:GetShieldPercent() )
|
||||
|
||||
local effectdata = EffectData()
|
||||
effectdata:SetOrigin( dmginfo:GetDamagePosition() )
|
||||
effectdata:SetEntity( self )
|
||||
util.Effect( "lvs_shield_impact", effectdata )
|
||||
end
|
||||
350
lua/entities/lvs_base_doorhandler.lua
Normal file
350
lua/entities/lvs_base_doorhandler.lua
Normal 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
Reference in New Issue
Block a user