add sborka

This commit is contained in:
2026-03-31 10:27:04 +03:00
commit f5e5f56c84
2345 changed files with 382127 additions and 0 deletions

View File

@@ -0,0 +1,224 @@
if SERVER then AddCSLuaFile() end
TFA = TFA or {}
local version = 4.794
local version_string = "4.7.9.4"
local function testFunc()
end
local my_path = debug.getinfo(testFunc)
if my_path and type(my_path) == "table" and my_path.short_src then
my_path = my_path["short_src"]
else
my_path = "legacy"
end
local official_modules_sorted = {
"tfa_commands.lua",
"cl_tfa_commands.lua", -- we need to load clientside convars before anything else
"tfa_envcheck.lua",
"tfa_data.lua",
"tfa_ammo.lua",
"tfa_attachments.lua",
"tfa_ballistics.lua",
"tfa_bodygroups.lua",
"tfa_darkrp.lua",
"tfa_effects.lua",
"tfa_functions.lua",
"tfa_hooks.lua",
"tfa_keybinds.lua",
"tfa_keyvalues.lua",
"tfa_matproxies.lua",
"tfa_melee_autorun.lua",
"tfa_meta.lua",
"tfa_netcode.lua",
"tfa_small_entities.lua",
"tfa_npc_teamcolor.lua",
"tfa_npc_weaponmenu.lua",
"tfa_nzombies.lua",
"tfa_particles.lua",
"tfa_snd_timescale.lua",
"tfa_soundscripts.lua",
"tfa_tttpatch.lua",
"sv_tfa_settingsmenu.lua", -- TFA.BASE_LOAD_COMPLETE server
"cl_tfa_attachment_icon.lua",
"cl_tfa_attachment_panel.lua",
"cl_tfa_attachment_tip.lua",
"cl_tfa_devtools.lua",
"cl_tfa_fonts.lua",
"cl_tfa_hitmarker.lua",
"cl_tfa_inspection.lua",
"cl_tfa_materials.lua",
"cl_tfa_models.lua",
"cl_tfa_particles_lua.lua",
"cl_tfa_projtex.lua",
"cl_tfa_rendertarget.lua",
"cl_tfa_rtbgblur.lua",
"cl_tfa_settingsmenu.lua",
"cl_tfa_vgui.lua",
"cl_tfa_vm_blur.lua",
"cl_tfa_stencilsights.lua",
"cl_tfa_subcategories.lua", -- TFA.BASE_LOAD_COMPLETE client
}
local official_modules = {}
for _, modulename in ipairs(official_modules_sorted) do
official_modules[modulename] = true
end
TFA_BASE_VERSION = version
TFA_BASE_VERSION_STRING = version_string
TFA_BASE_VERSION_CHANGES = "In-game changelog has been removed.\nCheck GitHub or Steam Workshop change notes."
TFA_FILE_PATH = my_path
TFA.BASE_LOAD_COMPLETE = false
TFA.Enum = TFA.Enum or {}
local flist = file.Find("tfa/enums/*.lua","LUA")
for _, filename in pairs(flist) do
local typev = "SHARED"
if filename:StartWith("cl_") then
typev = "CLIENT"
elseif filename:StartWith("sv_") then
typev = "SERVER"
end
if SERVER and typev ~= "SERVER" then
AddCSLuaFile("tfa/enums/" .. filename)
end
if SERVER and typev ~= "CLIENT" or CLIENT and typev ~= "SERVER" then
include("tfa/enums/" .. filename)
end
end
hook.Run("TFABase_PreEarlyInit")
for _, filename in ipairs(official_modules_sorted) do
if filename:StartWith("cl_") then
if SERVER then
AddCSLuaFile("tfa/modules/" .. filename)
else
include("tfa/modules/" .. filename)
end
elseif filename:StartWith("sv_") then
if SERVER then
include("tfa/modules/" .. filename)
end
else
if SERVER then
AddCSLuaFile("tfa/modules/" .. filename)
end
include("tfa/modules/" .. filename)
end
end
hook.Run("TFABase_EarlyInit")
hook.Run("TFABase_PreInit")
flist = file.Find("tfa/modules/*.lua", "LUA")
local toload = {}
local toload2 = {}
for _, filename in pairs(flist) do
if not official_modules[filename] then
local typev = "SHARED"
if filename:StartWith("cl_") then
typev = "CLIENT"
elseif filename:StartWith("sv_") then
typev = "SERVER"
end
if SERVER and typev ~= "SERVER" then
AddCSLuaFile("tfa/modules/" .. filename)
end
if SERVER and typev == "SERVER" or CLIENT and typev == "CLIENT" then
table.insert(toload2, filename)
elseif typev == "SHARED" then
table.insert(toload, filename)
end
end
end
local yell = #toload ~= 0 or #toload2 ~= 0
table.sort(toload)
table.sort(toload2)
for _, filename in ipairs(toload) do
include("tfa/modules/" .. filename)
print("[TFA Base] [!] Loaded unofficial module " .. string.sub(filename, 1, -5) .. ".")
end
for _, filename in ipairs(toload2) do
include("tfa/modules/" .. filename)
print("[TFA Base] [!] Loaded unofficial module " .. string.sub(filename, 1, -5) .. ".")
end
hook.Run("TFABase_Init")
hook.Run("TFABase_PreFullInit")
flist = file.Find("tfa/external/*.lua", "LUA")
toload = {}
toload2 = {}
for _, filename in pairs(flist) do
local typev = "SHARED"
if filename:StartWith("cl_") then
typev = "CLIENT"
elseif filename:StartWith("sv_") then
typev = "SERVER"
end
if SERVER and typev ~= "SERVER" then
AddCSLuaFile("tfa/external/" .. filename)
end
if SERVER and typev == "SERVER" or CLIENT and typev == "CLIENT" then
table.insert(toload2, filename)
elseif typev == "SHARED" then
table.insert(toload, filename)
end
end
table.sort(toload)
table.sort(toload2)
for _, filename in ipairs(toload) do
include("tfa/external/" .. filename)
end
for _, filename in ipairs(toload2) do
include("tfa/external/" .. filename)
end
if yell then
print("[TFA Base] [!] Some of files not belonging to TFA Base were loaded from tfa/modules/ directory")
print("[TFA Base] This behavior is kept for backward compatiblity and using this is highly discouraged!")
print("[TFA Base] Files loaded this way have no pre-defined sorting applied and result of execution of those files is undefined.")
print("[TFA Base] If you are author of these files, please consider moving your modules to tfa/external/ as soon as possible.")
end
hook.Run("TFABase_FullInit")
if not VLL2_FILEDEF then
TFAUpdateAttachments(false)
end
hook.Run("TFABase_LateInit")

View File

@@ -0,0 +1,25 @@
function EFFECT:Init(data)
local posoffset = data:GetOrigin()
local emitter = ParticleEmitter(posoffset)
if TFA.GetGasEnabled() then
local p = emitter:Add("sprites/heatwave", posoffset)
p:SetVelocity(50 * data:GetNormal() + 0.5 * VectorRand())
p:SetAirResistance(200)
p:SetStartSize(math.random(12.5, 17.5))
p:SetEndSize(2)
p:SetDieTime(math.Rand(0.15, 0.225))
p:SetRoll(math.Rand(-180, 180))
p:SetRollDelta(math.Rand(-0.75, 0.75))
end
emitter:Finish()
end
function EFFECT:Think()
return false
end
function EFFECT:Render()
return false
end

View File

@@ -0,0 +1,41 @@
function EFFECT:Init(data)
local ply = data:GetEntity()
local ent
if IsValid(ply) and ply:IsPlayer() then
ent = ply:GetActiveWeapon()
end
local sfac = (IsValid(ent) and ent.Primary and ent.Primary.Damage) and math.sqrt(ent.Primary.Damage / 30) or 1
local sfac_sqrt = math.sqrt(sfac)
local posoffset = data:GetOrigin()
local forward = data:GetNormal()
local emitter = ParticleEmitter(posoffset)
for i = 0, math.Round(8 * sfac) do
local p = emitter:Add("particle/particle_smokegrenade", posoffset)
p:SetVelocity(90 * math.sqrt(i) * forward)
p:SetAirResistance(400)
p:SetStartAlpha(math.Rand(255, 255))
p:SetEndAlpha(0)
p:SetDieTime(math.Rand(0.75, 1) * (1 + math.sqrt(i) / 3))
local iclamped = math.Clamp(i, 1, 8)
local iclamped_sqrt = math.sqrt(iclamped / 8) * 8
p:SetStartSize(math.Rand(1, 1) * sfac_sqrt * iclamped_sqrt)
p:SetEndSize(math.Rand(1.5, 1.75) * sfac_sqrt * iclamped)
p:SetRoll(math.Rand(-25, 25))
p:SetRollDelta(math.Rand(-0.05, 0.05))
p:SetColor(255, 255, 255)
p:SetLighting(true)
end
emitter:Finish()
end
function EFFECT:Think()
return false
end
function EFFECT:Render()
return false
end

View File

@@ -0,0 +1,56 @@
local gravity_cv = GetConVar("sv_gravity")
EFFECT.VelocityRandom = 0.25
EFFECT.VelocityMin = 95
EFFECT.VelocityMax = 125
EFFECT.ParticleCountMin = 4
EFFECT.ParticleCountMax = 7
EFFECT.ParticleLife = 1.3
function EFFECT:Init(data)
self.StartPos = data:GetOrigin()
self.Dir = data:GetNormal()
self.LifeTime = 0.1
self.DieTime = CurTime() + self.LifeTime
self.PartMult = 0.2
self.Grav = Vector(0, 0, -gravity_cv:GetFloat())
self.SparkLife = 1
local emitter = ParticleEmitter(self.StartPos)
local partcount = math.random(self.ParticleCountMin, self.ParticleCountMax)
--Sparks
for _ = 1, partcount do
local part = emitter:Add("effects/yellowflare", self.StartPos)
part:SetVelocity(Lerp(self.VelocityRandom, self.Dir, VectorRand()) * math.Rand(self.VelocityMin, self.VelocityMax))
part:SetDieTime(math.Rand(0.25, 1) * self.SparkLife)
part:SetStartAlpha(255)
part:SetStartSize(math.Rand(2, 4))
part:SetEndSize(0)
part:SetRoll(0)
part:SetGravity(self.Grav)
part:SetCollide(true)
part:SetBounce(0.55)
part:SetAirResistance(0.5)
part:SetStartLength(0.2)
part:SetEndLength(0)
part:SetVelocityScale(true)
part:SetCollide(true)
end
--Impact
local part = emitter:Add("effects/yellowflare", self.StartPos)
part:SetStartAlpha(255)
part:SetStartSize(15 * self.PartMult)
part:SetDieTime(self.LifeTime * 1)
part:SetEndSize(0)
part:SetEndAlpha(0)
part:SetRoll(math.Rand(0, 360))
emitter:Finish()
end
function EFFECT:Think()
return false
end
function EFFECT:Render()
return false
end

View File

@@ -0,0 +1,124 @@
local vector_origin = Vector()
local smokecol = Color(225, 225, 225, 200)
local smokemat = Material("trails/smoke")
smokemat:SetInt("$nocull", 1)
function EFFECT:AddPart()
local pos, rawdat, norm
pos = self.startpos
norm = self.startnormal
if self.targent and self.targatt then
--pos = self:GetTracerShootPos(self.startpos, self.targent, self.targatt)
rawdat = self.targent:GetAttachment(self.targatt)
if rawdat then
pos = rawdat.Pos
norm = rawdat.Ang:Forward()
end
end
local p = {}
p.position = pos
p.normal = norm
p.velocity = p.normal * 5
p.startlife = CurTime()
p.lifetime = self.lifetime
p.radius = self.radius
if self.vparticles then
table.insert(self.vparticles, #self.vparticles + 1, p)
end
end
function EFFECT:ProcessFakeParticles()
self.stepcount = self.stepcount + 1
if self.vparticles then
if CurTime() < self.emittime and self.stepcount % self.partinterval == 0 then
self:AddPart()
end
for k, v in ipairs(self.vparticles) do
v.position = v.position + v.velocity * FrameTime()
v.velocity = v.velocity + self.grav * FrameTime()
if CurTime() > v.startlife + v.lifetime then
--print("Curtime:"..CurTime())
--print("Lifetime:"..v.lifetime)
--print("CTime:"..v.startlife)
table.remove(self.vparticles, k)
end
end
if #self.vparticles <= 0 then
return false
else
return true
end
else
return true
end
end
local cv_gr = GetConVar("sv_gravity")
function EFFECT:Init(ef)
self.lifetime = 1
self.stepcount = 0
self.partinterval = 3
self.emittime = CurTime() + 3
self.targent = ef:GetEntity()
self.targatt = ef:GetAttachment()
self.startpos = ef:GetOrigin()
self.startnormal = ef:GetNormal()
self.radius = ef:GetRadius()
self.grav = Vector(0, 0, cv_gr:GetFloat() * 0.2)
self.randfac = 1
if not self.startpos then
self.startpos = vector_origin
if LocalPlayer():IsValid() then
self.startpos = LocalPlayer():GetShootPos()
end
end
if not self.startnormal then
self.startnormal = vector_origin
end
if not self.radius or self.radius == 0 then
self.radius = 1
end
self.vparticles = {}
self:AddPart()
end
function EFFECT:Think()
if self.vparticles and #self.vparticles <= 0 then return false end
return true
end
function EFFECT:DrawBeam()
render.StartBeam(#self.vparticles)
for k, v in ipairs(self.vparticles) do
local alphac = ColorAlpha(smokecol, (1 - (CurTime() - v.startlife) / v.lifetime) * 64)
render.AddBeam(v.position, v.radius * (1 - k / #self.vparticles), k / #self.vparticles, alphac)
end
render.EndBeam()
end
function EFFECT:Render()
self:ProcessFakeParticles()
if self.vparticles and #self.vparticles >= 2 then
render.SetMaterial(smokemat)
self:DrawBeam()
end
end

View File

@@ -0,0 +1,10 @@
include("tfa/muzzleflash_base.lua")
EFFECT.Life = 0.1
EFFECT.XFlashSize = 1
EFFECT.FlashSize = 1
EFFECT.SmokeSize = 0
EFFECT.SparkSize = 1.5
EFFECT.HeatSize = 1.5
EFFECT.Color = Color(162,192,255)
EFFECT.ColorSprites = true

View File

@@ -0,0 +1,10 @@
include("tfa/muzzleflash_base.lua")
EFFECT.Life = 0.15
EFFECT.XFlashSize = 1
EFFECT.FlashSize = 1
EFFECT.SmokeSize = 0
EFFECT.SparkSize = 1.25
EFFECT.HeatSize = 1
EFFECT.Color = Color(128,192,255)
EFFECT.ColorSprites = true

View File

@@ -0,0 +1,102 @@
local blankvec = Vector(0, 0, 0)
local vector_origin = Vector()
function EFFECT:Init(data)
self.Position = blankvec
self.WeaponEnt = data:GetEntity()
self.WeaponEntOG = self.WeaponEnt
self.Attachment = data:GetAttachment()
self.Dir = data:GetNormal()
local owent
if IsValid(self.WeaponEnt) then
owent = self.WeaponEnt:GetOwner()
end
if not IsValid(owent) then
owent = self.WeaponEnt:GetParent()
end
if IsValid(owent) and owent:IsPlayer() then
if owent ~= LocalPlayer() or owent:ShouldDrawLocalPlayer() then
self.WeaponEnt = owent:GetActiveWeapon()
if not IsValid(self.WeaponEnt) then return end
else
local theirweapon = self.WeaponEnt
self.WeaponEnt = self.WeaponEnt.OwnerViewModel
if IsValid(theirweapon) and theirweapon.ViewModelFlip or theirweapon.ViewModelFlipped then
self.Flipped = true
end
if not IsValid(self.WeaponEnt) then return end
end
end
if IsValid(self.WeaponEntOG) and self.WeaponEntOG.MuzzleAttachment then
self.Attachment = self.WeaponEnt:LookupAttachment(self.WeaponEntOG.MuzzleAttachment)
if not self.Attachment or self.Attachment <= 0 then
self.Attachment = 1
end
if self.WeaponEntOG:GetStatL("IsAkimbo") then
self.Attachment = 2 - self.WeaponEntOG:GetAnimCycle()
end
end
local angpos = self.WeaponEnt:GetAttachment(self.Attachment)
if not angpos or not angpos.Pos then
angpos = {
Pos = vector_origin,
Ang = angle_zero
}
end
if self.Flipped then
local tmpang = (self.Dir or angpos.Ang:Forward()):Angle()
local localang = self.WeaponEnt:WorldToLocalAngles(tmpang)
localang.y = localang.y + 180
localang = self.WeaponEnt:LocalToWorldAngles(localang)
--localang:RotateAroundAxis(localang:Up(),180)
--tmpang:RotateAroundAxis(tmpang:Up(),180)
self.Dir = localang:Forward()
end
-- Keep the start and end Pos - we're going to interpolate between them
self.Position = self:GetTracerShootPos(angpos.Pos, self.WeaponEnt, self.Attachment)
self.Norm = self.Dir
self.vOffset = self.Position
local dir = self.Norm
local dlight
if IsValid(self.WeaponEnt) then
dlight = DynamicLight(self.WeaponEnt:EntIndex())
else
dlight = DynamicLight(0)
end
local fadeouttime = 0.2
if (dlight) then
dlight.Pos = self.Position + dir * 1 - dir:Angle():Right() * 5
dlight.r = 25
dlight.g = 200
dlight.b = 255
dlight.Brightness = 4.0
dlight.size = 96
dlight.decay = 1000
dlight.DieTime = CurTime() + fadeouttime
end
ParticleEffectAttach("tfa_muzzle_gauss", PATTACH_POINT_FOLLOW, self.WeaponEnt, data:GetAttachment())
end
function EFFECT:Think()
return false
end
function EFFECT:Render()
end

View File

@@ -0,0 +1,10 @@
include("tfa/muzzleflash_base.lua")
EFFECT.Life = 0.075
EFFECT.XFlashSize = 0.5
EFFECT.FlashSize = 0.8
EFFECT.SmokeSize = 1
EFFECT.SparkSize = 1
EFFECT.HeatSize = 1
EFFECT.Color = Color(255, 225, 128)
EFFECT.ColorSprites = false

View File

@@ -0,0 +1,10 @@
include("tfa/muzzleflash_base.lua")
EFFECT.Life = 0.125
EFFECT.XFlashSize = 0
EFFECT.FlashSize = 1
EFFECT.SmokeSize = 2
EFFECT.SparkSize = 2
EFFECT.HeatSize = 2
EFFECT.Color = Color(255, 128, 64)
EFFECT.ColorSprites = false

View File

@@ -0,0 +1,10 @@
include("tfa/muzzleflash_base.lua")
EFFECT.Life = 0.07
EFFECT.XFlashSize = 0.5
EFFECT.FlashSize = 0.8
EFFECT.SmokeSize = 1
EFFECT.SparkSize = 1
EFFECT.HeatSize = 1
EFFECT.Color = Color(255, 225, 128)
EFFECT.ColorSprites = false

View File

@@ -0,0 +1,10 @@
include("tfa/muzzleflash_base.lua")
EFFECT.Life = 0.1
EFFECT.XFlashSize = 0
EFFECT.FlashSize = 1
EFFECT.SmokeSize = 2
EFFECT.SparkSize = 1
EFFECT.HeatSize = 1.25
EFFECT.Color = Color(255, 225, 128)
EFFECT.ColorSprites = false

View File

@@ -0,0 +1,10 @@
include("tfa/muzzleflash_base.lua")
EFFECT.Life = 0.1
EFFECT.XFlashSize = 1
EFFECT.FlashSize = 1
EFFECT.SmokeSize = 1
EFFECT.SparkSize = 1
EFFECT.HeatSize = 1
EFFECT.Color = Color(255, 192, 64)
EFFECT.ColorSprites = false

View File

@@ -0,0 +1,10 @@
include("tfa/muzzleflash_base.lua")
EFFECT.Life = 0.125
EFFECT.XFlashSize = 0
EFFECT.FlashSize = 1.3
EFFECT.SmokeSize = 2
EFFECT.SparkSize = 1.5
EFFECT.HeatSize = 2
EFFECT.Color = Color(255, 225, 128)
EFFECT.ColorSprites = false

View File

@@ -0,0 +1,12 @@
include("tfa/muzzleflash_base.lua")
EFFECT.Life = 0.1
EFFECT.XFlashSize = 0
EFFECT.FlashSize = 0.1
EFFECT.SmokeSize = 2
EFFECT.SparkSize = 1
EFFECT.HeatSize = 1
EFFECT.Color = Color(255, 225, 128)
EFFECT.ColorSprites = false
EFFECT.UseDynamicLight = false

View File

@@ -0,0 +1,10 @@
include("tfa/muzzleflash_base.lua")
EFFECT.Life = 0.075
EFFECT.XFlashSize = 1
EFFECT.FlashSize = 1
EFFECT.SmokeSize = 1
EFFECT.SparkSize = 1
EFFECT.HeatSize = 1
EFFECT.Color = Color(255, 225, 128)
EFFECT.ColorSprites = false

View File

@@ -0,0 +1,10 @@
include("tfa/muzzleflash_base.lua")
EFFECT.Life = 0.125
EFFECT.XFlashSize = 1.5
EFFECT.FlashSize = 1.2
EFFECT.SmokeSize = 2
EFFECT.SparkSize = 1.3
EFFECT.HeatSize = 2
EFFECT.Color = Color(255, 225, 128)
EFFECT.ColorSprites = false

View File

@@ -0,0 +1,10 @@
include("tfa/muzzleflash_base.lua")
EFFECT.Life = 0.125
EFFECT.XFlashSize = 2
EFFECT.FlashSize = 2
EFFECT.SmokeSize = 0
EFFECT.SparkSize = 1.45
EFFECT.HeatSize = 2
EFFECT.Color = Color(128,192,255)
EFFECT.ColorSprites = true

View File

@@ -0,0 +1,74 @@
local ang
local limit_particle_cv = GetConVar("cl_tfa_fx_muzzlesmoke_limited")
local SMOKEDELAY = 1.5
function EFFECT:Init(data)
self.WeaponEnt = data:GetEntity()
if not IsValid(self.WeaponEnt) then return end
self.WeaponEntOG = self.WeaponEnt
if limit_particle_cv:GetBool() and self.WeaponEnt:GetOwner() ~= LocalPlayer() then return end
self.Attachment = data:GetAttachment()
local smokepart = "smoke_trail_tfa"
local delay = self.WeaponEnt.GetStatL and self.WeaponEnt:GetStatL("SmokeDelay") or self.WeaponEnt.SmokeDelay
if self.WeaponEnt.SmokeParticle then
smokepart = self.WeaponEnt.SmokeParticle
elseif self.WeaponEnt.SmokeParticles then
smokepart = self.WeaponEnt.SmokeParticles[self.WeaponEnt.DefaultHoldType or self.WeaponEnt.HoldType] or smokepart
end
self.Position = self:GetTracerShootPos(data:GetOrigin(), self.WeaponEnt, self.Attachment)
if IsValid(self.WeaponEnt:GetOwner()) then
if self.WeaponEnt:GetOwner() == LocalPlayer() then
if not self.WeaponEnt:IsFirstPerson() then
ang = self.WeaponEnt:GetOwner():EyeAngles()
ang:Normalize()
--ang.p = math.max(math.min(ang.p,55),-55)
self.Forward = ang:Forward()
else
self.WeaponEnt = self.WeaponEnt.OwnerViewModel
end
--ang.p = math.max(math.min(ang.p,55),-55)
else
ang = self.WeaponEnt:GetOwner():EyeAngles()
ang:Normalize()
self.Forward = ang:Forward()
end
end
if TFA.GetMZSmokeEnabled == nil or TFA.GetMZSmokeEnabled() then
local e = self.WeaponEnt
local w = self.WeaponEntOG
local a = self.Attachment
local tn = "tfasmokedelay_" .. w:EntIndex() .. "_" .. a
local sp = smokepart
if timer.Exists(tn) then
timer.Remove(tn)
end
e.SmokePCF = e.SmokePCF or {}
local _a = w:GetStatL("IsAkimbo") and a or 1
if IsValid(e.SmokePCF[_a]) then
e.SmokePCF[_a]:StopEmission()
end
timer.Create(tn, delay or SMOKEDELAY, 1, function()
if not IsValid(e) then return end
e.SmokePCF[_a] = CreateParticleSystem(e, sp, PATTACH_POINT_FOLLOW, a)
if IsValid(e.SmokePCF[_a]) then
e.SmokePCF[_a]:StartEmission()
end
end)
end
end
function EFFECT:Think()
return false
end
function EFFECT:Render()
end

View File

@@ -0,0 +1,132 @@
local PenetColor = Color(255, 255, 255, 255)
local PenetMat = Material("trails/smoke")
local PenetMat2 = Material("effects/yellowflare")
local cv_gv = GetConVar("sv_gravity")
local cv_sl = GetConVar("cl_tfa_fx_impact_ricochet_sparklife")
--local cv_sc = GetConVar("cl_tfa_fx_impact_ricochet_sparks")
local DFX = {
["AR2Tracer"] = true,
["Tracer"] = true,
["GunshipTracer"] = true,
["GaussTracer"] = true,
["AirboatGunTracer"] = true,
["AirboatGunHeavyTracer"] = true
}
function EFFECT:Init(data)
self.StartPos = data:GetOrigin()
self.Dir = data:GetNormal()
self.Dir:Normalize()
self.Len = 32
self.EndPos = self.StartPos + self.Dir * self.Len
self.LifeTime = 0.75
self.DieTime = CurTime() + self.LifeTime
self.Thickness = 1
self.Grav = Vector(0, 0, -cv_gv:GetFloat())
self.PartMult = data:GetRadius()
self.SparkLife = cv_sl:GetFloat()
self.WeaponEnt = data:GetEntity()
if not IsValid(self.WeaponEnt) then return end
if self.WeaponEnt.TracerPCF then
local traceres = util.QuickTrace(self.StartPos, self.Dir * 9999999, Entity(math.Round(data:GetScale())))
self.EndPos = traceres.HitPos or self.StartPos
local efn = self.WeaponEnt.TracerName
local spos = self.StartPos
local cnt = math.min(math.Round(data:GetMagnitude()), 6000)
timer.Simple(cnt / 1000000, function()
TFA.ParticleTracer(efn, spos, traceres.HitPos or spos, false)
end)
return
end
local tn = self.WeaponEnt.BulletTracerName
if tn and tn ~= "" and not DFX[tn] then
local fx = EffectData()
fx:SetStart(self.StartPos)
local traceres = util.QuickTrace(self.StartPos, self.Dir * 9999999, Entity(math.Round(data:GetScale())))
self.EndPos = traceres.HitPos or self.StartPos
fx:SetOrigin(self.EndPos)
fx:SetEntity(self.WeaponEnt)
fx:SetMagnitude(1)
util.Effect(tn, fx)
SafeRemoveEntityDelayed(self, 0)
--Sparks
--Impact
return
else
local emitter = ParticleEmitter(self.StartPos)
--[[
for i = 1, cv_sc:GetFloat() * self.PartMult * 0.1 do
local part = emitter:Add("effects/yellowflare", self.StartPos)
part:SetVelocity((self.Dir + VectorRand() * 0.5) * math.Rand(75, 185))
part:SetDieTime(math.Rand(0.25, 1) * self.SparkLife)
part:SetStartAlpha(255)
part:SetStartSize(math.Rand(2, 4))
part:SetEndSize(0)
part:SetRoll(0)
part:SetGravity(self.Grav)
part:SetCollide(true)
part:SetBounce(0.55)
part:SetAirResistance(0.5)
part:SetStartLength(0.2)
part:SetEndLength(0)
part:SetVelocityScale(true)
part:SetCollide(true)
end
]]
--
local part = emitter:Add("effects/select_ring", self.StartPos)
part:SetStartAlpha(225)
part:SetStartSize(1)
part:SetDieTime(self.LifeTime / 5)
part:SetEndSize(0)
part:SetEndAlpha(0)
part:SetRoll(math.Rand(0, 360))
part:SetColor(200, 200, 200)
part = emitter:Add("effects/select_ring", self.StartPos)
part:SetStartAlpha(255)
part:SetStartSize(1.5 * self.PartMult)
part:SetDieTime(self.LifeTime / 6)
part:SetEndSize(0)
part:SetEndAlpha(0)
part:SetRoll(math.Rand(0, 360))
part:SetColor(200, 200, 200)
emitter:Finish()
end
end
function EFFECT:Think()
if self.DieTime and (CurTime() > self.DieTime) then return false end
return true
end
function EFFECT:Render()
if self.DieTime then
local fDelta = (self.DieTime - CurTime()) / self.LifeTime
fDelta = math.Clamp(fDelta, 0, 1)
render.SetMaterial(PenetMat)
local color = ColorAlpha(PenetColor, 32 * fDelta)
local precision = 16
local i = 1
while i <= precision do
render.DrawBeam(self.StartPos + self.Dir * self.Len * ((i - 1) / precision), self.StartPos + self.Dir * self.Len * (i / precision), self.Thickness * fDelta * (1 - i / precision), 0.5, 0.5, color)
i = i + 1
end
render.SetMaterial(PenetMat2)
i = 1
while i <= precision do
render.DrawBeam(self.StartPos + self.Dir * self.Len * ((i - 1) / precision), self.StartPos + self.Dir * self.Len * (i / precision), self.Thickness / 3 * 2 * fDelta * (1 - i / precision), 0.5, 0.5, color)
i = i + 1
end
end
end

View File

@@ -0,0 +1,90 @@
local RicochetColor = Color(255, 255, 255, 255)
local RicochetIDOffset = 33
local RicochetMat = Material("effects/yellowflare")
local cv_gv = GetConVar("sv_gravity")
local cv_sl = GetConVar("cl_tfa_fx_impact_ricochet_sparklife")
local cv_sc = GetConVar("cl_tfa_fx_impact_ricochet_sparks")
function EFFECT:Init(data)
self.StartPos = data:GetOrigin()
self.Dir = data:GetNormal()
self.Dir:Normalize()
self.Len = 128
self.EndPos = self.StartPos + self.Dir * self.Len
self.LifeTime = 0.1
self.DieTime = CurTime() + self.LifeTime
self.Grav = Vector(0, 0, -cv_gv:GetFloat())
self.PartMult = data:GetMagnitude()
self.SparkLife = cv_sl:GetFloat()
local emitter = ParticleEmitter(self.StartPos)
--Sparks
for _ = 1, cv_sc:GetInt() * self.PartMult do
local part = emitter:Add("effects/yellowflare", self.StartPos)
part:SetVelocity((self.Dir + VectorRand() * 0.5) * math.Rand(75, 185))
part:SetDieTime(math.Rand(0.25, 1) * self.SparkLife)
part:SetStartAlpha(255)
part:SetStartSize(math.Rand(2, 4))
part:SetEndSize(0)
part:SetRoll(0)
part:SetGravity(self.Grav)
part:SetCollide(true)
part:SetBounce(0.55)
part:SetAirResistance(0.5)
part:SetStartLength(0.2)
part:SetEndLength(0)
part:SetVelocityScale(true)
part:SetCollide(true)
end
--Impact
local part = emitter:Add("effects/yellowflare", self.StartPos)
part:SetStartAlpha(225)
part:SetStartSize(64)
part:SetDieTime(self.LifeTime)
part:SetEndSize(0)
part:SetEndAlpha(0)
part:SetRoll(math.Rand(0, 360))
part = emitter:Add("effects/yellowflare", self.StartPos)
part:SetStartAlpha(255)
part:SetStartSize(30 * self.PartMult)
part:SetDieTime(self.LifeTime * 1.5)
part:SetEndSize(0)
part:SetEndAlpha(0)
part:SetRoll(math.Rand(0, 360))
emitter:Finish()
local dlight = DynamicLight(LocalPlayer():EntIndex() + RicochetIDOffset)
if (dlight) then
dlight.Pos = self.StartPos
dlight.r = 255
dlight.g = 225
dlight.b = 185
dlight.Brightness = 2.75 * self.PartMult
dlight.size = 48
--dlight.DieTime = CurTime() + self.DieTime*0.7
dlight.Decay = 1000 / math.max(0.01, math.min(self.SparkLife * 0.66, 1))
end
end
function EFFECT:Think()
if self.DieTime and (CurTime() > self.DieTime) then return false end
return true
end
function EFFECT:Render()
if self.DieTime then
local fDelta = (self.DieTime - CurTime()) / self.LifeTime
fDelta = math.Clamp(fDelta, 0, 1)
render.SetMaterial(RicochetMat)
local color = ColorAlpha(RicochetColor, 255 * fDelta)
local precision = 16
local i = 1
while i <= precision do
render.DrawBeam(self.StartPos + self.Dir * self.Len * ((i - 1) / precision), self.StartPos + self.Dir * self.Len * (i / precision), 8 * fDelta * (1 - i / precision), 0.5, 0.5, color)
i = i + 1
end
end
end

View File

@@ -0,0 +1,230 @@
EFFECT.Velocity = {120, 160}
EFFECT.VelocityRand = {-15, 40}
EFFECT.VelocityAngle = Vector(1,1,10)
EFFECT.VelocityRandAngle = Vector(10,10,5)
local modelReplaceLookup = {
["models/hdweapons/rifleshell.mdl"] = "models/tfa/rifleshell.mdl",
["models/hdweapons/rifleshell_hd.mdl"] = "models/tfa/rifleshell.mdl",
["models/weapons/rifleshell_hd.mdl"] = "models/tfa/rifleshell.mdl",
["models/hdweapons/shell.mdl"] = "models/tfa/pistolshell.mdl",
["models/hdweapons/shell_hd.mdl"] = "models/tfa/pistolshell.mdl",
["models/weapons/shell_hd.mdl"] = "models/tfa/pistolshell.mdl",
["models/hdweapons/shotgun_shell.mdl"] = "models/tfa/shotgunshell.mdl",
["models/hdweapons/shotgun_shell_hd.mdl"] = "models/tfa/shotgunshell.mdl",
["models/weapons/shotgun_shell_hd.mdl"] = "models/tfa/shotgunshell.mdl",
}
EFFECT.ShellPresets = {
["sniper"] = {"models/tfa/rifleshell.mdl", math.pow(0.487 / 1.236636, 1 / 3), 90}, --1.236636 is shell diameter, then divide base diameter into that for 7.62x54mm
["rifle"] = {"models/tfa/rifleshell.mdl", math.pow(0.4709 / 1.236636, 1 / 3), 90}, --1.236636 is shell diameter, then divide base diameter into that for standard nato rifle
["pistol"] = {"models/tfa/pistolshell.mdl", math.pow(0.391 / 0.955581, 1 / 3), 90}, --0.955581 is shell diameter, then divide base diameter into that for 9mm luger
["smg"] = {"models/tfa/pistolshell.mdl", math.pow(.476 / 0.955581, 1 / 3), 90}, --.45 acp
["shotgun"] = {"models/tfa/shotgunshell.mdl", 1, 90}
}
EFFECT.SoundFiles = {Sound(")player/pl_shell1.wav"), Sound(")player/pl_shell2.wav"), Sound(")player/pl_shell3.wav")}
EFFECT.SoundFilesSG = {Sound(")weapons/fx/tink/shotgun_shell1.wav"), Sound(")weapons/fx/tink/shotgun_shell2.wav"), Sound(")weapons/fx/tink/shotgun_shell3.wav")}
EFFECT.SoundLevel = {45, 55}
EFFECT.SoundPitch = {80, 120}
EFFECT.SoundVolume = {0.85, 0.95}
EFFECT.LifeTime = 15
EFFECT.FadeTime = 0.5
EFFECT.SmokeTime = {3, 3}
EFFECT.SmokeParticle = "tfa_ins2_weapon_shell_smoke"
local cv_eject
local cv_life
local upVec = Vector(0,0,1)
function EFFECT:ComputeSmokeLighting()
if not self.PCFSmoke then return end
local licht = render.ComputeLighting(self:GetPos() + upVec * 2, upVec)
local lichtFloat = math.Clamp((licht.r + licht.g + licht.b) / 3, 0, TFA.Particles.SmokeLightingClamp) / TFA.Particles.SmokeLightingClamp
local lichtFinal = LerpVector(lichtFloat, TFA.Particles.SmokeLightingMin, TFA.Particles.SmokeLightingMax)
self.PCFSmoke:SetControlPoint(1, lichtFinal)
end
function EFFECT:Init(data)
self.IsTFAShell = true
if not cv_eject then
cv_eject = GetConVar("cl_tfa_fx_ejectionsmoke")
end
if not cv_life then
cv_life = GetConVar("cl_tfa_fx_ejectionlife")
end
if cv_life then
self.LifeTime = cv_life:GetFloat()
end
self.StartTime = CurTime()
self.Emitter = ParticleEmitter(self:GetPos())
self.SmokeDelta = 0
if cv_eject:GetBool() then
self.SmokeDeath = self.StartTime + math.Rand(self.SmokeTime[1], self.SmokeTime[2])
else
self.SmokeDeath = -1
end
self.WeaponEnt = data:GetEntity()
if not IsValid(self.WeaponEnt) then return end
self.WeaponEntOG = self.WeaponEnt
if self.WeaponEntOG.LuaShellEffect and self.WeaponEntOG.LuaShellEffect == "" then return end
self.Attachment = data:GetAttachment()
self.Dir = data:GetNormal()
self.DirAng = data:GetNormal():Angle()
self.OriginalOrigin = data:GetOrigin()
local owent = self.WeaponEnt:GetOwner()
if self.LifeTime <= 0 or not IsValid(owent) then
self.StartTime = -1000
self.SmokeDeath = -1000
return
end
if owent:IsPlayer() and owent == GetViewEntity() and not owent:ShouldDrawLocalPlayer() then
self.WeaponEnt = self.WeaponEnt.OwnerViewModel
if not IsValid(self.WeaponEnt) then return end
end
local model, scale, yaw = self:FindModel(self.WeaponEntOG)
model = self.WeaponEntOG:GetStatL("ShellModel") or self.WeaponEntOG:GetStatL("LuaShellModel") or model
model = modelReplaceLookup[model] or model
scale = self.WeaponEntOG:GetStatL("ShellScale") or self.WeaponEntOG:GetStatL("LuaShellScale") or scale
yaw = self.WeaponEntOG:GetStatL("ShellYaw") or self.WeaponEntOG:GetStatL("LuaShellYaw") or yaw
if model:lower():find("shotgun") then
self.Shotgun = true
end
self:SetModel(model)
self:SetModelScale(scale, 0)
self:SetPos(data:GetOrigin())
local mdlang = self.DirAng * 1
mdlang:RotateAroundAxis(mdlang:Up(), yaw)
local owang = IsValid(owent) and owent:EyeAngles() or mdlang
self:SetAngles(owang)
self:SetRenderMode(RENDERMODE_TRANSALPHA)
self:SetCollisionGroup(COLLISION_GROUP_DEBRIS)
self:SetCollisionBounds(self:OBBMins(), self:OBBMaxs())
self:PhysicsInitBox(self:OBBMins(), self:OBBMaxs())
local velocity = self.Dir * math.Rand(self.Velocity[1], self.Velocity[2]) + owang:Forward() * math.Rand(self.VelocityRand[1], self.VelocityRand[2])
if IsValid(owent) then
velocity = velocity + owent:GetVelocity()
end
local physObj = self:GetPhysicsObject()
if physObj:IsValid() then
physObj:SetDamping(0.1, 1)
physObj:SetMass(5)
physObj:SetMaterial("gmod_silent")
physObj:SetVelocity(velocity)
local localVel = velocity:Length() * self.WeaponEnt:WorldToLocalAngles(velocity:Angle()):Forward()
physObj:AddAngleVelocity(localVel.y * self.VelocityAngle)
physObj:AddAngleVelocity(VectorRand() * velocity:Length() * self.VelocityRandAngle * 0.5)
end
local ss = self.WeaponEntOG:GetStatL("ShellSound") or self.WeaponEntOG:GetStatL("LuaShellSound")
if ss then
self.ImpactSound = ss
else
self.ImpactSound = self.Shotgun and self.SoundFilesSG[math.random(1, #self.SoundFiles)] or self.SoundFiles[math.random(1, #self.SoundFiles)]
end
self.setup = true
end
function EFFECT:FindModel(wep)
if not IsValid(wep) then return unpack(self.ShellPresets["rifle"]) end
local ammotype = (wep.Primary.Ammo or wep:GetPrimaryAmmoType()):lower()
local guntype = (wep.Type or wep:GetHoldType()):lower()
if guntype:find("sniper") or ammotype:find("sniper") or guntype:find("dmr") then
return unpack(self.ShellPresets["sniper"])
elseif guntype:find("rifle") or ammotype:find("rifle") then
return unpack(self.ShellPresets["rifle"])
elseif ammotype:find("pist") or guntype:find("pist") then
return unpack(self.ShellPresets["pistol"])
elseif ammotype:find("smg") or guntype:find("smg") then
return unpack(self.ShellPresets["smg"])
elseif ammotype:find("buckshot") or ammotype:find("shotgun") or guntype:find("shot") then
return unpack(self.ShellPresets["shotgun"])
end
return unpack(self.ShellPresets["rifle"])
end
function EFFECT:BounceSound()
sound.Play(self.ImpactSound, self:GetPos(), math.Rand(self.SoundLevel[1], self.SoundLevel[2]), math.Rand(self.SoundPitch[1], self.SoundPitch[2]), math.Rand(self.SoundVolume[1], self.SoundVolume[2]))
end
function EFFECT:PhysicsCollide(data)
if self:WaterLevel() > 0 then return end
if TFA.GetEJSmokeEnabled() and not self.PCFSmoke and CurTime() < self.SmokeDeath then
self.PCFSmoke = CreateParticleSystem(self, self.SmokeParticle, self:GetAttachment(1) ~= nil and PATTACH_POINT_FOLLOW or PATTACH_ABSORIGIN_FOLLOW, 1)
if IsValid(self.PCFSmoke) then
self:ComputeSmokeLighting()
self.PCFSmoke:StartEmission()
else
self.PCFSmoke = nil
end
end
if data.Speed > 60 then
self:BounceSound()
local impulse = (data.OurOldVelocity - 2 * data.OurOldVelocity:Dot(data.HitNormal) * data.HitNormal) * 0.33
local phys = self:GetPhysicsObject()
if phys:IsValid() then
phys:ApplyForceCenter(impulse)
end
end
end
function EFFECT:Think()
if CurTime() > self.SmokeDeath and self.PCFSmoke then
self.PCFSmoke:StopEmission()
self.PCFSmoke = nil
else
self:ComputeSmokeLighting()
end
if self:WaterLevel() > 0 and not self.WaterSplashed then
self.WaterSplashed = true
local ef = EffectData()
ef:SetOrigin(self:GetPos())
ef:SetScale(1)
util.Effect("watersplash", ef)
end
if CurTime() > self.StartTime + self.LifeTime then
if self.Emitter then
self.Emitter:Finish()
end
return false
else
return true
end
end
function EFFECT:Render()
if not self.setup then return end
self:SetColor(ColorAlpha(color_white, (1 - math.Clamp(CurTime() - (self.StartTime + self.LifeTime - self.FadeTime), 0, self.FadeTime) / self.FadeTime) * 255))
self:SetupBones()
self:DrawModel()
end
hook.Add("EntityEmitSound", "TFA_BlockShellScrapeSound", function(sndData)
if IsValid(sndData.Entity) and sndData.Entity.IsTFAShell and sndData.SoundName:find("scrape") then
return false
end
end)

View File

@@ -0,0 +1,90 @@
local vector_origin = Vector()
function EFFECT:Init(data)
self.WeaponEnt = data:GetEntity()
if not IsValid(self.WeaponEnt) then return end
self.WeaponEntOG = self.WeaponEnt
self.Attachment = data:GetAttachment()
self.Dir = data:GetNormal()
local owent = self.WeaponEnt:GetOwner()
if not IsValid(owent) then
owent = self.WeaponEnt:GetParent()
end
if IsValid(owent) and owent:IsPlayer() then
if owent ~= LocalPlayer() or owent:ShouldDrawLocalPlayer() then
self.WeaponEnt = owent:GetActiveWeapon()
if not IsValid(self.WeaponEnt) then return end
else
local theirweapon = self.WeaponEnt
self.WeaponEnt = self.WeaponEnt.OwnerViewModel
if IsValid(theirweapon) and theirweapon.ViewModelFlip or theirweapon.ViewModelFlipped then
self.Flipped = true
end
if not IsValid(self.WeaponEnt) then return end
end
end
if IsValid(self.WeaponEntOG) and self.WeaponEntOG.ShellAttachment then
self.Attachment = self.WeaponEnt:LookupAttachment(self.WeaponEntOG.ShellAttachment)
if not self.Attachment or self.Attachment <= 0 then
self.Attachment = 2
end
if self.WeaponEntOG:GetStatL("IsAkimbo") then
self.Attachment = 4 - self.WeaponEntOG:GetAnimCycle()
end
if self.WeaponEntOG.ShellAttachmentRaw then
self.Attachment = self.WeaponEntOG.ShellAttachmentRaw
end
end
local angpos = self.WeaponEnt:GetAttachment(self.Attachment)
if not angpos or not angpos.Pos then
angpos = {
Pos = vector_origin,
Ang = angle_zero
}
end
if self.Flipped then
local tmpang = (self.Dir or angpos.Ang:Forward()):Angle()
local localang = self.WeaponEnt:WorldToLocalAngles(tmpang)
localang.y = localang.y + 180
localang = self.WeaponEnt:LocalToWorldAngles(localang)
--localang:RotateAroundAxis(localang:Up(),180)
--tmpang:RotateAroundAxis(tmpang:Up(),180)
self.Dir = localang:Forward()
end
-- Keep the start and end Pos - we're going to interpolate between them
self.Pos = self:GetTracerShootPos(angpos.Pos, self.WeaponEnt, self.Attachment)
self.Norm = angpos.Ang:Forward() --angpos.Ang:Forward()
--print(self.Norm)
self.Magnitude = data:GetMagnitude()
self.Scale = data:GetScale()
local fx = EffectData()
fx:SetOrigin(self.Pos)
fx:SetStart(self.Pos)
fx:SetEntity(self.WeaponEnt)
fx:SetAttachment(self.Attachment)
fx:SetNormal(self.Norm)
fx:SetAngles(self.Norm:Angle())
fx:SetScale(self.Scale)
fx:SetMagnitude(self.Magnitude)
local se = (self.WeaponEntOG.LuaShellEffect or self.WeaponEntOG.Blowback_Shell_Effect) or "ShellEject"
util.Effect(se, fx)
end
function EFFECT:Think()
return false
end
function EFFECT:Render()
end

View File

@@ -0,0 +1,89 @@
local vector_origin = Vector()
EFFECT.SmokeParticle = "tfa_ins2_shell_eject"
local upVec = Vector(0, 0, 1)
function EFFECT:ComputeSmokeLighting(part, pos)
if not IsValid(part) then return end
local licht = render.ComputeLighting(pos + upVec * 2, upVec)
local lichtFloat = math.Clamp((licht.r + licht.g + licht.b) / 3, 0, TFA.Particles.SmokeLightingClamp) / TFA.Particles.SmokeLightingClamp
local lichtFinal = LerpVector(lichtFloat, TFA.Particles.SmokeLightingMin, TFA.Particles.SmokeLightingMax)
lichtFinal.x = math.sqrt(math.Clamp(lichtFinal.x-0.2,0,0.8)) / 0.8
lichtFinal.y = math.sqrt(math.Clamp(lichtFinal.y-0.2,0,0.8)) / 0.8
lichtFinal.z = math.sqrt(math.Clamp(lichtFinal.z-0.2,0,0.8)) / 0.8
part:SetControlPoint(1, lichtFinal)
end
function EFFECT:Init(data)
if not TFA.GetEJSmokeEnabled() then return end
self.WeaponEnt = data:GetEntity()
if not IsValid(self.WeaponEnt) then return end
self.WeaponEntOG = self.WeaponEnt
self.Attachment = data:GetAttachment()
local owent = self.WeaponEnt:GetOwner()
if not IsValid(owent) then
owent = self.WeaponEnt:GetParent()
end
if IsValid(owent) and owent:IsPlayer() then
if owent ~= LocalPlayer() or owent:ShouldDrawLocalPlayer() then
self.WeaponEnt = owent:GetActiveWeapon()
if not IsValid(self.WeaponEnt) then return end
else
local theirweapon = owent:GetActiveWeapon()
self.WeaponEnt = self.WeaponEnt.OwnerViewModel
if IsValid(theirweapon) and theirweapon.ViewModelFlip or theirweapon.ViewModelFlipped then
self.Flipped = true
end
if not IsValid(self.WeaponEnt) then return end
end
end
if IsValid(self.WeaponEntOG) and self.WeaponEntOG.ShellAttachment then
self.Attachment = self.WeaponEnt:LookupAttachment(self.WeaponEntOG.ShellAttachment)
if not self.Attachment or self.Attachment <= 0 then
self.Attachment = 2
end
if self.WeaponEntOG:GetStatL("IsAkimbo") then
self.Attachment = 3 + self.WeaponEntOG:GetAnimCycle()
end
if self.WeaponEntOG.ShellAttachmentRaw then
self.Attachment = self.WeaponEntOG.ShellAttachmentRaw
end
end
local angpos = self.WeaponEnt:GetAttachment(self.Attachment)
if not angpos or not angpos.Pos then
angpos = {
Pos = vector_origin,
Ang = angle_zero
}
end
local PCFSmoke = CreateParticleSystem(self.WeaponEnt, self.SmokeParticle, PATTACH_POINT_FOLLOW, self.Attachment)
if IsValid(PCFSmoke) then
self:ComputeSmokeLighting(PCFSmoke, angpos.Pos)
PCFSmoke:StartEmission()
timer.Simple(0.2, function()
if IsValid(PCFSmoke) then
PCFSmoke:StopEmission(false,true)
end
end)
end
end
function EFFECT:Think()
return false
end
function EFFECT:Render()
end

View File

@@ -0,0 +1,55 @@
EFFECT.Mat = Material("effects/laser_tracer")
EFFECT.Col1 = Color(255, 255, 255, 255) --Color(225,225,225,225)
EFFECT.Col2 = Color(65, 128, 255, 200)
EFFECT.Speed = 1024*3
EFFECT.TracerLength = 128
--[[---------------------------------------------------------
Init( data table )
-----------------------------------------------------------]]
function EFFECT:Init(data)
self.Position = data:GetStart()
self.WeaponEnt = data:GetEntity()
self.Attachment = data:GetAttachment()
if IsValid(self.WeaponEnt) and self.WeaponEnt.GetMuzzleAttachment then
self.Attachment = self.WeaponEnt:GetMuzzleAttachment()
end
-- Keep the start and end pos - we're going to interpolate between them
self.StartPos = self:GetTracerShootPos(self.Position, self.WeaponEnt, self.Attachment)
self.EndPos = data:GetOrigin()
self.Normal = (self.EndPos - self.StartPos):GetNormalized()
self.Length = (self.EndPos - self.StartPos):Length()
--self.Alpha = 255
self.Life = 0
self.MaxLife = self.Length / self.Speed
self:SetRenderBoundsWS(self.StartPos, self.EndPos, Vector(1000,1000,1000))
self.CurPos = self.StartPos
end
--[[---------------------------------------------------------
THINK
-----------------------------------------------------------]]
function EFFECT:Think()
self.Life = self.Life + FrameTime() * (1 / self.MaxLife)
--self.Alpha = 255 * ( 1 - self.Life )
return self.Life < 1
end
--[[---------------------------------------------------------
Draw the effect
-----------------------------------------------------------]]
local lerpedcol = Color(225, 225, 225, 225)
function EFFECT:Render()
render.SetMaterial(self.Mat)
lerpedcol.r = Lerp(self.Life, self.Col1.r, self.Col2.r)
lerpedcol.g = Lerp(self.Life, self.Col1.g, self.Col2.g)
lerpedcol.b = Lerp(self.Life, self.Col1.b, self.Col2.b)
lerpedcol.a = Lerp(self.Life, self.Col1.a, self.Col2.a)
local startbeampos = LerpVector(self.Life, self.StartPos, self.EndPos)
local endbeampos = LerpVector(self.Life + self.TracerLength / self.Length, self.StartPos, self.EndPos)
render.DrawBeam(startbeampos, endbeampos, 8, 0, 1, lerpedcol)
end

View File

@@ -0,0 +1,123 @@
local vector_origin = Vector()
EFFECT.Thickness = 16
EFFECT.Life = 0.25
EFFECT.RotVelocity = 30
EFFECT.InValid = false
local Mat_Impact = Material("effects/combinemuzzle2")
local Mat_Beam = Material("effects/tool_tracer")
local Mat_TracePart = Material("effects/select_ring")
function EFFECT:Init(data)
self.Position = data:GetStart()
self.WeaponEnt = data:GetEntity()
self.Attachment = data:GetAttachment()
if IsValid(self.WeaponEnt) and self.WeaponEnt.GetMuzzleAttachment then
self.Attachment = self.WeaponEnt:GetMuzzleAttachment()
end
local owent
if IsValid(self.WeaponEnt) then
owent = self.WeaponEnt:GetOwner()
if not IsValid(owent) then
owent = self.WeaponEnt:GetParent()
end
end
if IsValid(owent) and owent:IsPlayer() then
if owent ~= LocalPlayer() or owent:ShouldDrawLocalPlayer() then
self.WeaponEnt = owent:GetActiveWeapon()
if not IsValid(self.WeaponEnt) then return end
else
local theirweapon = self.WeaponEnt
self.WeaponEnt = self.WeaponEnt.OwnerViewModel
if IsValid(theirweapon) and theirweapon.ViewModelFlip or theirweapon.ViewModelFlipped then
self.Flipped = true
end
if not IsValid(self.WeaponEnt) then return end
end
end
local angpos
if IsValid(self.WeaponEnt) then
angpos = self.WeaponEnt:GetAttachment(self.Attachment)
end
if not angpos or not angpos.Pos then
angpos = {
Pos = vector_origin,
Ang = angle_zero
}
end
if self.Flipped then
local tmpang = (self.Dir or angpos.Ang:Forward()):Angle()
local localang = self.WeaponEnt:WorldToLocalAngles(tmpang)
localang.y = localang.y + 180
localang = self.WeaponEnt:LocalToWorldAngles(localang)
--localang:RotateAroundAxis(localang:Up(),180)
--tmpang:RotateAroundAxis(tmpang:Up(),180)
self.Dir = localang:Forward()
end
-- Keep the start and end Pos - we're going to interpolate between them
if IsValid(owent) and self.Position:Distance(owent:EyePos()) > 72 then
self.WeaponEnt = nil
end
self.StartPos = self:GetTracerShootPos(self.WeaponEnt and angpos.Pos or self.Position, self.WeaponEnt, self.Attachment)
self.EndPos = data:GetOrigin()
self.Entity:SetRenderBoundsWS(self.StartPos, self.EndPos)
self.Normal = (self.EndPos - self.StartPos):GetNormalized()
self.StartTime = 0
self.LifeTime = self.Life
self.data = data
self.rot = 0
end
function EFFECT:Think()
if self.InValid then return false end
self.LifeTime = self.LifeTime - FrameTime()
self.StartTime = self.StartTime + FrameTime()
return self.LifeTime > 0
end
local beamcol = table.Copy(color_white)
local beamcol2 = Color(0, 225, 255, 255)
function EFFECT:Render()
if self.InValid then return false end
self.StartPos = self:GetTracerShootPos(self.StartPos, self.WeaponEnt, self.Attachment)
local startPos = self.StartPos
local endPos = self.EndPos
local tracerpos
beamcol.a = self.LifeTime / self.Life * 255
self.rot = self.rot + FrameTime() * self.RotVelocity
render.SetMaterial(Mat_Impact)
render.DrawSprite(endPos, 12, 12, ColorAlpha(color_white, beamcol.a))
render.SetMaterial(Mat_TracePart)
tracerpos = Lerp(math.Clamp(self.LifeTime / self.Life - 0.1, 0, 1), endPos, startPos)
render.DrawQuadEasy(tracerpos, self.Normal, 12, 12, beamcol2, self.rot - 60)
tracerpos = Lerp(math.Clamp(self.LifeTime / self.Life - 0.05, 0, 1), endPos, startPos)
render.DrawQuadEasy(tracerpos, self.Normal, 12, 12, beamcol2, self.rot - 30)
tracerpos = Lerp(math.Clamp(self.LifeTime / self.Life, 0, 1), endPos, startPos)
render.DrawQuadEasy(tracerpos, self.Normal, 12, 12, beamcol2, self.rot)
tracerpos = Lerp(math.Clamp(self.LifeTime / self.Life + 0.05, 0, 1), endPos, startPos)
render.DrawQuadEasy(tracerpos, self.Normal, 12, 12, beamcol2, self.rot + 30)
tracerpos = Lerp(math.Clamp(self.LifeTime / self.Life + 0.1, 0, 1), endPos, startPos)
render.DrawQuadEasy(tracerpos, self.Normal, 12, 12, beamcol2, self.rot + 60)
tracerpos = Lerp(math.Clamp(self.LifeTime / self.Life + 0.15, 0, 1), endPos, startPos)
render.DrawQuadEasy(tracerpos, self.Normal, 12, 12, beamcol2, self.rot + 30)
tracerpos = Lerp(math.Clamp(self.LifeTime / self.Life + 0.2, 0, 1), endPos, startPos)
render.DrawQuadEasy(tracerpos, self.Normal, 12, 12, beamcol2, self.rot + 60)
render.SetMaterial(Mat_Beam)
render.DrawBeam(startPos, endPos, self.Thickness, 0 + beamcol.a / 128, endPos:Distance(startPos) / 64 + beamcol.a / 128, beamcol)
end

View File

@@ -0,0 +1,55 @@
EFFECT.Mat = Material("effects/laser_tracer")
EFFECT.Col1 = Color(255, 90, 25, 200) --Color(225,225,225,225)
EFFECT.Col2 = Color(225, 25, 25, 200)
EFFECT.Speed = 8192
EFFECT.TracerLength = 128
--[[---------------------------------------------------------
Init( data table )
-----------------------------------------------------------]]
function EFFECT:Init(data)
self.Position = data:GetStart()
self.WeaponEnt = data:GetEntity()
self.Attachment = data:GetAttachment()
if IsValid(self.WeaponEnt) and self.WeaponEnt.GetMuzzleAttachment then
self.Attachment = self.WeaponEnt:GetMuzzleAttachment()
end
-- Keep the start and end pos - we're going to interpolate between them
self.StartPos = self:GetTracerShootPos(self.Position, self.WeaponEnt, self.Attachment)
self.EndPos = data:GetOrigin()
self.Normal = (self.EndPos - self.StartPos):GetNormalized()
self.Length = (self.EndPos - self.StartPos):Length()
--self.Alpha = 255
self.Life = 0
self.MaxLife = self.Length / self.Speed
self:SetRenderBoundsWS(self.StartPos, self.EndPos)
self.CurPos = self.StartPos
end
--[[---------------------------------------------------------
THINK
-----------------------------------------------------------]]
function EFFECT:Think()
self.Life = self.Life + FrameTime() * (1 / self.MaxLife)
--self.Alpha = 255 * ( 1 - self.Life )
return self.Life < 1
end
--[[---------------------------------------------------------
Draw the effect
-----------------------------------------------------------]]
local lerpedcol = Color(225, 225, 225, 225)
function EFFECT:Render()
render.SetMaterial(self.Mat)
lerpedcol.r = Lerp(self.Life, self.Col1.r, self.Col2.r)
lerpedcol.g = Lerp(self.Life, self.Col1.g, self.Col2.g)
lerpedcol.b = Lerp(self.Life, self.Col1.b, self.Col2.b)
lerpedcol.a = Lerp(self.Life, self.Col1.a, self.Col2.a)
local startbeampos = Lerp(self.Life, self.StartPos, self.EndPos)
local endbeampos = Lerp(self.Life + self.TracerLength / self.Length, self.StartPos, self.EndPos)
render.DrawBeam(startbeampos, endbeampos, 8, 0, 1, lerpedcol)
end

View File

@@ -0,0 +1,55 @@
EFFECT.Mat = Material("effects/laser_tracer")
EFFECT.Col1 = Color(128, 255, 255) --Color(225,225,225,225)
EFFECT.Col2 = Color(97, 218, 255)
EFFECT.Speed = 4096
EFFECT.TracerLength = 128
--[[---------------------------------------------------------
Init( data table )
-----------------------------------------------------------]]
function EFFECT:Init(data)
self.Position = data:GetStart()
self.WeaponEnt = data:GetEntity()
self.Attachment = data:GetAttachment()
if IsValid(self.WeaponEnt) and self.WeaponEnt.GetMuzzleAttachment then
self.Attachment = self.WeaponEnt:GetMuzzleAttachment()
end
-- Keep the start and end pos - we're going to interpolate between them
self.StartPos = self:GetTracerShootPos(self.Position, self.WeaponEnt, self.Attachment)
self.EndPos = data:GetOrigin()
self.Normal = (self.EndPos - self.StartPos):GetNormalized()
self.Length = (self.EndPos - self.StartPos):Length()
--self.Alpha = 255
self.Life = 0
self.MaxLife = self.Length / self.Speed
self:SetRenderBoundsWS(self.StartPos, self.EndPos)
self.CurPos = self.StartPos
end
--[[---------------------------------------------------------
THINK
-----------------------------------------------------------]]
function EFFECT:Think()
self.Life = self.Life + FrameTime() * (1 / self.MaxLife)
--self.Alpha = 255 * ( 1 - self.Life )
return self.Life < 1
end
--[[---------------------------------------------------------
Draw the effect
-----------------------------------------------------------]]
local lerpedcol = Color(225, 225, 225, 225)
function EFFECT:Render()
render.SetMaterial(self.Mat)
lerpedcol.r = Lerp(self.Life, self.Col1.r, self.Col2.r)
lerpedcol.g = Lerp(self.Life, self.Col1.g, self.Col2.g)
lerpedcol.b = Lerp(self.Life, self.Col1.b, self.Col2.b)
lerpedcol.a = Lerp(self.Life, self.Col1.a, self.Col2.a)
local startbeampos = Lerp(self.Life, self.StartPos, self.EndPos)
local endbeampos = Lerp(self.Life + self.TracerLength / self.Length, self.StartPos, self.EndPos)
render.DrawBeam(startbeampos, endbeampos, 8, 0, 1, lerpedcol)
end

View File

@@ -0,0 +1,211 @@
ENT.Type = "anim"
ENT.Base = "base_anim"
ENT.PrintName = "AmmoBase"
ENT.Category = "TFA Ammunition"
ENT.Spawnable = false
ENT.AdminSpawnable = false
ENT.Class = ""
ENT.MyModel = "models/props_junk/popcan01a.mdl"
ENT.ImpactSound = "Default.ImpactSoft"
ENT.AmmoCount = 100
ENT.AmmoType = "357"
ENT.TextPosition = Vector(-2.5, -3.3, 4)
ENT.TextAngles = Vector(48, -90, 0)
ENT.TextColor = Color(240, 35, 35, 255)
ENT.DrawText = false
ENT.ShouldDrawShadow = true
ENT.ImpactSound = "Default.ImpactSoft"
ENT.DamageThreshold = 80
ENT.ExplosionOffset = Vector(0, 0, 10)
ENT.Damage = 30
ENT.TextOffX = 30
ENT.TextOffY = -20
ENT.TextScale = 1
if SERVER then
AddCSLuaFile()
function ENT:SpawnFunction(ply, tr, classname)
if (not tr.Hit) then return end
local pos = tr.HitPos + tr.HitNormal * 4
local ent = ents.Create(classname)
ent:SetPos(pos)
ent:Spawn()
ent:Activate()
ent.Class = classname
ent.Spawner = ply
return ent
end
function ENT:Initialize()
local model = self.MyModel
self.Class = self:GetClass()
self:SetModel(model)
self:PhysicsInit(SOLID_VPHYSICS)
self:SetMoveType(MOVETYPE_VPHYSICS)
self:SetSolid(SOLID_VPHYSICS)
self:DrawShadow(self.ShouldDrawShadow)
self:SetCollisionGroup(COLLISION_GROUP_WEAPON)
self:SetUseType(SIMPLE_USE)
self:SetHealth(self.DamageThreshold)
self:SetNW2Bool("ShouldRemove", false)
local phys = self:GetPhysicsObject()
if (phys:IsValid()) then
phys:Wake()
end
end
function ENT:PhysicsCollide(data, physobj)
if (data.Speed > 60 and data.DeltaTime > 0.2) then
self:EmitSound(self.ImpactSound)
end
end
function ENT:Use(activator, caller)
if IsValid(activator) and activator:IsPlayer() then
activator:GiveAmmo(self.AmmoCount, self.AmmoType)
self:SetNW2Bool("ShouldRemove", true)
end
end
local bul = {}
local randvec = Vector(0, 0, 0)
bul.Tracer = 3
bul.Num = 1
bul.TracerName = "Tracer"
bul.Spread = Vector(0, 0, 0)
local cv_dc = GetConVar("sv_tfa_ammo_detonation_chain")
local cv_dm = GetConVar("sv_tfa_ammo_detonation_mode")
function ENT:OnTakeDamage(dmginfo)
if not IsValid(self) then return end
local at = dmginfo:GetInflictor()
local shouldtakedamage = true
if IsValid(at) then
local base = at.Base
if (base and string.find(base, "tfa_ammo_base")) or string.find(at:GetClass(), "tfa_ammo_") and not cv_dc:GetBool() then
shouldtakedamage = false
end
end
if dmginfo:GetDamage() < 1 then
shouldtakedamage = false
end
self.Attacker = at
if shouldtakedamage then
self:SetHealth(self:Health() - dmginfo:GetDamage())
end
self:EmitSound(self.ImpactSound)
local phy = self:GetPhysicsObject()
if IsValid(phy) then
local f = dmginfo:GetDamageForce()
local p = dmginfo:GetDamagePosition()
if f and p then
phy:ApplyForceOffset(f / 4, p)
end
end
end
function ENT:Think()
if self:GetNW2Bool("ShouldRemove", false) then
self:Remove()
return false
end
if not cv_dc:GetBool() then return true end
if self:Health() <= 0 then
self:EmitSound(self.ImpactSound)
local adm = cv_dm:GetInt()
bul.AmmoType = self.AmmoType
bul.Damage = self.Damage
bul.Force = math.Max(self.Damage / 25, 0.1)
bul.Attacker = self
if IsValid(self.Attacker) then
bul.Attacker = self.Attacker
end
local upang = self:GetAngles():Up()
bul.Dir = upang + randvec * 0.75
local numbuls = math.random(math.Round(self.AmmoCount * 0.25), math.Round(self.AmmoCount * 0.75))
local i = 1
if adm == 2 then
bul.Damage = bul.Damage / 2
end
bul.Dir = (upang + randvec * 0.75):GetNormalized()
bul.Src = self:GetPos()
self:FireBullets(bul)
if adm ~= 1 then
while i <= math.Clamp(numbuls, 1, 35) do
randvec.x = math.Rand(-1, 1)
randvec.y = math.Rand(-1, 1)
randvec.z = math.Rand(-1, 1)
bul.Dir = (upang + randvec * 0.75):GetNormalized()
bul.Src = self:GetPos()
self:FireBullets(bul)
i = i + 1
end
end
local effectdata = EffectData()
effectdata:SetOrigin(self:GetPos())
effectdata:SetMagnitude(0.1)
effectdata:SetScale(0.5)
if adm == 1 then
bul.Damage = bul.Damage * 3 / 4
end
if adm > 0 then
util.BlastDamage(bul.Attacker, bul.Attacker, bul.Src, (bul.Damage * 6 + 128) / 2, bul.Damage * 2)
util.Effect("Explosion", effectdata)
end
if adm ~= 1 then
util.Effect("cball_explode", effectdata)
end
self:SetNW2Bool("ShouldRemove", true)
end
end
end
if CLIENT then
function ENT:Initialize()
self.Class = self:GetClass()
end
function ENT:Draw()
self:DrawModel()
if self.TextPosition and self.TextAngles and self.DrawText then
local pos = self:GetPos() + (self:GetUp() * self.TextPosition.z) + (self:GetRight() * self.TextPosition.x) + (self:GetForward() * self.TextPosition.y)
local ang = self:GetAngles()
ang:RotateAroundAxis(ang:Right(), self.TextAngles.x)
ang:RotateAroundAxis(ang:Up(), self.TextAngles.y)
ang:RotateAroundAxis(ang:Forward(), self.TextAngles.z)
if not self.Text then
self.Text = string.upper(self.AmmoType)
end
cam.Start3D2D(pos, ang, .07 * self.TextScale)
draw.SimpleText(self.Text, "DermaLarge", self.TextOffX, self.TextOffY, self.TextColor, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
cam.End3D2D()
end
end
end

View File

@@ -0,0 +1,9 @@
include("shared.lua")
function ENT:Draw()
self:DrawModel()
end
function ENT:IsTranslucent()
return true
end

View File

@@ -0,0 +1,79 @@
AddCSLuaFile("cl_init.lua")
AddCSLuaFile("shared.lua")
include("shared.lua")
ENT.DefaultModel = Model("models/weapons/w_eq_fraggrenade.mdl")
ENT.Damage = 100
ENT.Delay = 3
function ENT:Initialize()
local mdl = self:GetModel()
if not mdl or mdl == "" or mdl == "models/error.mdl" then
self:SetModel(self.DefaultModel)
end
self:PhysicsInit(SOLID_VPHYSICS)
self:SetMoveType(MOVETYPE_VPHYSICS)
self:SetSolid(SOLID_VPHYSICS)
local phys = self:GetPhysicsObject()
if IsValid(phys) then
phys:Wake()
end
self:SetFriction(self.Delay)
self.killtime = CurTime() + self.Delay
self:DrawShadow(true)
if not self.Inflictor and self:GetOwner():IsValid() and self:GetOwner():GetActiveWeapon():IsValid() then
self.Inflictor = self:GetOwner():GetActiveWeapon()
end
end
function ENT:Think()
if self.killtime < CurTime() then
self:Explode()
return false
end
self:NextThink(CurTime())
return true
end
ENT.ExplosionSound = "BaseExplosionEffect.Sound"
function ENT:DoExplosionEffect()
local effectdata = EffectData()
effectdata:SetOrigin(self:GetPos())
util.Effect("HelicopterMegaBomb", effectdata)
util.Effect("Explosion", effectdata)
self:EmitSoundNet(self.ExplosionSound)
end
function ENT:Explode()
if not IsValid(self.Inflictor) then
self.Inflictor = self
end
self.Damage = self.mydamage or self.Damage
local dmg = DamageInfo()
dmg:SetInflictor(self.Inflictor)
dmg:SetAttacker(IsValid(self:GetOwner()) and self:GetOwner() or self)
dmg:SetDamage(self.Damage)
dmg:SetDamageType(bit.bor(DMG_BLAST, DMG_AIRBOAT))
util.BlastDamageInfo(dmg, self:GetPos(), math.pow( self.Damage / 100, 0.75) * 200)
util.ScreenShake(self:GetPos(), self.Damage * 20, 255, self.Damage / 200, math.pow(self.Damage / 100, 0.75) * 400)
self:DoExplosionEffect()
self:Remove()
end

View File

@@ -0,0 +1,32 @@
ENT.Type = "anim"
ENT.PrintName = "Base Explosive"
ENT.Author = ""
ENT.Contact = ""
ENT.Purpose = ""
ENT.Instructions = ""
ENT.DoNotDuplicate = true
ENT.DisableDuplicator = true
local sp = game.SinglePlayer()
function ENT:EmitSoundNet(sound)
if CLIENT or sp then
if sp and not IsFirstTimePredicted() then return end
self:EmitSound(sound)
return
end
local filter = RecipientFilter()
filter:AddPAS(self:GetPos())
if IsValid(self:GetOwner()) then
filter:RemovePlayer(self:GetOwner())
end
net.Start("tfaSoundEvent", true)
net.WriteEntity(self)
net.WriteString(sound)
net.WriteBool(false)
net.Send(filter)
end

View File

@@ -0,0 +1,10 @@
AddCSLuaFile()
ENT.Base = "tfa_exp_base"
ENT.PrintName = "Contact Explosive"
function ENT:PhysicsCollide(data, phys)
if data.Speed > 60 then
self.killtime = -1
end
end

View File

@@ -0,0 +1,96 @@
AddCSLuaFile()
ENT.Base = "tfa_exp_base"
ENT.PrintName = "Rocket-Propelled Explosive"
-- EDITABLE PARAMETERS -- START
ENT.LaunchSound = "" -- none, replace to enable
ENT.PropelSound = Sound("Missile.Accelerate") -- looped propel sound
ENT.BaseSpeed = 500 -- base rocket speed, in units
ENT.AccelerationTime = 0.25 -- time in seconds to accelerate to max speed
ENT.MaxSpeed = 1500 -- maximum speed, works if AccelerationTime > 0
ENT.HasTrail = true -- create trail
-- EDITABLE PARAMETERS -- END
ENT.AccelProgress = 0
ENT.DefaultModel = Model("models/weapons/w_missile.mdl")
ENT.Delay = 10
DEFINE_BASECLASS(ENT.Base)
-- Creates HL2 rocket trail by default, feel free to copy and edit to your needs
function ENT:CreateRocketTrail()
if not SERVER then return end
local rockettrail = ents.Create("env_rockettrail")
rockettrail:DeleteOnRemove(self)
rockettrail:SetPos(self:GetPos())
rockettrail:SetAngles(self:GetAngles())
rockettrail:SetParent(self)
rockettrail:SetMoveType(MOVETYPE_NONE)
rockettrail:AddSolidFlags(FSOLID_NOT_SOLID)
rockettrail:SetSaveValue("m_Opacity", 0.2)
rockettrail:SetSaveValue("m_SpawnRate", 100)
rockettrail:SetSaveValue("m_ParticleLifetime", 0.5)
rockettrail:SetSaveValue("m_StartColor", Vector(0.65, 0.65, 0.65))
rockettrail:SetSaveValue("m_EndColor", Vector(0, 0, 0))
rockettrail:SetSaveValue("m_StartSize", 8)
rockettrail:SetSaveValue("m_EndSize", 32)
rockettrail:SetSaveValue("m_SpawnRadius", 4)
rockettrail:SetSaveValue("m_MinSpeed", 2)
rockettrail:SetSaveValue("m_MaxSpeed", 16)
rockettrail:SetSaveValue("m_nAttachment", 0)
rockettrail:SetSaveValue("m_flDeathTime", CurTime() + 999)
rockettrail:Activate()
rockettrail:Spawn()
end
function ENT:Initialize(...)
BaseClass.Initialize(self, ...)
self:EmitSoundNet(self.PropelSound)
if self.LaunchSound and self.LaunchSound ~= "" then
self:EmitSoundNet(self.LaunchSound)
end
self:SetFriction(0)
self:SetLocalAngularVelocity(angle_zero)
self:SetMoveType(bit.bor(MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE))
self:SetLocalVelocity(self:GetForward() * self.BaseSpeed)
if self.HasTrail then
self:CreateRocketTrail()
end
end
function ENT:Think(...)
if self.AccelerationTime > 0 and self.AccelProgress < 1 then
self.LastAccelThink = self.LastAccelThink or CurTime()
self.AccelProgress = Lerp((CurTime() - self.LastAccelThink) / self.AccelerationTime, self.AccelProgress, 1)
end
self:SetLocalVelocity(self:GetForward() * Lerp(self.AccelProgress, self.BaseSpeed, self.MaxSpeed))
return BaseClass.Think(self, ...)
end
function ENT:Explode(...)
self:StopSound(self.PropelSound)
return BaseClass.Explode(self, ...)
end
function ENT:Touch()
self.killtime = -1
end

View File

@@ -0,0 +1,17 @@
AddCSLuaFile()
ENT.Base = "tfa_exp_base"
ENT.PrintName = "Timed Explosive"
ENT.BounceSound = Sound("HEGrenade.Bounce")
function ENT:PhysicsCollide(data, phys)
if data.Speed > 60 then
if self.BounceSound then
self:EmitSoundNet(self.BounceSound)
end
local impulse = (data.OurOldVelocity - 2 * data.OurOldVelocity:Dot(data.HitNormal) * data.HitNormal) * 0.25
phys:ApplyForceCenter(impulse)
end
end

View File

@@ -0,0 +1,5 @@
include("shared.lua")
function ENT:Draw()
self:DrawModel()
end

View File

@@ -0,0 +1,176 @@
AddCSLuaFile("cl_init.lua")
AddCSLuaFile("shared.lua")
include("shared.lua")
function ENT:Initialize()
local mdl = self:GetModel()
if not mdl or mdl == "" or string.find(mdl, "error") then
self:SetModel("models/weapons/w_knife_t.mdl")
end
self:PhysicsInit(SOLID_VPHYSICS)
self:SetMoveType(MOVETYPE_VPHYSICS)
self:SetSolid(SOLID_VPHYSICS)
local phys = self:GetPhysicsObject()
self:NextThink(CurTime() + 1)
if (phys:IsValid()) then
phys:Wake()
phys:SetMass(10)
end
for _, v in pairs(self.HitSounds) do
for _, o in pairs(v) do
util.PrecacheSound(o)
end
end
local bounds = self:OBBMaxs() - self:OBBMins()
if bounds.z > bounds.x and bounds.z > bounds.y then
self.up = true
elseif bounds.y > bounds.x and bounds.y > bounds.z then
self.right = true
end
self:SetUseType(SIMPLE_USE)
self.mydamage = self.mydamage or 40
self.DestroyTime = CurTime() + 30
end
function ENT:Think()
if CurTime() > self.DestroyTime then
self:Remove()
end
end
function ENT:Stick()
self.DestroyTime = CurTime() + 60
timer.Simple(0,function()
if IsValid(self) then
self:SetCollisionGroup(COLLISION_GROUP_WEAPON)
self:GetPhysicsObject():EnableMotion(false)
end
end)
end
function ENT:PhysicsCollide(data, phys)
timer.Simple(0,function()
if not IsValid(self) then return end
local owner = self:GetOwner()
self:SetOwner(nil)
local fwdang = self:GetAngles()
local fwdvec
if self.up then
fwdvec = fwdang:Up()
elseif self.right then
fwdvec = fwdang:Right()
else
fwdvec = fwdang:Forward()
end
local ent = data.HitEntity
if not IsValid(ent) and not (ent and ent:IsWorld()) then return end
local dmg = self.mydamage * math.sqrt(data.Speed / 1500)
if dmg > 5 and ent and not ent:IsWorld() then
local dmginfo = DamageInfo()
dmginfo:SetDamage(dmg)
dmginfo:SetDamagePosition(data.HitPos)
dmginfo:SetDamageForce(data.OurOldVelocity)
dmginfo:SetInflictor(self)
dmginfo:SetDamageType(DMG_SLASH)
local att = self:GetPhysicsAttacker()
if not IsValid(att) then
att = owner
end
if not IsValid(att) then
att = self
end
dmginfo:SetAttacker(att)
ent:TakeDamageInfo(dmginfo)
end
local traceres = util.QuickTrace(self:GetPos(), data.OurOldVelocity, self)
if not traceres.HitPos then return end
if data.Speed > 50 then
local soundtbl
if self.HitSounds[traceres.MatType] then
soundtbl = self.HitSounds[traceres.MatType]
else
soundtbl = self.HitSounds[MAT_DIRT]
end
local snd = soundtbl[math.random(1, #soundtbl)]
self:EmitSound(snd)
end
local dp = traceres.HitNormal:Dot(fwdvec)
if dp >= -0.3 then
local fx = EffectData()
fx:SetOrigin(data.HitPos)
fx:SetMagnitude(1)
fx:SetScale((data.Speed / 1500 * (dp + 0.6)) / 5)
util.Effect("Sparks", fx)
end
local canstick = data.Speed > 250 and dp < (-1 + data.Speed / 1000 * 0.3)
if ent:IsWorld() and canstick then
util.Decal("ManhackCut", traceres.HitPos + traceres.HitNormal, traceres.HitPos - traceres.HitNormal)
self:EmitSound(self.ImpactSound)
self:SetPos(traceres.HitPos + traceres.HitNormal * 12)
local tmpang = data.HitNormal:Angle()
tmpang:RotateAroundAxis(tmpang:Right(), 270)
--self:SetAngles(tmpang)
local fx = EffectData()
fx:SetOrigin(data.HitPos)
fx:SetMagnitude(2)
fx:SetScale(0.1)
util.Effect("Sparks", fx)
self:Stick()
elseif IsValid(ent) then
if not (ent:IsPlayer() or ent:IsNPC() or ent:GetClass() == "prop_ragdoll") then
if canstick then
util.Decal("ManhackCut", data.HitPos + data.HitNormal, data.HitPos - data.HitNormal)
end
self:SetCollisionGroup(COLLISION_GROUP_WEAPON)
else
local fx = EffectData()
fx:SetOrigin(data.HitPos)
util.Effect("BloodImpact", fx)
self:GetPhysicsObject():SetVelocity(-(data.OurOldVelocity / 8))
end
if IsValid(self) then
self:SetCollisionGroup(COLLISION_GROUP_WEAPON)
end
end
if canstick then
self:GetPhysicsObject():SetVelocity(-(data.OurOldVelocity / 16))
end
self:GetPhysicsObject():AddAngleVelocity(-self:GetPhysicsObject():GetAngleVelocity() / 3)
end)
end
function ENT:Use(ply, caller)
local classname = self:GetNW2String("ClassName")
if not classname or classname == "" then return end
if ply:IsPlayer() and ply:GetWeapon(classname) == NULL then
ply:Give(classname)
self:Remove()
end
end

View File

@@ -0,0 +1,14 @@
ENT.Type = "anim"
ENT.PrintName = "Thrown Blade"
ENT.Author = ""
ENT.Contact = ""
ENT.Purpose = ""
ENT.Instructions = ""
ENT.DoNotDuplicate = true
ENT.HitSounds = {
[MAT_DIRT] = {Sound("physics/metal/metal_grenade_impact_hard1.wav"), Sound("physics/metal/metal_grenade_impact_hard2.wav"), Sound("physics/metal/metal_grenade_impact_hard3.wav")},
[MAT_FLESH] = {Sound("physics/flesh/flesh_impact_bullet1.wav"), Sound("physics/flesh/flesh_impact_bullet2.wav"), Sound("physics/flesh/flesh_impact_bullet3.wav")}
}
ENT.ImpactSound = Sound("weapons/blades/impact.mp3")

View File

@@ -0,0 +1,23 @@
include("shared.lua")
local cv_ht = GetConVar("host_timescale")
function ENT:Draw()
local ang, tmpang
tmpang = self:GetAngles()
ang = tmpang
if not self.roll then
self.roll = 0
end
local phobj = self:GetPhysicsObject()
if IsValid(phobj) then
self.roll = self.roll + phobj:GetVelocity():Length() / 3600 * cv_ht:GetFloat()
end
ang:RotateAroundAxis(ang:Forward(), self.roll)
self:SetAngles(ang)
self:DrawModel() -- Draw the model.
self:SetAngles(tmpang)
end

View File

@@ -0,0 +1,3 @@
AddCSLuaFile("cl_init.lua") -- Make sure clientside
AddCSLuaFile("shared.lua") -- and shared scripts are sent.
include("shared.lua")

View File

@@ -0,0 +1,247 @@
local vector_origin = Vector()
ENT.Type = "anim"
ENT.PrintName = "TFBow Arrow"
ENT.Author = "TheForgottenArchitect"
ENT.Contact = "Don't"
ENT.Purpose = "Arrow Entity"
ENT.Instructions = "Spawn this with a velocity, get rich"
local function GetBoneCenter(ent, bone)
local bonechildren = ent:GetChildBones(bone)
if #bonechildren <= 0 then
return ent:GetBonePosition(bone)
else
local bonepos = ent:GetBonePosition(bone)
local tmppos = bonepos
if tmppos then
for i = 1, #bonechildren do
local childpos = ent:GetBonePosition(bonechildren[i])
if childpos then
tmppos = (tmppos + childpos) / 2
end
end
else
return ent:GetPos()
end
return tmppos
end
end
function ENT:GetClosestBonePos(ent, pos)
local i, count, dist, ppos, cbone
i = 1
count = ent:GetBoneCount()
cbone = 0
dist = 99999999
ppos = ent:GetPos()
while (i < count) do
local bonepos = GetBoneCenter(ent, i)
if bonepos:Distance(pos) < dist then
dist = bonepos:Distance(pos)
cbone = i
ppos = bonepos
end
i = i + 1
end
return ppos, cbone
end
local cv_al = GetConVar("sv_tfa_arrow_lifetime")
local cv_ht = GetConVar("host_timescale")
function ENT:Initialize()
if SERVER then
if not IsValid(self.myowner) then
self.myowner = self:GetOwner()
if not IsValid(self.myowner) then
self.myowner = self
end
end
timer.Simple(0, function()
if self.model then
self:SetModel(self.model)
end
end)
if cv_al:GetInt() ~= -1 then
timer.Simple( cv_al:GetFloat() + 5, function()
if IsValid(self) then
self:Remove()
end
end)
end
self:PhysicsInit(SOLID_VPHYSICS)
self:SetMoveType(MOVETYPE_VPHYSICS)
self:SetSolid(SOLID_VPHYSICS)
local phys = self:GetPhysicsObject()
if (phys:IsValid()) then
phys:Wake()
if self.velocity then
phys:SetVelocityInstantaneous(self.velocity)
end
phys:EnableCollisions(false)
self:StartMotionController()
self:PhysicsUpdate(phys, 0.1 * cv_ht:GetFloat() )
end
end
self:SetNW2Vector("lastpos", self:GetPos())
if not self.mydamage then
self.mydamage = 60
end
if not self.gun then
if IsValid(self:GetOwner()) and self:GetOwner():IsPlayer() then
self:UpdateGun()
else
timer.Simple(0, function()
if IsValid(self) and IsValid(self:GetOwner()) and self:GetOwner():IsPlayer() then
self:UpdateGun()
end
end)
end
end
end
function ENT:UpdateGun()
local wep = self:GetOwner():GetActiveWeapon()
if IsValid(wep) then
self.gun = wep:GetClass()
end
end
local wl,tracedata,tr
local cv_fm = GetConVar("sv_tfa_force_multiplier")
function ENT:HitCB(a,b,c)
c:SetDamageType(bit.bor(DMG_NEVERGIB, DMG_CLUB))
if IsValid(self) and IsValid(self:GetOwner()) then
if b.HitWorld then
local arrowstuck = ents.Create("tfbow_arrow_stuck")
arrowstuck:SetModel(self:GetModel())
arrowstuck.gun = self.gun
arrowstuck:SetPos(tr.HitPos)
local phys = self:GetPhysicsObject()
arrowstuck:SetAngles((phys:GetVelocity()):Angle())
arrowstuck:Spawn()
else
if IsValid(b.Entity) then
if (not b.Entity:IsWorld()) then
local arrowstuck = ents.Create("tfbow_arrow_stuck_clientside")
arrowstuck:SetModel(self:GetModel())
arrowstuck:SetPos(tr.HitPos)
local ang = self:GetAngles()
arrowstuck.gun = self.gun
arrowstuck:SetAngles(ang)
arrowstuck.targent = tr.Entity
arrowstuck.targphysbone = tr.PhysicsBone
arrowstuck:Spawn()
else
local arrowstuck = ents.Create("tfbow_arrow_stuck")
arrowstuck:SetModel(self:GetModel())
arrowstuck.gun = self.gun
arrowstuck:SetPos(tr.HitPos)
arrowstuck:SetAngles(self:GetAngles())
arrowstuck:Spawn()
end
end
end
self:Remove()
elseif IsValid(self) then
self:Remove()
end
end
function ENT:Think()
wl = self:WaterLevel()
if not self.prevwaterlevel then
self.prevwaterlevel = wl
end
if self.prevwaterlevel ~= wl and wl - self.prevwaterlevel >= 1 then
--print(wl)
local ef = EffectData()
ef:SetOrigin(self:GetPos())
util.Effect("watersplash", ef)
end
self.prevwaterlevel = wl
if wl >= 2 then
local phys = self:GetPhysicsObject()
if IsValid(phys) then
phys:SetVelocity(phys:GetVelocity() * math.sqrt(9 / 10))
end
end
tracedata = {}
tracedata.start = self:GetNW2Vector("lastpos", self:GetPos())
tracedata.endpos = self:GetPos()
tracedata.mask = MASK_SOLID
tracedata.filter = {self.myowner, self:GetOwner(), self}
tr = util.TraceLine(tracedata)
--self:SetAngles((((tracedata.endpos-tracedata.start):GetNormalized()+self:GetAngles():Forward())/2):Angle())
if (tr.Hit and tr.Fraction < 1 and tr.Fraction > 0) then
debugoverlay.Line(tracedata.start, tr.HitPos, 10, Color(255, 0, 0, 255), true)
debugoverlay.Cross(tr.HitPos, 5, 10, Color(255, 0, 0, 255), true)
if SERVER then
--[[
local bul ={}
bul.Attacker=self:GetOwner() and self:GetOwner() or self:GetOwner()
bul.Spread=vector_origin
bul.Src=tracedata.start
bul.Force=self.mydamage*0.25*GetConVarNumber("sv_tfbow_force_multiplier",1)
bul.Damage=self.mydamage
bul.Tracer = 0 -- Show a tracer on every x bullets
bul.TracerName = "None"
bul.Dir=((tr.HitPos-bul.Src):GetNormalized())
bul.Attacker:FireBullets( bul )
]]
--
local bul = {}
bul.Attacker = self:GetOwner() and self:GetOwner() or self:GetOwner()
bul.Spread = vector_origin
bul.Src = tracedata.start
bul.Force = self.mydamage * 0.25 * cv_fm:GetFloat()
bul.Damage = self.mydamage
bul.Tracer = 0 -- Show a tracer on every x bullets
bul.TracerName = "None"
bul.Dir = (tr.HitPos - bul.Src):GetNormalized()
bul.Callback = function(a, b, c)
self:HitCB(a,b,c)
end
bul.Attacker:FireBullets(bul)
end
return
end
self:SetNW2Vector("lastpos", self:GetPos())
end

View File

@@ -0,0 +1,5 @@
include("shared.lua")
function ENT:Draw()
self:DrawModel() -- Draw the model.
end

View File

@@ -0,0 +1,3 @@
AddCSLuaFile("cl_init.lua") -- Make sure clientside
AddCSLuaFile("shared.lua") -- and shared scripts are sent.
include("shared.lua")

View File

@@ -0,0 +1,54 @@
ENT.Type = "anim"
ENT.PrintName = "TFBow Arrow Stuck"
ENT.Author = "TheForgottenArchitect"
ENT.Contact = "Don't"
ENT.Purpose = "Arrow Entity"
ENT.Instructions = "Arrow that's stuck in ground"
local cv_al = GetConVar("sv_tfa_arrow_lifetime")
function ENT:Initialize()
if SERVER then
if cv_al:GetInt() ~= -1 then
timer.Simple( cv_al:GetFloat(), function()
if IsValid(self) then
self:Remove()
end
end)
end
self:PhysicsInit(SOLID_VPHYSICS)
self:SetMoveType(MOVETYPE_VPHYSICS)
self:SetSolid(SOLID_VPHYSICS)
local phys = self:GetPhysicsObject()
if (phys:IsValid()) then
phys:Wake()
phys:SetMass(2)
end
if IsValid(self) and self.SetUseType then
self:SetUseType(SIMPLE_USE)
end
end
if (self:GetModel() and self:GetModel() == "") then
self:SetModel("models/weapons/w_tfa_arrow.mdl")
end
self:SetOwner(nil)
self.PhysicsCollide = function() end
self:SetCollisionGroup(COLLISION_GROUP_WEAPON)
local phys = self:GetPhysicsObject()
if (phys:IsValid()) then
phys:Sleep()
end
end
function ENT:Use(activator, caller)
if activator:IsPlayer() and activator:GetWeapon(self.gun) then
activator:GiveAmmo(1, activator:GetWeapon(self.gun):GetPrimaryAmmoType(), false)
self:Remove()
end
end

View File

@@ -0,0 +1,9 @@
include("shared.lua")
function ENT:Draw()
if IsValid( self:GetParent() ) then
self:GetParent():SetupBones()
end
self:SetupBones()
self:DrawModel()
end

View File

@@ -0,0 +1,13 @@
AddCSLuaFile("cl_init.lua")
AddCSLuaFile("shared.lua")
include("shared.lua")
function ENT:Use( activator, caller, usetype, val )
if activator:IsPlayer() and activator:GetWeapon(self.gun) ~= nil then
activator:GiveAmmo(1, activator:GetWeapon(self.gun):GetPrimaryAmmoType(), false)
self:Remove()
end
end
function ENT:PhysicsCollide(data, phys)
end

View File

@@ -0,0 +1,197 @@
ENT.Type = "anim"
ENT.PrintName = "Sawblade"
ENT.Author = ""
ENT.Contact = ""
ENT.Purpose = ""
ENT.Instructions = ""
ENT.DoNotDuplicate = true
ENT.DisableDuplicator = true
ENT.glitchthreshold = 24 --threshold distance from bone to reset pos
ENT.glitchthresholds = {}
ENT.glitchthresholds["ValveBiped.Bip01_Head1"] = 8
ENT.glitchthresholds["ValveBiped.Bip01_Head"] = 8
ENT.glitchthresholds["ValveBiped.Bip01_R_Hand"] = 1
ENT.glitchthresholds["ValveBiped.Bip01_L_Hand"] = 1
ENT.glitchthresholds["ValveBiped.Bip01_Spine2"] = 40
ENT.Hull = 1.5 --Expand hull to make it easier to grab
ENT.PredictCL = false
ENT.UseMod = false --Experimentally modify the parent's Use func
local cv_al = GetConVar("sv_tfa_arrow_lifetime")
local nzombies
local function GetBoneCenter(ent, bone)
local bonechildren = ent:GetChildBones(bone)
if #bonechildren <= 0 then
return ent:GetBonePosition(bone)
else
local bonepos = ent:GetBonePosition(bone)
local tmppos = bonepos
if tmppos then
for i = 1, #bonechildren do
local childpos = ent:GetBonePosition(bonechildren[i])
if childpos then
tmppos = (tmppos + childpos) / 2
end
end
else
return ent:GetPos()
end
return tmppos
end
end
function ENT:Initialize()
if nzombies == nil then
nzombies = nZombies or NZ or NZombies or engine.ActiveGamemode() == "nzombies"
end
local mdl = self:GetModel()
if not mdl or mdl == "" or mdl == "models/error.mdl" then
self:SetModel("models/weapons/w_tfa_arrow.mdl")
end
if SERVER then
local mins = (self:OBBMins() and self:OBBMins() or Vector(0, 0, 0)) - Vector(1, 1, 1)
local maxs = (self:OBBMaxs() and self:OBBMaxs() or Vector(0, 0, 0)) + Vector(1, 1, 1)
self:PhysicsInitBox(mins * self.Hull, maxs * self.Hull)
--self:PhysicsInit( SOLID_VPHYSICS )
--self:SetSolid( SOLID_VPHYSICS )
self:SetMoveType( MOVETYPE_VPHYSICS )
local phys = self:GetPhysicsObject()
if (phys:IsValid()) then
phys:Wake()
phys:SetMass(2)
phys:EnableGravity(false)
phys:EnableCollisions(false)
end
if self.SetUseType then
self:SetUseType(SIMPLE_USE)
end
if cv_al:GetInt() ~= -1 then
timer.Simple( cv_al:GetFloat(), function()
if IsValid(self) then
self:Remove()
end
end)
end
self:SetUseType( SIMPLE_USE )
end
if SERVER then
self:TargetEnt( true )
end
if CLIENT then
self:SetPredictable(false)
end
if (self:GetModel() and self:GetModel() == "") then
self:SetModel("models/weapons/w_tfa_arrow.mdl")
end
self:SetCollisionGroup(COLLISION_GROUP_WEAPON)
self:DrawShadow(true)
end
function ENT:TargetEnt( init )
if self.targent and IsValid(self.targent) then
if init then
local ent, bone, bonepos, bonerot
ent = self.targent
bone = self.targent:TranslatePhysBoneToBone(self.targphysbone)
self.targbone = bone
if not ent:GetBoneCount() or ent:GetBoneCount() <= 1 or string.find(ent:GetModel(), "door") then
bonepos = ent:GetPos()
bonerot = ent:GetAngles()
self.enthasbones = false
else
if ent.SetupBones then
ent:SetupBones()
end
bonepos, bonerot = ent:GetBonePosition(bone)
self.enthasbones = true
end
if self.enthasbones == true then
local gpos = self:GetPos()
local bonepos2 = GetBoneCenter(ent, bone)
local tmpgts = self.glitchthresholds[ent:LookupBone(bone)] or self.glitchthreshold
while gpos:Distance(bonepos2) > tmpgts do
self:SetPos((gpos + bonepos2) / 2)
gpos = (gpos + bonepos2) / 2
end
end
if not bonepos then
bonepos = ent:GetPos()
bonerot = ent:GetAngles()
end
self.posoff, self.angoff = WorldToLocal(self:GetPos(), self:GetAngles(), bonepos, bonerot)
end
self:FollowBone( self.targent, self.targbone or -1 )
self:SetOwner( self.targent )
self:SetLocalPos( self.posoff )
self:SetLocalAngles( self.angoff )
self.HTE = true
if SERVER and self.PredictCL then
timer.Simple(0.05,function()
if IsValid(self) then
net.Start("tfaArrowFollow")
net.WriteEntity( self )
net.WriteEntity( self.targent )
net.WriteInt( self.targbone, 8 )
net.WriteVector( self.posoff )
net.WriteAngle( self.angoff )
net.Broadcast()
end
end)
end
end
end
function ENT:Think()
if CLIENT and not self.PredictCL then return end
if IsValid(self.targent) and self.targent.Health and self.targent:Health() <= 0 and self.targent.GetRagdollEntity then
local rag = self.targent:GetRagdollEntity()
if IsValid(rag) then
self.targent = rag
self:TargetEnt( false )
end
end
local par = self:GetParent()
if IsValid(par) and self.UseMod and not par.HasUseMod then
par.HasUseMod = true
par.ArrowUseOld = par.ArrowUseOld or par.Use
par.Use = function( parent, ... )
for _,v in pairs( par:GetChildren() ) do
if v.Use then v:Use(...) end
end
parent:Use( ... )
end
par:SetUseType( SIMPLE_USE )
end
if SERVER and not self.HTE then
self:TargetEnt( true )
end
self:NextThink(CurTime())
return true
end

View File

@@ -0,0 +1,260 @@
local SWEP = {}
local BaseClass = baseclass.Get("tfa_gun_base")
local scopeshadowcvar = GetConVar("cl_tfa_3dscope_overlay")
local sp = game.SinglePlayer()
function SWEP:Do3DScope()
return true
end
function SWEP:Do3DScopeOverlay()
if scopeshadowcvar then
return scopeshadowcvar:GetBool()
else
return false
end
end
function SWEP:UpdateScopeType()
-- empty, function retains for error preventing
end
function SWEP:Initialize(...)
local unsetA = self.Primary_TFA == nil
local unsetB = self.Secondary_TFA == nil
self.Primary_TFA = self.Primary_TFA or self.Primary
self.Secondary_TFA = self.Secondary_TFA or self.Secondary
if unsetA then
self.Primary_TFA = nil
end
if unsetB then
self.Secondary_TFA = nil
end
BaseClass.Initialize(self, ...)
end
local flipcv = GetConVar("cl_tfa_viewmodel_flip")
local cd = {}
local crosscol = Color(255, 255, 255, 255)
SWEP.RTOpaque = true
local cv_cc_r = GetConVar("cl_tfa_hud_crosshair_color_r")
local cv_cc_g = GetConVar("cl_tfa_hud_crosshair_color_g")
local cv_cc_b = GetConVar("cl_tfa_hud_crosshair_color_b")
local cv_cc_a = GetConVar("cl_tfa_hud_crosshair_color_a")
SWEP.defaultscrvec = Vector()
SWEP.ScopeAngleTransformISAngleFallback = true -- fallback to reverse ironsights angle for scope transforms
SWEP.ScopeAngleTransforms = { -- this is bad this is bad this is bad this is bad this is bad this is bad this is bad this is bad this is bad
-- {"P", 0}, -- Pitch
-- {"Y", 0}, -- Yaw
-- {"R", 0}, -- Roll
}
function SWEP:RTCode(rt, scrw, scrh)
local legacy = self.ScopeLegacyOrientation
local rttw = ScrW()
local rtth = ScrH()
if not self:VMIV() then return end
if not self.myshadowmask then
self.myshadowmask = surface.GetTextureID(self.ScopeShadow or "vgui/scope_shadowmask_test")
end
if not self.myreticule then
self.myreticule = Material(self.ScopeReticule or "scope/gdcw_scopesightonly")
end
if not self.mydirt then
self.mydirt = Material(self.ScopeDirt or "vgui/scope_dirt")
end
local vm = self.OwnerViewModel
if not self.LastOwnerPos then
self.LastOwnerPos = self:GetOwner():GetShootPos()
end
local owoff = self:GetOwner():GetShootPos() - self.LastOwnerPos
self.LastOwnerPos = self:GetOwner():GetShootPos()
local scrpos
local attShadowID = self:GetStatL("RTScopeShadowAttachment")
if attShadowID and attShadowID > 0 then
vm:SetupBones()
local att = vm:GetAttachment(attShadowID)
if att and att.Pos then
local pos = att.Pos - owoff
cam.Start3D()
cam.End3D()
scrpos = pos:ToScreen()
end
end
if not scrpos then
local spos = self:GetOwner():GetShootPos() + self:GetOwner():EyeAngles():Forward() * 16
local pos = spos - owoff
cam.Start3D()
cam.End3D()
scrpos = pos:ToScreen()
-- self.defaultscrvec.x = scrw / 2
-- self.defaultscrvec.y = scrh / 2
-- scrpos = self.defaultscrvec
end
scrpos.x = scrpos.x - scrw / 2 + self.ScopeOverlayTransforms[1]
scrpos.y = scrpos.y - scrh / 2 + self.ScopeOverlayTransforms[2]
scrpos.x = scrpos.x / scrw * 1920
scrpos.y = scrpos.y / scrw * 1920
scrpos.x = math.Clamp(scrpos.x, -1024, 1024)
scrpos.y = math.Clamp(scrpos.y, -1024, 1024)
--scrpos.x = scrpos.x * ( 2 - self:GetIronSightsProgress()*1 )
--scrpos.y = scrpos.y * ( 2 - self:GetIronSightsProgress()*1 )
scrpos.x = scrpos.x * self.ScopeOverlayTransformMultiplier
scrpos.y = scrpos.y * self.ScopeOverlayTransformMultiplier
if not self.scrpos then
self.scrpos = scrpos
end
self.scrpos.x = math.Approach(self.scrpos.x, scrpos.x, (scrpos.x - self.scrpos.x) * FrameTime() * 10)
self.scrpos.y = math.Approach(self.scrpos.y, scrpos.y, (scrpos.y - self.scrpos.y) * FrameTime() * 10)
scrpos = self.scrpos
render.OverrideAlphaWriteEnable(true, true)
surface.SetDrawColor(color_white)
surface.DrawRect(-512, -512, 1024, 1024)
render.OverrideAlphaWriteEnable(true, true)
local ang = legacy and self:GetOwner():EyeAngles() or vm:GetAngles()
local attID = self:GetStatL("RTScopeAttachment")
if attID and attID > 0 then
vm:SetupBones()
local AngPos = vm:GetAttachment( attID )
if AngPos then
ang = AngPos.Ang
if flipcv:GetBool() then
ang.y = -ang.y
end
end
elseif self:GetStatL("ScopeAngleTransformISAngleFallback") then
local isang = self:GetStatL("IronSightsAngle") * self:GetIronSightsProgress()
ang:RotateAroundAxis(ang:Forward(), -isang.z)
ang:RotateAroundAxis(ang:Right(), -isang.x)
ang:RotateAroundAxis(ang:Up(), -isang.y)
ang:RotateAroundAxis(ang:Forward(), isang.z)
end
-- WHY WHY WHY WHY WHY WHY WHY WHY
for _, v in ipairs(self:GetStatL("ScopeAngleTransforms")) do
if v[1] == "P" then
ang:RotateAroundAxis(ang:Right(), v[2])
elseif v[1] == "Y" then
ang:RotateAroundAxis(ang:Up(), v[2])
elseif v[1] == "R" then
ang:RotateAroundAxis(ang:Forward(), v[2])
end
end
cd.angles = ang
cd.origin = self:GetOwner():GetShootPos()
if not self.RTScopeOffset then
self.RTScopeOffset = {0, 0}
end
if not self.RTScopeScale then
self.RTScopeScale = {1, 1}
end
local rtow, rtoh = self.RTScopeOffset[1], self.RTScopeOffset[2]
local rtw, rth = rttw * self.RTScopeScale[1], rtth * self.RTScopeScale[2]
cd.x = 0
cd.y = 0
cd.w = rtw
cd.h = rth
cd.fov = self:GetStatL("RTScopeFOV")
cd.drawviewmodel = false
cd.drawhud = false
render.Clear(0, 0, 0, 255, true, true)
render.SetScissorRect(0 + rtow, 0 + rtoh, rtw + rtow, rth + rtoh, true)
if self:GetIronSightsProgress() > 0.01 and self.Scoped_3D then
render.RenderView(cd)
end
render.SetScissorRect(0, 0, rtw, rth, false)
render.OverrideAlphaWriteEnable(false, true)
cam.Start2D()
draw.NoTexture()
surface.SetTexture(self.myshadowmask)
surface.SetDrawColor(color_white)
if self:Do3DScopeOverlay() then
surface.DrawTexturedRect(scrpos.x + rtow - rtw / 2, scrpos.y + rtoh - rth / 2, rtw * 2, rth * 2)
end
if self.ScopeReticule_CrossCol then
crosscol.r = cv_cc_r:GetFloat()
crosscol.g = cv_cc_g:GetFloat()
crosscol.b = cv_cc_b:GetFloat()
crosscol.a = cv_cc_a:GetFloat()
surface.SetDrawColor(crosscol)
end
surface.SetMaterial(self.myreticule)
local tmpborderw = rtw * (1 - self.ScopeReticule_Scale[1]) / 2
local tmpborderh = rth * (1 - self.ScopeReticule_Scale[2]) / 2
surface.DrawTexturedRect(rtow + tmpborderw, rtoh + tmpborderh, rtw - tmpborderw * 2, rth - tmpborderh * 2)
surface.SetDrawColor(color_black)
draw.NoTexture()
if self:Do3DScopeOverlay() then
surface.DrawRect(scrpos.x - 2048 + rtow, -1024 + rtoh, 2048, 2048)
surface.DrawRect(scrpos.x + rtw + rtow, -1024 + rtoh, 2048, 2048)
surface.DrawRect(-1024 + rtow, scrpos.y - 2048 + rtoh, 2048, 2048)
surface.DrawRect(-1024 + rtow, scrpos.y + rth + rtoh, 2048, 2048)
end
surface.SetDrawColor(ColorAlpha(color_black, 255 - 255 * (math.Clamp(self:GetIronSightsProgress() - 0.75, 0, 0.25) * 4)))
surface.DrawRect(-1024 + rtow, -1024 + rtoh, 2048, 2048)
surface.SetMaterial(self.mydirt)
surface.SetDrawColor(ColorAlpha(color_white, 128))
surface.DrawTexturedRect(0, 0, rtw, rth)
surface.SetDrawColor(ColorAlpha(color_white, 64))
surface.DrawTexturedRectUV(rtow, rtoh, rtw, rth, 2, 0, 0, 2)
cam.End2D()
end
local function l_Lerp(v, f, t)
return f + (t - f) * v
end
function SWEP:AdjustMouseSensitivity(...)
local retVal = BaseClass.AdjustMouseSensitivity(self, ...)
retVal = retVal * l_Lerp(self:GetIronSightsProgress(), 1, self:Get3DSensitivity())
return retVal
end
return SWEP

View File

@@ -0,0 +1,35 @@
if not ATTACHMENT then
ATTACHMENT = {}
end
-- ATTACHMENT.TFADataVersion = 1 -- Uncomment this in your attachment file
-- If it is undefined, if fallback to 0 and WeaponTable gets migrated like SWEPs do
ATTACHMENT.Name = "Base Attachment"
ATTACHMENT.ShortName = nil --Abbreviation, 5 chars or less please
ATTACHMENT.Description = {} --TFA.Attachments.Colors["+"], "Does something good", TFA.Attachments.Colors["-"], "Does something bad" }
ATTACHMENT.Icon = nil --Revers to label, please give it an icon though! This should be the path to a png, like "entities/tfa_ammo_match.png"
ATTACHMENT.WeaponTable = {} --put replacements for your SWEP talbe in here e.g. ["Primary"] = {}
ATTACHMENT.DInv2_GridSizeX = nil -- DInventory/2 Specific. Determines attachment's width in grid.
ATTACHMENT.DInv2_GridSizeY = nil -- DInventory/2 Specific. Determines attachment's height in grid.
ATTACHMENT.DInv2_Volume = nil -- DInventory/2 Specific. Determines attachment's volume in liters.
ATTACHMENT.DInv2_Mass = nil -- DInventory/2 Specific. Determines attachment's mass in kilograms.
ATTACHMENT.DInv2_StackSize = nil -- DInventory/2 Specific. Determines attachment's maximal stack size.
ATTACHMENT.TFADataVersion = nil -- TFA.LatestDataVersion, specifies version of TFA Weapon Data this attachment utilize in `WeaponTable`
-- 0 is original, M9K-like data, and is the fallback if `TFADataVersion` is undefined
function ATTACHMENT:CanAttach(wep)
return true --can be overridden per-attachment
end
function ATTACHMENT:Attach(wep)
end
function ATTACHMENT:Detach(wep)
end
if not TFA_ATTACHMENT_ISUPDATING then
TFAUpdateAttachments()
end

View File

@@ -0,0 +1,109 @@
if not ATTACHMENT then
ATTACHMENT = {}
end
ATTACHMENT.Name = "RT Scope Base"
ATTACHMENT.Description = {}
ATTACHMENT.WeaponTable = {
["RTDrawEnabled"] = true,
-- ["RTScopeFOV"] = 90 / 1 / 2, -- Default FOV / Scope Zoom / screenscale
-- ["RTScopeAttachment"] = -1,
-- ["RTReticleMaterial"] = Material("scope/gdcw_acog"),
-- ["RTReticleColor"] = color_white,
-- ["RTReticleScale"] = 1,
-- ["RTShadowMaterial"] = Material("vgui/scope_shadowmask_test"),
-- ["RTShadowColor"] = color_white,
-- ["RTShadowScale"] = 2,
}
local cd = {}
local fallbackReticle = Material("scope/gdcw_scopesightonly")
local fallbackShadow = Material("vgui/scope_shadowmask_test")
local flipcv = GetConVar("cl_tfa_viewmodel_flip")
function ATTACHMENT:RTCode(wep, rt, scrw, scrh)
if not wep.OwnerIsValid or not wep:VMIV() then return end
local rtw, rth = rt:Width(), rt:Height()
-- clearing view
render.OverrideAlphaWriteEnable(true, true)
surface.SetDrawColor(color_white)
surface.DrawRect(-rtw, -rth, rtw * 2, rth * 2)
local vm = wep.OwnerViewModel
local ang = vm:GetAngles()
local isang = wep:GetStatL("IronSightsAngle") * wep:GetIronSightsProgress()
ang:RotateAroundAxis(ang:Forward(), -isang.z)
ang:RotateAroundAxis(ang:Right(), -isang.x)
ang:RotateAroundAxis(ang:Up(), -isang.y)
ang:RotateAroundAxis(ang:Forward(), isang.z)
local scopeAtt = wep:GetStatL("RTScopeAttachment", -1)
if scopeAtt > 0 then
local AngPos = vm:GetAttachment(scopeAtt)
if AngPos then
ang = AngPos.Ang
if flipcv:GetBool() then
ang.y = -ang.y
end
end
end
cd.angles = ang
cd.origin = wep:GetOwner():GetShootPos()
cd.x = 0
cd.y = 0
cd.w = rtw
cd.h = rth
cd.fov = wep:GetStatL("RTScopeFOV", 90 / wep:GetStatL("ScopeZoom", 1) / 2)
cd.drawviewmodel = false
cd.drawhud = false
-- main RT render view
render.Clear(0, 0, 0, 255, true, true)
render.SetScissorRect(0, 0, rtw, rth, true)
if wep:GetIronSightsProgress() > 0.005 then
render.RenderView(cd)
end
render.SetScissorRect(0, 0, rtw, rth, false)
render.OverrideAlphaWriteEnable(false, true)
cam.Start2D()
-- ADS transition darkening
draw.NoTexture()
surface.SetDrawColor(ColorAlpha(color_black, 255 * (1 - wep:GetIronSightsProgress())))
surface.DrawRect(0, 0, rtw, rth)
surface.SetMaterial(wep:GetStatL("RTReticleMaterial", fallbackReticle))
surface.SetDrawColor(wep:GetStatL("RTReticleColor", color_white))
local retScale = wep:GetStatL("RTReticleScale", 1)
surface.DrawTexturedRect(rtw / 2 - rtw * retScale / 2, rth / 2 - rth * retScale / 2, rtw * retScale, rth * retScale)
surface.SetMaterial(wep:GetStatL("RTShadowMaterial", fallbackShadow))
surface.SetDrawColor(wep:GetStatL("RTShadowColor", color_white))
local shadScale = wep:GetStatL("RTShadowScale", 2)
surface.DrawTexturedRect(rtw / 2 - rtw * shadScale / 2, rth / 2 - rth * shadScale / 2, rtw * shadScale, rth * shadScale)
cam.End2D()
end
if not TFA_ATTACHMENT_ISUPDATING then
TFAUpdateAttachments()
end

View File

@@ -0,0 +1,254 @@
if not TFA_ATTACHMENT_ISUPDATING then TFAUpdateAttachments(false) return end
TFA.Attachments.RegisterFromTable("am_gib", {
Name = "G.I.B Ammunition",
ShortName = "GIB",
Description = {
TFA.Attachments.Colors["+"], "Always gibs enemies",
TFA.Attachments.Colors["+"], "10% more damage",
TFA.Attachments.Colors["-"], "20% more recoil",
TFA.Attachments.Colors["-"], "10% more spread"
},
Icon = "entities/tfa_ammo_gib.png",
TFADataVersion = TFA.LatestDataVersion,
WeaponTable = {
Primary = {
DamageType = function(wep,stat) return bit.bor( stat or DMG_BULLET, DMG_ALWAYSGIB ) end,
Damage = function( wep, stat ) return stat * 1.1 end,
Spread = function( wep, stat ) return stat * 1.1 end,
IronAccuracy = function( wep, stat ) return stat * 1.1 end,
KickUp = function( wep, stat ) return stat * 1.2 end,
KickDown = function( wep, stat ) return stat * 1.2 end
}
}
})
TFA.Attachments.RegisterFromTable("am_magnum", {
Name = "Magnum Ammunition",
ShortName = "MAG",
Description = {
TFA.Attachments.Colors["+"], "10% more damage",
TFA.Attachments.Colors["-"], "15% more recoil",
TFA.Attachments.Colors["-"], "10% more spread"
},
Icon = "entities/tfa_ammo_magnum.png",
TFADataVersion = TFA.LatestDataVersion,
WeaponTable = {
Primary = {
Damage = function( wep, stat ) return stat * 1.1 end,
Spread = function( wep, stat ) return stat * 1.1 end,
IronAccuracy = function( wep, stat ) return stat * 1.1 end,
KickUp = function( wep, stat ) return stat * 1.15 end,
KickDown = function( wep, stat ) return stat * 1.15 end
}
}
})
TFA.Attachments.RegisterFromTable("am_match", {
Name = "Match Ammunition",
ShortName = "Match",
Description = {
TFA.Attachments.Colors["+"], "20% lower spread kick",
"10% lower recoil",
TFA.Attachments.Colors["-"], "20% lower spread recovery"
},
Icon = "entities/tfa_ammo_match.png",
TFADataVersion = TFA.LatestDataVersion,
WeaponTable = {
Primary = {
SpreadIncrement = function( wep, stat ) return stat * 0.9 end,
SpreadRecovery = function( wep, stat ) return stat * 0.8 end,
KickUp = function( wep, stat ) return stat * 0.9 end,
KickDown = function( wep, stat ) return stat * 0.9 end
}
}
})
TFA.Attachments.RegisterFromTable("sg_frag", {
Name = "Frag Ammunition",
ShortName = "Frag",
Description = {
TFA.Attachments.Colors["+"], "Explosive Damage",
"2x damage",
TFA.Attachments.Colors["-"], "0.5x pellets"
},
Icon = "entities/tfa_ammo_fragshell.png",
TFADataVersion = TFA.LatestDataVersion,
WeaponTable = {
["Primary"] = {
["DamageType"] = function(wep,stat) return bit.bor( stat or 0, DMG_BLAST ) end,
["Damage"] = function(wep,stat) return stat * 2 end,
["NumShots"] = function(wep,stat) return stat / 2 end
}
}
})
TFA.Attachments.RegisterFromTable("sg_slug", {
Name = "Slug Ammunition",
ShortName = "Slug",
Description = {
TFA.Attachments.Colors["+"], "Much lower spread",
TFA.Attachments.Colors["+"], "100m higher range",
TFA.Attachments.Colors["-"], "30% less damage",
"One pellet"
},
Icon = "entities/tfa_ammo_slug.png",
TFADataVersion = TFA.LatestDataVersion,
WeaponTable = {
Primary = {
Damage = function( wep, stat ) return wep.Primary_TFA.NumShots * stat * 0.7 end,
NumShots = function( wep, stat ) return 1, true end,
Spread = function( wep, stat ) return math.max( stat - 0.015, stat * 0.5 ) end,
IronAccuracy = function( wep, stat ) return math.max( stat - 0.03, stat * 0.25 ) end,
Range = function( wep, stat ) return stat + 100 * 39.370 end
}
}
})
TFA.Attachments.RegisterFromTable("br_supp", {
Name = "Suppressor",
Description = {
TFA.Attachments.Colors["+"], "Less firing noise",
TFA.Attachments.Colors["-"], "10% less spread",
TFA.Attachments.Colors["-"], "5% less damage",
TFA.Attachments.Colors["-"], "10% less vertical recoil"
},
Icon = "entities/tfa_br_supp.png",
ShortName = "SUPP",
TFADataVersion = TFA.LatestDataVersion,
WeaponTable = {
["ViewModelElements"] = {
["suppressor"] = {
["active"] = true
}
},
["WorldModelElements"] = {
["suppressor"] = {
["active"] = true
}
},
["Primary"] = {
["Damage"] = function(wep,stat) return stat * 0.95 end,
["KickUp"] = function(wep,stat) return stat * 0.9 end,
["KickDown"] = function(wep,stat) return stat * 0.9 end,
["Spread"] = function(wep,stat) return stat * 0.9 end,
["IronAccuracy"] = function(wep,stat) return stat * 0.9 end,
["Sound"] = function(wep,stat) return wep.Primary.SilencedSound or stat end
},
["MuzzleFlashEffect"] = "tfa_muzzleflash_silenced",
["MuzzleAttachmentMod"] = function(wep,stat) return wep.MuzzleAttachmentSilenced or stat end
}
})
TFA.Attachments.RegisterFromTable("si_acog", {
Base = "si_rt_base",
Name = "ACOG",
Description = {
TFA.Attachments.Colors["="], "4x zoom",
TFA.Attachments.Colors["-"], "20% higher zoom time",
TFA.Attachments.Colors["-"], "10% slower aimed walking"
},
Icon = "entities/tfa_si_acog.png",
ShortName = "ACOG",
TFADataVersion = TFA.LatestDataVersion,
WeaponTable = {
["ViewModelElements"] = {
["acog"] = {
["active"] = true
},
["rtcircle_acog"] = {
["active"] = true
}
},
["WorldModelElements"] = {
["acog"] = {
["active"] = true
}
},
["IronSightsPosition"] = function( wep, val ) return wep.IronSightsPos_ACOG or val, true end,
["IronSightsAngle"] = function( wep, val ) return wep.IronSightsAng_ACOG or val, true end,
["IronSightsSensitivity"] = function(wep,val) return TFA.CalculateSensitivtyScale( 90 / 4 / 2, wep:GetStatL("Secondary.OwnerFOV"), wep.ACOGScreenScale ) end ,
["Secondary"] = {
["OwnerFOV"] = function( wep, val ) return val * 0.7 end
},
["IronSightTime"] = function( wep, val ) return val * 1.20 end,
["IronSightMoveSpeed"] = function(stat) return stat * 0.9 end,
["RTOpaque"] = true,
["RTMaterialOverride"] = -1,
["RTScopeFOV"] = 90 / 4 / 2, -- Default FOV / Scope Zoom / screenscale
["RTReticleMaterial"] = Material("scope/gdcw_acog"),
["RTReticleScale"] = 1,
}
})
TFA.Attachments.RegisterFromTable("si_aimpoint", {
Name = "Aimpoint",
Description = {
TFA.Attachments.Colors["="], "10% higher zoom",
TFA.Attachments.Colors["-"], "10% higher zoom time"
},
Icon = "entities/tfa_si_aimpoint.png",
ShortName = "AIM",
TFADataVersion = TFA.LatestDataVersion,
WeaponTable = {
["ViewModelElements"] = {
["aimpoint"] = {
["active"] = true
},
["aimpoint_spr"] = {
["active"] = true
}
},
["WorldModelElements"] = {
["aimpoint"] = {
["active"] = true
},
["aimpoint_spr"] = {
["active"] = true
}
},
["IronSightsPosition"] = function( wep, val ) return wep.IronSightsPos_AimPoint or val, true end,
["IronSightsAngle"] = function( wep, val ) return wep.IronSightsAng_AimPoint or val, true end,
["Secondary"] = {
["OwnerFOV"] = function( wep, val ) return val * 0.9 end
},
["IronSightTime"] = function( wep, val ) return val * 1.10 end
}
})
TFA.Attachments.RegisterFromTable("si_eotech", {
Name = "EOTech",
Description = {
TFA.Attachments.Colors["="], "10% higher zoom",
TFA.Attachments.Colors["-"], "10% higher zoom time"
},
Icon = "entities/tfa_si_eotech.png",
ShortName = "EOTEK",
TFADataVersion = TFA.LatestDataVersion,
WeaponTable = {
["ViewModelElements"] = {
["eotech"] = {
["active"] = true
}
},
["WorldModelElements"] = {
["eotech"] = {
["active"] = true
}
},
["IronSightsPosition"] = function( wep, val ) return wep.IronSightsPos_EOTech or val, true end,
["IronSightsAngle"] = function( wep, val ) return wep.IronSightsAng_EOTech or val, true end,
["Secondary"] = {
["OwnerFOV"] = function( wep, val ) return val * 0.9 end
},
["IronSightTime"] = function( wep, val ) return val * 1.10 end
}
})

View File

@@ -0,0 +1,372 @@
local vector_origin = Vector()
--[[Bullet Struct:
[BULLET_ID] = {
["owner"] = Entity, --used for dmginfo SetAttacker
["inflictor"] = Entity, --used for dmginfo SetInflictor
["damage"] = Double, --floating point number representing inflicted damage
["force"] = Double,
["pos"] = Vector, --vector representing current position
["velocity"] = Vector, --vector representing movement velocity
["model"] = String --optional variable representing the given model,
["bul"] = {} --optional table containing bullet data,
["smokeparticle"] = String, --smoke particle name from within pcf
["bulletOverride"] = Bool --disable coming out of gun barrel on clientside
}
]]
local BallisticBullet = {
["owner"] = NULL,
["inflictor"] = NULL,
["damage"] = 0,
["force"] = 0,
["pos"] = vector_origin,
["velocity"] = vector_origin,
["model"] = "models/bullets/w_pbullet1.mdl",
["bul"] = {},
["delete"] = false,
["smokeparticle"] = "tfa_bullet_smoke_tracer"
}
local traceRes = {}
local traceData = {
mask = MASK_SHOT,
collisiongroup = COLLISION_GROUP_NONE,
ignoreworld = false,
output = traceRes
}
local MASK_SHOT_NOWATER = MASK_SHOT
--main update block
function BallisticBullet:Update(delta)
if self.delete then return end
self:_setup()
if self.delete then return end
local realdelta = (delta - self.last_update) / TFA.Ballistics.SubSteps
self.last_update = delta
local newPos = self:_getnewPosition(realdelta)
newPos = self:_checkWater(realdelta, newPos)
self:_accelerate(realdelta)
self:_moveSafe(newPos)
end
--internal function for sanity checks, etc.
function BallisticBullet:_setup()
self.creationTime = CurTime()
if (not IsValid(self.owner)) or (not IsValid(self.inflictor)) then
self:Remove()
end
if CurTime() > self.creationTime + TFA.Ballistics.BulletLife then
self:Remove()
end
self.playerOwned = self.owner.IsPlayer and self.owner:IsPlayer()
self.startVelocity = self.velocity:Length()
self.startDamage = self.damage
end
function BallisticBullet:_think()
if (not IsValid(self.owner)) or (not IsValid(self.inflictor)) then
self:Remove()
end
if CurTime() > self.creationTime + TFA.Ballistics.BulletLife then
self:Remove()
end
end
--internal function for calculating position change
function BallisticBullet:_getnewPosition(delta)
--verlet
return self.pos + (self.velocity + TFA.Ballistics.Gravity * delta * 0.5) * delta
end
--internal function for handling water
function BallisticBullet:_checkWater(delta, target)
local newPos = target
traceData.start = self.pos
traceData.endpos = newPos
traceData.filter = {self.owner, self.inflictor}
traceData.mask = MASK_WATER
util.TraceLine(traceData)
if traceRes.Hit and traceRes.Fraction < 1 and traceRes.Fraction > 0 and not self.Underwater then
self.Underwater = true
newPos = traceRes.HitPos + traceRes.Normal
self.velocity = self.velocity / TFA.Ballistics.WaterEntranceResistance
local fx = EffectData()
fx:SetOrigin(newPos)
local sc = math.sqrt(self.damage / 28) * 6
fx:SetScale(sc)
util.Effect("gunshotsplash", fx)
end
return newPos
end
--internal function for handling acceleration
local function GetWind()
return vector_origin
end
if StormFox and StormFox.Version then
if StormFox.Version < 2 then -- SF1
local SF_GetNetworkData = StormFox.GetNetworkData
function GetWind()
local windSpeed = SF_GetNetworkData("Wind") * TFA.Ballistics.UnitScale
local windAng = Angle(0, SF_GetNetworkData("WindAngle"), 0)
return windSpeed * windAng:Forward():GetNormalized()
end
elseif StormFox.Wind then -- SF2
local SFW_GetForce = StormFox.Wind.GetForce
local SFW_GetYaw = StormFox.Wind.GetYaw
function GetWind()
local windSpeed = SFW_GetForce() * TFA.Ballistics.UnitScale
local windAng = Angle(0, SFW_GetYaw(), 0)
return windSpeed * windAng:Forward():GetNormalized()
end
end
end
function BallisticBullet:_accelerate(delta)
local dragDensity = self.Underwater and TFA.Ballistics.WaterResistance or TFA.Ballistics.AirResistance
local drag = -self.velocity:GetNormalized() * self.velocity:Length() * self.velocity:Length() * 0.00006 * dragDensity
local wind = GetWind()
if self.Underwater then
self.velocity = self.velocity / (1 + TFA.Ballistics.WaterResistance * delta)
end
self.velocity = self.velocity + (TFA.Ballistics.Gravity + drag + wind) * delta
self.damage = self.startDamage * math.sqrt(self.velocity:Length() / self.startVelocity)
end
local IsInWorld, IsInWorld2
do
local tr = {collisiongroup = COLLISION_GROUP_WORLD}
function IsInWorld2(pos)
tr.start = pos
tr.endpos = pos
return not util.TraceLine(tr).AllSolid
end
end
if CLIENT then
IsInWorld = IsInWorld2
else
IsInWorld = util.IsInWorld
end
--internal function for moving with collision test
function BallisticBullet:_moveSafe(newPos)
if not self.tr_filter then
if IsValid(self.IgnoreEntity) then
self.tr_filter = {self.owner, self.inflictor, self.IgnoreEntity}
else
self.tr_filter = {self.owner, self.inflictor}
end
end
traceData.start = self.pos
traceData.endpos = newPos + (newPos - self.pos):GetNormalized()
traceData.filter = self.tr_filter
traceData.mask = MASK_SHOT_NOWATER
--collision trace
if self.playerOwned then
self.owner:LagCompensation(true)
end
util.TraceLine(traceData)
if self.playerOwned then
self.owner:LagCompensation(false)
end
--collision check
if traceRes.Hit and traceRes.Fraction < 1 and traceRes.Fraction > 0 then
self:Impact(traceRes)
elseif IsInWorld(newPos) then
self.pos = newPos
else
self:Remove()
end
end
--called when hitting something, or manually if necessary
function BallisticBullet:Impact(tr)
self.pos = tr.HitPos
self:Remove()
if CLIENT and (game.SinglePlayer() or self.owner ~= LocalPlayer()) then return end
if tr.HitSky then return end
local vn = self.velocity:GetNormalized()
local bul = {
["Damage"] = self.damage,
["Force"] = self.force,
["Num"] = 1,
["Src"] = self.pos - vn * 4,
["Dir"] = vn * 8,
["Spread"] = vector_origin,
["IgnoreEntity"] = self.owner,
["Attacker"] = self.owner,
["Distance"] = 8,
["Tracer"] = 0
}
setmetatable(bul, {
["__index"] = self.bul
})
self.owner:FireBullets(bul)
end
--Render
--local cv_bullet_style, cv_tracers_adv
local cv_bullet_style
if CLIENT then
CreateClientConVar("cl_tfa_ballistics_mp", "1", true, false, "Receive bullet data from other players?")
cv_bullet_style = CreateClientConVar("cl_tfa_ballistics_fx_bullet", "1", true, false, "Display bullet models for each TFA ballistics bullet?")
CreateClientConVar("cl_tfa_ballistics_fx_tracers_style", "1", true, false, "Style of tracers for TFA ballistics? 0=disable,1=smoke")
CreateClientConVar("cl_tfa_ballistics_fx_tracers_mp", "1", true, false, "Enable tracers for other TFA ballistics users?")
--cv_tracers_adv = CreateClientConVar("cl_tfa_ballistics_fx_tracers_adv", "1", true, false, "Enable advanced tracer calculations for other users? This corrects smoke trail to their barrel")
--[[
cv_receive = GetConVar("cl_tfa_ballistics_mp")
cv_bullet_style = GetConVar("cl_tfa_ballistics_fx_bullet")
cv_tracers_style = GetConVar("cl_tfa_ballistics_fx_tracers_style")
cv_tracers_mp = GetConVar("cl_tfa_ballistics_fx_tracers_mp")
cv_tracers_adv = GetConVar("cl_tfa_ballistics_fx_tracers_adv")
]]
--
end
--[[local DEFANGPOS = {
Pos = vector_origin,
Ang = angle_zero
}]]
function BallisticBullet:Render()
if SERVER then return end
if self.delete then return end
if not self.curmodel then
self.curmodel = ClientsideModel(self.model, RENDERGROUP_OPAQUE)
self.curmodel:SetNoDraw(not cv_bullet_style:GetBool())
end
--[==[if IsValid(self.curmodel) and (cv_bullet_style:GetBool() or self.smokeparticle ~= "") then
if self.customPosition then
fpos = self.pos
--fang = self.velocity:Angle()
else
if self.owner == GetViewEntity() or self.owner == LocalPlayer() then
local spos, sang = self.pos, self.velocity:Angle()
self.curmodel:SetPos(spos)
self.curmodel:SetAngles(sang)
if not self.vOffsetPos then
local att
if self.inflictor.GetMuzzleAttachment and self.inflictor:GetMuzzleAttachment() then
att = self.inflictor:GetMuzzleAttachment()
else
att = self.inflictor.MuzzleAttachmentRaw or 1
end
if LocalPlayer():ShouldDrawLocalPlayer() then
local npos = LocalPlayer():GetActiveWeapon():GetAttachment(att) or DEFANGPOS
self.vOffsetPos = self.curmodel:WorldToLocal(npos.Pos)
self.vOffsetAng = self.curmodel:WorldToLocalAngles(npos.Ang)
else
local npos = LocalPlayer():GetViewModel():GetAttachment(att) or DEFANGPOS
self.vOffsetPos = self.curmodel:WorldToLocal(npos.Pos)
self.vOffsetAng = self.curmodel:WorldToLocalAngles(npos.Ang)
end
end
fpos = self.curmodel:LocalToWorld(self.vOffsetPos)
--fang = self.curmodel:LocalToWorldAngles(self.vOffsetAng)
elseif self.owner:IsPlayer() and cv_tracers_adv:GetBool() then
local spos, sang = self.pos, self.velocity:Angle()
self.curmodel:SetPos(spos)
self.curmodel:SetAngles(sang)
if not self.vOffsetPos then
local npos = self.owner:GetActiveWeapon():GetAttachment(1) or DEFANGPOS
self.vOffsetPos = self.curmodel:WorldToLocal(npos.Pos)
self.vOffsetAng = self.curmodel:WorldToLocalAngles(npos.Ang)
end
fpos = self.curmodel:LocalToWorld(self.vOffsetPos)
--fang = self.curmodel:LocalToWorldAngles(self.vOffsetAng)
else
fpos = self.pos
--fang = self.velocity:Angle()
end
end
--[[if cv_bullet_style:GetBool() then
self.curmodel:SetupBones()
self.curmodel:DrawModel()
end]]
end]==]
local fpos, fang = self.pos, self.velocity:Angle()
self.curmodel:SetPos(fpos)
self.curmodel:SetAngles(fang)
if self.smokeparticle ~= "" and not self.cursmoke then
self.cursmoke = CreateParticleSystem(self.curmodel, self.smokeparticle, PATTACH_ABSORIGIN_FOLLOW, 1)
if not self.cursmoke then return end
self.cursmoke:StartEmission()
elseif self.cursmoke and IsValid(self.owner) then
self.cursmoke:SetSortOrigin(self.owner.GetShootPos and self.owner:GetShootPos() or self.owner.EyePos and self.owner:EyePos() or vector_origin)
if self.Underwater then
self.cursmoke:StopEmission()
self.cursmoke = nil
self.smokeparticle = ""
end
end
end
function BallisticBullet:Remove()
if self.cursmoke then
self.cursmoke:StopEmission()
self.cursmoke = nil
end
if self.curmodel and self.curmodel.Remove then
self.curmodel:Remove()
self.curmodel = nil
end
self.delete = true
end
local CopyTable = table.Copy
function TFA.Ballistics:Bullet(t)
local b = CopyTable(t or {})
setmetatable(b, {
["__index"] = BallisticBullet
})
return b
end

View File

@@ -0,0 +1,248 @@
-- TFA Base Animations reference
-- Example of animation table:
SWEP.Animations = {
["shoot1"] = {
-- Еype of the animation entry, either TFA.Enum.ANIMATION_ACT or TFA.Enum.ANIMATION_SEQ
-- ALL ENTRIES OF THE ANIMATION MUST BE OF THE SAME TYPE!!!
-- for ANIMATION_ACT, "value" must be one of the enums from https://wiki.facepunch.com/gmod/Enums/ACT
-- for ANIMATION_SEQ "value" is the sequence name
["type"] = TFA.Enum.ANIMATION_ACT,
-- Basic
["value"] = ACT_VM_PRIMARYATTACK,
["value_empty"] = ACT_VM_DRYFIRE,
["value_last"] = ACT_VM_PRIMARYATTACK_EMPTY,
-- Silenced
["value_sil"] = ACT_VM_PRIMARYATTACK_SILENCED,
["value_sil_empty"] = ACT_VM_DRYFIRE_SILENCED,
-- Ironsights/ADS
["value_is"] = ACT_VM_PRIMARYATTACK_1,
["value_is_empty"] = ACT_VM_PRIMARYATTACK_2,
["value_is_last"] = ACT_VM_PRIMARYATTACK_3,
-- ADS + Silenced
["value_is_sil"] = ACT_VM_PRIMARYATTACK_DEPLOYED_1,
["value_is_sil_empty"] = ACT_VM_PRIMARYATTACK_DEPLOYED_2,
["value_is_sil_last"] = ACT_VM_PRIMARYATTACK_DEPLOYED_3,
-- Force enable animation (when it's not autodetected)
["enabled"] = true
},
}
-- Uncomment entry and add to SWEP.Animations table of your SWEP. DO NOT COPY THE WHOLE BLOCK!
-- SWEP.Animations = {
--[[ Gun Base ]]--
-- ["draw_first"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_DRAW_DEPLOYED,
-- ["enabled"] = nil
-- },
-- ["draw"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_DRAW
-- },
-- ["draw_empty"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_DRAW_EMPTY
-- },
-- ["draw_silenced"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_DRAW_SILENCED
-- },
-- ["shoot1"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_PRIMARYATTACK
-- },
-- ["shoot1_last"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_PRIMARYATTACK_EMPTY
-- },
-- ["shoot1_empty"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_DRYFIRE
-- },
-- ["shoot1_silenced"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_PRIMARYATTACK_SILENCED
-- },
-- ["shoot1_silenced_empty"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_DRYFIRE_SILENCED or 0
-- },
-- ["shoot1_is"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_PRIMARYATTACK_1
-- },
-- ["shoot2"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_SECONDARYATTACK
-- },
-- ["shoot2_last"] = {
-- ["type"] = TFA.Enum.ANIMATION_SEQ,
-- ["value"] = "shoot2_last"
-- },
-- ["shoot2_empty"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_DRYFIRE
-- },
-- ["shoot2_silenced"] = {
-- ["type"] = TFA.Enum.ANIMATION_SEQ,
-- ["value"] = "shoot2_silenced"
-- },
-- ["shoot2_is"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_ISHOOT_M203
-- },
-- ["idle"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_IDLE
-- },
-- ["idle_empty"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_IDLE_EMPTY
-- },
-- ["idle_silenced"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_IDLE_SILENCED
-- },
-- ["reload"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_RELOAD
-- },
-- ["reload_empty"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_RELOAD_EMPTY
-- },
-- ["reload_silenced"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_RELOAD_SILENCED
-- },
-- ["reload_shotgun_start"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_SHOTGUN_RELOAD_START
-- },
-- ["reload_shotgun_finish"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_SHOTGUN_RELOAD_FINISH
-- },
-- ["reload_is"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_RELOAD_ADS
-- },
-- ["reload_empty_is"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_RELOAD_EMPTY_ADS
-- },
-- ["reload_silenced_is"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_RELOAD_SILENCED_ADS
-- },
-- ["reload_shotgun_start_is"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_SHOTGUN_RELOAD_START_ADS
-- },
-- ["reload_shotgun_finish_is"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_SHOTGUN_RELOAD_FINISH_ADS
-- },
-- ["holster"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_HOLSTER
-- },
-- ["holster_empty"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_HOLSTER_EMPTY
-- },
-- ["holster_silenced"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_HOLSTER_SILENCED
-- },
-- ["silencer_attach"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_ATTACH_SILENCER
-- },
-- ["silencer_detach"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_DETACH_SILENCER
-- },
-- ["rof"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_FIREMODE
-- },
-- ["rof_is"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_IFIREMODE
-- },
-- ["inspect"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_FIDGET
-- },
-- ["inspect_empty"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_FIDGET_EMPTY
-- },
-- ["inspect_silenced"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_FIDGET_SILENCED
-- },
--[[ Bash Base ]]--
-- ["bash"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_HITCENTER
-- },
-- ["bash_silenced"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_HITCENTER2
-- },
-- ["bash_empty"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_MISSCENTER
-- },
-- ["bash_empty_silenced"] = {
-- ["type"] = TFA.Enum.ANIMATION_ACT,
-- ["value"] = ACT_VM_MISSCENTER2
-- },
-- }
-- Fake ACT enum entries added by the base
-- These values do not exist in the engine, so for those names use the replacement value in the $sequence definition:
-- ACT_VM_FIDGET_EMPTY - ACT_CROSSBOW_FIDGET_UNLOADED
-- ACT_VM_FIDGET_SILENCED - ACT_RPG_FIDGET_UNLOADED
-- ACT_VM_HOLSTER_SILENCED - ACT_CROSSBOW_HOLSTER_UNLOADED
-- ACT_VM_RELOAD_ADS - ACT_IDLE_AIM_RIFLE_STIMULATED
-- ACT_VM_RELOAD_EMPTY_ADS - ACT_WALK_AIM_RIFLE_STIMULATED
-- ACT_VM_RELOAD_SILENCED_ADS - ACT_RUN_AIM_RIFLE_STIMULATED
-- ACT_SHOTGUN_RELOAD_START_ADS - ACT_IDLE_SHOTGUN_RELAXED
-- ACT_SHOTGUN_RELOAD_FINISH_ADS - ACT_IDLE_SHOTGUN_STIMULATED
--[[ Bow Base ]]--
-- SWEP.BowAnimations = {
-- ["shake"] = {
-- ["type"] = TFA.Enum.ANIMATION_SEQ,
-- ["value"] = "tiredloop",
-- ["enabled"] = true
-- },
-- ["shoot"] = {
-- ["type"] = TFA.Enum.ANIMATION_SEQ,
-- ["value"] = "fire_1",
-- ["enabled"] = true
-- },
-- ["cancel"] = {
-- ["type"] = TFA.Enum.ANIMATION_SEQ,
-- ["value"] = "cancelarrow",
-- ["enabled"] = true
-- },
-- ["draw"] = {
-- ["type"] = TFA.Enum.ANIMATION_SEQ,
-- ["value"] = "drawarrow",
-- ["enabled"] = true
-- }
-- }

View File

@@ -0,0 +1,95 @@
-- TFA Base Attachment Template by TFA Base Devs
-- To the extent possible under law, the person who associated CC0 with
-- TFA Base Template has waived all copyright and related or neighboring rights
-- to TFA Base Template.
-- You should have received a copy of the CC0 legalcode along with this
-- work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
if not ATTACHMENT then
ATTACHMENT = {}
end
ATTACHMENT.TFADataVersion = 1 -- If it is undefined, it fallbacks to 0 and WeaponTable gets migrated like SWEPs do
-- ATTACHMENT.Base = "base" -- Attachment baseclass, defaults to "base" attachment
ATTACHMENT.Name = "Example Attachment"
ATTACHMENT.ShortName = nil -- Abbreviation shown on the bottom left of the icon, generated from name if not defined
ATTACHMENT.Description = {
TFA.Attachments.Colors["+"], "Does something good",
TFA.Attachments.Colors["-"], "Does something bad",
-- Color(255, 255, 255), "bottom text",
} -- all colors are defined in lua/tfa/modules/tfa_attachments.lua
ATTACHMENT.Icon = nil -- "entities/tfa_ammo_match.png" -- Full path to the icon, reverts to '?' by default
ATTACHMENT.WeaponTable = { -- The place where you change the stats (CACHED STATS ONLY!)
["Primary"] = {
["Damage"] = 60, -- For example, you want to change SWEP.Primary.Damage value to 60
["ClipSize"] = function(wep, stat)
return wep.Primary_TFA.ClipSize_Override or stat * 1.5
end -- Stat functions support changing value dynamically (which is cached afterwards), SWEP.Primary_TFA contains original unchanged values
}
}
-- ATTACHMENT.DInv2_GridSizeX = nil -- DInventory/2 Specific. Determines attachment's width in grid.
-- ATTACHMENT.DInv2_GridSizeY = nil -- DInventory/2 Specific. Determines attachment's height in grid.
-- ATTACHMENT.DInv2_Volume = nil -- DInventory/2 Specific. Determines attachment's volume in liters.
-- ATTACHMENT.DInv2_Mass = nil -- DInventory/2 Specific. Determines attachment's mass in kilograms.
-- ATTACHMENT.DInv2_StackSize = nil -- DInventory/2 Specific. Determines attachment's maximal stack size.
--[[
-- Default behavior is always allow, override to change
function ATTACHMENT:CanAttach(wep)
return true
end
]]--
--[[
-- These functions are called BEFORE stat cache is rebuilt
function ATTACHMENT:Attach(wep)
end
function ATTACHMENT:Detach(wep)
end
]]--
-- Attachment functions called from base
--[[
-- Called from render target code if SWEP.RTDrawEnabled is true
function ATTACHMENT:RTCode(wep, rt_texture, w, h)
end
]]--
--[[
-- Called from FireBullets for each bullet trace hit; arguments are passed from bullet callback
function ATTACHMENT:CustomBulletCallback(wep, attacker, trace, dmginfo)
end
]]--
--[[
-- Called before stencil sight reticle is drawn
function ATTACHMENT:PreDrawStencilSight(wep, vm, ply, sightVElementTable)
-- 3D rendering context from PostDrawViewModel
-- https://wiki.facepunch.com/gmod/3D_Rendering_Functions
-- return true -- to prevent SWEP:PreDrawStencilSight from being called
-- return false -- to stop reticle from drawing
end
]]--
--[[
-- Called right after stencil sight reticle is drawn
function ATTACHMENT:PostDrawStencilSight(wep, vm, ply, sightVElementTable)
-- 3D rendering context from PostDrawViewModel
-- https://wiki.facepunch.com/gmod/3D_Rendering_Functions
-- return true -- to prevent SWEP:PostDrawStencilSight from being called
end
]]--
if not TFA_ATTACHMENT_ISUPDATING then
TFAUpdateAttachments()
end

View File

@@ -0,0 +1,97 @@
-- TFA Base Batch Attachment Registration Template by TFA Base Devs
-- To the extent possible under law, the person who associated CC0 with
-- TFA Base Template has waived all copyright and related or neighboring rights
-- to TFA Base Template.
-- You should have received a copy of the CC0 legalcode along with this
-- work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
-- Place your file in addons/<YOUR ADDON NAME HERE>/lua/tfa/attbatch/<YOUR FILE NAME HERE>.lua
-- These are loaded after regular lua/tfa/att folder, allowing to place base attachments there.
if not TFA_ATTACHMENT_ISUPDATING then TFAUpdateAttachments(false) return end
-- TFA.Attachments.RegisterFromTable(string id, table ATTACHMENT)
TFA.Attachments.RegisterFromTable("your_att_id_here", {
TFADataVersion = 1, -- If it is undefined, it fallbacks to 0 and WeaponTable gets migrated like SWEPs do
-- Base = "base", -- Attachment baseclass, defaults to "base" attachment
Name = "Example Attachment",
ShortName = nil, -- Abbreviation shown on the bottom left of the icon, generated from name if not defined
Description = {
TFA.Attachments.Colors["+"], "Does something good",
TFA.Attachments.Colors["-"], "Does something bad",
-- Color(255, 255, 255), "bottom text",
}, -- all colors are defined in lua/tfa/modules/tfa_attachments.lua
Icon = nil, -- "entities/tfa_ammo_match.png" -- Full path to the icon, reverts to '?' by default
WeaponTable = { -- The place where you change the stats (CACHED STATS ONLY!)
["Primary"] = {
["Damage"] = 60, -- For example, you want to change SWEP.Primary.Damage value to 60
["ClipSize"] = function(wep, stat)
return wep.Primary_TFA.ClipSize_Override or stat * 1.5
end -- Stat functions support changing value dynamically (which is cached afterwards), SWEP.Primary_TFA contains original unchanged values
}
},
-- DInv2_GridSizeX = nil, -- DInventory/2 Specific. Determines attachment's width in grid.
-- DInv2_GridSizeY = nil, -- DInventory/2 Specific. Determines attachment's height in grid.
-- DInv2_Volume = nil, -- DInventory/2 Specific. Determines attachment's volume in liters.
-- DInv2_Mass = nil, -- DInventory/2 Specific. Determines attachment's mass in kilograms.
-- DInv2_StackSize = nil, -- DInventory/2 Specific. Determines attachment's maximal stack size.
--[[
-- Default behavior is always allow, override to change
CanAttach = function(self, wep)
return true
end,
]]--
--[[
-- These functions are called BEFORE stat cache is rebuilt
Attach = function(self, wep)
end,
Detach = function(self, wep)
end,
]]--
-- Attachment functions called from base
--[[
-- Called from render target code if SWEP.RTDrawEnabled is true
RTCode = function(self, wep, rt_texture, w, h)
end,
]]--
--[[
-- Called from FireBullets for each bullet trace hit; arguments are passed from bullet callback
CustomBulletCallback = function(self, wep, attacker, trace, dmginfo)
end,
]]--
--[[
-- Called before stencil sight reticle is drawn
PreDrawStencilSight = function(self, wep, vm, ply, sightVElementTable)
-- 3D rendering context from PostDrawViewModel
-- https://wiki.facepunch.com/gmod/3D_Rendering_Functions
-- return true -- to prevent SWEP:PreDrawStencilSight from being called
-- return false -- to stop reticle from drawing
end,
]]--
--[[
-- Called right after stencil sight reticle is drawn
PostDrawStencilSight = function(self, wep, vm, ply, sightVElementTable)
-- 3D rendering context from PostDrawViewModel
-- https://wiki.facepunch.com/gmod/3D_Rendering_Functions
-- return true -- to prevent SWEP:PostDrawStencilSight from being called
end,
]]--
})
-- and so on

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,918 @@
-- TFA Base custom hooks reference
-- To be used with https://wiki.facepunch.com/gmod/hook.Add in your own code
-- All returns are optional, you can use hooks just to listen for events happening
-- Where state is listed as Shared, hook has to be added both on server and client to avoid any issues!
---------------------------
-- --
-- BASE INITIALIZATION --
-- --
---------------------------
GM:TFABase_PreEarlyInit()
-- Hook name: TFABase_PreEarlyInit
-- Description: Called after base enums has been loaded; preparing to load official modules
-- State: Shared
GM:TFABase_EarlyInit()
-- Hook name: TFABase_EarlyInit
-- Description: Called after official base modules has been loaded
-- State: Shared
GM:TFABase_PreInit()
-- Hook name: TFABase_PreInit
-- Description: Called before unofficial modules has been loaded
-- State: Shared
GM:TFABase_Init()
-- Hook name: TFABase_Init
-- Description: Called after unofficial modules have been found and loaded
-- State: Shared
GM:TFABase_PreFullInit()
-- Hook name: TFABase_PreFullInit
-- Description: Called before loading external files
-- State: Shared
GM:TFABase_FullInit()
-- Hook name: TFABase_FullInit
-- Description: Called after external files have been loaded
-- State: Shared
GM:TFABase_LateInit()
-- Hook name: TFABase_LateInit
-- Description: Called after (re)initializing the attachments
-- State: Shared
----------------------------------
-- --
-- ATTACHMENTS INITIALIZATION --
-- --
----------------------------------
boolean GM:TFABase_ShouldLoadAttachment(string attachmentID, string path)
-- Hook name: TFABase_ShouldLoadAttachment
-- Description: Called to determine if attachment should be registered
-- State: Shared
-- Arguments:
-- 1. string attachmentID - Requested attachment ID
-- 2. string path - Attachment file path
-- Returns:
-- 1. boolean - Return false to prevent attachment from loading and registration
-- Example:
hook.Add("TFABase_ShouldLoadAttachment", "TFA_Hooks_Example", function(id, path)
if id and (id == "ins2_fg_gp25" or id == "ins2_fg_m203") then
return false -- block INS2 Shared Parts grenade launcher attachments from loading
end
end)
GM:TFABase_PreBuildAttachment(string attachmentID, string path, table attTbl)
-- Hook name: TFABase_PreBuildAttachment
-- Description: Called before loading attachment file
-- State: Shared
-- Arguments:
-- 1. string attachmentID - Requested attachment ID
-- 2. string path - Attachment file path
-- 3. table attTbl - Empty attachment reference table (only containing assigned ID)
GM:TFABase_BuildAttachment(string attachmentID, string path, table attTbl)
-- Hook name: TFABase_BuildAttachment
-- Description: Called after attachment file has been loaded and executed
-- State: Shared
-- Arguments:
-- 1. string attachmentID - Requested attachment ID
-- 2. string path - Attachment file path
-- 3. table attTbl - Populated attachment reference table
GM:TFABase_RegisterAttachment(string attachmentID, table attTbl)
-- Hook name: TFABase_RegisterAttachment
-- Description: Called when attachment has been registered
-- State: Shared
-- Arguments:
-- 1. string attachmentID - Requested attachment ID
-- 2. table attTbl - Attachment reference table
GM:TFAAttachmentsLoaded()
-- Hook name: TFAAttachmentsLoaded
-- Description: Called after all attachments has been loaded and registered
-- State: Shared
GM:TFAAttachmentsInitialized()
-- Hook name: TFAAttachmentsInitialized
-- Description: Called after all attachments has been fully loaded and initialized
-- State: Shared
-------------------------
-- --
-- WEAPON STAT CACHE --
-- --
-------------------------
any GM:TFA_GetStat(Weapon weapon, string stat, any value)
-- Hook name: TFA_GetStat
-- Description: Called when a cached stat value is requested from weapon, allowing to intercept and modify it.
-- State: Shared
-- Arguments:
-- - 1. Weapon weapon
-- - 2. string stat - Cached stat name
-- - 3. any value - Cached stat value that was received from weapon
-- Returns:
-- - 1. any - Return the modified stat to pass it to :GetStat call
-- Example #1:
hook.Add("TFA_GetStat", "TFA_Hooks_Example", function(weapon, stat, value)
if stat == "Primary.ClipSize" then -- We want to modify SWEP.Primary.ClipSize which is a cached stat
return value + 10 -- We add 10 to it's current (number) value
end
end)
-- Example #2:
hook.Add("TFA_GetStat", "TFA_Hooks_Example_2", function(weapon, stat, value)
if stat == "Primary.AmmoConsumption" or stat == "Secondary.AmmoConsumption" then
return 0 -- We tell the base that the gun does not consume any ammo - infinite ammo hook!
end
end)
GM:TFA_ClearStatCache(Weapon weapon)
-- Hook name: TFA_ClearStatCache
-- Description: Called after weapon's stat cache has been cleared
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
---------------------------------
-- --
-- DEPLOY AND INITIALIZATION --
-- --
---------------------------------
GM:TFA_SetupDataTables(Weapon weapon)
-- Hook name: TFA_SetupDataTables
-- Description: Called after WEAPON:SetupDataTables, allowing to add custom networked data values
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- Example:
hook.Add("TFA_SetupDataTables", "TFA_Hooks_Example", function(weapon)
weapon:NetworkVarTFA("Bool", "TestVar") -- Adding a boolean variable with name TestVar with getter WEAPON:GetTestVar() and setter WEAPON:SetTestVar(true)
-- Syntax of WEAPON:NetworkVarTFA is quite identical to Entity:NetworkVar ( https://wiki.facepunch.com/gmod/Entity:NetworkVar )
-- WEAPON:NetworkVarTFA(string type, string name)
end)
GM:TFA_PreInitialize(Weapon weapon)
-- Hook name: TFA_PreInitialize
-- Description: Called from SWEP:Initialize, allowing to do things before weapon is initialized
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
GM:TFA_Initialize(Weapon weapon)
-- Hook name: TFA_Initialize
-- Description: Called after weapon has been initialized from SWEP:Initialize
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
GM:TFA_PreDeploy(Weapon weapon)
-- Hook name: TFA_PreDeploy
-- Description: Called from SWEP:Deploy before weapon has been deployed
-- State: Shared, Predicted (not called in SP clientside)
-- Arguments:
-- 1. Weapon weapon
boolean GM:TFA_Deploy(Weapon weapon)
-- Hook name: TFA_Deploy
-- Description: Called from SWEP:Deploy after weapon has been deployed.
-- State: Shared, Predicted (not called in SP clientside)
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. boolean - Return a boolean to override SWEP:Deploy output (default behavior returns true)
---------------------------
-- --
-- HOLSTER AND REMOVAL --
-- --
---------------------------
boolean GM:TFA_PreHolster(Weapon weapon, Entity target)
-- Hook name: TFA_PreHolster
-- Description: Called from SWEP:Holster, enabling to prevent switching from currently equipped weapon
-- State: Shared, Predicted (not called in SP clientside)
-- Arguments:
-- 1. Weapon weapon
-- 2. Entity target - The weapon that is being switched to
-- Returns:
-- 1. boolean - Return true to switch to target weapon instantly
GM:TFA_Holster(Weapon weapon)
-- Hook name: TFA_Holster
-- Description: Called when weapon is finished holstering and ready to switch
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
GM:TFA_OnRemove(Weapon weapon)
-- Hook name: TFA_OnRemove
-- Description: Called from SWEP:OnRemove when weapon is being removed
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
GM:TFA_OnDrop(Weapon weapon)
-- Hook name: TFA_OnDrop
-- Description: Called from SWEP:OnDrop when weapon is dropped by the player
-- State: Server
-- Arguments:
-- 1. Weapon weapon
-------------------------------------
-- --
-- PRIMARY AND SECONDARY ATTACKS --
-- --
-------------------------------------
boolean GM:TFA_PreCanPrimaryAttack(Weapon weapon)
-- Hook name: TFA_PreCanPrimaryAttack
-- Description: Called before weapon checks if it can shoot
-- State: Shared, Predicted (not called in SP clientside)
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. boolean - Return to override return value of SWEP:CanPrimaryAttack
-- Example:
hook.Add("TFA_PreCanPrimaryAttack", "TFA_Hooks_Example", function(weapon)
if IsValid(weapon:GetOwner()) and weapon:GetOwner():IsPlayer() and weapon:GetOwner():Crouching() then
return false -- we are blocking shooting if player that holding the weapon is crouching
end
end)
boolean GM:TFA_CanPrimaryAttack(Weapon weapon)
-- Hook name: TFA_CanPrimaryAttack
-- Description: Same as above but called after all checks were done (except jamming)
-- State: Shared, Predicted (not called in SP clientside)
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. boolean - Return to override return value of SWEP:CanPrimaryAttack
boolean GM:TFA_PrimaryAttack(Weapon weapon)
-- Hook name: TFA_PrimaryAttack
-- Description: Called before weapon shoots
-- State: Shared, Predicted (not called in SP clientside)
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. boolean - Return true to prevent weapon shooting
GM:TFA_PostPrimaryAttack(Weapon weapon)
-- Hook name: TFA_PostPrimaryAttack
-- Description: Called after successful weapon attack
-- State: Shared, Predicted (not called in SP clientside)
-- Arguments:
-- 1. Weapon weapon
boolean GM:TFA_PreCanSecondaryAttack(Weapon weapon)
-- Hook name: TFA_PreCanSecondaryAttack
-- Description: Called before weapon checks the right-click attack in SWEP:CanSecondaryAttack overrides.
-- State: Shared, Predicted (not called in SP clientside)
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. boolean - Return to override return value of SWEP:CanSecondaryAttack
boolean GM:TFA_CanSecondaryAttack(Weapon weapon)
-- Hook name: TFA_CanSecondaryAttack
-- Description: Checks if weapon is allowed to use right-click attack. DOES NOT PREVENT AIMING DOWN THE SIGHTS!
-- State: Shared, Predicted (not called in SP clientside)
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. boolean - Return to override return value of SWEP:CanSecondaryAttack
boolean GM:TFA_SecondaryAttack(Weapon weapon)
-- Hook name: TFA_SecondaryAttack
-- Description: Called when weapon is attempting to attack with right click (AltAttack/melee bash)
-- State: Shared, Predicted (not called in SP clientside)
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. boolean - Return true to prevent secondary attack
GM:TFA_PostSecondaryAttack(Weapon weapon)
-- Hook name: TFA_PostSecondaryAttack
-- Description: Called after successful right-click attack
-- State: Shared, Predicted (not called in SP clientside)
-- Arguments:
-- 1. Weapon weapon
--------------------------
-- --
-- BULLET PENETRATION --
-- --
--------------------------
boolean GM:TFA_Bullet_Penetrate(Weapon weapon, Entity attacker, TraceResult traceres, CTakeDamageInfo dmginfo, table penetrated, Vector previousStartPos)
-- Hook name: TFA_Bullet_Penetrate
-- Description: Called before bullet is allowed to penetrate next surface
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- 2. Entity attacker - The entity (player or NPC) who used the weapon
-- 3. TraceResult traceres - Bullet trace result
-- 4. CTakeDamageInfo dmginfo - Bullet callback damage info structure
-- 5. table penetrated - Table of entities bullet has gone through already
-- 6. Vector previousStartPos - Either previous penetration hit position or nil
-- Returns:
-- 1. boolean - Return false to block penetration
GM:TFA_BulletPenetration(Bullet bullet, Entity attacker, TraceResult traceres, CTakeDamageInfo dmginfo)
-- Hook name: TFA_BulletPenetration
-- Description: Called when bullet hits/penetrates surface
-- State: Shared
-- Arguments:
-- 1. Bullet bullet - The bullet (to access weapon which fired the bullet use bullet.Wep)
-- 2. Entity attacker - The entity (player or NPC) who used the weapon
-- 3. TraceResult traceres - Bullet trace result
-- 4. CTakeDamageInfo dmginfo - Bullet callback damage info structure
-----------------
-- --
-- RELOADING --
-- --
-----------------
boolean GM:TFA_PreReload(Weapon weapon, boolean released)
-- Hook name: TFA_PreReload
-- Description: Called when reload key is pressed/released (before any checks done)
-- State: Shared, Predicted (not called in SP clientside)
-- Arguments:
-- 1. Weapon weapon
-- 2. boolean released - If legacy reloads are enabled, reload happens when reload key is pressed, otherwise when it's released (allowing for stuff like inspect)
-- Returns:
-- 1. boolean - Return true to prevent weapon from reloading
boolean GM:TFA_Reload(Weapon weapon)
-- Hook name: TFA_Reload
-- Description: Called when weapon is attempting to enter reload status
-- State: Shared, Predicted (not called in SP clientside)
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. boolean - Return true to prevent weapon from reloading
GM:TFA_PostReload(Weapon weapon)
-- Hook name: TFA_PostReload
-- Description: Called after reload status checks
-- State: Shared, Predicted (not called in SP clientside)
-- Arguments:
-- 1. Weapon weapon
boolean GM:TFA_LoadShell(Weapon weapon)
-- Hook name: TFA_LoadShell
-- Description: Called when shotgun reload type weapons attempt to enter looped reload status and play animation
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. boolean - Return true to prevent default logic
boolean GM:TFA_Pump(Weapon weapon)
-- Hook name: TFA_Pump
-- Description: Called when weapon is attempting to play pump/bolt animation
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. boolean - Return true to prevent default logic
boolean GM:TFA_CompleteReload(Weapon weapon)
-- Hook name: TFA_CompleteReload
-- Description: Called when weapon is exiting reloading status and trying to take ammo (and clear jamming status)
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. boolean - Return true to prevent default logic
-- Example:
hook.Add("TFA_CompleteReload", "TFA_Hooks_Example", function(weapon)
weapon:SetClip1(weapon:GetPrimaryClipSizeForReload(true)) -- We set weapon's primary clip to max clipsize without taking ammo from the player
weapon:SetJammed(false) -- Force clear jammed status (since default logic does that)
return true -- Suppressing default logic
end)
boolean GM:TFA_CheckAmmo(Weapon weapon)
-- Hook name: TFA_CheckAmmo
-- Description: Called when player is holding Reload key to play inspect animation (called even if player has bound inspection keybind)
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. boolean - Return true to prevent default logic
-------------------
-- --
-- ATTACHMENTS --
-- --
-------------------
GM:TFA_PreInitAttachments(Weapon weapon)
-- Hook name: TFA_PreInitAttachments
-- Description: Called before attachments are initialized
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
GM:TFA_PostInitAttachments(Weapon weapon)
-- Hook name: TFA_PostInitAttachments
-- Description: Called after attachments sorting and initial setup but before cleanup and attachment cache build
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
GM:TFA_FinalInitAttachments(Weapon weapon)
-- Hook name: TFA_FinalInitAttachments
-- Description: Called after full attachments setup/initialization
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
boolean GM:TFA_PreCanAttach(Weapon weapon, string attachmentID)
-- Hook name: TFA_PreCanAttach
-- Description: Called before weapon checks to determine if attachment can be attached
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- 2. string attachmentID - The ID of the attachment
-- Returns:
-- 1. boolean - Return a boolean to prevent checks and override
boolean GM:TFA_CanAttach(Weapon weapon, string attachmentID)
-- Hook name: TFA_CanAttach
-- Description: Called after default checks to determine if attachment can be attached
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- 2. string attachmentID - The ID of the attachment
-- Returns:
-- 1. boolean - Return a boolean to override the return value
GM:TFA_Attachment_Attached(Weapon weapon, string attachmentID, table attTable, number category, number index, boolean forced)
-- Hook name: TFA_Attachment_Attached
-- Description: Called after attachment has been attached to the weapon
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- 2. string attachmentID - The ID of the attachment
-- 3. table attTable - The ATTACHMENT reference table of the attachment
-- 4. number category - The category of the attachment in SWEP.Attachments
-- 5. number index - The index of the attachment in the category's attachments table
-- 6. boolean forced - If attachment was applied forcefully (bypassing any checks)
boolean GM:TFA_PreCanDetach(Weapon weapon, string attachmentID)
-- Hook name: TFA_PreCanDetach
-- Description: Equivalent of TFA_PreCanAttach but for detaching
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- 2. string attachmentID - The ID of the attachment
-- Returns:
-- 1. boolean - Return a boolean to prevent checks and override
boolean GM:TFA_CanDetach(Weapon weapon, string attachmentID)
-- Hook name: TFA_CanDetach
-- Description: Equivalent of TFA_CanAttach but for detaching
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- 2. string attachmentID - The ID of the attachment
-- Returns:
-- 1. boolean - Return a boolean to override the return value
GM:TFA_Attachment_Detached(Weapon weapon, string attachmentID, table attTable, number category, number index, boolean forced)
-- Hook name: TFA_Attachment_Detached
-- Description: Equivalent of TFA_Attachment_Attached but for detaching
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- 2. string attachmentID - The ID of the attachment that was detached
-- 3. table attTable - The ATTACHMENT reference table of the attachment
-- 4. number category - The category of the attachment in SWEP.Attachments
-- 5. number index - The index of the attachment in the category's attachments table that's going to be selected
-- 6. boolean forced - If attachment was applied forcefully (bypassing any checks)
-----------
-- --
-- FOV --
-- --
-----------
number GM:TFA_PreTranslateFOV(Weapon weapon, number fov)
-- Hook name: TFA_PreTranslateFOV
-- Description: Called before weapon modifies player's FOV
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- 2. number fov - Reference FOV received by weapon
-- Returns:
-- 1. number - Return a number to override FOV and prevent default logic
number GM:TFA_TranslateFOV(Weapon weapon, number fov)
-- Hook name: TFA_TranslateFOV
-- Description: Called after weapon calculated modified FOV
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- 2. number fov - Modified FOV value returned by weapon
-- Returns:
-- 1. number - Return to override modified FOV
------------------
-- --
-- ANIMATIONS --
-- --
------------------
number GM:TFA_AnimationRate(Weapon weapon, number sequence, number rate)
-- Hook name: TFA_AnimationRate
-- Description: Called
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- 2. number sequence - Index of sequence from viewmodel animations
-- 3. number rate - Playback speed multiplier calculated by weapon
-- Returns:
-- 1. number - Return modified playback speed multiplier to override
---------------
-- --
-- EFFECTS --
-- --
---------------
any GM:TFA_MakeShell(Weapon weapon)
-- Hook name: TFA_MakeShell
-- Description: Called when weapon is trying to emit a shell casing effect
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. any - Return anything to cancel
any GM:TFA_EjectionSmoke(Weapon weapon)
-- Hook name: TFA_EjectionSmoke
-- Description: Called when weapon is trying to emit smoke from shell ejection port
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. any - Return anything to cancel
any GM:TFA_MuzzleFlash(Weapon weapon)
-- Hook name: TFA_MuzzleFlash
-- Description: Called when weapon is trying to emit muzzle flash effect
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. any - Return anything to cancel
any GM:TFA_MuzzleSmoke(Weapon weapon)
-- Hook name: TFA_MuzzleSmoke
-- Description: Called when weapon is trying to emit smoke from muzzle
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. any - Return anything to cancel
------------------
-- --
-- IRONSIGHTS --
-- --
------------------
any GM:TFA_IronSightSounds(Weapon weapon)
-- Hook name: TFA_IronSightSounds
-- Description: Called when weapon tries to play ironsights enter/exit sound
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. any - Return anything to cancel
-----------------------
-- --
-- MELEE / BASHING --
-- --
-----------------------
boolean GM:TFA_CanBash(Weapon weapon)
-- Hook name: TFA_CanBash
-- Description: Called when player is attempting to use melee bash attack (after initial checks)
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. boolean - Return false to disallow melee attack
GM:TFA_Bash(Weapon weapon)
-- Hook name: TFA_Bash
-- Description: Called when weapon is about to perform a melee attack
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
GM:TFA_PostBash(Weapon weapon)
-- Hook name: TFA_PostBash
-- Description: Called after weapon has entered melee attack status
-- State: Shared
-- Arguments:
-- 1. Weapon weapon
boolean GM:TFA_MeleeCanBlockDamage(Weapon weapon, Player player, CTakeDamageInfo dmginfo, boolean canblock)
-- Hook name: TFA_MeleeCanBlockDamage
-- Description: Callen when melee weapon is asked to block the incoming damage
-- State: Shared (only for bullet damage), Server (for any other type)
-- Arguments:
-- 1. Weapon weapon
-- 2. Player player - The player that is receiving the damage that's about to be blocked
-- 3. CTakeDamageInfo dmginfo - Incoming damage
-- 4. boolean canblock - Original decision of weapon if it's allowed to block or not
-- Returns:
-- 1. boolean - Return to override the decision
----------------------------
-- --
-- HUD / USER INTERFACE --
-- --
----------------------------
boolean GM:TFA_DrawCrosshair(Weapon weapon, number x, number y)
-- Hook name: TFA_DrawCrosshair
-- Description: Called from SWEP:DoDrawCrosshair when weapon is about to draw crosshair
-- State: Client
-- Arguments:
-- 1. Weapon weapon
-- 2. number x
-- 3. number y
-- Returns:
-- 1. boolean - Return false to draw only engine crosshair, true to block both
boolean, number, number, number GM:TFA_DrawHUDAmmo(Weapon weapon, number x, number y, number alpha)
-- Hook name: TFA_DrawHUDAmmo
-- Description: Called before drawing holographic ammo indicator on screen
-- State: Client
-- Arguments:
-- 1. Weapon weapon
-- 2. number x - left position
-- 3. number y - top position
-- 4. number alpha
-- Returns:
-- 1. boolean - Return false to prevent drawing (next returns ignored), true to modify following values:
-- 2. number - modified X position
-- 3. number - modified Y position
-- 4. number - modified alpha
boolean GM:TFA_DrawScopeOverlay(Weapon weapon)
-- Hook name: TFA_DrawScopeOverlay
-- Description: Called before drawing 2D scope overlay
-- State: Client
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. boolean - Return true to prevent drawing the overlay
table GM:TFA_PopulateKeyBindHints(Weapon weapon, table rawKeysTable)
-- Hook name: TFA_PopulateKeyBindHints
-- Description: Allows to populate keybinds table prior to drawing (called from SWEP:PopulateKeyBindHints() function)
-- State: Client
-- Arguments:
-- 1. Weapon weapon
-- 2. table rawKeysTable - Raw keybindings table
-- Returns:
-- 1. table rawKeysTableResult - Return the table to stop further processing
-- Example:
hook.Add("TFA_PopulateKeyBindHints", "TFA_Hooks_Example", function(wep, keys)
-- self:GetKeyBind({"+use", "+reload"}, "firemode")
-- First argument is a table of commands
-- Second argument is an ID of keybind registered through TFA.RegisterKeyBind, replaces all keys from first argument if key is bound (optional)
table.insert(keys, {
label = "Open Spawnmenu", -- it is recommended to use a localized string with language.GetPhrase
keys = {wep:GetKeyBind({"+menu"})} -- can have multiple keys; GetKeyBind args: first is table of commands (they will be chained with +), the second (optional) is keybind identifier added with TFA.RegisterKeyBind and will be displayed instead of first
}) -- this will add "[Q] - Open Spawnmenu" at the end of the keys table (before the TAB one, it always comes the last)
table.insert(keys, 1, {
label = "Sprint Forward",
keys = {wep:GetKeyBind({"+speed", "+forward"})}
}) -- this will add "[SHIFT + W] - Sprint Forward" at the start of the keys table
-- table.insert(keys, {
-- label = language.GetPhrase("tfa.hint.keys.safety"),
-- keys = {self:GetKeyBind({"+speed"}), self:GetKeyBind({"+use", "+reload"}, "firemode")}
-- }) -- example from the base, will display SHIFT+E+R or SHIFT+<firemode bind override key>
end)
boolean GM:TFA_PreDrawKeyBindHint(Weapon weapon, number x, number y, number alpha, table rawKeysTable, table keyStrings)
-- Hook name: TFA_PreDrawKeyBindHint
-- Description: Called before keybinds hint is drawn (only if alpha > 0)
-- State: Client
-- Arguments:
-- 1. Weapon weapon
-- 2. number x
-- 3. number y
-- 4. number alpha
-- 5. table rawKeysTable
-- 6. table keyStrings - Processed keybinds in format "[KEY(S)] - Label"
-- Returns:
-- 1. boolean - Return true to prevent drawing
GM:TFA_PostDrawKeyBindHint(Weapon weapon, number x, number y, number alpha, table rawKeysTable, table keyStrings)
-- Hook name: TFA_PostDrawKeyBindHint
-- Description: Called after keybinds hint is drawn
-- State: Client
-- Arguments:
-- 1. Weapon weapon
-- 2. number x
-- 3. number y
-- 4. number alpha
-- 5. table rawKeysTable
-- 6. table keyStrings
boolean GM:TFA_ShouldDrawStencilSight(Weapon weapon)
-- Hook name: TFA_ShouldDrawStencilSight
-- Description: Called when weapon is trying to draw stencil sight reticle
-- State: Client
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. boolean - Return false to prevent reticle from drawing
GM:TFA_FontsLoaded()
-- Hook name: TFA_FontsLoaded
-- Description: Called after base (re)registers all it's fonts
-- State: Client
---------------------------------------
-- --
-- CUSTOMIZATION / INSPECTION MENU --
-- --
---------------------------------------
boolean GM:TFA_InspectVGUI_Start(Weapon weapon)
-- Hook name: TFA_InspectVGUI_Start
-- Description: Called before main customization screen panel is generated
-- State: Client
-- Arguments:
-- 1. Weapon weapon
-- Returns:
-- 1. boolean - Return false to prevent creation
GM:TFA_InspectVGUI_Finish(Weapon weapon, Panel mainpanel, Panel contentpanel)
-- Hook name: TFA_InspectVGUI_Finish
-- Description: Called after every panel has been initialized
-- State: Client
-- Arguments:
-- 1. Weapon weapon
-- 2. Panel mainpanel - The parent inspection screen panel
-- 3. Panel contentpanel - The padded panel that contains all elements
boolean GM:TFA_InspectVGUI_InfoStart(Weapon weapon, Panel contentpanel)
-- Hook name: TFA_InspectVGUI_InfoStart
-- Description: Called before populating screen with weapon info elements
-- State: Client
-- Arguments:
-- 1. Weapon weapon
-- 2. Panel contentpanel - The padded panel that contains all elements
-- Returns:
-- 1. boolean - Return false to prevent creation
GM:TFA_InspectVGUI_InfoFinish(Weapon weapon, Panel contentpanel, Panel infopanel)
-- Hook name: TFA_InspectVGUI_InfoFinish
-- Description: Called when weapon info elements are added
-- State: Client
-- Arguments:
-- 1. Weapon weapon
-- 2. Panel contentpanel - The padded panel that contains all elements
-- 3. Panel infopanel - The container panel for all displayed info elements
boolean GM:TFA_InspectVGUI_StatsStart(Weapon weapon, Panel contentpanel)
-- Hook name: TFA_InspectVGUI_StatsStart
-- Description: Called before adding weapon stats panel
-- State: Client
-- Arguments:
-- 1. Weapon weapon
-- 2. Panel contentpanel - The padded panel that contains all elements
-- Returns:
-- 1. boolean - Return false to prevent creation
GM:TFA_InspectVGUI_StatsFinish(Weapon weapon, Panel contentpanel, Panel statspanel)
-- Hook name: TFA_InspectVGUI_StatsFinish
-- Description: Called when weapon stats are added
-- State: Client
-- Arguments:
-- 1. Weapon weapon
-- 2. Panel contentpanel - The padded panel that contains all elements
-- 3. Panel statspanel - The container panel for all displayed stats
boolean GM:TFA_InspectVGUI_AttachmentsStart(Weapon weapon, Panel contentpanel)
-- Hook name: TFA_InspectVGUI_AttachmentsStart
-- Description: Called before adding attachments selector panel
-- State: Client
-- Arguments:
-- 1. Weapon weapon
-- 2. Panel contentpanel - The padded panel that contains all elements
-- Returns:
-- 1. boolean - Return false to prevent creation (also blocks damage falloff graph)
GM:TFA_InspectVGUI_AttachmentsFinish(Weapon weapon, Panel contentpanel, Panel attachmentspanel)
-- Hook name: TFA_InspectVGUI_AttachmentsFinish
-- Description: Called after attachments selector panel is generated
-- State: Client
-- Arguments:
-- 1. Weapon weapon
-- 2. Panel contentpanel - The padded panel that contains all elements
-- 3. Panel attachmentspanel - The resulting attachments selector panel
boolean GM:TFA_InspectVGUI_FalloffStart(Weapon weapon, Panel contentpanel)
-- Hook name: TFA_InspectVGUI_FalloffStart
-- Description: Called before damage falloff graph is initialized
-- State: Client
-- Arguments:
-- 1. Weapon weapon
-- 2. Panel contentpanel - The padded panel that contains all elements
-- Returns:
-- 1. boolean - Return false to prevent creation
GM:TFA_InspectVGUI_FalloffFinish(Weapon weapon, Panel contentpanel, Panel falloffpanel)
-- Hook name: TFA_InspectVGUI_FalloffFinish
-- Description: Called after damage falloff graph panel is initialized
-- State: Client
-- Arguments:
-- 1. Weapon weapon
-- 2. Panel contentpanel - The padded panel that contains all elements
-- 3. Panel falloffpanel - The resulting damage falloff graph panel
----------------------
-- --
-- DEPTH OF FIELD --
-- --
----------------------
boolean GM:TFA_AllowDoFDraw(Weapon weapon, Player player, Entity viewmodel)
-- Hook name: TFA_AllowDoFDraw
-- Description: Called before drawing aim DoF effect on viewmodel
-- State: Client
-- Arguments:
-- 1. Weapon weapon
-- 2. Player player - The player that is currently holding the weapon
-- 3. Entity viewmodel - Weapon's viewmodel
-- Returns:
-- 1. boolean - Return false to prevent effect from drawing
number GM:TFA_GetDoFMuzzleAttachmentID(Weapon weapon, Player player, Entity viewmodel)
-- Hook name: TFA_GetDoFMuzzleAttachmentID
-- Description: Called when deciding reference attachment for DoF effect focus
-- State: Client.
-- Arguments:
-- 1. Weapon weapon
-- 2. Player player - The player that is currently holding the weapon
-- 3. Entity viewmodel - Weapon's viewmodel
-- Returns:
-- 1. number - Return viewmodel's attachment point index to override

View File

@@ -0,0 +1,703 @@
-- TFA Base Template by TFA Base Devs
-- To the extent possible under law, the person who associated CC0 with
-- TFA Base Template has waived all copyright and related or neighboring rights
-- to TFA Base Template.
-- You should have received a copy of the CC0 legalcode along with this
-- work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --
-- ! ! --
-- ! WARNING! This template is outdated, not supported anymore ! --
-- ! and is only kept in for reference/comparison reasons. ! --
-- ! ! --
-- ! Please use the updated template ! --
-- ! located at lua/weapons/tfa_base_template/shared.lua ! --
-- ! for future weapon development purposes. ! --
-- ! ! --
-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --
SWEP.Base = "tfa_gun_base"
SWEP.Category = "TFA Template" -- The category. Please, just choose something generic or something I've already done if you plan on only doing like one swep..
SWEP.Manufacturer = nil -- Gun Manufactrer (e.g. Hoeckler and Koch )
SWEP.Author = "" -- Author Tooltip
SWEP.Contact = "" -- Contact Info Tooltip
SWEP.Purpose = "" -- Purpose Tooltip
SWEP.Instructions = "" -- Instructions Tooltip
SWEP.Spawnable = false -- Can you, as a normal user, spawn this?
SWEP.AdminSpawnable = false -- Can an adminstrator spawn this? Does not tie into your admin mod necessarily, unless its coded to allow for GMod's default ranks somewhere in its code. Evolve and ULX should work, but try to use weapon restriction rather than these.
SWEP.DrawCrosshair = true -- Draw the crosshair?
SWEP.DrawCrosshairIS = false -- Draw the crosshair in ironsights?
SWEP.PrintName = "TFA Base Template" -- Weapon name (Shown on HUD)
SWEP.Slot = 2 -- Slot in the weapon selection menu. Subtract 1, as this starts at 0.
SWEP.SlotPos = 73 -- Position in the slot
SWEP.AutoSwitchTo = true -- Auto switch to if we pick it up
SWEP.AutoSwitchFrom = true -- Auto switch from if you pick up a better weapon
SWEP.Weight = 30 -- This controls how "good" the weapon is for autopickup.
-- [[WEAPON HANDLING]] --
SWEP.Primary.Sound = Sound("") -- This is the sound of the weapon, when you shoot.
SWEP.Primary.SilencedSound = nil -- This is the sound of the weapon, when silenced.
SWEP.Primary.PenetrationMultiplier = 1 -- Change the amount of something this gun can penetrate through
-- the LESSER this value is, the BETTER is penetration
-- this is basically multiplier for next values
-- you don't need to uncomment these if you are not going to modify them!
--[[
SWEP.PenetrationMaterials = {
[MAT_DEFAULT] = 1,
[MAT_VENT] = 0.4, --Since most is aluminum and stuff
[MAT_METAL] = 0.6, --Since most is aluminum and stuff
[MAT_WOOD] = 0.2,
[MAT_PLASTIC] = 0.23,
[MAT_FLESH] = 0.48,
[MAT_CONCRETE] = 0.87,
[MAT_GLASS] = 0.16,
[MAT_SAND] = 1,
[MAT_SLOSH] = 1,
[MAT_DIRT] = 0.95, --This is plaster, not dirt, in most cases.
[MAT_FOLIAGE] = 0.9
}
]]
SWEP.Primary.Damage = 0.01 -- Damage, in standard damage points.
SWEP.Primary.DamageTypeHandled = true -- true will handle damagetype in base
SWEP.Primary.DamageType = nil -- See DMG enum. This might be DMG_SHOCK, DMG_BURN, DMG_BULLET, etc. Leave nil to autodetect. DMG_AIRBOAT opens doors.
SWEP.Primary.Force = nil -- Force value, leave nil to autocalc
SWEP.Primary.Knockback = nil -- Autodetected if nil; this is the velocity kickback
SWEP.Primary.HullSize = 0 -- Big bullets, increase this value. They increase the hull size of the hitscan bullet.
SWEP.Primary.NumShots = 1 -- The number of shots the weapon fires. SWEP.Shotgun is NOT required for this to be >1.
SWEP.Primary.Automatic = true -- Automatic/Semi Auto
SWEP.Primary.RPM = 600 -- This is in Rounds Per Minute / RPM
SWEP.Primary.RPM_Semi = nil -- RPM for semi-automatic or burst fire. This is in Rounds Per Minute / RPM
SWEP.Primary.RPM_Burst = nil -- RPM for burst fire, overrides semi. This is in Rounds Per Minute / RPM
SWEP.Primary.DryFireDelay = nil -- How long you have to wait after firing your last shot before a dryfire animation can play. Leave nil for full empty attack length. Can also use SWEP.StatusLength[ ACT_VM_BLABLA ]
SWEP.Primary.BurstDelay = nil -- Delay between bursts, leave nil to autocalculate
SWEP.Primary.LoopSound = nil -- Looped fire sound, unsilenced
SWEP.Primary.LoopSoundSilenced = nil -- Looped fire sound, silenced
SWEP.Primary.LoopSoundTail = nil -- Loop end/tail sound, unsilenced
SWEP.Primary.LoopSoundTailSilenced = nil -- Loop end/tail sound, silenced
SWEP.Primary.LoopSoundAutoOnly = false -- Play loop sound for full-auto only? Fallbacks to Primary.Sound for semi/burst if true
-- WORLD/THIRDPERSON/NPC FIRING SOUNDS! Fallbacks to first person sound if not defined.
SWEP.Primary.Sound_World = nil -- This is the sound of the weapon, when you shoot.
SWEP.Primary.SilencedSound_World = nil -- This is the sound of the weapon, when silenced.
SWEP.Primary.LoopSound_World = nil -- Looped fire sound, unsilenced
SWEP.Primary.LoopSoundSilenced_World = nil -- Looped fire sound, silenced
SWEP.Primary.LoopSoundTail_World = nil -- Loop end/tail sound, unsilenced
SWEP.Primary.LoopSoundTailSilenced_World = nil -- Loop end/tail sound, silenced
SWEP.ViewModelPunchPitchMultiplier = nil -- Default value is 0.5
SWEP.ViewModelPunchPitchMultiplier_IronSights = nil -- Default value is 0.09
SWEP.ViewModelPunch_MaxVertialOffset = nil -- Default value is 3
SWEP.ViewModelPunch_MaxVertialOffset_IronSights = nil -- Default value is 1.95
SWEP.ViewModelPunch_VertialMultiplier = nil -- Default value is 1
SWEP.ViewModelPunch_VertialMultiplier_IronSights = nil -- Default value is 0.25
SWEP.ViewModelPunchYawMultiplier = nil -- Default value is 0.6
SWEP.ViewModelPunchYawMultiplier_IronSights = nil -- Default value is 0.25
SWEP.CanJam = true -- whenever weapon cam jam
SWEP.JamChance = 0.04 -- the (maximal) chance the weapon will jam. Newly spawned weapon will never jam on first shot for example.
-- Default value is 0.04 (4%)
-- Maxmial value is 1, means weapon will always jam when factor become 100
-- Also remember that there is a minimal factor before weapon can jam
-- This number is not treated "as-is" but as basic value that needs to be concluded as chance
-- You don't really need to cry over it and trying to balance it, TFA Base will do the job for you
-- (TFA Base will calculate the best value between 0 and JamChance based on current JamFactor of the weapon)
SWEP.JamFactor = 0.06 -- How to increase jam factor after each shot.
-- When factor reach 100 it will mean that on each shot there will be SWEP.Primary.JamChance chance to jam
-- When factor reach 50 it will mean that on each shot there will be SWEP.Primary.JamChance / 2 chance to jam
-- and so on
-- Default value is 0.06, means weapon will jam with SWEP.Primary.JamChance chance right after 1666 shots
-- These settings are good for Assault Rifles, however, not good for anything else.
-- Suggested stats:
--[[
-- Pistols
SWEP.JamChance = 0.20
SWEP.JamFactor = 0.14
]]
--[[
-- Revolvers
SWEP.JamChance = 0.17
SWEP.JamFactor = 0.50
]]
--[[
-- Miniguns
SWEP.JamChance = 0.03
SWEP.JamFactor = 0.01
]]
--[[
-- Submachine gun
SWEP.JamChance = 0.04
SWEP.JamFactor = 0.09
]]
--[[
-- Auto shotguns
SWEP.JamChance = 0.15
SWEP.JamFactor = 0.2
]]
--[[
-- Pump-action shotguns
SWEP.JamChance = 0.25
SWEP.JamFactor = 0.3
]]
--[[
-- Sniper rifle
SWEP.JamChance = 0.17
SWEP.JamFactor = 0.35
]]
SWEP.FiresUnderwater = false
-- Miscelaneous Sounds
SWEP.IronInSound = nil -- Sound to play when ironsighting in? nil for default
SWEP.IronOutSound = nil -- Sound to play when ironsighting out? nil for default
-- Silencing
SWEP.CanBeSilenced = false -- Can we silence? Requires animations.
SWEP.Silenced = false -- Silenced by default?
-- Selective Fire Stuff
SWEP.SelectiveFire = false -- Allow selecting your firemode?
SWEP.DisableBurstFire = false -- Only auto/single?
SWEP.OnlyBurstFire = false -- No auto, only burst/single?
SWEP.BurstFireCount = nil -- Burst fire count override (autocalculated by the clip size if nil)
SWEP.DefaultFireMode = "" -- Default to auto or whatev
SWEP.FireModeName = nil -- Change to a text value to override it
SWEP.FireSoundAffectedByClipSize = true -- Whenever adjuct pitch (and proably other properties) of fire sound based on current clip / maxclip
-- This is always false when either:
-- Weapon has no primary clip
-- Weapon's clip is smaller than 4 rounds
-- Weapon is a shotgun
-- Ammo Related
SWEP.Primary.ClipSize = 0 -- This is the size of a clip
SWEP.Primary.DefaultClip = 0 -- This is the number of bullets the gun gives you, counting a clip as defined directly above.
SWEP.Primary.Ammo = "none" -- What kind of ammo. Options, besides custom, include pistol, 357, smg1, ar2, buckshot, slam, SniperPenetratedRound, and AirboatGun.
SWEP.Primary.AmmoConsumption = 1 -- Ammo consumed per shot
-- Pistol, buckshot, and slam like to ricochet. Use AirboatGun for a light metal peircing shotgun pellets
SWEP.DisableChambering = false -- Disable round-in-the-chamber
-- Recoil Related
SWEP.Primary.KickUp = 0 -- This is the maximum upwards recoil (rise)
SWEP.Primary.KickDown = 0 -- This is the maximum downwards recoil (skeet)
SWEP.Primary.KickHorizontal = 0 -- This is the maximum sideways recoil (no real term)
SWEP.Primary.StaticRecoilFactor = 0.5 -- Amount of recoil to directly apply to EyeAngles. Enter what fraction or percentage (in decimal form) you want. This is also affected by a convar that defaults to 0.5.
-- Firing Cone Related
SWEP.Primary.Spread = .01 -- This is hip-fire acuracy. Less is more (1 is horribly awful, .0001 is close to perfect)
SWEP.Primary.IronAccuracy = .005 -- Ironsight accuracy, should be the same for shotguns
-- Unless you can do this manually, autodetect it. If you decide to manually do these, uncomment this block and remove this line.
SWEP.Primary.SpreadMultiplierMax = nil -- How far the spread can expand when you shoot. Example val: 2.5
SWEP.Primary.SpreadIncrement = nil -- What percentage of the modifier is added on, per shot. Example val: 1/3.5
SWEP.Primary.SpreadRecovery = nil -- How much the spread recovers, per second. Example val: 3
-- Range Related
-- DEPRECATED. Automatically converted to RangeFalloffLUT table
SWEP.Primary.Range = -1 -- The distance the bullet can travel in source units. Set to -1 to autodetect based on damage/rpm.
SWEP.Primary.RangeFalloff = -1 -- The percentage of the range the bullet damage starts to fall off at. Set to 0.8, for example, to start falling off after 80% of the range.
-- Use these if you don't want/understand how to use LUT below. These values are automatically converted to RangeFalloffLUT table
SWEP.Primary.FalloffMetricBased = false -- Set to true if you set up values below
SWEP.Primary.FalloffByMeter = nil -- How much damage points will bullet loose when travel
SWEP.Primary.MinRangeStartFalloff = nil -- How long will bullet travel in Meters before starting to lose damage?
SWEP.Primary.MaxFalloff = nil -- Maximal amount of damage to be lost
-- Use this for full control over damage dropoff.
--[[
SWEP.Primary.RangeFalloffLUT = {
bezier = true, -- Whenever to use Bezier or not to interpolate points?
-- you probably always want it to be set to true
range_func = "quintic", -- function to spline range
-- "linear" for linear splining.
-- Possible values are "quintic", "cubic", "cosine", "sinusine", "linear" or your own function
units = "meters", -- possible values are "inches", "inch", "hammer", "hu" (are all equal)
-- everything else is considered to be meters
lut = { -- providing zero point is not required
-- without zero point it is considered to be as {range = 0, damage = 1}
{range = 5, damage = 0.9},
{range = 12, damage = 0.8},
{range = 18, damage = 0.5},
{range = 24, damage = 0.2},
{range = 30, damage = 0.55},
{range = 38, damage = 0.76},
{range = 50, damage = 1},
{range = 52, damage = 0.96},
{range = 60, damage = 0.3},
{range = 70, damage = 0.1},
}
}
]]
SWEP.DisplayFalloff = nil -- Defaults to true (false for melees)
--[[
SWEP.Primary.RecoilLUT_IronSightsMult = nil -- Defaults to 0.5
-- controls how much effective LUT is when iron sighting
SWEP.Primary.RecoilLUT_AnglePunchMult = nil -- Defaults to 0.25
-- controls how much effective LUT at pushing EyeAngles of shooter
SWEP.Primary.RecoilLUT_ViewPunchMult = nil -- Defaults to 1
-- controls how much effective LUT at viewpunch
SWEP.Primary.RecoilLUT = {
["in"] = {
bezier = true,
func = "quintic", -- function to inerpolate progress when sampling points from table
-- Possible values are "quintic", "cubic", "cosine", "sinusine", "linear" or your own function
cooldown_speed = 1, -- how much to loose progress when we are at this stage
-- 1 means we lose entire progress in a second
increase = 0.1, -- how much to increase progress after shot
-- 0.1 means that this stage would be full after 10 shots
wait = 0.1, -- how much time do we wait in seconds after we stopped shooting
-- after this time, IN stage begin to cooldown until it reach zero
-- table is always prepended with an Angle()
-- only Pitch and Yaw are utilized
-- sampled point is added to aimvector of player
-- when they shoot
points = {
Angle(-1, 0.4),
Angle(-4, -2),
Angle(-6, -4),
Angle(-10, -6),
}
},
["loop"] = {
bezier = true,
func = "quintic",
-- this stage can not cooldown, so no cooldown_speed is defined
increase = 0.1, -- when LOOP stage reach 1, it is reset to 0
wait = 0.1, -- how much time do we wait in seconds after we stopped shooting
-- after this time, stage switch to OUT
-- table is NOT prepended with an Angle()
-- make sure it's starting point match the one from IN stage
-- last and first points are connected automatically
points = {
Angle(-10, -6),
Angle(-12, -0.4),
Angle(-8, 9),
Angle(-11, 12),
Angle(-13, 2),
Angle(-8, -4),
}
},
["out"] = {
bezier = true,
func = "quintic",
-- this stage is different
-- it is only started after LOOP took place
-- shooting in this stage will actually roll back it's state
-- until it reach zero and switch back to LOOP
-- cooling down actually increase stage's progress
cooldown_speed = 1,
-- increase act as negative number to reach zero in this stage
increase = 0.2,
-- after this stage reach 1, everything reset to IN and wait for next fire
-- table is always appended with an Angle()
-- starting point is dynamic
-- and will always match current LOOP's one
points = {
Angle(-7, -2),
Angle(-4, -1),
Angle(-2, 0),
}
}
}
]]
-- Penetration Related
SWEP.MaxPenetrationCounter = 4 -- The maximum number of ricochets. To prevent stack overflows.
-- Misc
SWEP.IronRecoilMultiplier = 0.5 -- Multiply recoil by this factor when we're in ironsights. This is proportional, not inversely.
SWEP.CrouchAccuracyMultiplier = 0.5 -- Less is more. Accuracy * 0.5 = Twice as accurate, Accuracy * 0.1 = Ten times as accurate
-- Movespeed
SWEP.MoveSpeed = 1 -- Multiply the player's movespeed by this.
SWEP.IronSightsMoveSpeed = 0.8 -- Multiply the player's movespeed by this when sighting.
-- PROJECTILES
SWEP.Primary.Projectile = nil -- Entity to shoot
SWEP.Primary.ProjectileVelocity = 0 -- Entity to shoot's velocity
SWEP.Primary.ProjectileModel = nil -- Entity to shoot's model
-- VIEWMODEL
SWEP.ViewModel = "models/your/path/here.mdl" -- Viewmodel path
SWEP.ViewModelFOV = 65 -- This controls how big the viewmodel looks. Less is more.
SWEP.ViewModelFlip = false -- Set this to true for CSS models, or false for everything else (with a righthanded viewmodel.)
SWEP.UseHands = false -- Use gmod c_arms system.
SWEP.VMPos = Vector(0, 0, 0) -- The viewmodel positional offset, constantly. Subtract this from any other modifications to viewmodel position.
SWEP.VMAng = Vector(0, 0, 0) -- The viewmodel angular offset, constantly. Subtract this from any other modifications to viewmodel angle.
SWEP.VMPos_Additive = true -- Set to false for an easier time using VMPos. If true, VMPos will act as a constant delta ON TOP OF ironsights, run, whateverelse
SWEP.CenteredPos = nil -- The viewmodel positional offset, used for centering. Leave nil to autodetect using ironsights.
SWEP.CenteredAng = nil -- The viewmodel angular offset, used for centering. Leave nil to autodetect using ironsights.
SWEP.Bodygroups_V = nil -- {
-- [0] = 1,
-- [1] = 4,
-- [2] = etc.
-- }
SWEP.AllowIronSightsDoF = true -- whenever allow DoF effect on viewmodel when zoomed in with iron sights
SWEP.IronSightsReloadEnabled = nil -- Enable ADS reload animations support (requires animations to be enabled in SWEP.Animations)
SWEP.IronSightsReloadLock = true -- Lock ADS state when reloading
-- WORLDMODEL
SWEP.WorldModel = "models/your/wmodel/path/here.mdl" -- Weapon world model path
SWEP.Bodygroups_W = nil -- {
-- [0] = 1,
-- [1] = 4,
-- [2] = etc.
-- }
SWEP.HoldType = "" -- This is how others view you carrying the weapon. Options include:
-- normal melee melee2 fist knife smg ar2 pistol rpg physgun grenade shotgun crossbow slam passive
-- You're mostly going to use ar2, smg, shotgun or pistol. rpg and crossbow make for good sniper rifles
SWEP.Offset = {
Pos = {
Up = 0,
Right = 0,
Forward = 0
},
Ang = {
Up = -1,
Right = -2,
Forward = 178
},
Scale = 1
} -- Procedural world model animation, defaulted for CS:S purposes.
SWEP.ThirdPersonReloadDisable = false -- Disable third person reload? True disables.
-- SCOPES
SWEP.IronSightsSensitivity = 1 -- Useful for a RT scope. Change this to 0.25 for 25% sensitivity. This is if normal FOV compenstaion isn't your thing for whatever reason, so don't change it for normal scopes.
SWEP.BoltAction = false -- Unscope/sight after you shoot?
SWEP.Scoped = false -- Draw a scope overlay?
SWEP.ScopeOverlayThreshold = 0.875 -- Percentage you have to be sighted in to see the scope.
SWEP.BoltTimerOffset = 0.25 -- How long you stay sighted in after shooting, with a bolt action.
SWEP.ScopeScale = 0.5 -- Scale of the scope overlay
SWEP.ReticleScale = 0.7 -- Scale of the reticle overlay
-- GDCW Overlay Options. Only choose one.
SWEP.Secondary.UseACOG = false -- Overlay option
SWEP.Secondary.UseMilDot = false -- Overlay option
SWEP.Secondary.UseSVD = false -- Overlay option
SWEP.Secondary.UseParabolic = false -- Overlay option
SWEP.Secondary.UseElcan = false -- Overlay option
SWEP.Secondary.UseGreenDuplex = false -- Overlay option
if surface then
SWEP.Secondary.ScopeTable = nil --[[
{
scopetex = surface.GetTextureID("scope/gdcw_closedsight"),
reticletex = surface.GetTextureID("scope/gdcw_acogchevron"),
dottex = surface.GetTextureID("scope/gdcw_acogcross")
}
]] --
end
-- [[SHOTGUN CODE]] --
SWEP.Shotgun = false -- Enable shotgun style reloading.
SWEP.ShotgunEmptyAnim = false -- Enable emtpy reloads on shotguns?
SWEP.ShotgunEmptyAnim_Shell = true -- Enable insertion of a shell directly into the chamber on empty reload?
SWEP.ShotgunStartAnimShell = false -- shotgun start anim inserts shell
SWEP.ShellTime = .35 -- For shotguns, how long it takes to insert a shell.
-- [[SPRINTING]] --
SWEP.RunSightsPos = Vector(0, 0, 0) -- Change this, using SWEP Creation Kit preferably
SWEP.RunSightsAng = Vector(0, 0, 0) -- Change this, using SWEP Creation Kit preferably
-- [[CROUCHING]] --
-- Viewmodel offset when player is crouched
-- SWEP.CrouchPos = Vector(0, 0, 0)
-- SWEP.CrouchAng = Vector(0, 0, 0)
-- [[IRONSIGHTS]] --
SWEP.data = {}
SWEP.data.ironsights = 1 -- Enable Ironsights
SWEP.Secondary.IronFOV = 70 -- How much you "zoom" in. Less is more! Don't have this be <= 0. A good value for ironsights is like 70.
-- SWEP.IronViewModelFOV = 65 -- Target viewmodel FOV when aiming down the sights.
SWEP.IronSightsPos = Vector(0, 0, 0) -- Change this, using SWEP Creation Kit preferably
SWEP.IronSightsAng = Vector(0, 0, 0) -- Change this, using SWEP Creation Kit preferably
-- [[INSPECTION]] --
SWEP.InspectPos = nil -- Vector(0, 0, 0) -- Replace with a vector, in style of ironsights position, to be used for inspection
SWEP.InspectAng = nil -- Vector(0, 0, 0) -- Replace with a vector, in style of ironsights angle, to be used for inspection
-- [[VIEWMODEL BLOWBACK]] --
SWEP.BlowbackEnabled = false -- Enable Blowback?
SWEP.BlowbackVector = Vector(0, -1, 0) -- Vector to move bone <or root> relative to bone <or view> orientation.
SWEP.BlowbackAngle = nil -- Angle(0, 0, 0)
SWEP.BlowbackCurrentRoot = 0 -- Amount of blowback currently, for root
SWEP.BlowbackCurrent = 0 -- Amount of blowback currently, for bones
SWEP.BlowbackBoneMods = nil -- Viewmodel bone mods via SWEP Creation Kit
SWEP.Blowback_Only_Iron = true -- Only do blowback on ironsights
SWEP.Blowback_PistolMode = false -- Do we recover from blowback when empty?
SWEP.Blowback_Shell_Enabled = true -- Shoot shells through blowback animations
SWEP.Blowback_Shell_Effect = "ShellEject" -- Which shell effect to use
SWEP.BlowbackAllowAnimation = nil -- Allow playing shoot animation with blowback?
-- [[VIEWMODEL PROCEDURAL ANIMATION]] --
SWEP.DoProceduralReload = false -- Animate first person reload using lua?
SWEP.ProceduralReloadTime = 1 -- Procedural reload time?
-- [[HOLDTYPES]] --
SWEP.IronSightHoldTypeOverride = "" -- This variable overrides the ironsights holdtype, choosing it instead of something from the above tables. Change it to "" to disable.
SWEP.SprintHoldTypeOverride = "" -- This variable overrides the sprint holdtype, choosing it instead of something from the above tables. Change it to "" to disable.
-- [[ANIMATION]] --
SWEP.StatusLengthOverride = {} -- Changes the status delay of a given animation; only used on reloads. Otherwise, use SequenceLengthOverride or one of the others
SWEP.SequenceLengthOverride = {} -- Changes both the status delay and the nextprimaryfire of a given animation
SWEP.SequenceTimeOverride = {} -- Like above but changes animation length to a target
SWEP.SequenceRateOverride = {} -- Like above but scales animation length rather than being absolute
SWEP.ProceduralHolsterEnabled = nil
SWEP.ProceduralHolsterTime = 0.3
SWEP.ProceduralHolsterPos = Vector(3, 0, -5)
SWEP.ProceduralHolsterAng = Vector(-40, -30, 10)
SWEP.Idle_Mode = TFA.Enum.IDLE_BOTH -- TFA.Enum.IDLE_DISABLED = no idle, TFA.Enum.IDLE_LUA = lua idle, TFA.Enum.IDLE_ANI = mdl idle, TFA.Enum.IDLE_BOTH = TFA.Enum.IDLE_ANI + TFA.Enum.IDLE_LUA
SWEP.Idle_Blend = 0.25 -- Start an idle this far early into the end of a transition
SWEP.Idle_Smooth = 0.05 -- Start an idle this far early into the end of another animation
-- MDL Animations Below
SWEP.Sights_Mode = TFA.Enum.LOCOMOTION_LUA -- LOCOMOTION_ANI = mdl, LOCOMOTION_HYBRID = ani + lua, LOCOMOTION_LUA = lua only
--[[
SWEP.IronAnimation = {
["in"] = {
["type"] = TFA.Enum.ANIMATION_SEQ, -- Sequence or act
["value"] = "Idle_To_Iron", -- Number for act, String/Number for sequence
["value_empty"] = "Idle_To_Iron_Dry",
["transition"] = true
}, -- Inward transition
["loop"] = {
["type"] = TFA.Enum.ANIMATION_SEQ, -- Sequence or act
["value"] = "Idle_Iron", -- Number for act, String/Number for sequence
["value_empty"] = "Idle_Iron_Dry"
}, -- Looping Animation
["out"] = {
["type"] = TFA.Enum.ANIMATION_SEQ, -- Sequence or act
["value"] = "Iron_To_Idle", -- Number for act, String/Number for sequence
["value_empty"] = "Iron_To_Idle_Dry",
["transition"] = true
}, -- Outward transition
["shoot"] = {
["type"] = TFA.Enum.ANIMATION_SEQ, -- Sequence or act
["value"] = "Fire_Iron", -- Number for act, String/Number for sequence
["value_last"] = "Fire_Iron_Last",
["value_empty"] = "Fire_Iron_Dry"
} -- What do you think
}
]]
SWEP.Sprint_Mode = TFA.Enum.LOCOMOTION_LUA -- LOCOMOTION_ANI = mdl, LOCOMOTION_HYBRID = ani + lua, LOCOMOTION_LUA = lua only
--[[
SWEP.SprintAnimation = {
["in"] = {
["type"] = TFA.Enum.ANIMATION_SEQ, -- Sequence or act
["value"] = "Idle_to_Sprint", -- Number for act, String/Number for sequence
["value_empty"] = "Idle_to_Sprint_Empty",
["transition"] = true
}, -- Inward transition
["loop"] = {
["type"] = TFA.Enum.ANIMATION_SEQ, -- Sequence or act
["value"] = "Sprint_", -- Number for act, String/Number for sequence
["value_empty"] = "Sprint_Empty_",
["is_idle"] = true
}, -- looping animation
["out"] = {
["type"] = TFA.Enum.ANIMATION_SEQ, -- Sequence or act
["value"] = "Sprint_to_Idle", -- Number for act, String/Number for sequence
["value_empty"] = "Sprint_to_Idle_Empty",
["transition"] = true
} -- Outward transition
}
]]
SWEP.Walk_Mode = TFA.Enum.LOCOMOTION_LUA -- LOCOMOTION_ANI = mdl, LOCOMOTION_HYBRID = ani + lua, LOCOMOTION_LUA = lua only
--[[
SWEP.WalkAnimation = {
["in"] = {
["type"] = TFA.Enum.ANIMATION_SEQ, -- Sequence or act
["value"] = "Idle_to_Walk", -- Number for act, String/Number for sequence
["value_empty"] = "Idle_to_Walk_Empty",
["transition"] = true
}, -- Inward transition
["loop"] = {
["type"] = TFA.Enum.ANIMATION_SEQ, -- Sequence or act
["value"] = "Walk", -- Number for act, String/Number for sequence
["value_empty"] = "Walk_Empty",
["is_idle"] = true
}, -- looping animation
["out"] = {
["type"] = TFA.Enum.ANIMATION_SEQ, -- Sequence or act
["value"] = "Walk_to_Idle", -- Number for act, String/Number for sequence
["value_empty"] = "Walk_to_Idle_Empty",
["transition"] = true
} -- Outward transition
}
]]
--[[
-- Looping fire animation (full-auto only)
SWEP.ShootAnimation = {
["in"] = {
["type"] = TFA.Enum.ANIMATION_SEQ, -- Sequence or act
["value"] = "ShootLoop_Start", -- Number for act, String/Number for sequence
["value_is"] = "ShootLoop_Iron_Start", -- Number for act, String/Number for sequence
["transition"] = true
}, -- Looping Start, fallbacks to loop
["loop"] = {
["type"] = TFA.Enum.ANIMATION_SEQ, -- Sequence or act
["value"] = "ShootLoop", -- Number for act, String/Number for sequence,
["value_is"] = "ShootLoop_Iron", -- Number for act, String/Number for sequence,
["is_idle"] = true,
}, -- Looping Animation
["out"] = {
["type"] = TFA.Enum.ANIMATION_SEQ, -- Sequence or act
["value"] = "ShootLoop_End", -- Number for act, String/Number for sequence
["value_is"] = "ShootLoop_Iron_End", -- Number for act, String/Number for sequence
["transition"] = true
}, -- Looping End
}
]]
SWEP.Customize_Mode = TFA.Enum.LOCOMOTION_LUA -- LOCOMOTION_ANI = mdl, LOCOMOTION_HYBRID = ani + lua, LOCOMOTION_LUA = lua only
--[[
SWEP.CustomizeAnimation = {
["in"] = {
["type"] = TFA.Enum.ANIMATION_SEQ, -- Sequence or act
["value"] = "customization_in", -- Number for act, String/Number for sequence
["transition"] = true
},
["loop"] = {
["type"] = TFA.Enum.ANIMATION_SEQ, -- Sequence or act
["value"] = "customization_idle", -- Number for act, String/Number for sequence
["is_idle"] = true
},
["out"] = {
["type"] = TFA.Enum.ANIMATION_SEQ, -- Sequence or act
["value"] = "customization_out", -- Number for act, String/Number for sequence
["transition"] = true
}
}
]]
--[[
SWEP.PumpAction = { -- Pump/bolt animations
["type"] = TFA.Enum.ANIMATION_ACT, -- Sequence or act
["value"] = ACT_VM_PULLBACK_HIGH, -- Number for act, String/Number for sequence
["value_empty"] = ACT_VM_PULLBACK, -- Last shot pump
["value_is"] = ACT_VM_PULLBACK_LOW, -- ADS pump
}
]] --
-- [[EFFECTS]] --
-- Attachments
SWEP.MuzzleAttachment = "1" -- Should be "1" for CSS models or "muzzle" for hl2 models
SWEP.ShellAttachment = "2" -- Should be "2" for CSS models or "shell" for hl2 models
SWEP.MuzzleFlashEnabled = true -- Enable muzzle flash
SWEP.MuzzleAttachmentRaw = nil -- This will override whatever string you gave. This is the raw attachment number. This is overridden or created when a gun makes a muzzle event.
SWEP.AutoDetectMuzzleAttachment = false -- For multi-barrel weapons, detect the proper attachment?
SWEP.MuzzleFlashEffect = nil -- Change to a string of your muzzle flash effect. Copy/paste one of the existing from the base.
SWEP.SmokeParticle = nil -- Smoke particle (ID within the PCF), defaults to something else based on holdtype; "" to disable
SWEP.EjectionSmokeEnabled = true -- Disable automatic ejection smoke
-- Shell eject override
SWEP.LuaShellEject = false -- Enable shell ejection through lua?
SWEP.LuaShellEjectDelay = 0 -- The delay to actually eject things
SWEP.LuaShellModel = nil -- The model to use for ejected shells
SWEP.LuaShellScale = nil -- The model scale to use for ejected shells
SWEP.LuaShellYaw = nil -- The model yaw rotation ( relative ) to use for ejected shells
-- Tracer Stuff
SWEP.TracerName = nil -- Change to a string of your tracer name. Can be custom. There is a nice example at https://github.com/garrynewman/garrysmod/blob/master/garrysmod/gamemodes/base/entities/effects/tooltracer.lua
SWEP.TracerCount = 3 -- 0 disables, otherwise, 1 in X chance
-- Impact Effects
SWEP.ImpactEffect = nil -- Impact Effect
SWEP.ImpactDecal = nil -- Impact Decal
-- [[EVENT TABLE]] --
SWEP.EventTable = {} -- Event Table, used for custom events when an action is played. This can even do stuff like playing a pump animation after shooting.
-- example:
-- SWEP.EventTable = {
-- [ACT_VM_RELOAD] = {
-- -- ifp is IsFirstTimePredicted()
-- { ["time"] = 0.1, ["type"] = "lua", ["value"] = function( wep, viewmodel, ifp ) end, ["client"] = true, ["server"] = true},
-- { ["time"] = 0.1, ["type"] = "sound", ["value"] = Sound("x") }
-- }
-- }
-- [[RENDER TARGET]] --
SWEP.RTMaterialOverride = nil -- Take the material you want out of print(LocalPlayer():GetViewModel():GetMaterials()), subtract 1 from its index, and set it to this.
SWEP.RTOpaque = false -- Do you want your render target to be opaque?
SWEP.RTCode = nil -- function(self) return end -- This is the function to draw onto your rendertarget
SWEP.RTBGBlur = true -- Draw background blur when 3D scope is active?
-- [[AKIMBO]] --
SWEP.Akimbo = false -- Akimbo gun? Alternates between primary and secondary attacks.
SWEP.AnimCycle = 1 -- Start on the right
SWEP.AkimboHUD = true -- Draw holographic HUD for both weapons?
-- [[ATTACHMENTS]] --
SWEP.VElements = nil -- Export from SWEP Creation Kit. For each item that can/will be toggled, set active=false in its individual table
SWEP.WElements = nil -- Export from SWEP Creation Kit. For each item that can/will be toggled, set active=false in its individual table
SWEP.Attachments = {
-- [ORDER] = = { atts = { "si_eotech" }, sel = 0 }
-- sel allows you to have an attachment pre-selected, and is used internally by the base to show which attachment is selected in each category.
}
SWEP.AttachmentDependencies = {} -- {["si_acog"] = {"bg_rail", ["type"] = "OR"}} -- type could also be AND to require multiple
SWEP.AttachmentExclusions = {} -- { ["si_iron"] = { [1] = "bg_heatshield"} }
SWEP.AttachmentTableOverride = {} --[[{ -- overrides WeaponTable for attachments
["ins2_ub_laser"] = { -- attachment id, root of WeaponTable override
["VElements"] = {
["laser_rail"] = {
["active"] = true
},
},
}
}]]
SWEP.DInv2_GridSizeX = nil -- DInventory/2 Specific. Determines weapon's width in grid. This is not TFA Base specific and can be specified to any Scripted SWEP.
SWEP.DInv2_GridSizeY = nil -- DInventory/2 Specific. Determines weapon's height in grid. This is not TFA Base specific and can be specified to any Scripted SWEP.
SWEP.DInv2_Volume = nil -- DInventory/2 Specific. Determines weapon's volume in liters. This is not TFA Base specific and can be specified to any Scripted SWEP.
SWEP.DInv2_Mass = nil -- DInventory/2 Specific. Determines weapon's mass in kilograms. This is not TFA Base specific and can be specified to any Scripted SWEP.
-- [[MISC INFO FOR MODELERS]] --
--[[
Used Animations (for modelers):
ACT_VM_DRAW - Draw
ACT_VM_DRAW_EMPTY - Draw empty
ACT_VM_DRAW_SILENCED - Draw silenced, overrides empty
ACT_VM_IDLE - Idle
ACT_VM_IDLE_SILENCED - Idle empty, overwritten by silenced
ACT_VM_IDLE_SILENCED - Idle silenced
ACT_VM_PRIMARYATTACK - Shoot
ACT_VM_PRIMARYATTACK_EMPTY - Shoot last chambered bullet
ACT_VM_PRIMARYATTACK_SILENCED - Shoot silenced, overrides empty
ACT_VM_PRIMARYATTACK_1 - Shoot ironsights, overriden by everything besides normal shooting
ACT_VM_DRYFIRE - Dryfire
ACT_VM_RELOAD - Reload / Tactical Reload / Insert Shotgun Shell
ACT_SHOTGUN_RELOAD_START - Start shotgun reload, unless ACT_VM_RELOAD_EMPTY is there.
ACT_SHOTGUN_RELOAD_FINISH - End shotgun reload.
ACT_VM_RELOAD_EMPTY - Empty mag reload, chambers the new round. Works for shotguns too, where applicable.
ACT_VM_RELOAD_SILENCED - Silenced reload, overwrites all
ACT_VM_HOLSTER - Holster
ACT_VM_HOLSTER_SILENCED - Holster empty, overwritten by silenced
ACT_VM_HOLSTER_SILENCED - Holster silenced
]] --
DEFINE_BASECLASS( SWEP.Base )

View File

@@ -0,0 +1,80 @@
-- Name: PlayerWeaponColorStatic
-- Description: Static/direct variation of PlayerWeaponColor, without any flickering.
-- Parameters:
-- 1. resultvar - Result variable for the color (such as $color2)
-- VMT Example:
--[[
Proxies
{
PlayerWeaponColorStatic
{
resultvar $color2
}
}
]]
-- Name: TFALaserColor
-- Description:
-- Parameters:
-- 1. resultvar - Result variable for the color (such as $color2)
-- VMT Example:
--[[
Proxies
{
TFALaserColor
{
resultVar $color2
}
}
]]
-- Name: TFAReticuleColor
-- Description:
-- Parameters:
-- 1. resultvar - Result variable for the color (such as $color2)
-- VMT Example:
--[[
Proxies
{
TFAReticuleColor
{
resultVar $color2
}
}
]]
-- Name: TFA_RTScope
-- Description: Replaces $basetexture with render target texture of 3D scopes
-- VMT Example:
--[[
Proxies
{
TFA_RTScope
{
}
}
]]
-- Name: TFA_CubemapTint
-- Description: Tints
-- Parameters:
-- 1. resultvar - Variable for resulting envmap tint ($envmaptint)
-- 2. multiplier - Variable for base tint multiplier (a vector)
-- VMT Example:
--[[
$envmapmultiplier "[1 1 1]" // Lighting will be multiplied by this value
Proxies
{
TFA_CubemapTint
{
resultvar $envmaptint // Write final output to $envmaptint
multiplier $envmapmultiplier // Use our value for default envmap tint
}
}
]]

View File

@@ -0,0 +1,95 @@
-- TFA Base Melee Template by TFA Base Devs
-- To the extent possible under law, the person who associated CC0 with
-- TFA Base Template has waived all copyright and related or neighboring rights
-- to TFA Base Template.
-- You should have received a copy of the CC0 legalcode along with this
-- work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
-- M9K compatible version is dated as 0 (and 0 is also fallback if TFADataVersion not present)
-- as well as everything made for TFA Base before 4.7
SWEP.TFADataVersion = 1
----------------- Basic Garry's Mod SWEP structure stats / TFA Base properties
SWEP.Base = "tfa_melee_base"
SWEP.Category = "TFA Template" -- The category.
-- Please, just choose something generic or something I've already done if you plan on only doing like one (or two or three) swep(s).
SWEP.Manufacturer = nil -- Gun Manufactrer (e.g. Hoeckler and Koch)
SWEP.Author = "" -- Author Tooltip
SWEP.Contact = "" -- Contact Info Tooltip
SWEP.Purpose = "" -- Purpose Tooltip
SWEP.Instructions = "" -- Instructions Tooltip
SWEP.Spawnable = false -- Can you, as a normal user, spawn this?
SWEP.AdminSpawnable = false -- Can an adminstrator spawn this? Does not tie into your admin mod necessarily, unless its coded to allow for GMod's default ranks somewhere in its code. Evolve and ULX should work, but try to use weapon restriction rather than these.
SWEP.DrawCrosshair = true -- Draw the crosshair?
SWEP.PrintName = "TFA Base Melee Template" -- Weapon name (Shown on HUD)
SWEP.Slot = 0 -- Slot in the weapon selection menu. Subtract 1, as this starts at 0.
SWEP.SlotPos = 73 -- Position in the slot
SWEP.AutoSwitchTo = true -- Auto switch to if we pick it up
SWEP.AutoSwitchFrom = true -- Auto switch from if you pick up a better weapon
SWEP.Weight = 30 -- This controls how "good" the weapon is for autopickup.
-- For base values please refer to the base template at lua/weapons/tfa_base_template/shared.lua
-- Display values (inspection screen etc.)
SWEP.Primary.Damage = 0.01 -- Damage, in standard damage points.
SWEP.Primary.RPM = 600 -- This is in Rounds Per Minute / RPM
----------------- ViewModel related
SWEP.ViewModel = "models/your/path/here.mdl" -- Viewmodel path
SWEP.ViewModelFOV = 65 -- This controls how big the viewmodel looks. Less is more.
SWEP.ViewModelFlip = false -- Set this to true for CSS models, or false for everything else (with a righthanded viewmodel.)
SWEP.UseHands = false -- Use gmod c_arms system.
----------------- Worldmodel related
SWEP.WorldModel = "models/your/wmodel/path/here.mdl" -- Weapon world model path
SWEP.HoldType = "" -- This is how others view you carrying the weapon. Options include:
-- normal melee melee2 fist knife smg ar2 pistol rpg physgun grenade shotgun crossbow slam passive
-- Attacks - Primary
SWEP.Primary.Attacks = { -- main attacks table, the values are selected randomly
{
["act"] = ACT_VM_HITLEFT, -- Animation acvitity to use (ACT_ enum value)
["len"] = 8 * 4.5, -- Trace distance
["src"] = Vector(20, 10, 0), -- Trace source; X ( +right, -left ), Y ( +forward, -back ), Z ( +up, -down )
["dir"] = Vector(-40, 30, 0), -- Trace direction/length; X ( +right, -left ), Y ( +forward, -back ), Z ( +up, -down )
["dmg"] = 60, -- Damage
["dmgtype"] = DMG_SLASH, -- Damage type (DMG_ enum value)
["delay"] = 0.2, -- Delay (in seconds) before attack trace
["force"] = 12, -- Damage force
["hull"] = 10, -- Trace hull size
["spr"] = true, -- Allow attack while sprinting?
["snd"] = "Swing.Sound", -- Soundscript name for swing sound
["hitflesh"] = "TFA.BashFlesh", -- Soundscript name for flesh hit
["hitworld"] = "TFA.BashWall", -- Soundscript name for non-flesh hit
["snd_delay"] = 0.1, -- Delay before swing sound
["viewpunch"] = Angle(1, -10, 0), -- Viewpunch angle
["end"] = 0.5, -- Time (from attack start) until next attack is allowed
["direction"] = "L", -- Swing direction (for directional preference); L,R,F,B
},
}
SWEP.Primary.MaxCombo = -1 -- How many attacks are allowed on single attack key hold
SWEP.Primary.Directional = false -- Prefer attacks with player's movement direction first
SWEP.Primary.SplitDamage = true -- Use the "dmg" value of the attack table? If false, SWEP.Primary.Damage will be used instead.
-- Attacks - Secondary
-- If secondary attacks table is empty or not defined, it falls back to primary table
SWEP.Secondary.Attacks = {} -- same as SWEP.Primary.Attacks
SWEP.Secondary.MaxCombo = -1
SWEP.Secondary.Directional = false
SWEP.Secondary.SplitDamage = true -- Use the "dmg" value of the attack table? If false, SWEP.Secondary.Damage will be used instead.
SWEP.Secondary.PrimaryFallback = true -- Allow falling back to primary attacks if secondary attacks table is empty/unavailable
-- Attacks - Alternative (melee bash)
SWEP.Secondary.CanBash = true -- set to false to disable bashing
SWEP.Secondary.BashDamage = 25 -- Melee bash damage
SWEP.Secondary.BashSound = "TFA.Bash" -- Soundscript name for bash swing sound
SWEP.Secondary.BashHitSound = "TFA.BashWall" -- Soundscript name for non-flesh hit sound
SWEP.Secondary.BashHitSound_Flesh = "TFA.BashFlesh" -- Soundscript name for flesh hit sound
SWEP.Secondary.BashLength = 54 -- Length of bash melee trace in units
SWEP.Secondary.BashDelay = 0.2 -- Delay (in seconds) from bash start to bash attack trace
SWEP.Secondary.BashDamageType = DMG_SLASH -- Damage type (DMG_ enum value)
SWEP.Secondary.BashEnd = nil -- Bash end time (in seconds), defaults to animation end if undefined
SWEP.Secondary.BashInterrupt = false -- Bash attack interrupts everything (reload, draw, whatever)

View File

@@ -0,0 +1,14 @@
-- luacheck: globals ACT_VM_FIDGET_EMPTY ACT_VM_FIDGET_SILENCED ACT_VM_BLOWBACK ACT_VM_HOLSTER_SILENCED
TFA.Enum.ANIMATION_ACT = 0
TFA.Enum.ANIMATION_SEQ = 1
ACT_VM_FIDGET_EMPTY = ACT_VM_FIDGET_EMPTY or ACT_CROSSBOW_FIDGET_UNLOADED
ACT_VM_FIDGET_SILENCED = ACT_VM_FIDGET_SILENCED or ACT_RPG_FIDGET_UNLOADED
ACT_VM_HOLSTER_SILENCED = ACT_VM_HOLSTER_SILENCED or ACT_CROSSBOW_HOLSTER_UNLOADED
ACT_VM_BLOWBACK = ACT_VM_BLOWBACK or -2
-- luacheck: globals ACT_VM_RELOAD_ADS ACT_VM_RELOAD_EMPTY_ADS ACT_VM_RELOAD_SILENCED_ADS ACT_SHOTGUN_RELOAD_START_ADS ACT_SHOTGUN_RELOAD_FINISH_ADS
ACT_VM_RELOAD_ADS = ACT_VM_RELOAD_ADS or ACT_IDLE_AIM_RIFLE_STIMULATED
ACT_VM_RELOAD_EMPTY_ADS = ACT_VM_RELOAD_EMPTY_ADS or ACT_WALK_AIM_RIFLE_STIMULATED
ACT_VM_RELOAD_SILENCED_ADS = ACT_VM_RELOAD_SILENCED_ADS or ACT_RUN_AIM_RIFLE_STIMULATED
ACT_SHOTGUN_RELOAD_START_ADS = ACT_SHOTGUN_RELOAD_START_ADS or ACT_IDLE_SHOTGUN_RELAXED
ACT_SHOTGUN_RELOAD_FINISH_ADS = ACT_SHOTGUN_RELOAD_FINISH_ADS or ACT_IDLE_SHOTGUN_STIMULATED

View File

@@ -0,0 +1,5 @@
--IDLE TYPE ENUM
TFA.Enum.IDLE_DISABLED = 0
TFA.Enum.IDLE_LUA = 1
TFA.Enum.IDLE_ANI = 2
TFA.Enum.IDLE_BOTH = 3

View File

@@ -0,0 +1,4 @@
--LOCOMOTION ENUM
TFA.Enum.LOCOMOTION_LUA = 0
TFA.Enum.LOCOMOTION_HYBRID = 1
TFA.Enum.LOCOMOTION_ANI = 2

View File

@@ -0,0 +1,12 @@
TFA.Enum.SIGHTSPOS_ATTACH = 0
TFA.Enum.SIGHTSPOS_BONE = 1
TFA.Enum.RETICLE_FLAT = bit.lshift(1, 1)
TFA.Enum.RETICLE_MODEL = bit.lshift(1, 2)
TFA.Enum.RETICLE_QUAD = bit.lshift(1, 3)
TFA.Enum.RETICLE_DRAW_ORDER = {
TFA.Enum.RETICLE_MODEL,
TFA.Enum.RETICLE_QUAD,
TFA.Enum.RETICLE_FLAT,
}

View File

@@ -0,0 +1,158 @@
TFA.ENUM_COUNTER = TFA.ENUM_COUNTER or 0
TFA.Enum.InverseStatus = TFA.Enum.InverseStatus or {}
local upper = string.upper
local function gen(input)
return "STATUS_" .. upper(input)
end
function TFA.AddStatus(input)
local key = gen(input)
local getkey = TFA.Enum[key]
if not getkey then
getkey = TFA.ENUM_COUNTER
TFA.ENUM_COUNTER = TFA.ENUM_COUNTER + 1
TFA.Enum[key] = getkey
end
TFA.Enum.InverseStatus[getkey] = key
return getkey
end
function TFA.GetStatus(input)
local key = gen(input)
local getkey = TFA.Enum[key]
if not getkey then
return TFA.AddStatus(input) -- DANGEROUS:
-- Race condition:
-- If something go terribly wrong and order of addition of new statuses fuck up
-- everything will fail horribly!
end
return getkey
end
TFA.AddStatus("idle")
TFA.AddStatus("draw")
TFA.AddStatus("holster")
TFA.AddStatus("holster_final")
TFA.AddStatus("holster_ready")
TFA.AddStatus("reloading")
TFA.AddStatus("reloading_wait")
TFA.AddStatus("reloading_loop_start")
TFA.AddStatus("reloading_loop_start_empty")
TFA.AddStatus("reloading_loop")
TFA.AddStatus("reloading_loop_end")
TFA.Enum.STATUS_RELOADING_SHOTGUN_START = TFA.Enum.STATUS_RELOADING_LOOP_START
TFA.Enum.STATUS_RELOADING_SHOTGUN_START_SHELL = TFA.Enum.STATUS_RELOADING_LOOP_START_EMPTY
TFA.Enum.STATUS_RELOADING_SHOTGUN_LOOP = TFA.Enum.STATUS_RELOADING_LOOP
TFA.Enum.STATUS_RELOADING_SHOTGUN_END = TFA.Enum.STATUS_RELOADING_LOOP_END
TFA.AddStatus("shooting")
TFA.AddStatus("silencer_toggle")
TFA.AddStatus("bashing")
TFA.AddStatus("bashing_wait")
TFA.AddStatus("inspecting")
TFA.AddStatus("fidget")
TFA.AddStatus("firemode")
TFA.AddStatus("pump")
TFA.AddStatus("knife_slash")
TFA.AddStatus("knife_stab")
TFA.AddStatus("grenade_pull")
TFA.AddStatus("grenade_ready")
TFA.AddStatus("grenade_throw")
TFA.AddStatus("blocking")
TFA.AddStatus("blocking_end")
TFA.AddStatus("bow_shoot")
TFA.AddStatus("bow_cancel")
TFA.AddStatus("grenade_pull")
TFA.AddStatus("grenade_throw")
TFA.AddStatus("grenade_ready")
TFA.AddStatus("grenade_throw_wait")
TFA.Enum.HolsterStatus = {
[TFA.Enum.STATUS_HOLSTER] = true,
[TFA.Enum.STATUS_HOLSTER_FINAL] = true,
[TFA.Enum.STATUS_HOLSTER_READY] = true
}
TFA.Enum.HolsterStatusFinal = {
[TFA.Enum.STATUS_HOLSTER_FINAL] = true,
[TFA.Enum.STATUS_HOLSTER_READY] = true
}
TFA.Enum.ReloadStatus = {
[TFA.Enum.STATUS_RELOADING] = true,
[TFA.Enum.STATUS_RELOADING_WAIT] = true,
[TFA.Enum.STATUS_RELOADING_LOOP_START] = true,
[TFA.Enum.STATUS_RELOADING_LOOP_START_EMPTY] = true,
[TFA.Enum.STATUS_RELOADING_LOOP] = true,
[TFA.Enum.STATUS_RELOADING_LOOP_END] = true
}
TFA.Enum.ReadyStatus = {
[TFA.Enum.STATUS_IDLE] = true,
[TFA.Enum.STATUS_INSPECTING] = true,
[TFA.Enum.STATUS_FIDGET] = true
}
TFA.Enum.IronStatus = {
[TFA.Enum.STATUS_IDLE] = true,
[TFA.Enum.STATUS_SHOOTING] = true,
[TFA.Enum.STATUS_PUMP] = true,
[TFA.Enum.STATUS_FIREMODE] = true--,
--[TFA.Enum.STATUS_FIDGET] = true
}
TFA.Enum.HUDDisabledStatus = {
[TFA.Enum.STATUS_IDLE] = true,
[TFA.Enum.STATUS_SHOOTING] = true,
[TFA.Enum.STATUS_FIREMODE] = true,
[TFA.Enum.STATUS_BASHING] = true,
[TFA.Enum.STATUS_BASHING_WAIT] = true,
[TFA.Enum.STATUS_HOLSTER] = true,
[TFA.Enum.STATUS_HOLSTER_FINAL] = true,
[TFA.Enum.STATUS_HOLSTER_READY] = true,
[TFA.Enum.STATUS_KNIFE_SLASH] = true,
[TFA.Enum.STATUS_KNIFE_STAB] = true,
[TFA.Enum.STATUS_GRENADE_PULL] = true,
[TFA.Enum.STATUS_GRENADE_READY] = true,
[TFA.Enum.STATUS_GRENADE_THROW] = true,
[TFA.Enum.STATUS_BLOCKING] = true,
[TFA.Enum.STATUS_BLOCKING_END] = true,
[TFA.Enum.STATUS_PUMP] = true
}
TFA.Enum.BashStatus = {
[TFA.Enum.STATUS_BASHING] = true,
[TFA.Enum.STATUS_BASHING_WAIT] = true,
}
TFA.Enum.SHOOT_IDLE = 0
TFA.Enum.SHOOT_START = 1
TFA.Enum.SHOOT_LOOP = 2
TFA.Enum.SHOOT_CHECK = 3
TFA.Enum.SHOOT_END = 4
TFA.Enum.ShootReadyStatus = {
[TFA.Enum.SHOOT_IDLE] = true,
[TFA.Enum.SHOOT_END] = true
}
TFA.Enum.ShootLoopingStatus = {
[TFA.Enum.SHOOT_START] = true,
[TFA.Enum.SHOOT_LOOP] = true,
[TFA.Enum.SHOOT_CHECK] = true
}

View File

@@ -0,0 +1,136 @@
local padding = TFA.Attachments.UIPadding
local PANEL = {}
PANEL.Wep = nil
PANEL.ID = nil
PANEL.Att = nil --Weapon attachment
PANEL.Attachment = nil --Actual TFA attachment table
function PANEL:Init()
self.Wep = nil --Weapon Entity
self.ID = nil --Attachment ID
self.Att = nil --Attachment Category
self.Attachment = nil --TFA Attachment Name
self:SetMouseInputEnabled(true)
self:SetZPos(500)
end
function PANEL:SetWeapon(wep)
if IsValid(wep) then
self.Wep = wep
end
end
function PANEL:SetGunAttachment(att)
if att ~= nil then
self.Att = att
end
end
function PANEL:SetAttachment(att)
self.Attachment = att
end
function PANEL:SetID(id)
if id ~= nil then
self.ID = id
end
end
function PANEL:GetSelected()
if not IsValid(self.Wep) then return false end
if not self.Att then return end
if not self.ID then return end
if not self.Wep.Attachments[self.Att] then return end
return self.Wep.Attachments[self.Att].sel == self.ID
end
function PANEL:AttachSound(attached)
if self.Attachment and TFA.Attachments.Atts[self.Attachment] then
local att = TFA.Attachments.Atts[self.Attachment]
local snd = attached and att.AttachSound or att.DetachSound
if snd and IsValid(self.Wep) then
self.Wep:EmitSound(snd)
return
end
end
chat.PlaySound()
end
function PANEL:OnMousePressed()
if not IsValid(self.Wep) or not self.Attachment or self.Attachment == "" then return end
if self:GetSelected() and self.Wep:CanAttach(self.Attachment, true) then
self.Wep:SetTFAAttachment(self.Att, -1, true)
self:AttachSound(false)
elseif self.Wep.Attachments[self.Att] and self.Wep:CanAttach(self.Attachment) then
self.Wep:SetTFAAttachment(self.Att, self.ID, true)
self:AttachSound(true)
end
end
local function abbrev(str)
local tbl = string.Explode(" ",str,false)
local retstr = ""
for k,v in ipairs(tbl) do
local tmpstr = utf8.sub(v,1,1)
retstr = retstr .. ((k == 1) and string.upper(tmpstr) or string.lower(tmpstr))
end
return retstr
end
function PANEL:Paint(w, h)
if not IsValid(self.Wep) then return end
if self.Attachment == nil then return end
if not TFA.Attachments.Atts[self.Attachment] then self:SetMouseInputEnabled(false) return end
local sel = self:GetSelected()
local col = sel and TFA.Attachments.Colors["active"] or TFA.Attachments.Colors["background"]
if not sel and not self.Wep:CanAttach(self.Attachment) then
col = TFA.Attachments.Colors["error"]
elseif sel and not self.Wep:CanAttach(self.Attachment, true) then
col = TFA.Attachments.Colors["error_attached"]
end
draw.RoundedBox(0, 0, 0, w, h, ColorAlpha(col, self.Wep:GetInspectingProgress() * 225))
if not TFA.Attachments.Atts[self.Attachment].Icon then
TFA.Attachments.Atts[self.Attachment].Icon = "entities/tfa_qmark.png"
end
if not TFA.Attachments.Atts[self.Attachment].Icon_Cached then
TFA.Attachments.Atts[self.Attachment].Icon_Cached = Material(TFA.Attachments.Atts[self.Attachment].Icon, "noclamp smooth")
end
local attachmentIcon = TFA.Attachments.Atts[self.Attachment].Icon_Cached
local iconOverride = self.Wep:GetStat("AttachmentIconOverride." .. self.Attachment)
if iconOverride and type(iconOverride) == "IMaterial" then
attachmentIcon = iconOverride
end
surface.SetDrawColor(ColorAlpha(color_white, self.Wep:GetInspectingProgress() * 255))
surface.SetMaterial(attachmentIcon)
surface.DrawTexturedRect(padding, padding, w - padding * 2, h - padding * 2)
if not TFA.Attachments.Atts[self.Attachment].ShortName then
TFA.Attachments.Atts[self.Attachment].ShortName = abbrev(language.GetPhrase(TFA.Attachments.Atts[self.Attachment].Name) or "")
TFA.Attachments.Atts[self.Attachment].ShortNameGenerated = true
end
draw.SimpleText(string.upper(TFA.Attachments.Atts[self.Attachment].ShortName) , "TFAAttachmentIconFontTiny", padding / 4, h, ColorAlpha(TFA.Attachments.Colors["primary"], self.Wep:GetInspectingProgress() * (sel and 192 or 64)), TEXT_ALIGN_LEFT, TEXT_ALIGN_BOTTOM)
end
vgui.Register("TFAAttachmentIcon", PANEL, "Panel")
-- cleanup generated shortnames
cvars.AddChangeCallback("gmod_language", function()
for id, att in pairs(TFA.Attachments.Atts or {}) do
if att.ShortNameGenerated then
att.ShortName = nil
att.ShortNameGenerated = nil
end
end
end, "tfa_attachment_clearshortnames")

View File

@@ -0,0 +1,268 @@
if SERVER then
AddCSLuaFile()
return
end
local dimensions, padding
local tooltip_mincount = 1
local PANEL = {}
PANEL.HasInitialized = false
PANEL.Wep = nil
PANEL.Att = nil
PANEL.x = -1
PANEL.y = -1
PANEL.AttachmentTable = {}
PANEL.AttachmentIcons = {}
PANEL.VAtt = 0
function PANEL:Init()
self.HasInitialized = false
self.Wep = nil
self.Att = nil
self.x = -1
self.y = -1
self.AttachmentTable = {}
self.AttachmentIcons = {}
self:SetMouseInputEnabled(true)
end
function PANEL:Initialize()
if not IsValid(self.Wep) then return false end
if not self.Att then return end
self.AttachmentTable = self.Wep.Attachments[ self.VAtt ]
self.VGUIAttachmentTable = self.Wep.VGUIAttachments[ self.VAtt ]
dimensions = math.Round(TFA.ScaleH(TFA.Attachments.IconSize))
padding = math.Round(TFA.ScaleH(TFA.Attachments.UIPadding))
local attCnt = #self.VGUIAttachmentTable.atts
local truewidth = dimensions * attCnt + padding * ( math.max(0,attCnt-1) + 2 )
local finalwidth = math.max( truewidth, dimensions * tooltip_mincount + padding * ( math.max(0,tooltip_mincount-1) + 2 ) )
self:SetSize( finalwidth, dimensions + padding * 2 ) --+ tooltipheightmax + padding * 2 )
self:DockPadding( 0, 0, 0, 0 )
local toppanel = self:Add("DPanel")
--toppanel:Dock( FILL )
--toppanel:Dock(TOP)
toppanel:SetWidth( finalwidth )
toppanel:SetHeight( self:GetTall() )
toppanel:DockPadding( padding,padding, padding, padding )
toppanel.Paint = function(myself,w,h)
if not IsValid(self.Wep) then return end
draw.RoundedBox( 0, 0, 0, w, h, ColorAlpha( TFA.Attachments.Colors["secondary"], ( self.Wep:GetInspectingProgress() or 0 ) * 128 ) )
end
self.FinalWidth = finalwidth
self.TopDockPanel = toppanel
--self:InitializeTooltip()
--[[
local tooltip = self:Add("TFAAttachmentTip")
tooltip:SetWeapon( self.Wep )
tooltip:SetAttachment( self.Att )
--tooltip:SetHeight( tooltipheightmax + padding * 2 )
tooltip:SetSize( finalwidth, tooltipheightmax + padding * 2 )
tooltip:SetPos(0, toppanel:GetTall() )
self.ToolTip = tooltip
]]--
--local keyz = table.GetKeys( self.AttachmentTable.atts )
--table.sort(keyz)
--PrintTable(keyz)
--for _,k in ipairs(keyz) do
-- local v = self.AttachmentTable.atts[k]
self.HasInitialized = true
return true
end
function PANEL:PopulateIcons()
dimensions = math.Round(TFA.ScaleH(TFA.Attachments.IconSize))
padding = math.Round(TFA.ScaleH(TFA.Attachments.UIPadding))
local i = 0
for k,v in ipairs( self.VGUIAttachmentTable.atts ) do
local p = self.TopDockPanel:Add("TFAAttachmentIcon")
p:SetWeapon( self.Wep )
p:SetGunAttachment( self.Att )
p:SetAttachment( v[1] )
p:SetID( v[2] )
p:SetName("Attachment Icon: " .. v[1])
p:SetSize(dimensions, dimensions)
p:SetPos(dimensions * i + padding * ( i + 1 ), padding)
i = i + 1
--p:SetPos(0,0)
--p:DockMargin( 0,0, padding, 0 )
--p:Dock(LEFT)
self.AttachmentIcons[k] = p
end
return self
end
function PANEL:InitializeTooltip()
local tooltip = vgui.Create("TFAAttachmentTip")
tooltip.Anchor = self
tooltip:SetWeapon(self.Wep)
tooltip:SetAttachment(self.Att)
tooltip:SetWidth(self.FinalWidth)
tooltip:SetPos(0, self.TopDockPanel:GetTall())
self.ToolTip = tooltip
tooltip.LastTouched = 0
tooltip.LastFrameAffectedImportant = 0
return tooltip
end
function PANEL:OnRemove()
if IsValid(self.ToolTip) then
self.ToolTip:Remove()
end
end
function PANEL:SetupTooltip(tooltip)
tooltip.Anchor = self
tooltip:SetWidth(math.max(self.FinalWidth, tooltip:GetWide()))
tooltip:SetPos(0, self.TopDockPanel:GetTall())
self.ToolTip = tooltip
return tooltip
end
--[[
function PANEL:CalcVAtt()
if not self.VAtt then
self.VAtt = 0
local keyz = table.GetKeys( self.Wep.Attachments or {} )
table.RemoveByValue( keyz, "BaseClass" )
table.sort( keyz, function(a,b)
--A and B are keys
local v1 = self.Wep.Attachments[a]
local v2 = self.Wep.Attachments[b]
if v1 and v2 and v1.order then
return v1.order < ( v2.order or math.huge )
else
return a < b
end
end)
for k,v in ipairs(keyz) do
if self.Att == v then
self.VAtt = k
end
end
--self:SetZPos( 100 - self.VAtt )
end
end
]]--
function PANEL:Think()
if not IsValid(self.ToolTip) then return end
--self:CalcVAtt()
local header
local texttable
for _,v in pairs( self.AttachmentIcons ) do
if v:IsHovered() then
header = TFA.Attachments.Atts[v.Attachment].Name
texttable = TFA.Attachments.Atts[v.Attachment].Description
break
end
end
if not header then
for _,v in pairs( self.AttachmentIcons ) do
if v:GetSelected() then
header = TFA.Attachments.Atts[v.Attachment].Name
texttable = {}--TFA.Attachments.Atts[v.Attachment].Description
break
end
end
end
if header and header ~= "" or self.ToolTip.LastTouched < RealTime() then
if texttable and #texttable == 0 and self.ToolTip.LastFrameAffectedImportant > RealTime() then
return
end
self.ToolTip:SetHeader(header)
self.ToolTip:SetTextTable(texttable)
self.ToolTip:SetActive( texttable and #texttable > 0 )
self.ToolTip:SetContentPanel( self.ContentPanel )
self.ToolTip.LastTouched = RealTime() + 0.1
if texttable and #texttable ~= 0 then
self.ToolTip.LastFrameAffectedImportant = RealTime() + 0.1
end
end
end
function PANEL:SetContentPanel( p )
if IsValid(p) then
self.ContentPanel = p
else
self.ContentPanel = nil
end
end
function PANEL:SetWeapon( wepv )
if IsValid(wepv) then
self.Wep = wepv
end
end
function PANEL:SetAttachment( att )
if att ~= nil then
self.VAtt = att
end
end
function PANEL:SetCategory( att )
if att ~= nil then
self.Att = att
end
end
function PANEL:GetAnchoredH()
return true
end
-- @Deprecated
function PANEL:Position()
-- self:SetPos( math.floor( self:GetParent():GetWide() - 32 - self:GetWide() ), math.max( self.VAtt - 1, 0 ) * dimensions + math.max( self.VAtt - 1, 0 ) * padding * 4 + math.max( self.VAtt - 1, 0 ) * spacing )
-- self.HAnchored = true
end
function PANEL:Paint( w, h )
if not self.HasInitialized then return false end
if not IsValid(self.Wep)
or not IsValid(self.Wep:GetOwner())
or not self.Wep:GetOwner():IsPlayer()
or self.Wep:GetOwner():GetActiveWeapon() ~= self.Wep
or (self.Wep:GetInspectingProgress() or 0) < 0.01 then
if IsValid(self.ToolTip) then
self.ToolTip:Remove()
end
self:Remove()
end
end
vgui.Register( "TFAAttachmentPanel", PANEL, "Panel" )

View File

@@ -0,0 +1,230 @@
if SERVER then
AddCSLuaFile()
return
end
local padding = TFA.Attachments.UIPadding
local PANEL = {}
PANEL.Wep = nil
PANEL.Header = nil
PANEL.TextTable = {}
PANEL.DefaultWidth = 0
PANEL.DefaultHeight = 0
function PANEL:SetWidthNeue( val )
self.DefaultWidth = val
end
function PANEL:SetHeightNeue( val )
self.DefaultHeight = val
end
function PANEL:Init()
self.Wep = nil
self.Header = nil
self.TextTable = {}
self.DefaultHeight = 0
self.DefaultWidth = 0
self:SetMouseInputEnabled(false)
self:SetZPos(0)
self.SetWidthOld = self.SetWidthOld or self.SetWidth
self.SetWidth = self.SetWidthNeue
self.SetHeightOld = self.SetHeightOld or self.SetHeight
self.SetHeight = self.SetHeightNeue
end
function PANEL:SetWeapon( wepv )
if IsValid(wepv) then
self.Wep = wepv
end
end
function PANEL:SetAttachment( att )
if att ~= nil then
self:SetZPos( 200 - att )
end
end
function PANEL:SetHeader( h )
self.Header = h
end
function PANEL:SetTextTable( t )
self.TextTable = t or {}
end
PANEL.HeaderFont = "TFAAttachmentTTHeader"
PANEL.BodyFont = "TFAAttachmentTTBody"
function PANEL:GetHeaderHeight()
if not IsValid(self.Wep) then return 0 end
if not self.Header then return 0 end
surface.SetFont(self.HeaderFont)
local _, th = surface.GetTextSize( language.GetPhrase(self.Header) )
return th + padding * 2
end
function PANEL:GetHeaderSize()
if not IsValid(self.Wep) then return 0, 0 end
if not self.Header then return 0, 0 end
surface.SetFont(self.HeaderFont)
local tw, th = surface.GetTextSize( language.GetPhrase(self.Header) )
return tw + padding * 2, th + padding * 2
end
function PANEL:GetTextTableHeight()
if not self.TextTable or #self.TextTable <= 0 then return 0 end
local hv = padding
surface.SetFont(self.BodyFont)
for _,v in pairs(self.TextTable) do
if type(v) == "string" then
v = language.GetPhrase(v)
local _, th = surface.GetTextSize( v )
hv = hv + th
end
end
hv = hv + padding
return hv
end
function PANEL:GetTextTableSize( )
if not self.TextTable or #self.TextTable <= 0 then return 0, 0 end
local mw = 0
local hv = padding
surface.SetFont(self.BodyFont)
for _,v in pairs(self.TextTable) do
if type(v) == "string" then
v = language.GetPhrase(v)
local tw, th = surface.GetTextSize( v )
hv = hv + th
mw = math.max( mw, tw )
end
end
hv = hv + padding
return mw + padding * 2, hv
end
function PANEL:DrawHeader( w, h )
if not self.Header then return 0 end
surface.SetFont(self.HeaderFont)
local header = language.GetPhrase(self.Header)
local _, th = surface.GetTextSize( header )
draw.RoundedBox( 0, 0, 0, w, th + padding * 2, ColorAlpha( TFA.Attachments.Colors["background"], self.Wep:GetInspectingProgress() * 192 ) )
if self.AnchoredH then
draw.DrawText( header, self.HeaderFont, self:GetWide() / 2 , padding, ColorAlpha( TFA.Attachments.Colors["primary"], self.Wep:GetInspectingProgress() * 225 ), TEXT_ALIGN_CENTER, TEXT_ALIGN_TOP )
--draw.RoundedBox( 0, w / 2 - tw / 2, padding + th + padding / 4, tw, padding / 2, ColorAlpha( TFA.Attachments.Colors["primary"], self.Wep:GetInspectingProgress() * 225 ) )
--draw.DrawText( header, self.HeaderFont, self:GetWide() - padding, padding, ColorAlpha( TFA.Attachments.Colors["primary"], self.Wep:GetInspectingProgress() * 225 ), TEXT_ALIGN_RIGHT, TEXT_ALIGN_TOP )
--draw.RoundedBox( 0, w - padding - tw, padding + th + padding / 4, tw, padding / 2, ColorAlpha( TFA.Attachments.Colors["primary"], self.Wep:GetInspectingProgress() * 225 ) )
else
draw.DrawText( header, self.HeaderFont, padding, padding, ColorAlpha( TFA.Attachments.Colors["primary"], self.Wep:GetInspectingProgress() * 225 ), TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP )
--draw.RoundedBox( 0, padding, padding + th + padding / 4, tw, padding / 2, ColorAlpha( TFA.Attachments.Colors["primary"], self.Wep:GetInspectingProgress() * 225 ) )
end
return th + padding * 2
end
function PANEL:DrawTextTable( x, y )
if not self.TextTable then return 0 end
--y = y + padding
local hv = padding
local acol = TFA.Attachments.Colors["primary"]
surface.SetFont(self.BodyFont)
for _,v in pairs(self.TextTable) do
if type(v) == "table" or type(v) == "vector" then
if v.r then
acol = Color( v.r or 0, v.g or 0, v.b or 0, v.a or 255 )
elseif v.x then
acol = Color( v.x or 0, v.y or 0, v.z or 0, v.a or 255 )
end
end
if type(v) == "string" then
v = language.GetPhrase(v)
local _, th = surface.GetTextSize( v )
if self.AnchoredH then
--draw.DrawText( v, self.BodyFont, x + self:GetWide() - padding, y + hv, ColorAlpha( acol, self.Wep:GetInspectingProgress() * 225 ), TEXT_ALIGN_RIGHT, TEXT_ALIGN_TOP )
draw.DrawText( v, self.BodyFont, x + padding * 2, y + hv, ColorAlpha( acol, self.Wep:GetInspectingProgress() * 225 ), TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP )
else
draw.DrawText( v, self.BodyFont, x + padding * 2, y + hv, ColorAlpha( acol, self.Wep:GetInspectingProgress() * 225 ), TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP )
end
hv = hv + th
end
end
hv = hv + padding
return hv
end
function PANEL:CalcSize()
local header_w, header_h = self:GetHeaderSize()
local text_w, text_h = self:GetTextTableSize()
self:SetWidthOld( math.max( self.DefaultWidth, math.max( header_w, text_w ) + padding * 2 ))
local h = header_h + text_h
if text_h > 0 then
h = h + padding * 2
end
if IsValid( self.ContentPanel ) and not self:GetActive() then
local _, cph = self.ContentPanel:LocalToScreen(0,self.ContentPanel:GetTall())
local _, yy = self:LocalToScreen(0,0)
h = math.min( h, cph - yy )
end
self:SetHeightOld( h )
end
function PANEL:CalcPos()
if IsValid(self.Anchor) then
local x,y = self.Anchor:LocalToScreen(0,0)
y = y
if self.Anchor:GetAnchoredH() then
self.AnchoredH = true
if IsValid( self.ContentPanel ) and self:GetActive() then
local _, cph = self.ContentPanel:LocalToScreen(0,self.ContentPanel:GetTall())
self:SetPos( x + self.Anchor:GetWide() - self:GetWide() , math.min( y + self.Anchor:GetTall(), cph - self:GetTall() ) )
else
self:SetPos( x + self.Anchor:GetWide() - self:GetWide() , math.min( y + self.Anchor:GetTall(), ScrH() - self:GetTall() ) )
end
else
self.AnchoredH = false
self:SetPos( x, y + self.Anchor:GetTall() )
end
end
end
function PANEL:Think()
self:CalcSize()
self:CalcPos()
end
function PANEL:SetContentPanel( p )
if IsValid(p) then
self.ContentPanel = p
else
self.ContentPanel = nil
end
end
function PANEL:Paint( w, h )
if not IsValid(self.Wep) then return end
if ( self.Wep:GetInspectingProgress() or 0 ) < 0.01 then self:Remove() end
if IsValid( self.ContentPanel ) and not self:GetActive() then
local _, cph = self.ContentPanel:LocalToScreen(0,math.max(self.ContentPanel:GetTall(),32))
local _, yy = self:LocalToScreen(0,0)
if cph - yy <= 0 then
return
end
end
draw.RoundedBox( 0, 0, 0, w, h, ColorAlpha( TFA.Attachments.Colors["background"], self.Wep:GetInspectingProgress() * 192 ) )
local hh = self:DrawHeader( w, h )
self:DrawTextTable( 0, hh )
render.SetScissorRect(0,0,ScrW(),ScrH(),false)
end
function PANEL:SetActive( a )
self.Active = a
end
function PANEL:GetActive( a )
return self.Active
end
vgui.Register( "TFAAttachmentTip", PANEL, "Panel" )

View File

@@ -0,0 +1,441 @@
if GetConVar("cl_tfa_inspection_bokeh") == nil then
CreateClientConVar("cl_tfa_inspection_bokeh", 0, true, false, "Enable inspection bokeh DOF")
end
if GetConVar("cl_tfa_inspection_bokeh_radius") == nil then
CreateClientConVar("cl_tfa_inspection_bokeh_radius", 0.1, true, false, "Inspection bokeh DOF radius", 0.01, 1)
end
if GetConVar("cl_tfa_inspect_hide_in_screenshots") == nil then
CreateClientConVar("cl_tfa_inspect_hide_in_screenshots", 0, true, false, "Hide inspection panel in screenshots")
end
if GetConVar("cl_tfa_inspect_hide") == nil then
CreateClientConVar("cl_tfa_inspect_hide", 0, false, false, "Hide inspection panel")
end
if GetConVar("cl_tfa_inspect_hide_hud") == nil then
CreateClientConVar("cl_tfa_inspect_hide_hud", 0, true, false, "Hide HUD when inspecting weapon (DLib required)")
end
if GetConVar("cl_tfa_inspect_newbars") == nil then
CreateClientConVar("cl_tfa_inspect_newbars", 0, true, false, "Use new stat bars in inspection screen")
end
if GetConVar("cl_tfa_inspect_spreadinmoa") == nil then
CreateClientConVar("cl_tfa_inspect_spreadinmoa", 0, true, false, "Show accuracy in MOA instead of degrees on inspection screen")
end
if GetConVar("cl_tfa_viewbob_intensity") == nil then
CreateClientConVar("cl_tfa_viewbob_intensity", 1, true, false, "View bob intensity multiplier", 0, 10)
end
if GetConVar("cl_tfa_gunbob_intensity") == nil then
CreateClientConVar("cl_tfa_gunbob_intensity", 1, true, false, "Gun bob intensity multiplier", 0, 10)
end
if GetConVar("cl_tfa_gunbob_custom") == nil then
CreateClientConVar("cl_tfa_gunbob_custom", 1, true, false, "Use custom gun bob")
end
if GetConVar("cl_tfa_gunbob_invertsway") == nil then
CreateClientConVar("cl_tfa_gunbob_invertsway", 0, true, false, "Invert gun sway direction")
end
if GetConVar("cl_tfa_3dscope_quality") == nil then
CreateClientConVar("cl_tfa_3dscope_quality", 0, true, true, "3D scope quality (0 - Full quality, 1 - Half, 2 - Quarter, 3 - Eighth)", 0, 3)
end
if GetConVar("cl_tfa_3dscope") == nil then
CreateClientConVar("cl_tfa_3dscope", 1, true, true, "[IGNORED] Enable 3D scopes?")
end
if GetConVar("cl_tfa_scope_sensitivity_3d") == nil then
CreateClientConVar("cl_tfa_scope_sensitivity_3d", 2, true, true, "3D scope sensitivity (0 - No compensation, 1 - Standard compensation, 2 - 3D compensation, 3 - 3D + FOV compensation)", 0, 3)
end
if GetConVar("cl_tfa_3dscope_overlay") == nil then
CreateClientConVar("cl_tfa_3dscope_overlay", 0, true, true, "Enable 3D scope shadows?")
end
if GetConVar("cl_tfa_scope_sensitivity_autoscale") == nil then
CreateClientConVar("cl_tfa_scope_sensitivity_autoscale", 1, true, true, "Compensate sensitivity for FOV?")
end
if GetConVar("cl_tfa_scope_sensitivity") == nil then
CreateClientConVar("cl_tfa_scope_sensitivity", 100, true, true, "3D scope sensitivity percentage", 0.01, 100)
end
if GetConVar("cl_tfa_ironsights_toggle") == nil then
CreateClientConVar("cl_tfa_ironsights_toggle", 1, true, true, "Toggle ironsights?")
end
if GetConVar("cl_tfa_ironsights_resight") == nil then
CreateClientConVar("cl_tfa_ironsights_resight", 1, true, true, "Keep ironsights after reload or sprint?")
end
if GetConVar("cl_tfa_ironsights_responsive") == nil then
CreateClientConVar("cl_tfa_ironsights_responsive", 0, true, true, "Allow both toggle and held down iron sights")
end
if GetConVar("cl_tfa_ironsights_responsive_timer") == nil then
CreateClientConVar("cl_tfa_ironsights_responsive_timer", 0.175, true, true, "Time in seconds to determine responsivness time", 0.01, 2)
end
if GetConVar("cl_tfa_laser_trails") == nil then
CreateClientConVar("cl_tfa_laser_trails", 1, true, true, "Enable laser dot trails?")
end
--Crosshair Params
if GetConVar("cl_tfa_hud_crosshair_length") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_length", 1, true, false, "Crosshair length")
end
if GetConVar("cl_tfa_hud_crosshair_length_use_pixels") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_length_use_pixels", 0, true, false, "Should crosshair length use pixels?")
end
if GetConVar("cl_tfa_hud_crosshair_width") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_width", 1, true, false, "Crosshair width")
end
if GetConVar("cl_tfa_hud_crosshair_enable_custom") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_enable_custom", 1, true, false, "Enable custom crosshair?")
end
if GetConVar("cl_tfa_hud_crosshair_gap_scale") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_gap_scale", 1, true, false, "Crosshair gap scale")
end
if GetConVar("cl_tfa_hud_crosshair_dot") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_dot", 0, true, false, "Enable crosshair dot?")
end
--Crosshair Color
if GetConVar("cl_tfa_hud_crosshair_color_r") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_color_r", 225, true, false, nil, 0, 255)
end
if GetConVar("cl_tfa_hud_crosshair_color_g") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_color_g", 225, true, false, nil, 0, 255)
end
if GetConVar("cl_tfa_hud_crosshair_color_b") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_color_b", 225, true, false, nil, 0, 255)
end
if GetConVar("cl_tfa_hud_crosshair_color_a") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_color_a", 200, true, false, nil, 0, 255)
end
if GetConVar("cl_tfa_hud_crosshair_color_team") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_color_team", 1, true, false, "Should crosshair use team color of entity being aimed at?")
end
-- Crosshair Team Color: Friendly
if GetConVar("cl_tfa_hud_crosshair_color_friendly_r") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_color_friendly_r", 0, true, false, nil, 0, 255)
end
if GetConVar("cl_tfa_hud_crosshair_color_friendly_g") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_color_friendly_g", 255, true, false, nil, 0, 255)
end
if GetConVar("cl_tfa_hud_crosshair_color_friendly_b") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_color_friendly_b", 0, true, false, nil, 0, 255)
end
-- Crosshair Team Color: Enemy
if GetConVar("cl_tfa_hud_crosshair_color_enemy_r") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_color_enemy_r", 255, true, false, nil, 0, 255)
end
if GetConVar("cl_tfa_hud_crosshair_color_enemy_g") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_color_enemy_g", 0, true, false, nil, 0, 255)
end
if GetConVar("cl_tfa_hud_crosshair_color_enemy_b") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_color_enemy_b", 0, true, false, nil, 0, 255)
end
--Crosshair Outline
if GetConVar("cl_tfa_hud_crosshair_outline_color_r") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_outline_color_r", 5, true, false, nil, 0, 255)
end
if GetConVar("cl_tfa_hud_crosshair_outline_color_g") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_outline_color_g", 5, true, false, nil, 0, 255)
end
if GetConVar("cl_tfa_hud_crosshair_outline_color_b") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_outline_color_b", 5, true, false, nil, 0, 255)
end
if GetConVar("cl_tfa_hud_crosshair_outline_color_a") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_outline_color_a", 200, true, false, nil, 0, 255)
end
if GetConVar("cl_tfa_hud_crosshair_outline_width") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_outline_width", 1, true, false, "Crosshair outline width")
end
if GetConVar("cl_tfa_hud_crosshair_outline_enabled") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_outline_enabled", 1, true, false, "Enable crosshair outline?")
end
if GetConVar("cl_tfa_hud_crosshair_triangular") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_triangular", 0, true, false, "Enable triangular Crysis-like crosshair?")
end
if GetConVar("cl_tfa_hud_crosshair_pump") == nil then
CreateClientConVar("cl_tfa_hud_crosshair_pump", 0, true, false, "Enable pump feedback on crosshair?")
end
if GetConVar("cl_tfa_hud_hitmarker_enabled") == nil then
CreateClientConVar("cl_tfa_hud_hitmarker_enabled", 1, true, false, "Enable hit marker?")
end
if GetConVar("cl_tfa_hud_hitmarker_fadetime") == nil then
CreateClientConVar("cl_tfa_hud_hitmarker_fadetime", 0.3, true, false, "Hit marker fade time (in seconds)", 0, 3)
end
if GetConVar("cl_tfa_hud_hitmarker_solidtime") == nil then
CreateClientConVar("cl_tfa_hud_hitmarker_solidtime", 0.1, true, false, nil, 0, 3)
end
if GetConVar("cl_tfa_hud_hitmarker_scale") == nil then
CreateClientConVar("cl_tfa_hud_hitmarker_scale", 1, true, false, "Hit marker scale", 0, 5)
end
-- Hitmarker Color
if GetConVar("cl_tfa_hud_hitmarker_color_r") == nil then
CreateClientConVar("cl_tfa_hud_hitmarker_color_r", 225, true, false, nil, 0, 255)
end
if GetConVar("cl_tfa_hud_hitmarker_color_g") == nil then
CreateClientConVar("cl_tfa_hud_hitmarker_color_g", 225, true, false, nil, 0, 255)
end
if GetConVar("cl_tfa_hud_hitmarker_color_b") == nil then
CreateClientConVar("cl_tfa_hud_hitmarker_color_b", 225, true, false, nil, 0, 255)
end
if GetConVar("cl_tfa_hud_hitmarker_color_a") == nil then
CreateClientConVar("cl_tfa_hud_hitmarker_color_a", 200, true, false, nil, 0, 255)
end
if GetConVar("cl_tfa_hud_hitmarker_3d_all") == nil then
CreateClientConVar("cl_tfa_hud_hitmarker_3d_all", 0, true, true)
end
if GetConVar("cl_tfa_hud_hitmarker_3d_shotguns") == nil then
CreateClientConVar("cl_tfa_hud_hitmarker_3d_shotguns", 1, true, true)
end
--Other stuff
if GetConVar("cl_tfa_hud_ammodata_fadein") == nil then
CreateClientConVar("cl_tfa_hud_ammodata_fadein", 0.2, true, false)
end
if GetConVar("cl_tfa_hud_hangtime") == nil then
CreateClientConVar("cl_tfa_hud_hangtime", 1, true, true)
end
if GetConVar("cl_tfa_hud_enabled") == nil then
CreateClientConVar("cl_tfa_hud_enabled", 1, true, false, "Enable 3D2D hud?")
end
if GetConVar("cl_tfa_hud_fallback_enabled") == nil then
CreateClientConVar("cl_tfa_hud_fallback_enabled", 1, true, false, "Enable basic fallback hud?")
end
if GetConVar("cl_tfa_hud_scale") == nil then
CreateClientConVar("cl_tfa_hud_scale", 1, true, false, "Size multiplier of HUD elements", .25, 4)
end
if GetConVar("cl_tfa_fx_gasblur") == nil then
CreateClientConVar("cl_tfa_fx_gasblur", 0, true, true, "Enable muzzle gas blur?")
end
if GetConVar("cl_tfa_fx_muzzlesmoke") == nil then
CreateClientConVar("cl_tfa_fx_muzzlesmoke", 1, true, true, "Enable muzzle smoke trail?")
end
if GetConVar("cl_tfa_fx_muzzlesmoke_limited") == nil then
CreateClientConVar("cl_tfa_fx_muzzlesmoke_limited", 1, true, true, "Limit muzzle smoke trails?")
end
if GetConVar("cl_tfa_fx_muzzleflashsmoke") == nil then
CreateClientConVar("cl_tfa_fx_muzzleflashsmoke", 1, true, true, "Enable muzzleflash smoke?")
end
if GetConVar("cl_tfa_legacy_shells") == nil then
CreateClientConVar("cl_tfa_legacy_shells", 0, true, true, "Use legacy shells?")
end
if GetConVar("cl_tfa_fx_ejectionsmoke") == nil then
CreateClientConVar("cl_tfa_fx_ejectionsmoke", 1, true, true, "Enable shell ejection smoke?")
end
if GetConVar("cl_tfa_fx_ejectionlife") == nil then
CreateClientConVar("cl_tfa_fx_ejectionlife", 15, true, true, "How long shells exist in the world")
end
if GetConVar("cl_tfa_fx_impact_enabled") == nil then
CreateClientConVar("cl_tfa_fx_impact_enabled", 1, true, true, "Enable custom bullet impact effects?")
end
if GetConVar("cl_tfa_fx_impact_ricochet_enabled") == nil then
CreateClientConVar("cl_tfa_fx_impact_ricochet_enabled", 1, true, true, "Enable bullet ricochet effect?")
end
if GetConVar("cl_tfa_fx_impact_ricochet_sparks") == nil then
CreateClientConVar("cl_tfa_fx_impact_ricochet_sparks", 6, true, true, "Enable bullet ricochet sparks?")
end
if GetConVar("cl_tfa_fx_impact_ricochet_sparklife") == nil then
CreateClientConVar("cl_tfa_fx_impact_ricochet_sparklife", 2, true, true)
end
if GetConVar("cl_tfa_fx_ads_dof") == nil then
CreateClientConVar("cl_tfa_fx_ads_dof", 0, true, true, "Enable iron sights DoF (Depth of Field)")
end
if GetConVar("cl_tfa_fx_ads_dof_hd") == nil then
CreateClientConVar("cl_tfa_fx_ads_dof_hd", 0, true, true, "Enable better quality for DoF")
end
--viewbob
if GetConVar("cl_tfa_viewbob_animated") == nil then
CreateClientConVar("cl_tfa_viewbob_animated", 1, true, false, "Use animated viewbob?")
end
--Viewmodel Mods
if GetConVar("cl_tfa_viewmodel_offset_x") == nil then
CreateClientConVar("cl_tfa_viewmodel_offset_x", 0, true, false, nil, -2, 2)
end
if GetConVar("cl_tfa_viewmodel_offset_y") == nil then
CreateClientConVar("cl_tfa_viewmodel_offset_y", 0, true, false, nil, -2, 2)
end
if GetConVar("cl_tfa_viewmodel_offset_z") == nil then
CreateClientConVar("cl_tfa_viewmodel_offset_z", 0, true, false, nil, -2, 2)
end
if GetConVar("cl_tfa_viewmodel_offset_fov") == nil then
CreateClientConVar("cl_tfa_viewmodel_offset_fov", 0, true, false, nil, -5, 5)
end
if GetConVar("cl_tfa_viewmodel_multiplier_fov") == nil then
CreateClientConVar("cl_tfa_viewmodel_multiplier_fov", 1, true, false, nil, 0.75, 2)
end
if GetConVar("cl_tfa_viewmodel_flip") == nil then
CreateClientConVar("cl_tfa_viewmodel_flip", 0, true, false)
end
if GetConVar("cl_tfa_viewmodel_centered") == nil then
CreateClientConVar("cl_tfa_viewmodel_centered", 0, true, false)
end
if GetConVar("cl_tfa_viewmodel_nearwall") == nil then
CreateClientConVar("cl_tfa_viewmodel_nearwall", 1, true, false)
end
if GetConVar("cl_tfa_viewmodel_vp_enabled") == nil then
CreateClientConVar("cl_tfa_viewmodel_vp_enabled", 1, true, false)
end
if GetConVar("cl_tfa_viewmodel_vp_pitch") == nil then
CreateClientConVar("cl_tfa_viewmodel_vp_pitch", 1, true, false, nil, 0, 10)
end
if GetConVar("cl_tfa_viewmodel_vp_pitch_is") == nil then
CreateClientConVar("cl_tfa_viewmodel_vp_pitch_is", 1, true, false, nil, 0, 10)
end
if GetConVar("cl_tfa_viewmodel_vp_vertical") == nil then
CreateClientConVar("cl_tfa_viewmodel_vp_vertical", 1, true, false, nil, 0, 10)
end
if GetConVar("cl_tfa_viewmodel_vp_vertical_is") == nil then
CreateClientConVar("cl_tfa_viewmodel_vp_vertical_is", 1, true, false, nil, 0, 10)
end
if GetConVar("cl_tfa_viewmodel_vp_max_vertical") == nil then
CreateClientConVar("cl_tfa_viewmodel_vp_max_vertical", 1, true, false, nil, 0, 10)
end
if GetConVar("cl_tfa_viewmodel_vp_max_vertical_is") == nil then
CreateClientConVar("cl_tfa_viewmodel_vp_max_vertical_is", 1, true, false, nil, 0, 10)
end
if GetConVar("cl_tfa_viewmodel_vp_yaw") == nil then
CreateClientConVar("cl_tfa_viewmodel_vp_yaw", 1, true, false, nil, 0, 10)
end
if GetConVar("cl_tfa_viewmodel_vp_yaw_is") == nil then
CreateClientConVar("cl_tfa_viewmodel_vp_yaw_is", 1, true, false, nil, 0, 10)
end
if GetConVar("cl_tfa_debug_crosshair") == nil then
CreateClientConVar("cl_tfa_debug_crosshair", 0, false, false, "Debug crosshair (Admin only)")
end
if GetConVar("cl_tfa_debug_animations") == nil then
CreateClientConVar("cl_tfa_debug_animations", 0, false, false, "Debug animations (Admin only)")
end
if GetConVar("cl_tfa_debug_rt") == nil then
CreateClientConVar("cl_tfa_debug_rt", 0, false, false, "Debug RT scopes (Admin only)")
end
if GetConVar("cl_tfa_debug_cache") == nil then
CreateClientConVar("cl_tfa_debug_cache", 0, false, false, "Disable stat caching (may cause heavy performance impact!)")
end
local function UpdateColorCVars()
timer.Create("tfa_apply_player_color", 0.5, 1, function()
RunConsoleCommand("sv_tfa_apply_player_colors")
end)
end
--Reticule Color
if GetConVar("cl_tfa_reticule_color_r") == nil then
CreateClientConVar("cl_tfa_reticule_color_r", 255, true, true)
cvars.AddChangeCallback("cl_tfa_reticule_color_r", UpdateColorCVars, "TFANetworkPlayerColors")
end
if GetConVar("cl_tfa_reticule_color_g") == nil then
CreateClientConVar("cl_tfa_reticule_color_g", 100, true, true)
cvars.AddChangeCallback("cl_tfa_reticule_color_g", UpdateColorCVars, "TFANetworkPlayerColors")
end
if GetConVar("cl_tfa_reticule_color_b") == nil then
CreateClientConVar("cl_tfa_reticule_color_b", 0, true, true)
cvars.AddChangeCallback("cl_tfa_reticule_color_b", UpdateColorCVars, "TFANetworkPlayerColors")
end
--Laser Color
if GetConVar("cl_tfa_laser_color_r") == nil then
CreateClientConVar("cl_tfa_laser_color_r", 255, true, true)
cvars.AddChangeCallback("cl_tfa_laser_color_r", UpdateColorCVars, "TFANetworkPlayerColors")
end
if GetConVar("cl_tfa_laser_color_g") == nil then
CreateClientConVar("cl_tfa_laser_color_g", 0, true, true)
cvars.AddChangeCallback("cl_tfa_laser_color_g", UpdateColorCVars, "TFANetworkPlayerColors")
end
if GetConVar("cl_tfa_laser_color_b") == nil then
CreateClientConVar("cl_tfa_laser_color_b", 0, true, true)
cvars.AddChangeCallback("cl_tfa_laser_color_b", UpdateColorCVars, "TFANetworkPlayerColors")
end
if GetConVar("cl_tfa_attachments_persist_enabled") == nil then
CreateClientConVar("cl_tfa_attachments_persist_enabled", 1, true, true, "Should attachments selection persist across different weapons/lives/sessions?")
end
if GetConVar("cl_tfa_hud_keybindhints_enabled") == nil then
CreateClientConVar("cl_tfa_hud_keybindhints_enabled", "1", true, false, "Enable keybind hints?")
end
if GetConVar("cl_tfa_hud_keybindhints_solidtime") == nil then
CreateClientConVar("cl_tfa_hud_keybindhints_solidtime", 3, true, false, "How long keybind hint will stay on screen (in seconds)", 0)
end
if GetConVar("cl_tfa_hud_keybindhints_fadeintime") == nil then
CreateClientConVar("cl_tfa_hud_keybindhints_fadeintime", 1, true, false, "Keybind hint fade-in time (in seconds)", 0.01)
end
if GetConVar("cl_tfa_hud_keybindhints_fadeouttime") == nil then
CreateClientConVar("cl_tfa_hud_keybindhints_fadeouttime", 4, true, false, "Keybind hint fade-out time (in seconds)", 0.01)
end

View File

@@ -0,0 +1,183 @@
local cv_dba = GetConVar("cl_tfa_debug_animations")
local cv_dbc = GetConVar("cl_tfa_debug_crosshair")
local color_red = Color(255, 0, 0, 255)
local color_white = Color(255, 255, 255, 255)
local state_strings = {}
for i = 1, 32 do
local strcomp = string.rep("%d", i)
local slice = {}
for i2 = 0, i - 1 do
table.insert(slice, "band(rshift(state, " .. i2 .. "), 1) == 0 and 0 or 1")
end
local fn = CompileString([[
local rshift = bit.rshift
local band = bit.band
return function(state)
return ]] .. table.concat(slice, ", ") .. [[
end
]], "tfa_dev_tools")()
state_strings[i] = function(state)
return string.format(strcomp, fn(state))
end
end
local lastStatusBarWidth = 300
local lastAnimStatusWidth = 300
local STATUS_BAR_COLOR = Color(255, 255, 255)
local STATUS_BAR_COLOR_BG = Color(74, 74, 74)
local function DrawDebugInfo(w, h, ply, wep)
if not cv_dba:GetBool() then return end
local x, y = w * .5, h * .2
if wep.event_table_overflow then
if wep.EventTableEdict[0] then
draw.SimpleTextOutlined("UNPREDICTED Event table state:", "TFASleekDebug", x + 240, y, color_white, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP, 1, color_black)
local y2 = y + TFA.Fonts.SleekHeightDebug
if not wep._built_event_debug_string_fn then
local str = ""
local str2 = ""
for i = 0, #wep.EventTableEdict do
str = str .. "%d"
if (i + 1) % 32 == 0 then
str = str .. "\n"
end
if str2 == "" then
str2 = "self.EventTableEdict[" .. i .. "].called and 1 or 0"
else
str2 = str2 .. ", self.EventTableEdict[" .. i .. "].called and 1 or 0"
end
end
wep._built_event_debug_string_fn = CompileString([[
local format = string.format
return function(self)
return format([==[]] .. str .. [[]==], ]] .. str2 .. [[)
end
]], "TFA Base Debug Tools")()
end
for line in string.gmatch(wep:_built_event_debug_string_fn(), "(%S+)") do
draw.SimpleTextOutlined(line, "TFASleekDebug", x + 240, y2, color_white, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP, 1, color_black)
y2 = y2 + TFA.Fonts.SleekHeightDebug
end
end
elseif wep._EventSlotCount ~= 0 then
draw.SimpleTextOutlined("Event table state:", "TFASleekDebug", x + 240, y, color_white, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP, 1, color_black)
local y2 = y + TFA.Fonts.SleekHeightDebug
for i = 1, wep._EventSlotCount do
local state = wep["GetEventStatus" .. i](wep)
local stringbake
if i ~= wep._EventSlotCount then
stringbake = state_strings[32](state)
else
local fn = state_strings[wep._EventSlotNum % 32 + 1]
if not fn then break end
stringbake = fn(state)
end
draw.SimpleTextOutlined(stringbake, "TFASleekDebug", x + 240, y2, color_white, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP, 1, color_black)
y2 = y2 + TFA.Fonts.SleekHeightDebug
end
end
local statusText = string.format(
"%s [%.2f, %.2f, %.2f, %.2f]",
TFA.Enum.InverseStatus[wep:GetStatus()] or wep:GetStatus(),
CurTime() + (wep.CurTimePredictionAdvance or 0),
wep:GetStatusProgress(true),
wep:GetStatusStart(),
wep:GetStatusEnd())
draw.SimpleTextOutlined(statusText, "TFASleekDebug", x, y, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_TOP, 1, color_black)
--[[if wep:GetStatusProgress() >= 1 then
local stW, stH = surface.GetTextSize(statusText)
lastStatusBarWidth = math.max(300, stW)
end]]
y = y + TFA.Fonts.SleekHeightDebug + 2
surface.SetDrawColor(STATUS_BAR_COLOR_BG)
surface.DrawRect(x - lastStatusBarWidth / 2, y, lastStatusBarWidth, 4)
surface.SetDrawColor(STATUS_BAR_COLOR)
surface.DrawRect(x - lastStatusBarWidth / 2, y, lastStatusBarWidth * wep:GetStatusProgress(true), 4)
y = y + 8
local vm = wep.OwnerViewModel
if IsValid(vm) then
local seq = vm:GetSequence()
draw.SimpleTextOutlined(string.format("%s [%d] (%s/%d)", vm:GetSequenceName(seq), seq, vm:GetSequenceActivityName(seq), vm:GetSequenceActivity(seq)), "TFASleekDebug", x, y, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_TOP, 1, color_black)
y = y + TFA.Fonts.SleekHeightDebug
local cycle = vm:GetCycle()
local len = vm:SequenceDuration(seq)
local rate = vm:GetPlaybackRate()
local animStatus = string.format("%.2fs / %.2fs (%.2f) @ %d%%", cycle * len, len, cycle, rate * 100)
draw.SimpleTextOutlined(animStatus, "TFASleekDebug", x, y, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_TOP, 1, color_black)
--local stW, stH = surface.GetTextSize(animStatus)
--lastAnimStatusWidth = math.max(300, stW)
y = y + TFA.Fonts.SleekHeightDebug + 2
surface.SetDrawColor(STATUS_BAR_COLOR_BG)
surface.DrawRect(x - lastAnimStatusWidth / 2, y, lastAnimStatusWidth, 4)
if len * rate >= 0.2 then
surface.SetDrawColor(STATUS_BAR_COLOR)
surface.DrawRect(x - lastAnimStatusWidth / 2, y, lastAnimStatusWidth * cycle, 4)
end
end
end
local function DrawDebugCrosshair(w, h, ply, wep)
if not cv_dbc:GetBool() then return end
surface.SetDrawColor(color_red)
surface.DrawRect(w * .5 - 1, h * .5 - 1, 2, 2)
local tr = util.QuickTrace(ply:GetShootPos(), wep:GetAimVector(), ply)
local tsc = tr.HitPos:ToScreen()
if tsc.visible then
surface.SetDrawColor(color_white)
surface.DrawRect(tsc.x - 1, tsc.y - 1, 2, 2)
end
end
local w, h
hook.Add("HUDPaint", "tfa_drawdebughud", function()
local ply = LocalPlayer() or NULL
if not ply:IsValid() or not ply:IsAdmin() then return end
local wep = ply:GetActiveWeapon() or NULL
if not wep:IsValid() or not wep.IsTFAWeapon then return end
w, h = ScrW(), ScrH()
DrawDebugInfo(w, h, ply, wep)
DrawDebugCrosshair(w, h, ply, wep)
end)

View File

@@ -0,0 +1,83 @@
TFA.Fonts = TFA.Fonts or {}
local ScaleH = TFA.ScaleH
local function GetFontHeight(fontname) -- UNCACHED!
surface.SetFont(fontname)
local _, h = surface.GetTextSize("W")
return h
end
local function CreateFonts()
local fontdata = {}
fontdata.font = "Inter"
fontdata.shadow = false
fontdata.extended = true
fontdata.weight = 400
fontdata.size = ScaleH(36)
surface.CreateFont("TFASleek", fontdata)
TFA.Fonts.SleekHeight = GetFontHeight("TFASleek")
fontdata.size = ScaleH(30)
surface.CreateFont("TFASleekMedium", fontdata)
TFA.Fonts.SleekHeightMedium = GetFontHeight("TFASleekMedium")
fontdata.size = ScaleH(24)
surface.CreateFont("TFASleekSmall", fontdata)
TFA.Fonts.SleekHeightSmall = GetFontHeight("TFASleekSmall")
fontdata.size = ScaleH(18)
surface.CreateFont("TFASleekTiny", fontdata)
TFA.Fonts.SleekHeightTiny = GetFontHeight("TFASleekTiny")
fontdata = {}
fontdata.font = "Inter"
fontdata.extended = true
fontdata.weight = 500
fontdata.size = ScaleH(64)
surface.CreateFont("TFA_INSPECTION_TITLE", fontdata)
TFA.Fonts.InspectionHeightTitle = GetFontHeight("TFA_INSPECTION_TITLE")
fontdata.size = ScaleH(32)
surface.CreateFont("TFA_INSPECTION_DESCR", fontdata)
TFA.Fonts.InspectionHeightDescription = GetFontHeight("TFA_INSPECTION_DESCR")
fontdata.size = ScaleH(24)
fontdata.weight = 400
surface.CreateFont("TFA_INSPECTION_SMALL", fontdata)
TFA.Fonts.InspectionHeightSmall = GetFontHeight("TFA_INSPECTION_SMALL")
fontdata = {}
fontdata.extended = true
fontdata.weight = 400
fontdata.font = "Inter"
fontdata.size = ScaleH(12)
surface.CreateFont("TFAAttachmentIconFont", fontdata)
fontdata.size = ScaleH(10)
surface.CreateFont("TFAAttachmentIconFontTiny", fontdata)
fontdata.font = "Inter"
fontdata.weight = 500
fontdata.size = ScaleH(24)
surface.CreateFont("TFAAttachmentTTHeader", fontdata)
fontdata.font = "Inter"
fontdata.weight = 300
fontdata.size = ScaleH(18)
surface.CreateFont("TFAAttachmentTTBody", fontdata)
surface.CreateFont("TFASleekDebug", { font = "Roboto", size = 24, extended = true })
TFA.Fonts.SleekHeightDebug = 24
hook.Run("TFA_FontsLoaded")
end
CreateFonts()
hook.Add("OnScreenSizeChanged", "TFA_Fonts_Regenerate", CreateFonts)
cvars.AddChangeCallback("cl_tfa_hud_scale", CreateFonts, "TFA_RecreateFonts")

View File

@@ -0,0 +1,94 @@
local ScrW, ScrH = ScrW, ScrH
local markers = {}
local cl_drawhud = GetConVar("cl_drawhud")
local enabledcvar = GetConVar("cl_tfa_hud_hitmarker_enabled")
local solidtimecvar = GetConVar("cl_tfa_hud_hitmarker_solidtime")
local fadetimecvar = GetConVar("cl_tfa_hud_hitmarker_fadetime")
local scalecvar = GetConVar("cl_tfa_hud_hitmarker_scale")
local tricross_cvar = GetConVar("cl_tfa_hud_crosshair_triangular")
local rcvar = GetConVar("cl_tfa_hud_hitmarker_color_r")
local gcvar = GetConVar("cl_tfa_hud_hitmarker_color_g")
local bcvar = GetConVar("cl_tfa_hud_hitmarker_color_b")
local acvar = GetConVar("cl_tfa_hud_hitmarker_color_a")
net.Receive("tfaHitmarker", function()
if not enabledcvar:GetBool() then return end
local marker = {
time = RealTime()
}
table.insert(markers, marker)
end)
net.Receive("tfaHitmarker3D", function()
if not enabledcvar:GetBool() then return end
local marker = {
pos = net.ReadVector(),
time = RealTime()
}
table.insert(markers, marker)
end)
local mat_regular = Material("vgui/tfa_hitmarker.png", "smooth mips")
local mat_triang = Material("vgui/tfa_hitmarker_triang.png", "smooth mips")
local cl_tfa_hud_crosshair_enable_custom = GetConVar("cl_tfa_hud_crosshair_enable_custom")
hook.Add("HUDPaint", "tfaDrawHitmarker", function()
if not enabledcvar:GetBool() or not cl_drawhud:GetBool() then return end
local solidtime = solidtimecvar:GetFloat()
local fadetime = math.max(fadetimecvar:GetFloat(), 0.001)
local r = rcvar:GetFloat()
local g = gcvar:GetFloat()
local b = bcvar:GetFloat()
local a = acvar:GetFloat()
local w, h = ScrW(), ScrH()
local sprh = math.floor((h / 1080) * 64 * scalecvar:GetFloat())
local sprh2 = sprh / 2
local mX, mY = w / 2, h / 2
local ltime = RealTime()
if cl_tfa_hud_crosshair_enable_custom:GetBool() and isnumber(TFA.LastCrosshairPosX) and isnumber(TFA.LastCrosshairPosY) then
local weapon = LocalPlayer():GetActiveWeapon()
if IsValid(weapon) and weapon.IsTFAWeapon then
mX, mY = TFA.LastCrosshairPosX, TFA.LastCrosshairPosY
end
end
for k, v in pairs(markers) do
if v.time then
local alpha = math.Clamp(v.time - ltime + solidtime + fadetime, 0, fadetime) / fadetime
if alpha > 0 then
local x, y = mX, mY
local visible = true
if v.pos then
local pos = v.pos:ToScreen()
x, y = pos.x, pos.y
visible = pos.visible
end
if visible then
surface.SetDrawColor(r, g, b, a * alpha)
surface.SetMaterial(tricross_cvar:GetBool() and mat_triang or mat_regular)
surface.DrawTexturedRect(x - sprh2, y - sprh2, sprh, sprh)
end
else
markers[k] = nil
end
else
markers[k] = nil
end
end
end)

View File

@@ -0,0 +1,76 @@
if CLIENT then
local doblur = GetConVar("cl_tfa_inspection_bokeh")
local blurdist = GetConVar("cl_tfa_inspection_bokeh_radius")
local tfablurintensity = 0
local blur_mat = Material("pp/bokehblur")
local tab = {}
tab["$pp_colour_addr"] = 0
tab["$pp_colour_addg"] = 0
tab["$pp_colour_addb"] = 0
tab["$pp_colour_brightness"] = 0
tab["$pp_colour_contrast"] = 1
tab["$pp_colour_colour"] = 1
tab["$pp_colour_mulr"] = 0
tab["$pp_colour_mulg"] = 0
tab["$pp_colour_mulb"] = 0
local function MyDrawBokehDOF()
render.UpdateScreenEffectTexture()
render.UpdateFullScreenDepthTexture()
blur_mat:SetTexture("$BASETEXTURE", render.GetScreenEffectTexture())
blur_mat:SetTexture("$DEPTHTEXTURE", render.GetResolvedFullFrameDepth())
blur_mat:SetFloat("$size", tfablurintensity * 6)
blur_mat:SetFloat("$focus", 0)
blur_mat:SetFloat("$focusradius", blurdist:GetFloat())
render.SetMaterial(blur_mat)
render.DrawScreenQuad()
end
local cv_dxlevel = GetConVar("mat_dxlevel")
local function Render()
tfablurintensity = 0
if cv_dxlevel:GetInt() < 90 then return end
if TFA.DrawingRenderTarget then return end
local ply = LocalPlayer()
if not IsValid(ply) then return end
local wep = ply:GetActiveWeapon()
if not IsValid(wep) or not wep.IsTFAWeapon then return end
tfablurintensity = wep:GetInspectingProgress()
if tfablurintensity > 0.01 then
if doblur and doblur:GetBool() then
MyDrawBokehDOF()
end
tab["$pp_colour_brightness"] = -tfablurintensity * 0.02
tab["$pp_colour_contrast"] = 1 - tfablurintensity * 0.1
DrawColorModify(tab)
end
end
local function InitTFABlur()
hook.Add("PreDrawViewModels", "PreDrawViewModels_TFA_INSPECT", Render)
local pp_bokeh = GetConVar( "pp_bokeh" )
hook.Remove("NeedsDepthPass","NeedsDepthPass_Bokeh")
hook.Add("NeedsDepthPass", "aaaaaaaaaaaaaaaaaaNeedsDepthPass_TFA_Inspect", function()
if not ( doblur and doblur:GetBool() ) then return end
if tfablurintensity > 0.01 or ( pp_bokeh and pp_bokeh:GetBool() ) then
DOFModeHack(true)
return true
end
end)
end
hook.Add("InitPostEntity","InitTFABlur",InitTFABlur)
InitTFABlur()
end

View File

@@ -0,0 +1,40 @@
TFA_SCOPE_ACOG = {
scopetex = surface.GetTextureID("scope/gdcw_closedsight"),
reticletex = surface.GetTextureID("scope/gdcw_acogchevron"),
dottex = surface.GetTextureID("scope/gdcw_acogcross")
}
TFA_SCOPE_MILDOT = {
scopetex = surface.GetTextureID("scope/gdcw_scopesight")
}
TFA_SCOPE_SVD = {
scopetex = surface.GetTextureID("scope/gdcw_svdsight")
}
TFA_SCOPE_PARABOLIC = {
scopetex = surface.GetTextureID("scope/gdcw_parabolicsight")
}
TFA_SCOPE_ELCAN = {
scopetex = surface.GetTextureID("scope/gdcw_elcansight"),
reticletex = surface.GetTextureID("scope/gdcw_elcanreticle")
}
TFA_SCOPE_GREENDUPLEX = {
scopetex = surface.GetTextureID("scope/gdcw_closedsight"),
reticletex = surface.GetTextureID("scope/gdcw_nvgilluminatedduplex")
}
TFA_SCOPE_AIMPOINT = {
scopetex = surface.GetTextureID("scope/gdcw_closedsight"),
reticletex = surface.GetTextureID("scope/aimpoint")
}
TFA_SCOPE_MATADOR = {
scopetex = surface.GetTextureID("scope/rocketscope")
}
TFA_SCOPE_SCOPESCALE = 4
TFA_SCOPE_RETICLESCALE = 1
TFA_SCOPE_DOTSCALE = 1

View File

@@ -0,0 +1,61 @@
TFA.ClientsideModels = TFA.ClientsideModels or {}
timer.Create("TFA_UpdateClientsideModels", 0.1, 0, function()
local i = 1
while i <= #TFA.ClientsideModels do
local t = TFA.ClientsideModels[i]
if not t then
table.remove(TFA.ClientsideModels, i)
elseif not IsValid(t.wep) then
t.mdl:Remove()
table.remove(TFA.ClientsideModels, i)
elseif IsValid(t.wep:GetOwner()) and t.wep:GetOwner().GetActiveWeapon and t.wep ~= t.wep:GetOwner():GetActiveWeapon() then
t.mdl:Remove()
table.remove(TFA.ClientsideModels, i)
elseif t.wep.IsHidden and t.wep:IsHidden() then
t.mdl:Remove()
table.remove(TFA.ClientsideModels, i)
else
i = i + 1
end
end
if #TFA.ClientsideModels == 0 then
timer.Stop("TFA_UpdateClientsideModels")
end
end)
if #TFA.ClientsideModels == 0 then
timer.Stop("TFA_UpdateClientsideModels")
end
function TFA.RegisterClientsideModel(cmdl, wepv) -- DEPRECATED
-- don't use please
-- pleaz
TFA.ClientsideModels[#TFA.ClientsideModels + 1] = {
["mdl"] = cmdl,
["wep"] = wepv
}
timer.Start("TFA_UpdateClientsideModels")
end
local function NotifyShouldTransmit(ent, notdormant)
if notdormant or not ent.IsTFAWeapon then return end
if ent:GetOwner() == LocalPlayer() then return end
ent:CleanModels(ent:GetStatRaw("ViewModelElements", TFA.LatestDataVersion))
ent:CleanModels(ent:GetStatRaw("WorldModelElements", TFA.LatestDataVersion))
end
local function EntityRemoved(ent)
if not ent.IsTFAWeapon then return end
ent:CleanModels(ent:GetStatRaw("ViewModelElements", TFA.LatestDataVersion))
ent:CleanModels(ent:GetStatRaw("WorldModelElements", TFA.LatestDataVersion))
end
hook.Add("NotifyShouldTransmit", "TFA_ClientsideModels", NotifyShouldTransmit)
hook.Add("EntityRemoved", "TFA_ClientsideModels", EntityRemoved)

View File

@@ -0,0 +1,172 @@
local vector_origin = Vector()
TFA.Particles = TFA.Particles or {}
TFA.Particles.FlareParts = {}
TFA.Particles.VMAttachments = {}
local VMAttachments = TFA.Particles.VMAttachments
local FlareParts = TFA.Particles.FlareParts
local ply, vm, wep
local IsValid_ = FindMetaTable("Entity").IsValid
local GetModel = FindMetaTable("Entity").GetModel
local lastVMModel, lastVMAtts
local lastRequired = 0
local RealTime = RealTime
local FrameTime = FrameTime
local LocalPlayer = LocalPlayer
local ipairs = ipairs
local istable = istable
local isfunction = isfunction
local WorldToLocal = WorldToLocal
local table = table
local thinkAttachments = {}
local slowThinkers = 0
hook.Add("PreDrawEffects", "TFAMuzzleUpdate", function()
if lastRequired < RealTime() then return end
if not ply then
ply = LocalPlayer()
end
if not IsValid_(vm) then
vm = ply:GetViewModel()
if not IsValid_(vm) then return end
end
local vmmodel = GetModel(vm)
if vmmodel ~= lastVMModel then
lastVMModel = vmmodel
lastVMAtts = vm:GetAttachments()
end
if not lastVMAtts then return end
if slowThinkers == 0 then
for i in pairs(thinkAttachments) do
VMAttachments[i] = vm:GetAttachment(i)
end
else
for i = 1, #lastVMAtts do
VMAttachments[i] = vm:GetAttachment(i)
end
end
for _, v in ipairs(FlareParts) do
if v and v.ThinkFunc then
v:ThinkFunc()
end
end
end)
function TFA.Particles.RegisterParticleThink(particle, partfunc)
if not particle or not isfunction(partfunc) then return end
if not ply then
ply = LocalPlayer()
end
if not IsValid_(vm) then
vm = ply:GetViewModel()
if not IsValid_(vm) then return end
end
particle.ThinkFunc = partfunc
if IsValid(particle.FollowEnt) and particle.Att then
local angpos = particle.FollowEnt:GetAttachment(particle.Att)
if angpos then
particle.OffPos = WorldToLocal(particle:GetPos(), particle:GetAngles(), angpos.Pos, angpos.Ang)
end
end
local att = particle.Att
local isFast = partfunc == TFA.Particles.FollowMuzzle and att ~= nil
local isVM = particle.FollowEnt == vm
if isFast then
if isVM then
thinkAttachments[att] = (thinkAttachments[att] or 0) + 1
end
else
slowThinkers = slowThinkers + 1
end
table.insert(FlareParts, particle)
timer.Simple(particle:GetDieTime(), function()
if particle then
table.RemoveByValue(FlareParts, particle)
end
if not isFast then
slowThinkers = slowThinkers - 1
elseif isVM and att then
thinkAttachments[att] = thinkAttachments[att] - 1
if thinkAttachments[att] <= 0 then thinkAttachments[att] = nil end
end
end)
lastRequired = RealTime() + 0.5
end
function TFA.Particles.FollowMuzzle(self, first)
if lastRequired < RealTime() then
lastRequired = RealTime() + 0.5
return
end
lastRequired = RealTime() + 0.5
if self.isfirst == nil then
self.isfirst = false
first = true
end
if not IsValid_(ply) or not IsValid_(vm) then return end
wep = ply:GetActiveWeapon()
if IsValid(wep) and wep.IsCurrentlyScoped and wep:IsCurrentlyScoped() then return end
if not IsValid(self.FollowEnt) then return end
local owent = self.FollowEnt:GetOwner() or self.FollowEnt
if not IsValid(owent) then return end
local firvel
if first then
firvel = owent:GetVelocity() * FrameTime() * 1.1
else
firvel = vector_origin
end
if not self.Att or not self.OffPos then return end
if self.FollowEnt == vm then
local angpos = VMAttachments[self.Att]
if angpos then
local tmppos = LocalToWorld(self.OffPos, self:GetAngles(), angpos.Pos, angpos.Ang)
local npos = tmppos + self:GetVelocity() * FrameTime()
self.OffPos = WorldToLocal(npos + firvel, self:GetAngles(), angpos.Pos, angpos.Ang)
self:SetPos(npos + firvel)
end
return
end
local angpos = self.FollowEnt:GetAttachment(self.Att)
if angpos then
local tmppos = LocalToWorld(self.OffPos, self:GetAngles(), angpos.Pos, angpos.Ang)
local npos = tmppos + self:GetVelocity() * FrameTime()
self.OffPos = WorldToLocal(npos + firvel * 0.5, self:GetAngles(), angpos.Pos, angpos.Ang)
self:SetPos(npos + firvel)
end
end

View File

@@ -0,0 +1,35 @@
local ply = LocalPlayer()
local LocalPlayer = LocalPlayer
hook.Add("PreRender", "TFACleanupProjectedTextures", function()
if not IsValid(ply) then
ply = LocalPlayer()
if not IsValid(ply) then return end
end
local wep = ply:GetActiveWeapon()
if not IsValid(wep) or not wep.IsTFAWeapon then
if IsValid(ply.TFAFlashlightGun) then
ply.TFAFlashlightGun:Remove()
end
if IsValid(ply.TFALaserDot) then
ply.TFALaserDot:Remove()
end
end
end)
hook.Add("PrePlayerDraw", "TFACleanupProjectedTextures", function(plyv)
local wep = plyv:GetActiveWeapon()
if not IsValid(wep) or not wep.IsTFAWeapon then
if IsValid(plyv.TFAFlashlightGun) then
plyv.TFAFlashlightGun:Remove()
end
if IsValid(plyv.TFALaserDot) then
plyv.TFALaserDot:Remove()
end
end
end)

View File

@@ -0,0 +1,144 @@
TFA.DrawingRenderTarget = false
local props = {
["$translucent"] = 1
}
local TFA_RTMat = CreateMaterial("tfa_rtmaterial", "UnLitGeneric", props) --Material("models/weapons/TFA/shared/optic")
local TFA_RTScreen, TFA_RTScreenO = {}, {}
local tgt
local old_bt
local ply, vm, wep
local w, h
local qualitySizes
local function callFunc()
if wep.RTCode then
wep:RTCode(TFA_RTMat, w, h)
end
if wep:GetStatL("RTDrawEnabled") then
wep:CallAttFunc("RTCode", TFA_RTMat, w, h)
end
end
hook.Add("OnScreenSizeChanged", "TFA_rendertargets", function()
qualitySizes = nil
TFA_RTScreen, TFA_RTScreenO = {}, {}
end)
local function TFARenderScreen()
ply = GetViewEntity()
if not IsValid(ply) or not ply:IsPlayer() then
ply = LocalPlayer()
return
end
wep = ply:GetActiveWeapon()
if not IsValid(wep) or not wep.IsTFAWeapon then return end
if not IsValid(vm) then
if not wep:VMIV() then return end
vm = wep.OwnerViewModel
end
if not wep.MaterialCached then
wep.MaterialCached = true
wep.MaterialCached_V = nil
wep.MaterialCached_W = nil
end
local skinStat = wep:GetStatL("Skin")
if isnumber(skinStat) then
if vm:GetSkin() ~= skinStat then
vm:SetSkin(skinStat)
end
end
if wep:GetStatL("MaterialTable_V") and not wep.MaterialCached_V then
wep.MaterialCached_V = {}
vm:SetSubMaterial()
local collectedKeys = table.GetKeys(wep:GetStatL("MaterialTable_V"))
table.Merge(collectedKeys, table.GetKeys(wep:GetStatL("MaterialTable")))
for _, k in pairs(collectedKeys) do
if k ~= "BaseClass" then
local v = wep:GetStatL("MaterialTable_V")[k]
if not wep.MaterialCached_V[k] then
vm:SetSubMaterial(k - 1, v)
wep.MaterialCached_V[k] = true
end
end
end
end
if not (wep:GetStatL("RTDrawEnabled") or wep.RTCode ~= nil) then return end
w, h = ScrW(), ScrH()
if not qualitySizes then
qualitySizes = {
[0] = h,
[1] = math.Round(h * 0.5),
[2] = math.Round(h * 0.25),
[3] = math.Round(h * 0.125),
}
end
local quality = TFA.RTQuality()
if wep:GetStatL("RTOpaque") then
tgt = TFA_RTScreenO[quality]
if not tgt then
local size = qualitySizes[quality] or qualitySizes[0]
tgt = GetRenderTargetEx("TFA_RT_ScreenO_" .. size, size, size, RT_SIZE_NO_CHANGE, MATERIAL_RT_DEPTH_SHARED, 0, CREATERENDERTARGETFLAGS_UNFILTERABLE_OK, IMAGE_FORMAT_RGB888)
TFA_RTScreenO[quality] = tgt
end
else
tgt = TFA_RTScreen[quality]
if not tgt then
local size = qualitySizes[quality] or qualitySizes[0]
tgt = GetRenderTargetEx("TFA_RT_Screen_" .. size, size, size, RT_SIZE_NO_CHANGE, MATERIAL_RT_DEPTH_SHARED, 0, CREATERENDERTARGETFLAGS_UNFILTERABLE_OK, IMAGE_FORMAT_RGBA8888)
TFA_RTScreen[quality] = tgt
end
end
TFA.LastRTUpdate = CurTime() + 0.01
render.PushRenderTarget(tgt)
render.Clear(0, 0, 0, 255, true, true)
TFA.DrawingRenderTarget = true
render.CullMode(MATERIAL_CULLMODE_CCW)
ProtectedCall(callFunc)
TFA.DrawingRenderTarget = false
render.SetScissorRect(0, 0, 0, 0, false)
render.PopRenderTarget()
if old_bt ~= tgt then
TFA_RTMat:SetTexture("$basetexture", tgt)
old_bt = tgt
end
if wep:GetStatL("RTMaterialOverride", -1) >= 0 then
vm:SetSubMaterial(wep:GetStatL("RTMaterialOverride"), "!tfa_rtmaterial")
end
end
hook.Remove("PostRender", "TFASCREENS")
hook.Add("PreRender", "TFASCREENS", function()
if not TFA.RT_DRAWING then
TFA.RT_DRAWING = true
TFARenderScreen()
TFA.RT_DRAWING = false
end
end)
TFA.RT_DRAWING = false

View File

@@ -0,0 +1,80 @@
local FT = FrameTime
local tfablurintensity = 0
local cv_3dscopes = GetConVar("cl_tfa_3dscope")
local cv_mode = CreateClientConVar("cl_tfa_fx_rtscopeblur_mode", "1", true, false)
local funcs = {}
local cv_blur_passes = CreateClientConVar("cl_tfa_fx_rtscopeblur_passes", "3", true, false)
local cv_blur_intensity = CreateClientConVar("cl_tfa_fx_rtscopeblur_intensity", "4", true, false)
local blurTex = Material("pp/blurscreen")
funcs[1] = function()
surface.SetDrawColor(color_white)
render.SetMaterial(blurTex)
local passes = cv_blur_passes:GetInt()
for _ = 1, passes do
render.UpdateScreenEffectTexture()
blurTex:SetFloat("$blur", tfablurintensity * cv_blur_intensity:GetFloat() / math.sqrt(passes) )
blurTex:Recompute()
render.DrawScreenQuad()
end
end
local blur_mat = Material("pp/bokehblur")
funcs[2] = function()
render.UpdateScreenEffectTexture()
render.UpdateFullScreenDepthTexture()
blur_mat:SetTexture("$BASETEXTURE", render.GetScreenEffectTexture())
blur_mat:SetTexture("$DEPTHTEXTURE", render.GetResolvedFullFrameDepth())
blur_mat:SetFloat("$size", tfablurintensity * cv_blur_intensity:GetFloat() * 1.5 )
blur_mat:SetFloat("$focus", 0)
blur_mat:SetFloat("$focusradius", 0.25)
render.SetMaterial(blur_mat)
render.DrawScreenQuad()
end
hook.Add("PostDrawTranslucentRenderables", "tfa_draw_rt_blur", function()
if TFA.DrawingRenderTarget then return end
if not cv_3dscopes:GetBool() then return end
local mode = cv_mode:GetInt()
if not isfunction(funcs[mode]) then return end
local ply = LocalPlayer()
if not IsValid(ply) or ply:ShouldDrawLocalPlayer() then return end
local wep = ply:GetActiveWeapon()
if not IsValid(wep) or not wep.IsTFAWeapon or not wep.GetStat then return end
if not wep:GetStatL("RTBGBlur") then return end
if not wep:GetStatL("RTDrawEnabled") and not wep:GetStatL("RTMaterialOverride") and not wep.RTCode then return end
if wep.GLDeployed and wep:GLDeployed() then
tfablurintensity = Lerp(FT() * 12.5, tfablurintensity, 0)
else
local progress = math.Clamp(wep.CLIronSightsProgress or 0, 0, 1)
tfablurintensity = Lerp(FT() * 25, tfablurintensity, progress)
end
if tfablurintensity > 0.05 then
funcs[mode]()
end
end)
hook.Add("NeedsDepthPass", "aaaaaaaaaaaaaaaaaaNeedsDepthPass_TJA_IronSight", function()
if tfablurintensity > 0.05 and cv_mode:GetInt() == 2 then
if not cv_3dscopes:GetBool() then return end
DOFModeHack(true)
return true
end
end)

View File

@@ -0,0 +1,116 @@
local IsSinglePlayer = game.SinglePlayer()
function TFA.NumSliderNet(_parent, label, convar, min, max, decimals, ...)
local gconvar = assert(GetConVar(convar), "Unknown ConVar: " .. convar .. "!")
local newpanel
if IsSinglePlayer then
newpanel = _parent:NumSlider(label, convar, min, max, decimals, ...)
else
newpanel = _parent:NumSlider(label, nil, min, max, decimals, ...)
end
decimals = decimals or 0
local sf = "%." .. decimals .. "f"
if not IsSinglePlayer then
local ignore = false
newpanel.Think = function(_self)
if _self._wait_for_update and _self._wait_for_update > RealTime() then return end
local float = gconvar:GetFloat()
if _self:GetValue() ~= float then
ignore = true
_self:SetValue(float)
ignore = false
end
end
newpanel.OnValueChanged = function(_self, _newval)
if ignore then return end
if not LocalPlayer():IsAdmin() then return end
_self._wait_for_update = RealTime() + 1
timer.Create("tfa_vgui_" .. convar, 0.5, 1, function()
if not LocalPlayer():IsAdmin() then return end
net.Start("TFA_SetServerCommand")
net.WriteString(convar)
net.WriteString(string.format(sf, _newval))
net.SendToServer()
end)
end
end
return newpanel
end
function TFA.CheckBoxNet(_parent, label, convar, ...)
local gconvar = assert(GetConVar(convar), "Unknown ConVar: " .. convar .. "!")
local newpanel
if IsSinglePlayer then
newpanel = _parent:CheckBox(label, convar, ...)
else
newpanel = _parent:CheckBox(label, nil, ...)
end
if not IsSinglePlayer then
if not IsValid(newpanel.Button) then return newpanel end
newpanel.Button.Think = function(_self)
local bool = gconvar:GetBool()
if _self:GetChecked() ~= bool then
_self:SetChecked(bool)
end
end
newpanel.OnChange = function(_self, _bVal)
if not LocalPlayer():IsAdmin() then return end
if _bVal == gconvar:GetBool() then return end
net.Start("TFA_SetServerCommand")
net.WriteString(convar)
net.WriteString(_bVal and "1" or "0")
net.SendToServer()
end
end
return newpanel
end
function TFA.ComboBoxNet(_parent, label, convar, ...)
local gconvar = assert(GetConVar(convar), "Unknown ConVar: " .. convar .. "!")
local combobox, leftpanel
if IsSinglePlayer then
combobox, leftpanel = _parent:ComboBox(label, convar, ...)
else
combobox, leftpanel = _parent:ComboBox(label, nil, ...)
end
if not IsSinglePlayer then
combobox.Think = function(_self)
local value = gconvar:GetString()
if _self:GetValue() ~= value then
_self:SetValue(value)
end
end
combobox.OnSelect = function(_self, _index, _value, _data)
if not LocalPlayer():IsAdmin() then return end
local _newval = tostring(_data or _value)
net.Start("TFA_SetServerCommand")
net.WriteString(convar)
net.WriteString(_newval)
net.SendToServer()
end
end
return combobox, leftpanel
end

View File

@@ -0,0 +1,348 @@
-- stencil functions
local useStencils = render.SupportsPixelShaders_2_0() and render.SupportsVertexShaders_2_0()
local function defineCanvas(ref)
render.UpdateScreenEffectTexture()
render.ClearStencil()
render.SetStencilEnable(true)
render.SetStencilCompareFunction(STENCIL_ALWAYS)
render.SetStencilPassOperation(STENCIL_REPLACE)
render.SetStencilFailOperation(STENCIL_KEEP)
render.SetStencilZFailOperation(STENCIL_REPLACE)
render.SetStencilWriteMask(255)
render.SetStencilTestMask(255)
render.SetStencilReferenceValue(ref or 54)
end
local function drawOn()
render.SetStencilCompareFunction(STENCIL_EQUAL)
end
local function stopCanvas()
render.SetStencilEnable(false)
end
-- main draw functions
local CachedMaterials = {}
local DrawFunctions = {}
do -- Flat reticle, stays at center or moves with recoil
local function ScreenScaleH(num)
return num * (ScrH() / 480)
end
DrawFunctions[TFA.Enum.RETICLE_FLAT] = function(vm, ply, wep, SightElementTable)
local ReticleMaterial = wep:GetStat("StencilSight_ReticleMaterial")
if not ReticleMaterial then return end
if type(ReticleMaterial) == "string" then
CachedMaterials[ReticleMaterial] = CachedMaterials[ReticleMaterial] or Material(ReticleMaterial, "noclamp nocull smooth")
ReticleMaterial = CachedMaterials[ReticleMaterial]
end
local ReticleSize = wep:GetStat("StencilSight_ReticleSize")
if not ReticleSize then return end
if wep:GetStat("StencilSight_ScaleReticleByScreenHeight", true) then
ReticleSize = ScreenScaleH(ReticleSize)
end
if wep:GetStat("StencilSight_ScaleReticleByProgress", true) then
ReticleSize = ReticleSize * wep.IronSightsProgress
end
local w, h = ScrW(), ScrH()
local x, y = w * .5, h * .5
if wep:GetStat("StencilSight_FollowRecoil", true) then
x, y = TFA.LastCrosshairPosX or x, TFA.LastCrosshairPosY or y
end
local TargetColor = wep:GetStat("StencilSight_ReticleTint", color_white)
if wep:GetStat("StencilSight_ReticleTintBySightColor", false) and IsValid(wep:GetOwner()) then
local Owner = wep:GetOwner()
local _GetNWVector = Owner.GetNW2Vector or Owner.GetNWVector
local ColorVec = _GetNWVector(Owner, "TFAReticuleColor")
if ColorVec then
TargetColor = Color(ColorVec.x, ColorVec.y, ColorVec.z)
end
end
if wep:GetStat("StencilSight_FadeReticleByProgress", false) then
TargetColor = ColorAlpha(TargetColor, wep.IronSightsProgress * 255)
end
render.DepthRange(0.0, 0.1)
cam.Start2D(0, 0, w, h)
surface.SetMaterial(ReticleMaterial)
surface.SetDrawColor(TargetColor)
surface.DrawTexturedRect(x - ReticleSize * .5, y - ReticleSize * .5, ReticleSize, ReticleSize)
cam.End2D()
if not wep.UseHands then
render.DepthRange(0.0, 1.0)
end
end
end
do -- Model reticle, for when you don't have an attach point
if IsValid(TFA.SightReticleEnt) then
TFA.SightReticleEnt:Remove()
TFA.SightReticleEnt = nil
end
TFA.SightReticleEnt = ClientsideModel("models/error.mdl", RENDERGROUP_VIEWMODEL)
TFA.SightReticleEnt:SetNoDraw(true)
local SightReticleEnt = TFA.SightReticleEnt
DrawFunctions[TFA.Enum.RETICLE_MODEL] = function(vm, ply, wep, SightElementTable)
if not SightElementTable.reticle then return end
local SightElementModel = SightElementTable.curmodel
SightReticleEnt:SetModel(SightElementTable.reticle)
if SightReticleEnt:GetModel() == "models/error.mdl" then return end
local matrix = Matrix()
matrix:Scale(SightElementTable.size)
SightReticleEnt:EnableMatrix("RenderMultiply", matrix)
if SightReticleEnt:GetParent() ~= SightElementModel then
SightReticleEnt:SetParent(SightElementModel)
SightReticleEnt:SetPos(SightElementModel:GetPos())
SightReticleEnt:SetAngles(SightElementModel:GetAngles())
if not SightReticleEnt:IsEffectActive(EF_BONEMERGE) then
SightReticleEnt:AddEffects(EF_BONEMERGE)
SightReticleEnt:AddEffects(EF_BONEMERGE_FASTCULL)
end
end
if wep.ViewModelFlip then render.CullMode(MATERIAL_CULLMODE_CW) end
if wep:GetStat("StencilSight_FadeReticleByProgress", false) then
local oldBlend = render.GetBlend()
render.SetBlend(wep.IronSightsProgress)
SightReticleEnt:DrawModel()
render.SetBlend(oldBlend)
else
SightReticleEnt:DrawModel()
end
if wep.ViewModelFlip then render.CullMode(MATERIAL_CULLMODE_CCW) end
if wep:GetStat("StencilSight_EnableQuad") and bit.band(wep:GetStat("StencilSight_ReticleType")) ~= TFA.Enum.RETICLE_QUAD then
DrawFunctions[TFA.Enum.RETICLE_QUAD](vm, ply, wep, SightElementTable)
end
end
end
do -- Quad/Attach reticle, TFA INS2 method
local function GetTargetPosition(wep, SightElementTable)
local TargetEntity = SightElementTable.curmodel
if not IsValid(TargetEntity) then return end
local Type = wep:GetStat("StencilSight_PositionType", TFA.Enum.SIGHTSPOS_ATTACH)
local pos, ang
if Type == TFA.Enum.SIGHTSPOS_ATTACH then
local AttachmentID = wep:GetStat("StencilSight_ReticleAttachment")
if not AttachmentID then return end
if type(AttachmentID) == "string" then
AttachmentID = TargetEntity:LookupAttachment(AttachmentID)
end
if not AttachmentID or AttachmentID <= 0 then return end
local Attachment = TargetEntity:GetAttachment(AttachmentID)
if not Attachment.Pos or not Attachment.Ang then return end
pos, ang = Attachment.Pos, Attachment.Ang
elseif Type == TFA.Enum.SIGHTSPOS_BONE then
local BoneID = wep:GetStat("StencilSight_ReticleBone")
if type(BoneID) == "string" then
BoneID = TargetEntity:LookupBone(BoneID)
end
if not BoneID or BoneID < 0 then return end
pos, ang = TargetEntity:GetBonePosition(BoneID)
if pos == TargetEntity:GetPos() then
pos = TargetEntity:GetBoneMatrix(BoneID):GetTranslation()
ang = TargetEntity:GetBoneMatrix(BoneID):GetAngles()
end
else
return
end
local OffsetPos = wep:GetStat("StencilSight_ReticleOffsetPos")
if OffsetPos then
pos = pos + ang:Right() * OffsetPos.x + ang:Forward() * OffsetPos.y + ang:Up() * OffsetPos.z
end
local OffsetAng = wep:GetStat("StencilSight_ReticleOffsetAng")
if OffsetAng then
ang:RotateAroundAxis(ang:Right(), OffsetAng.p)
ang:RotateAroundAxis(ang:Up(), OffsetAng.y)
ang:RotateAroundAxis(ang:Forward(), OffsetAng.r)
end
return pos, ang
end
DrawFunctions[TFA.Enum.RETICLE_QUAD] = function(vm, ply, wep, SightElementTable)
local ReticleMaterial = wep:GetStat("StencilSight_ReticleMaterial")
if not ReticleMaterial then return end
if type(ReticleMaterial) == "string" then
CachedMaterials[ReticleMaterial] = CachedMaterials[ReticleMaterial] or Material(ReticleMaterial, "noclamp nocull smooth")
ReticleMaterial = CachedMaterials[ReticleMaterial]
end
local ReticleSize = wep:GetStat("StencilSight_ReticleSize")
if not ReticleSize then return end
if wep:GetStat("StencilSight_ScaleReticleByProgress", false) then
ReticleSize = ReticleSize * wep.IronSightsProgress
end
local TargetColor = wep:GetStat("StencilSight_ReticleTint", color_white)
if wep:GetStat("StencilSight_ReticleTintBySightColor", false) and IsValid(wep:GetOwner()) then
local Owner = wep:GetOwner()
local _GetNWVector = Owner.GetNW2Vector or Owner.GetNWVector
local ColorVec = _GetNWVector(Owner, "TFAReticuleColor")
if ColorVec then
TargetColor = Color(ColorVec.x, ColorVec.y, ColorVec.z)
end
end
if wep:GetStat("StencilSight_FadeReticleByProgress", false) then
TargetColor = ColorAlpha(TargetColor, wep.IronSightsProgress * 255)
end
local p, a = GetTargetPosition(wep, SightElementTable)
if not p or not a then return end
render.OverrideDepthEnable(true, true)
render.SetMaterial(ReticleMaterial)
render.DrawQuadEasy(p, a:Forward() * -1, ReticleSize, ReticleSize, TargetColor, 180 + a.r * (wep.ViewModelFlip and 1 or -1))
render.OverrideDepthEnable(false, false)
end
end
-- hook logic
if IsValid(TFA.SightMaskEnt) then
TFA.SightMaskEnt:Remove()
TFA.SightMaskEnt = nil
end
TFA.SightMaskEnt = ClientsideModel("models/error.mdl", RENDERGROUP_VIEWMODEL)
TFA.SightMaskEnt:SetNoDraw(true)
local SightMaskEnt = TFA.SightMaskEnt
local function DrawSight(vm, ply, wep)
if not IsValid(wep) or not wep.IsTFAWeapon then return end
local shouldDraw = hook.Run("TFA_ShouldDrawStencilSight", wep)
if shouldDraw == false then return end
local wep2 = wep:GetTable()
if wep2.TFA_IsDrawingStencilSights then return end
wep2.TFA_IsDrawingStencilSights = true
if not wep2.GetStat(wep, "StencilSight") then wep2.TFA_IsDrawingStencilSights = false return end
if wep2.IronSightsProgress < wep2.GetStat(wep, "StencilSight_MinPercent", 0.05) then wep2.TFA_IsDrawingStencilSights = false return end
local SightElementName = wep2.GetStat(wep, "StencilSight_VElement")
if not SightElementName or not wep2.GetStat(wep, "VElements." .. SightElementName .. ".active") then wep2.TFA_IsDrawingStencilSights = false return end
local SightElementTable = wep2.VElements[SightElementName]
if not SightElementTable then wep2.TFA_IsDrawingStencilSights = false return end
local SightElementModel = SightElementTable.curmodel
if not IsValid(SightElementModel) then wep2.TFA_IsDrawingStencilSights = false return end
if useStencils then
defineCanvas()
local SightMaskModel = SightElementModel
if wep2.GetStat(wep, "StencilSight_UseMask", false) and SightElementTable.mask then
SightMaskEnt:SetModel(SightElementTable.mask)
if SightMaskEnt:GetModel() ~= "models/error.mdl" then
SightMaskModel = SightMaskEnt
local matrix = Matrix()
matrix:Scale(SightElementTable.size)
SightMaskEnt:EnableMatrix("RenderMultiply", matrix)
if SightMaskEnt:GetParent() ~= SightElementModel then
SightMaskEnt:SetParent(SightElementModel)
SightMaskEnt:SetPos(SightElementModel:GetPos())
SightMaskEnt:SetAngles(SightElementModel:GetAngles())
if not SightMaskEnt:IsEffectActive(EF_BONEMERGE) then
SightMaskEnt:AddEffects(EF_BONEMERGE)
SightMaskEnt:AddEffects(EF_BONEMERGE_FASTCULL)
end
end
end
end
if wep.ViewModelFlip then render.CullMode(MATERIAL_CULLMODE_CW) end
local oldBlend = render.GetBlend()
render.SetBlend(0)
SightMaskModel:DrawModel()
render.SetBlend(oldBlend)
if wep.ViewModelFlip then render.CullMode(MATERIAL_CULLMODE_CCW) end
drawOn()
end
local retValPre = wep2.CallAttFunc(wep, "PreDrawStencilSight", vm, ply, SightElementTable)
if retValPre ~= true then
retValPre = wep2.PreDrawStencilSight(wep, vm, ply, SightElementTable) or retValPre
end
if retValPre ~= false then
local funcType = wep2.GetStat(wep, "StencilSight_ReticleType", TFA.Enum.RETICLE_FLAT)
for _, retType in ipairs(TFA.Enum.RETICLE_DRAW_ORDER) do
if bit.band(funcType, retType) == retType and DrawFunctions[retType] then
ProtectedCall(function()
DrawFunctions[retType](vm, ply, wep, SightElementTable)
end)
end
end
local retValPost = wep2.CallAttFunc(wep, "PostDrawStencilSight", vm, ply, SightElementTable)
if retValPost ~= true then
wep2.PostDrawStencilSight(wep, vm, ply, SightElementTable)
end
end
if useStencils then
stopCanvas()
end
wep2.TFA_IsDrawingStencilSights = false
end
hook.Add("PostDrawViewModel", "TFA_DrawStencilSight", DrawSight)

View File

@@ -0,0 +1,84 @@
local cv_enable = CreateClientConVar("cl_tfa_subcategories_enabled", "1", true, false, "Enable spawnmenu subcategories? (Update spawnmenu with spawnmenu_reload command after changing this!)")
local cv_autoassign = CreateClientConVar("cl_tfa_subcategories_auto", "0", true, false, "Attempt to auto-assign missing subcategories based on weapon's type?")
local function PopulateWeapons(pnlContent, tree, browseNode)
if not cv_enable:GetBool() then return end
local cats, subs = {}, {}
for class, _wep in pairs(list.Get("Weapon") or {}) do
if not _wep.Spawnable then continue end
local cat = _wep.Category or "Other2"
cats[cat] = cats[cat] or {}
if not weapons.IsBasedOn(class, "tfa_gun_base") then
table.insert(cats[cat], _wep)
continue
end
local wep = weapons.Get(class)
local sub = wep.SubCategory or (cv_autoassign:GetBool() and wep:GetType())
if not sub or sub == "" then
table.insert(cats[cat], wep)
continue
end
subs[cat] = subs[cat] or {}
subs[cat][sub] = subs[cat][sub] or {}
table.insert(subs[cat][sub], wep)
end
local root = tree:Root()
if not IsValid(root) then return end
for _, node in ipairs(root:GetChildNodes()) do
local name = node:GetText()
if not name or not subs[name] then continue end
node.DoPopulate = function(self)
if self.PropPanel then return end
self.PropPanel = vgui.Create("ContentContainer", pnlContent)
self.PropPanel:SetVisible(false)
self.PropPanel:SetTriggerSpawnlistChange(false)
for sname, subcat in SortedPairs(subs[name]) do
spawnmenu.CreateContentIcon("header", self.PropPanel, {text = sname})
for _, ent in SortedPairsByMemberValue(subcat, "PrintName") do
spawnmenu.CreateContentIcon(ent.ScriptedEntityType or "weapon", self.PropPanel, {
nicename = ent.PrintName or ent.ClassName,
spawnname = ent.ClassName,
material = ent.IconOverride or "entities/" .. ent.ClassName .. ".png",
admin = ent.AdminOnly
})
end
end
if cats[name] and #cats[name] > 0 then
spawnmenu.CreateContentIcon("header", self.PropPanel, {text = "Other"})
for _, ent in SortedPairsByMemberValue(cats[name], "PrintName") do
spawnmenu.CreateContentIcon(ent.ScriptedEntityType or "weapon", self.PropPanel, {
nicename = ent.PrintName or ent.ClassName,
spawnname = ent.ClassName,
material = ent.IconOverride or "entities/" .. ent.ClassName .. ".png",
admin = ent.AdminOnly
})
end
end
end
end
end
hook.Add("PopulateWeapons", "AddTFAWeaponContent", function(pnlContent, tree, browseNode)
timer.Simple(0, function()
PopulateWeapons(pnlContent, tree, browseNode)
end)
end, 1)
TFA.BASE_LOAD_COMPLETE = true

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,244 @@
local supports
local cl_tfa_fx_dof, cl_tfa_fx_dof_hd
local fmat = CreateMaterial("TFA_DOF_Material4", "Refract", {
["$model"] = "1",
["$alpha"] = "1",
["$alphatest"] = "1",
["$normalmap"] = "effects/flat_normal",
["$refractamount"] = "0.1",
["$vertexalpha"] = "1",
["$vertexcolor"] = "1",
["$translucent"] = "1",
["$forcerefract"] = "0",
["$bluramount"] = "1.5",
["$nofog"] = "1"
})
local fmat2 = CreateMaterial("TFA_DOF_Material5", "Refract", {
["$model"] = "1",
["$alpha"] = "1",
["$alphatest"] = "1",
["$normalmap"] = "effects/flat_normal",
["$refractamount"] = "0.1",
["$vertexalpha"] = "1",
["$vertexcolor"] = "1",
["$translucent"] = "1",
["$forcerefract"] = "0",
["$bluramount"] = "0.9",
["$nofog"] = "1"
})
local fmat3 = CreateMaterial("TFA_DOF_Material16", "Refract", {
["$model"] = "1",
["$alpha"] = "1",
["$alphatest"] = "1",
["$normalmap"] = "effects/flat_normal",
["$refractamount"] = "0.1",
["$vertexalpha"] = "1",
["$vertexcolor"] = "1",
["$translucent"] = "1",
["$forcerefract"] = "0",
["$bluramount"] = "0.8",
["$nofog"] = "1"
})
local white = CreateMaterial("TFA_DOF_White", "UnlitGeneric", {
["$alpha"] = "0",
["$basetexture"] = "models/debug/debugwhite"
})
TFA.LastRTUpdate = TFA.LastRTUpdate or UnPredictedCurTime()
hook.Add("PreDrawViewModel", "TFA_DrawViewModel", function(vm, plyv, wep)
if not vm or not plyv or not wep then return end
if not wep.IsTFAWeapon then return end
if supports == nil then
supports = render.SupportsPixelShaders_1_4() and render.SupportsPixelShaders_2_0() and render.SupportsVertexShaders_2_0()
if not supports then
print("[TFA] Your videocard does not support pixel shaders! DoF of Iron Sights is disabled!")
end
end
if not supports then return end
if not cl_tfa_fx_dof then
cl_tfa_fx_dof = GetConVar("cl_tfa_fx_ads_dof")
end
if not cl_tfa_fx_dof or not cl_tfa_fx_dof:GetBool() then return end
if not wep.AllowIronSightsDoF then return end
local aimingDown = wep:GetIronSightsProgress() > 0.4
local scoped = TFA.LastRTUpdate > UnPredictedCurTime() or wep:GetStatL("Scoped")
if aimingDown and not scoped then
if hook.Run("TFA_AllowDoFDraw", wep, plyv, vm) == false then return end
wep.__TFA_AimDoFFrame = FrameNumber()
render.ClearStencil()
render.SetStencilEnable(true)
render.SetStencilTestMask(0)
render.SetStencilWriteMask(1)
render.SetStencilReferenceValue(1)
render.SetStencilCompareFunction(STENCIL_ALWAYS)
render.OverrideColorWriteEnable(true, true)
render.SetStencilZFailOperation(STENCIL_KEEP)
render.SetStencilPassOperation(STENCIL_REPLACE)
render.SetStencilFailOperation(STENCIL_KEEP)
end
end)
local transparent = Color(0, 0, 0, 0)
local color_white = Color(255, 255, 255)
local STOP = false
local function DrawDOF(muzzledata,fwd2)
local w, h = ScrW(), ScrH()
render.SetMaterial(fmat)
cam.Start2D()
surface.SetDrawColor(255, 255, 255)
surface.SetMaterial(fmat)
surface.DrawTexturedRect(0, 0, w, h)
cam.End2D()
if muzzledata then
-- :POG:
render.SetMaterial(fmat2)
for i = 28, 2, -1 do
render.UpdateScreenEffectTexture()
render.DrawSprite(muzzledata.Pos - fwd2 * i * 3, 200, 200, color_white)
end
end
render.SetMaterial(fmat3)
cam.Start2D()
surface.SetMaterial(fmat3)
for i = 0, 32 do
render.UpdateScreenEffectTexture()
surface.DrawTexturedRect(0, h / 1.6 + h / 2 * i / 32, w, h / 2)
end
cam.End2D()
end
hook.Add("PostDrawViewModel", "TFA_DrawViewModel", function(vm, plyv, wep)
if not wep.IsTFAWeapon then return end
if not supports then
wep:ViewModelDrawnPost()
return
end
if not cl_tfa_fx_dof then
cl_tfa_fx_dof = GetConVar("cl_tfa_fx_ads_dof")
end
if not cl_tfa_fx_dof_hd then
cl_tfa_fx_dof_hd = GetConVar("cl_tfa_fx_ads_dof_hd")
end
if not cl_tfa_fx_dof or not cl_tfa_fx_dof:GetBool() then
wep:ViewModelDrawnPost()
return
end
if not wep:GetStatL("AllowIronSightsDoF") then
wep:ViewModelDrawnPost()
return
end
local aimingDown = wep:GetIronSightsProgress() > 0.4
local eangles = EyeAngles()
local fwd2 = vm:GetAngles():Forward()
local scoped = TFA.LastRTUpdate > UnPredictedCurTime()
if aimingDown and not scoped and wep.__TFA_AimDoFFrame == FrameNumber() then
fmat:SetFloat("$alpha", wep:GetIronSightsProgress())
local muzzle = wep:GetStatL("IronSightsDoF_FocusAttachment")
if not muzzle then
wep:UpdateMuzzleAttachment()
wep:SetStatRawL("IronSightsDoF_FocusAttachment", wep.MuzzleAttachmentRaw)
muzzle = wep:GetStatL("IronSightsDoF_FocusAttachment")
end
muzzle = hook.Run("TFA_GetDoFMuzzleAttachmentID", wep, plyv, vm, muzzle) or muzzle
local muzzledata
if muzzle and muzzle ~= 0 then
muzzledata = vm:GetAttachment(muzzle)
end
local hands = plyv:GetHands()
if IsValid(hands) and wep.UseHands then
render.OverrideColorWriteEnable(true, false)
STOP = true
local candraw = hook.Run("PreDrawPlayerHands", hands, vm, plyv, wep)
STOP = false
if candraw ~= true then
if wep.ViewModelFlip then
render.CullMode(MATERIAL_CULLMODE_CW)
end
hands:DrawModel()
if wep.ViewModelFlip then
render.CullMode(MATERIAL_CULLMODE_CCW)
end
end
render.OverrideColorWriteEnable(false, false)
end
if muzzledata then
render.SetStencilPassOperation(STENCIL_ZERO)
render.SetMaterial(white)
render.DrawSprite(muzzledata.Pos - fwd2 * 6 + eangles:Up() * 4, 30, 30, transparent)
render.SetStencilPassOperation(STENCIL_REPLACE)
end
render.SetStencilTestMask(1)
render.SetStencilWriteMask(2)
render.SetStencilCompareFunction(STENCIL_EQUAL)
render.SetStencilPassOperation(STENCIL_REPLACE)
render.UpdateScreenEffectTexture()
render.PushFilterMin(TEXFILTER.ANISOTROPIC)
render.PushFilterMag(TEXFILTER.ANISOTROPIC)
if cl_tfa_fx_dof_hd and cl_tfa_fx_dof_hd:GetBool() then
DrawDOF(muzzledata,fwd2)
else
DrawToyTown(3,ScrH() * 2 / 3 )
end
render.PopFilterMin()
render.PopFilterMag()
--render.PopRenderTarget()
render.SetStencilEnable(false)
end
wep:ViewModelDrawnPost()
end)
hook.Add("PreDrawPlayerHands", "TFA_DrawViewModel", function(hands, vm, plyv, wep)
if STOP then return end
if not wep.IsTFAWeapon then return end
if not supports then return end
if not cl_tfa_fx_dof then
cl_tfa_fx_dof = GetConVar("cl_tfa_fx_ads_dof")
end
if not cl_tfa_fx_dof or not cl_tfa_fx_dof:GetBool() then return end
if not wep.AllowIronSightsDoF then return end
if TFA.LastRTUpdate > UnPredictedCurTime() then return end
if wep:GetIronSightsProgress() > 0.4 then return true end
end)
hook.Add("PostDrawPlayerHands", "TFA_DrawViewModel", function(hands, vm, plyv, wep)
if not wep.IsTFAWeapon then return end
wep:ViewModelDrawnPostFinal()
end)

View File

@@ -0,0 +1,27 @@
local IsSinglePlayer = game.SinglePlayer()
util.AddNetworkString("TFA_SetServerCommand")
local function QueueConVarChange(convarname, convarvalue)
if not convarname or not convarvalue then return end
timer.Create("tfa_cvarchange_" .. convarname, 0.1, 1, function()
if not string.find(convarname, "_tfa") or not GetConVar(convarname) then return end -- affect only TFA convars
RunConsoleCommand(convarname, convarvalue)
end)
end
local function ChangeServerOption(_length, _player)
local _cvarname = net.ReadString()
local _value = net.ReadString()
if IsSinglePlayer then return end
if not IsValid(_player) or not _player:IsAdmin() then return end
QueueConVarChange(_cvarname, _value)
end
net.Receive("TFA_SetServerCommand", ChangeServerOption)
TFA.BASE_LOAD_COMPLETE = true

View File

@@ -0,0 +1,17 @@
--[[Bow Ammo]]
--
game.AddAmmoType({
name = "tfbow_arrow",
dmgtype = DMG_CLUB,
tracer = 0,
minsplash = 5,
maxsplash = 5
})
game.AddAmmoType({
name = "tfbow_bolt",
dmgtype = DMG_CLUB,
tracer = 0,
minsplash = 5,
maxsplash = 5
})

View File

@@ -0,0 +1,408 @@
TFA.Attachments = TFA.Attachments or {}
TFA.Attachments.Atts = {}
TFA.Attachments.Colors = {
["active"] = Color(252, 151, 50, 255),
["error"] = Color(225, 0, 0, 255),
["error_attached"] = Color(140, 80, 30, 255),
["background"] = Color(15, 15, 15, 64),
["primary"] = Color(245, 245, 245, 255),
["secondary"] = Color(153, 253, 220, 255),
["+"] = Color(128, 255, 128, 255),
["-"] = Color(255, 128, 128, 255),
["="] = Color(192, 192, 192, 255)
}
TFA.Attachments.UIPadding = 2
TFA.Attachments.IconSize = 64
TFA.Attachments.CategorySpacing = 128
if SERVER then
util.AddNetworkString("TFA_Attachment_Set")
util.AddNetworkString("TFA_Attachment_SetStatus")
util.AddNetworkString("TFA_Attachment_Reload")
util.AddNetworkString("TFA_Attachment_Request")
local function UpdateWeapon(wep, ply)
for category, data in pairs(wep.Attachments or {}) do
if type(category) ~= "string" then
net.Start("TFA_Attachment_Set")
net.WriteEntity(wep)
net.WriteUInt(category, 8)
if data.atts and data.atts[data.sel] then
net.WriteString(data.atts[data.sel])
else
net.WriteString("")
end
net.Send(ply)
end
end
end
net.Receive("TFA_Attachment_Request", function(len, ply)
if not IsValid(ply) then return end
local wep = net.ReadEntity()
if not IsValid(wep) or not wep.IsTFAWeapon or not wep.HasInitAttachments or wep.AttachmentCount < 1 then return end
local ctime = SysTime()
local currentScheduleRequest = ply.__TFA_Base_Next_Attachment_Request or ctime
local nextScheduleRequest = math.max(ctime + 0.2, currentScheduleRequest + 0.2)
ply.__TFA_Base_Next_Attachment_Request = nextScheduleRequest
if currentScheduleRequest <= ctime then
UpdateWeapon(wep, ply)
-- elseif currentScheduleRequest - ctime >= 10 then
-- ply:Kick("TFA_Attachment_Request spam")
else
timer.Simple(nextScheduleRequest - ctime, function()
if IsValid(ply) and IsValid(wep) then
UpdateWeapon(wep, ply)
end
end)
end
end)
net.Receive("TFA_Attachment_Set", function(len, ply)
local wep = ply:GetWeapon(net.ReadString())
if not IsValid(wep) or not wep.IsTFAWeapon then return end
local cat = net.ReadUInt(8)
local ind = net.ReadString()
local status = wep:SetTFAAttachment(cat, ind, ply)
net.Start("TFA_Attachment_SetStatus")
net.WriteEntity(wep)
net.WriteBool(status)
if not status then
if wep.Attachments and wep.Attachments[cat] then
local data = wep.Attachments[cat]
net.WriteUInt(cat, 8)
if data.atts and data.atts[data.sel] then
net.WriteString(data.atts[data.sel])
else
net.WriteString("")
end
end
end
net.Send(ply)
end)
else
sql.Query([[
CREATE TABLE IF NOT EXISTS tfa_savedattachments (
class VARCHAR(80) NOT NULL,
atts TEXT NOT NULL,
PRIMARY KEY (class)
)
]])
net.Receive("TFA_Attachment_Set", function(len)
local wep = net.ReadEntity()
local cat = net.ReadUInt(8)
local ind = net.ReadString()
if IsValid(wep) and wep.SetTFAAttachment then
wep:SetTFAAttachment(cat, ind, false)
end
end)
net.Receive("TFA_Attachment_Reload", function(len)
TFAUpdateAttachments()
end)
net.Receive("TFA_Attachment_SetStatus", function(len)
local weapon = net.ReadEntity()
if not IsValid(weapon) then return end
local status = net.ReadBool()
if status then
weapon:SaveAttachments()
return
end
surface.PlaySound("buttons/button2.wav")
local cat = net.ReadUInt(8)
local ind = net.ReadString()
weapon:SetTFAAttachment(cat, ind, false)
end)
local function request(self)
if self._TFA_Attachment_Request then return end
if not self.HasInitAttachments or self.AttachmentCount < 1 then return end
net.Start("TFA_Attachment_Request")
net.WriteEntity(self)
net.SendToServer()
self._TFA_Attachment_Request = true
end
hook.Add("NotifyShouldTransmit", "TFA_AttachmentsRequest", function(self, notDormant)
if not self.IsTFAWeapon or not notDormant then return end
request(self)
end)
hook.Add("NetworkEntityCreated", "TFA_AttachmentsRequest", function(self)
timer.Simple(0, function()
if not IsValid(self) or not self.IsTFAWeapon then return end
request(self)
end)
end)
hook.Add("OnEntityCreated", "TFA_AttachmentsRequest", function(self)
timer.Simple(0, function()
if not IsValid(self) or not self.IsTFAWeapon then return end
request(self)
end)
end)
local LoadQuery = [[SELECT atts FROM tfa_savedattachments WHERE class = '%s']]
function TFA.GetSavedAttachments(Weapon)
if not IsValid(Weapon) or not Weapon.IsTFAWeapon then return end
local data = sql.QueryRow(string.format(LoadQuery, sql.SQLStr(Weapon:GetClass(), true)))
if data and data.atts then
return util.JSONToTable(data.atts)
end
end
local SaveQuery = [[REPLACE INTO tfa_savedattachments (class, atts) VALUES ('%s', '%s');]]
function TFA.SetSavedAttachments(Weapon)
if not IsValid(Weapon) or not Weapon.IsTFAWeapon or not next(Weapon.Attachments or {}) then return end
local seltbl = {}
for cat, catTbl in pairs(Weapon.Attachments or {}) do
if cat ~= "BaseClass" and catTbl.atts then
seltbl[cat] = catTbl.atts[catTbl.sel or -1] or ""
end
end
return sql.Query(string.format(SaveQuery, sql.SQLStr(Weapon:GetClass(), true), sql.SQLStr(util.TableToJSON(seltbl), true)))
end
end
local function basefunc(t, k)
if k == "Base" then return end
if t.Base then
local bt = TFA.Attachments.Atts[t.Base]
if bt then return bt[k] end
end
end
function TFA.Attachments.SetupBaseTable(id, path)
local ATTACHMENT = {}
setmetatable(ATTACHMENT, {
__index = basefunc
})
ATTACHMENT.ID = id
ProtectedCall(function()
hook.Run("TFABase_PreBuildAttachment", id, path, ATTACHMENT)
end)
return ATTACHMENT
end
function TFA.Attachments.Register(id, ATTACHMENT, path)
if istable(id) then
ATTACHMENT = id
id = ATTACHMENT.ID
end
assert(istable(ATTACHMENT), "Invalid attachment argument provided")
assert(isstring(id), "Invalid attachment ID provided")
local size = table.Count(ATTACHMENT)
if size == 0 or size == 1 and ATTACHMENT.ID ~= nil then
local id2 = id or ATTACHMENT.ID
if id2 then
ErrorNoHalt("[TFA Base] Attempt to register an empty attachment " .. id2 .. "\n")
else
ErrorNoHalt("[TFA Base] Attempt to register an empty attachment\n")
end
ErrorNoHalt(debug.traceback() .. "\n")
MsgC("\n")
return
end
ProtectedCall(function()
hook.Run("TFABase_BuildAttachment", id, path, ATTACHMENT)
end)
ATTACHMENT.ID = ATTACHMENT.ID or id
if ATTACHMENT.ID and ATTACHMENT.ID ~= "base" then
ATTACHMENT.Base = ATTACHMENT.Base or "base"
end
--[[if not TFA_ATTACHMENT_ISUPDATING and istable(ATTACHMENT.WeaponTable) then
TFA.MigrateStructure(ATTACHMENT, ATTACHMENT.WeaponTable, id or "<attachment>", false)
end]]
ProtectedCall(function()
hook.Run("TFABase_RegisterAttachment", id, ATTACHMENT)
end)
TFA.Attachments.Atts[ATTACHMENT.ID or ATTACHMENT.Name] = ATTACHMENT
end
function TFA.Attachments.RegisterFromTable(id, tbl, path)
local status
ProtectedCall(function()
status = hook.Run("TFABase_ShouldLoadAttachment", id, path)
end)
if status == false then return end
local ATTACHMENT = TFA.Attachments.SetupBaseTable(id)
for k, v in pairs(tbl) do
ATTACHMENT[k] = v
end
TFA.Attachments.Register(id, ATTACHMENT)
end
TFARegisterAttachment = TFA.Attachments.Register
TFA.Attachments.Path = "tfa/att/"
TFA.Attachments.Path_Batch = "tfa/attbatch/"
TFA_ATTACHMENT_ISUPDATING = false
local inheritanceCached = {}
local missingBaseWarningShown = {}
local function patchInheritance(t, basetbl)
if t.Base and t.Base ~= "base" and not TFA.Attachments.Atts[t.Base] then
if t.ID and not missingBaseWarningShown[t.ID] then
missingBaseWarningShown[t.ID] = true
print("[TFA Base] [!] Attachment '" .. t.ID .. "' depends on unknown attachment '" .. t.Base .. "'!")
end
t.Base = "base"
end
if not basetbl and t.Base then
basetbl = TFA.Attachments.Atts[t.Base]
if basetbl and istable(basetbl) and basetbl.ID and not inheritanceCached[basetbl.ID] then
inheritanceCached[basetbl.ID] = true
patchInheritance(basetbl)
end
end
if not (basetbl and istable(basetbl)) then return end
for k, v in pairs(t) do
local baseT = basetbl[k]
if istable(v) and baseT then
patchInheritance(v, baseT)
end
end
for k, v in pairs(basetbl) do
if rawget(t, k) == nil then
t[k] = v
end
end
end
function TFAUpdateAttachments(network)
if SERVER and network ~= false then
net.Start("TFA_Attachment_Reload")
net.Broadcast()
end
TFA.AttachmentColors = TFA.Attachments.Colors --for compatibility
TFA.Attachments.Atts = {}
TFA_ATTACHMENT_ISUPDATING = true
local tbl = file.Find(TFA.Attachments.Path .. "*base*", "LUA")
local addtbl = file.Find(TFA.Attachments.Path .. "*", "LUA")
for _, v in ipairs(addtbl) do
if not string.find(v, "base") then
table.insert(tbl, #tbl + 1, v)
end
end
table.sort(tbl)
for _, fname in ipairs(tbl) do
local path = TFA.Attachments.Path .. fname
local id = fname:lower():Replace(".lua", "")
local status
ProtectedCall(function()
status = hook.Run("TFABase_ShouldLoadAttachment", id, path)
end)
if status ~= false then
ATTACHMENT = TFA.Attachments.SetupBaseTable(id, path)
if SERVER then
AddCSLuaFile(path)
include(path)
else
include(path)
end
TFA.Attachments.Register(id, ATTACHMENT, path)
ATTACHMENT = nil
end
end
local tbl2 = file.Find(TFA.Attachments.Path_Batch .. "*", "LUA")
for _, fname in ipairs(tbl2) do
local path = TFA.Attachments.Path_Batch .. fname
if SERVER then
AddCSLuaFile(path)
include(path)
else
include(path)
end
end
ProtectedCall(function()
hook.Run("TFAAttachmentsLoaded")
end)
for _, v in pairs(TFA.Attachments.Atts) do
patchInheritance(v)
--[[if istable(v.WeaponTable) then
TFA.MigrateStructure(v, v.WeaponTable, v.ID or "<attachment>", false)
end]]
end
ProtectedCall(function()
hook.Run("TFAAttachmentsInitialized")
end)
TFA_ATTACHMENT_ISUPDATING = false
TFA.ATTACHMENTS_LOADED = true
end
if not VLL2_FILEDEF and TFA.ATTACHMENTS_LOADED then
TFAUpdateAttachments(false)
end
concommand.Add("sv_tfa_attachments_reload", function(ply, cmd, args, argStr)
if SERVER and (not IsValid(ply) or ply:IsAdmin()) then
TFAUpdateAttachments()
end
end, function() end, "Reloads all TFA Attachments", {FCVAR_SERVER_CAN_EXECUTE})

View File

@@ -0,0 +1,415 @@
-- Degrees to accuracy vector, Valve's formula from SDK 2013
TFA.DegreesToAccuracy = math.sin((math.pi / 180) / 2) -- approx. 0.00873
--default cvar integration
local cv_gravity = GetConVar("sv_gravity")
--[[local function TimeScale(v)
return v * game.GetTimeScale() / TFA.Ballistics.SubSteps
end]]
--init code
TFA.Ballistics = TFA.Ballistics or {}
TFA.Ballistics.Enabled = false
TFA.Ballistics.Gravity = Vector(0, 0, -cv_gravity:GetFloat())
TFA.Ballistics.Bullets = TFA.Ballistics.Bullets or {}
TFA.Ballistics.Bullets.bullet_registry = TFA.Ballistics.Bullets.bullet_registry or {}
TFA.Ballistics.BulletLife = 10
TFA.Ballistics.UnitScale = TFA.UnitScale or 39.3701 --meters to inches
TFA.Ballistics.AirResistance = 1
TFA.Ballistics.WaterResistance = 3
TFA.Ballistics.WaterEntranceResistance = 6
TFA.Ballistics.DamageVelocityLUT = {
[13] = 350, --shotgun
[25] = 425, --mp5k etc.
[35] = 900, --ak-12
[65] = 830, --SVD
[120] = 1100 --sniper cap
}
TFA.Ballistics.VelocityMultiplier = 1
TFA.Ballistics.SubSteps = 1
TFA.Ballistics.BulletCreationNetString = "TFABallisticsBullet"
TFA.Ballistics.TracerStyles = {
[0] = "",
[1] = "tfa_bullet_smoke_tracer",
[2] = "tfa_bullet_fire_tracer"
}
setmetatable(TFA.Ballistics.TracerStyles, {
["__index"] = function(t, k) return t[math.Round(tonumber(k) or 1)] or t[1] end
})
if SERVER then
util.AddNetworkString(TFA.Ballistics.BulletCreationNetString)
end
--bullet class
local function IncludeClass(fn)
include("tfa/ballistics/" .. fn .. ".lua")
AddCSLuaFile("tfa/ballistics/" .. fn .. ".lua")
end
IncludeClass("bullet")
--cvar code
local function CreateReplConVar(cvarname, cvarvalue, description, ...)
return CreateConVar(cvarname, cvarvalue, CLIENT and {FCVAR_REPLICATED} or {FCVAR_REPLICATED, FCVAR_ARCHIVE, FCVAR_NOTIFY}, description, ...)
end -- replicated only on clients, archive/notify on server
local cv_enabled = CreateReplConVar("sv_tfa_ballistics_enabled", "0", "Enable TFA Ballistics?")
local cv_bulletlife = CreateReplConVar("sv_tfa_ballistics_bullet_life", 10, "Time to process bullets before removing.")
local cv_res_air = CreateReplConVar("sv_tfa_ballistics_bullet_damping_air", 1, "Air resistance, which makes bullets arc faster.")
local cv_res_water = CreateReplConVar("sv_tfa_ballistics_bullet_damping_water", 3, "Water resistance, which makes bullets arc faster in water.")
local cv_vel = CreateReplConVar("sv_tfa_ballistics_bullet_velocity", 1, "Global velocity multiplier for TFA ballistics bullets.")
local cv_substep = CreateReplConVar("sv_tfa_ballistics_substeps", 1, "Substeps for ballistics; more is more precise, at the cost of performance.")
local sv_tfa_ballistics_custom_gravity = CreateReplConVar("sv_tfa_ballistics_custom_gravity", 0, "Enable sv_gravity override for ballistics")
local sv_tfa_ballistics_custom_gravity_value = CreateReplConVar("sv_tfa_ballistics_custom_gravity_value", 0, "Z velocity down of custom gravity")
CreateReplConVar("sv_tfa_ballistics_mindist", -1, "Minimum distance to activate; -1 for always.")
local function updateCVars()
TFA.Ballistics.BulletLife = cv_bulletlife:GetFloat()
TFA.Ballistics.AirResistance = cv_res_air:GetFloat()
TFA.Ballistics.WaterResistance = cv_res_water:GetFloat()
TFA.Ballistics.WaterEntranceResistance = TFA.Ballistics.WaterResistance * 2
TFA.Ballistics.VelocityMultiplier = cv_vel:GetFloat()
if sv_tfa_ballistics_custom_gravity:GetBool() then
TFA.Ballistics.Gravity.z = -sv_tfa_ballistics_custom_gravity_value:GetFloat()
else
TFA.Ballistics.Gravity.z = -cv_gravity:GetFloat()
end
TFA.Ballistics.Enabled = cv_enabled:GetBool()
TFA.Ballistics.SubSteps = cv_substep:GetInt()
end
cvars.AddChangeCallback("sv_tfa_ballistics_enabled", updateCVars, "TFA")
cvars.AddChangeCallback("sv_tfa_ballistics_bullet_life", updateCVars, "TFA")
cvars.AddChangeCallback("sv_tfa_ballistics_bullet_damping_air", updateCVars, "TFA")
cvars.AddChangeCallback("sv_tfa_ballistics_bullet_damping_water", updateCVars, "TFA")
cvars.AddChangeCallback("sv_tfa_ballistics_bullet_velocity", updateCVars, "TFA")
cvars.AddChangeCallback("sv_tfa_ballistics_substeps", updateCVars, "TFA")
cvars.AddChangeCallback("sv_tfa_ballistics_mindist", updateCVars, "TFA")
cvars.AddChangeCallback("sv_tfa_ballistics_custom_gravity", updateCVars, "TFA")
cvars.AddChangeCallback("sv_tfa_ballistics_custom_gravity_value", updateCVars, "TFA")
cvars.AddChangeCallback("sv_gravity", updateCVars, "TFA Ballistics")
updateCVars()
--client cvar code
local cv_receive, cv_tracers_style, cv_tracers_mp
if CLIENT then
cv_receive = CreateClientConVar("cl_tfa_ballistics_mp", "1", true, false, "Receive bullet data from other players?")
CreateClientConVar("cl_tfa_ballistics_fx_bullet", "1", true, false, "Display bullet models for each TFA ballistics bullet?")
cv_tracers_style = CreateClientConVar("cl_tfa_ballistics_fx_tracers_style", "1", true, false, "Style of tracers for TFA ballistics? 0=disable,1=smoke")
cv_tracers_mp = CreateClientConVar("cl_tfa_ballistics_fx_tracers_mp", "1", true, false, "Enable tracers for other TFA ballistics users?")
CreateClientConVar("cl_tfa_ballistics_fx_tracers_adv", "1", true, false, "Enable advanced tracer calculations for other users? This corrects smoke trail to their barrel")
end
--utility func
local function Remap(inp, u, v, x, y)
return (inp - u) / (v - u) * (y - x) + x
end
--Accessors
local CopyTable = table.Copy
function TFA.Ballistics.Bullets:Add(bulletStruct, originalBulletData)
local bullet = TFA.Ballistics:Bullet(bulletStruct)
bullet.bul = CopyTable(originalBulletData or bullet.bul)
bullet.last_update = CurTime() - TFA.FrameTime()
table.insert(self.bullet_registry, bullet)
bullet:_setup()
if SERVER and game.GetTimeScale() > 0.99 then
-- always update bullet since they are being added from predicted hook
bullet:Update(CurTime())
end
end
function TFA.Ballistics.Bullets:Update(ply)
--local delta = TimeScale(SysTime() - (self.lastUpdate or (SysTime() - FrameTime())))
local delta = CurTime()
--self.lastUpdate = SysTime()
local toremove
local lply = CLIENT and LocalPlayer()
for i, bullet in ipairs(self.bullet_registry) do
if bullet.delete then
if not toremove then
toremove = {}
end
table.insert(toremove, i)
elseif not ply and not bullet.playerOwned or CLIENT and bullet.owner ~= lply or ply == bullet.owner then
for _ = 1, TFA.Ballistics.SubSteps do
bullet:Update(delta)
end
end
end
if toremove then
for i = #toremove, 1, -1 do
table.remove(self.bullet_registry, toremove[i])
end
end
end
function TFA.Ballistics:AutoDetectVelocity(damage)
local lutMin, lutMax, LUT, DMGs
LUT = self.DamageVelocityLUT
DMGs = table.GetKeys(LUT)
table.sort(DMGs)
for _, v in ipairs(DMGs) do
if v < damage then
lutMin = v
elseif lutMin then
lutMax = v
break
end
end
if not lutMax then
lutMax = DMGs[#DMGs]
lutMin = DMGs[#DMGs - 1]
elseif not lutMin then
lutMin = DMGs[1]
lutMax = DMGs[2]
end
return Remap(damage, lutMin, lutMax, LUT[lutMin], LUT[lutMax])
end
function TFA.Ballistics:ShouldUse(wep)
if not IsValid(wep) or not wep.IsTFAWeapon then
return false
end
local shouldUse = wep:GetStatL("UseBallistics")
if shouldUse == nil then
if wep:GetStatL("TracerPCF") then
return false
end
return self.Enabled
else
return shouldUse
end
end
local sv_tfa_recoil_legacy = GetConVar("sv_tfa_recoil_legacy")
function TFA.Ballistics:FireBullets(wep, bulletStruct, angIn, bulletOverride)
if not IsValid(wep) then return end
if not IsValid(wep:GetOwner()) then return end
local vel
if bulletStruct.Velocity then
vel = bulletStruct.Velocity
elseif wep.GetStat and wep:GetStatL("Primary.Velocity") then
vel = wep:GetStatL("Primary.Velocity") * TFA.Ballistics.UnitScale
elseif wep.Primary and wep.Primary.Velocity then
vel = wep.Primary.Velocity * TFA.Ballistics.UnitScale
elseif wep.Velocity then
vel = wep.Velocity * TFA.Ballistics.UnitScale
else
local dmg
if wep.GetStat and wep:GetStatL("Primary.Damage") then
dmg = wep:GetStatL("Primary.Damage")
else
dmg = wep.Primary.Damage or wep.Damage or 30
end
vel = TFA.Ballistics:AutoDetectVelocity(dmg) * TFA.Ballistics.UnitScale
end
vel = vel * (TFA.Ballistics.VelocityMultiplier or 1)
local oldNum = bulletStruct.Num
bulletStruct.Num = 1
bulletStruct.IsBallistics = true
local owner = wep:GetOwner()
local isnpc = owner:IsNPC()
local ac = bulletStruct.Spread
local sharedRandomSeed = "Ballistics" .. CurTime()
for i = 1, oldNum do
local ang
if angIn then
ang = angIn
else
ang = owner:GetAimVector():Angle()
if sv_tfa_recoil_legacy:GetBool() and not isnpc then
ang:Add(owner:GetViewPunchAngles())
else
ang.p = ang.p + wep:GetViewPunchP()
ang.y = ang.y + wep:GetViewPunchY()
end
end
if not angIn then
ang:RotateAroundAxis(ang:Up(), util.SharedRandom(sharedRandomSeed, -ac.x * 45, ac.x * 45, 0 + i))
ang:RotateAroundAxis(ang:Right(), util.SharedRandom(sharedRandomSeed, -ac.y * 45, ac.y * 45, 1 + i))
end
local struct = {
owner = owner, --used for dmginfo SetAttacker
inflictor = wep, --used for dmginfo SetInflictor
damage = bulletStruct.Damage, --floating point number representing inflicted damage
force = bulletStruct.Force,
pos = bulletOverride and bulletStruct.Src or owner:GetShootPos(), --b.Src, --vector representing current position
velocity = (bulletOverride and bulletStruct.Dir or ang:Forward()) * vel, --b.Dir * vel, --vector representing movement velocity
model = wep.BulletModel or bulletStruct.Model, --optional variable representing the given model
smokeparticle = bulletStruct.SmokeParticle,
customPosition = bulletStruct.CustomPosition or bulletOverride,
IgnoreEntity = bulletStruct.IgnoreEntity
}
if CLIENT then
if not struct.smokeparticle then
struct.smokeparticle = TFA.Ballistics.TracerStyles[cv_tracers_style:GetInt()]
end
end
self.Bullets:Add(struct, bulletStruct)
if SERVER then
net.Start(TFA.Ballistics.BulletCreationNetString)
net.WriteEntity(struct.owner)
net.WriteEntity(struct.inflictor)
net.WriteFloat(struct.damage)
net.WriteFloat(struct.force)
net.WriteVector(struct.pos)
net.WriteDouble(struct.velocity.x)
net.WriteDouble(struct.velocity.y)
net.WriteDouble(struct.velocity.z)
net.WriteString(struct.model or '')
net.WriteString(struct.smokeparticle or '')
net.WriteBool(struct.customPosition == true)
net.WriteEntity(struct.IgnoreEntity or NULL)
net.WriteVector(bulletStruct.Src)
net.WriteNormal(bulletStruct.Dir)
net.WriteEntity(bulletStruct.Attacker)
net.WriteVector(bulletStruct.Spread)
net.WriteFloat(vel)
if game.SinglePlayer() or isnpc then
net.SendPVS(struct.pos)
else
net.SendOmit(owner)
end
end
end
end
function TFA.Ballistics.Bullets:Render()
for i = 1, #self.bullet_registry do
self.bullet_registry[i]:Render()
end
end
local sp = game.SinglePlayer()
--Netcode and Hooks
if CLIENT then
net.Receive(TFA.Ballistics.BulletCreationNetString, function()
if not sp and not cv_receive:GetBool() then return end
local owner = net.ReadEntity()
local inflictor = net.ReadEntity()
local damage = net.ReadFloat()
local force = net.ReadFloat()
local pos = net.ReadVector()
local velocity = Vector(net.ReadDouble(), net.ReadDouble(), net.ReadDouble())
local model = net.ReadString()
local smokeparticle = net.ReadString()
local customPosition = net.ReadBool()
local IgnoreEntity = net.ReadEntity()
local Src = net.ReadVector()
local Dir = net.ReadNormal()
local Attacker = net.ReadEntity()
local Spread = net.ReadVector()
local Velocity = net.ReadFloat()
if not IsValid(owner) or not IsValid(inflictor) then return end
if not cv_tracers_mp:GetBool() and owner ~= LocalPlayer() then
smokeparticle = ""
elseif smokeparticle == "" then
smokeparticle = TFA.Ballistics.TracerStyles[cv_tracers_style:GetInt()]
end
local struct = {
owner = owner,
inflictor = inflictor,
damage = damage,
force = force,
pos = pos,
velocity = velocity,
model = model ~= "" and model or nil,
smokeparticle = smokeparticle,
customPosition = customPosition,
IgnoreEntity = IgnoreEntity,
}
local bulletStruct = {
Damage = damage,
Force = force,
Num = 1,
Src = Src,
Dir = Dir,
Attacker = Attacker,
Spread = Spread,
SmokeParticle = smokeparticle,
CustomPosition = customPosition,
Model = model ~= "" and model or nil,
Velocity = Velocity,
IsBallistics = true,
}
TFA.Ballistics.Bullets:Add(struct, bulletStruct)
end)
end
if CLIENT then
hook.Add("FinishMove", "TFABallisticsTick", function(self)
if IsFirstTimePredicted() then
TFA.Ballistics.Bullets:Update(self)
end
end)
else
hook.Add("PlayerPostThink", "TFABallisticsTick", function(self)
TFA.Ballistics.Bullets:Update(self)
end)
end
hook.Add("Tick", "TFABallisticsTick", function()
TFA.Ballistics.Bullets:Update()
if CLIENT and sp then
TFA.Ballistics.Bullets:Update(LocalPlayer())
end
end)
--Rendering
hook.Add("PostDrawOpaqueRenderables", "TFABallisticsRender", function()
TFA.Ballistics.Bullets:Render()
end)

View File

@@ -0,0 +1,15 @@
local sp = game.SinglePlayer()
hook.Add("PlayerSwitchWeapon", "TFA_Bodygroups_PSW", function(ply, oldwep, wep)
if not IsValid(wep) or not wep.IsTFAWeapon then return end
timer.Simple(0, function()
if not IsValid(ply) or ply:GetActiveWeapon() ~= wep then return end
wep:ApplyViewModelModifications()
if sp then
wep:CallOnClient("ApplyViewModelModifications")
end
end)
end)

View File

@@ -0,0 +1,372 @@
local function CreateReplConVar(cvarname, cvarvalue, description, ...)
return CreateConVar(cvarname, cvarvalue, CLIENT and {FCVAR_REPLICATED} or {FCVAR_REPLICATED, FCVAR_ARCHIVE, FCVAR_NOTIFY}, description, ...)
end -- replicated only on clients, archive/notify on server
-- Shared Convars
if GetConVar("sv_tfa_soundscale") == nil then
CreateReplConVar("sv_tfa_soundscale", "1", "Scale sound pitch in accordance to timescale?")
end
if GetConVar("sv_tfa_weapon_strip") == nil then
CreateReplConVar("sv_tfa_weapon_strip", "0", "Allow the removal of empty weapons?")
end
if GetConVar("sv_tfa_spread_legacy") == nil then
CreateReplConVar("sv_tfa_spread_legacy", "0", "Use legacy spread algorithms?")
end
if GetConVar("sv_tfa_cmenu") == nil then
CreateReplConVar("sv_tfa_cmenu", "1", "Allow custom context menu?")
end
if GetConVar("sv_tfa_cmenu_key") == nil then
CreateReplConVar("sv_tfa_cmenu_key", "-1", "Override the inspection menu key? Uses the KEY enum available on the gmod wiki. -1 to not.")
end
if GetConVar("sv_tfa_range_modifier") == nil then
CreateReplConVar("sv_tfa_range_modifier", "0.5", "This controls how much the range affects damage. 0.5 means the maximum loss of damage is 0.5.")
end
if GetConVar("sv_tfa_allow_dryfire") == nil then
CreateReplConVar("sv_tfa_allow_dryfire", "1", "Allow dryfire?")
end
if GetConVar("sv_tfa_penetration_hardlimit") == nil then
CreateReplConVar("sv_tfa_penetration_hardlimit", "100", "Max number of objects we can penetrate through.")
end
if GetConVar("sv_tfa_bullet_penetration_power_mul") == nil then
CreateReplConVar("sv_tfa_bullet_penetration_power_mul", "1", "Power multiplier. 1 or 1.5 for CS 1.6 experience, 0.25 for semi-realistic behavior")
end
if GetConVar("sv_tfa_penetration_hitmarker") == nil then
CreateReplConVar("sv_tfa_penetration_hitmarker", "1", "Should penetrating bullet send hitmarker to attacker?")
end
if GetConVar("sv_tfa_damage_multiplier") == nil then
CreateReplConVar("sv_tfa_damage_multiplier", "1", "Multiplier for TFA base projectile damage.")
end
if GetConVar("sv_tfa_damage_multiplier_npc") == nil then
CreateReplConVar("sv_tfa_damage_multiplier_npc", "1", "Multiplier for TFA base projectile damage for NPCs.")
end
if GetConVar("sv_tfa_damage_mult_min") == nil then
CreateReplConVar("sv_tfa_damage_mult_min", "0.95", "This is the lower range of a random damage factor.")
end
if GetConVar("sv_tfa_damage_mult_max") == nil then
CreateReplConVar("sv_tfa_damage_mult_max", "1.05", "This is the higher range of a random damage factor.")
end
if GetConVar("sv_tfa_melee_damage_npc") == nil then
CreateReplConVar("sv_tfa_melee_damage_npc", "1", "Damage multiplier against NPCs using TFA Melees.")
end
if GetConVar("sv_tfa_melee_damage_ply") == nil then
CreateReplConVar("sv_tfa_melee_damage_ply", "0.65", "Damage multiplier against players using TFA Melees.")
end
if GetConVar("sv_tfa_melee_blocking_timed") == nil then
CreateReplConVar("sv_tfa_melee_blocking_timed", "1", "Enable timed blocking?")
end
if GetConVar("sv_tfa_melee_blocking_anglemult") == nil then
CreateReplConVar("sv_tfa_melee_blocking_anglemult", "1", "Players can block attacks in an angle around their view. This multiplies that angle.")
end
if GetConVar("sv_tfa_melee_blocking_deflection") == nil then
CreateReplConVar("sv_tfa_melee_blocking_deflection", "1", "For weapons that can deflect bullets ( e.g. certain katans ), can you deflect bullets? Set to 1 to enable for parries, or 2 for all blocks.")
end
if GetConVar("sv_tfa_melee_blocking_timed") == nil then
CreateReplConVar("sv_tfa_melee_blocking_timed", "1", "Enable timed blocking?")
end
if GetConVar("sv_tfa_melee_blocking_stun_enabled") == nil then
CreateReplConVar("sv_tfa_melee_blocking_stun_enabled", "1", "Stun NPCs on block?")
end
if GetConVar("sv_tfa_melee_blocking_stun_time") == nil then
CreateReplConVar("sv_tfa_melee_blocking_stun_time", "0.65", "How long to stun NPCs on block.")
end
if GetConVar("sv_tfa_melee_doordestruction") == nil then
CreateReplConVar("sv_tfa_melee_doordestruction", "1", "Allow players to bash open doors?")
end
if GetConVar("sv_tfa_door_respawn") == nil then
CreateReplConVar("sv_tfa_door_respawn", "-1", "Time for doors to respawn; -1 for never.")
end
if GetConVar("sv_tfa_npc_randomize_atts") == nil then
CreateReplConVar("sv_tfa_npc_randomize_atts", "1", "Randomize NPC's weapons attachments.")
end
local cv_dfc
if GetConVar("sv_tfa_default_clip") == nil then
cv_dfc = CreateReplConVar("sv_tfa_default_clip", "-1", "How many clips will a weapon spawn with? Negative reverts to default values.")
else
cv_dfc = GetConVar("sv_tfa_default_clip")
end
local function TFAUpdateDefaultClip()
local dfc = cv_dfc:GetInt()
local weplist = weapons.GetList()
if not weplist or #weplist <= 0 then return end
for _, v in pairs(weplist) do
local cl = v.ClassName and v.ClassName or v
local wep = weapons.GetStored(cl)
if wep and (wep.IsTFAWeapon or string.find(string.lower(wep.Base and wep.Base or ""), "tfa")) then
if not wep.Primary then
wep.Primary = {}
end
if not wep.Primary.TrueDefaultClip then
wep.Primary.TrueDefaultClip = wep.Primary.DefaultClip
end
if not wep.Primary.TrueDefaultClip then
wep.Primary.TrueDefaultClip = 0
end
if dfc < 0 then
wep.Primary.DefaultClip = wep.Primary.TrueDefaultClip
else
if wep.Primary.ClipSize and wep.Primary.ClipSize > 0 then
wep.Primary.DefaultClip = wep.Primary.ClipSize * dfc
else
wep.Primary.DefaultClip = wep.Primary.TrueDefaultClip * 1
end
end
end
end
end
hook.Add("InitPostEntity", "TFADefaultClipPE", TFAUpdateDefaultClip)
if TFAUpdateDefaultClip then
TFAUpdateDefaultClip()
end
--if GetConVar("sv_tfa_default_clip") == nil then
cvars.AddChangeCallback("sv_tfa_default_clip", function(convar_name, value_old, value_new)
TFAUpdateDefaultClip()
end, "TFAUpdateDefaultClip")
local function sv_tfa_range_modifier()
for k, v in ipairs(ents.GetAll()) do
if v.IsTFAWeapon and v.Primary_TFA.RangeFalloffLUT_IsConverted then
v.Primary_TFA.RangeFalloffLUT = nil
v:AutoDetectRange()
end
end
end
cvars.AddChangeCallback("sv_tfa_range_modifier", sv_tfa_range_modifier, "TFA")
sv_tfa_range_modifier()
if CLIENT then
hook.Add("InitPostEntity", "sv_tfa_range_modifier", sv_tfa_range_modifier)
end
--end
if GetConVar("sv_tfa_unique_slots") == nil then
CreateReplConVar("sv_tfa_unique_slots", "1", "Give TFA-based Weapons unique slots? 1 for true, 0 for false. RESTART AFTER CHANGING.")
end
if GetConVar("sv_tfa_spread_multiplier") == nil then
CreateReplConVar("sv_tfa_spread_multiplier", "1", "Increase for more spread, decrease for less.")
end
if GetConVar("sv_tfa_force_multiplier") == nil then
CreateReplConVar("sv_tfa_force_multiplier", "1", "Arrow force multiplier (not arrow velocity, but how much force they give on impact).")
end
if GetConVar("sv_tfa_recoil_multiplier") == nil then
CreateReplConVar("sv_tfa_recoil_multiplier", "1", "Recoil multiplier")
end
if GetConVar("sv_tfa_knockback_multiplier") == nil then
CreateReplConVar("sv_tfa_knockback_multiplier", "1", "Knockback force multiplier")
end
if GetConVar("sv_tfa_dynamicaccuracy") == nil then
CreateReplConVar("sv_tfa_dynamicaccuracy", "1", "Dynamic acuracy? (e.g.more accurate on crouch, less accurate on jumping.")
end
if GetConVar("sv_tfa_ammo_detonation") == nil then
CreateReplConVar("sv_tfa_ammo_detonation", "1", "Ammo Detonation? (e.g. shoot ammo until it explodes) ")
end
if GetConVar("sv_tfa_ammo_detonation_mode") == nil then
CreateReplConVar("sv_tfa_ammo_detonation_mode", "2", "Ammo Detonation Mode? (0=Bullets,1=Blast,2=Mix) ")
end
if GetConVar("sv_tfa_ammo_detonation_chain") == nil then
CreateReplConVar("sv_tfa_ammo_detonation_chain", "1", "Ammo Detonation Chain? (0=Ammo boxes don't detonate other ammo boxes, 1 you can chain them together) ")
end
if GetConVar("sv_tfa_scope_gun_speed_scale") == nil then
CreateReplConVar("sv_tfa_scope_gun_speed_scale", "0", "Scale player sensitivity based on player move speed?")
end
if GetConVar("sv_tfa_bullet_penetration") == nil then
CreateReplConVar("sv_tfa_bullet_penetration", "1", "Allow bullet penetration?")
end
if GetConVar("sv_tfa_bullet_doordestruction") == nil then
CreateReplConVar("sv_tfa_bullet_doordestruction", "1", "Allow to shoot down doors?")
end
if GetConVar("sv_tfa_bullet_doordestruction_keep") == nil then
CreateReplConVar("sv_tfa_bullet_doordestruction_keep", "0", "Don't shoot door off hinges")
end
if GetConVar("sv_tfa_npc_burst") == nil then
CreateReplConVar("sv_tfa_npc_burst", "0", "Whenever NPCs should fire in bursts like they do with HL2 weapons.")
end
if GetConVar("sv_tfa_bullet_ricochet") == nil then
CreateReplConVar("sv_tfa_bullet_ricochet", "0", "Allow bullet ricochet?")
end
if GetConVar("sv_tfa_bullet_randomseed") == nil then
CreateReplConVar("sv_tfa_bullet_randomseed", "0", "Populate extra seed serverside? This will cause spread to be out of sync with server!")
end
if GetConVar("sv_tfa_debug") == nil then
CreateReplConVar("sv_tfa_debug", "0", "Enable debug mode?")
end
if GetConVar("sv_tfa_holdtype_dynamic") == nil then
CreateReplConVar("sv_tfa_holdtype_dynamic", "1", "Allow dynamic holdtype?")
end
if GetConVar("sv_tfa_arrow_lifetime") == nil then
CreateReplConVar("sv_tfa_arrow_lifetime", "30", "Arrow lifetime.")
end
if GetConVar("sv_tfa_worldmodel_culldistance") == nil then
CreateReplConVar("sv_tfa_worldmodel_culldistance", "-1", "-1 to leave unculled. Anything else is feet*16.")
end
if GetConVar("sv_tfa_reloads_legacy") == nil then
CreateReplConVar("sv_tfa_reloads_legacy", "0", "Enable legacy-style reloading?")
end
if GetConVar("sv_tfa_recoil_legacy") == nil then
CreateReplConVar("sv_tfa_recoil_legacy", "0", "Enable legacy-style recoil? This will cause prediction issues in multiplayer. Always disabled for NPCs!")
end
if GetConVar("sv_tfa_recoil_mul_p") == nil then
CreateReplConVar("sv_tfa_recoil_mul_p", "1", "Pitch kick multiplier for recoil")
end
if GetConVar("sv_tfa_recoil_mul_y") == nil then
CreateReplConVar("sv_tfa_recoil_mul_y", "1", "Yaw kick multiplier for recoil")
end
if GetConVar("sv_tfa_recoil_mul_p_npc") == nil then
CreateReplConVar("sv_tfa_recoil_mul_p_npc", "1", "Pitch kick multiplier for recoil for NPCs")
end
if GetConVar("sv_tfa_recoil_mul_y_npc") == nil then
CreateReplConVar("sv_tfa_recoil_mul_y_npc", "1", "Yaw kick multiplier for recoil for NPCs")
end
if GetConVar("sv_tfa_recoil_viewpunch_mul") == nil then
CreateReplConVar("sv_tfa_recoil_viewpunch_mul", "1", "Multiplier for viewpunch recoil (visual viewmodel recoil)")
end
if GetConVar("sv_tfa_recoil_eyeangles_mul") == nil then
CreateReplConVar("sv_tfa_recoil_eyeangles_mul", "1", "Multiplier for eye angles recoil (real angle change recoil)")
end
if GetConVar("sv_tfa_fx_penetration_decal") == nil then
CreateReplConVar("sv_tfa_fx_penetration_decal", "1", "Enable decals on the other side of a penetrated object?")
end
local cv_ironsights = GetConVar("sv_tfa_ironsights_enabled")
if cv_ironsights == nil then
cv_ironsights = CreateReplConVar("sv_tfa_ironsights_enabled", "1", "Enable ironsights? Disabling this still allows scopes.")
end
local is_stats = {
["data.ironsights"] = 0,
["Secondary.IronSightsEnabled"] = false,
}
hook.Add("TFA_GetStat", "TFA_IronsightsConVarToggle", function(wep, stat, val)
if not IsValid(wep) or is_stats[stat] == nil then return end
if not cv_ironsights:GetBool() and not wep:GetStatRawL("Scoped") and not wep:GetStatRawL("Scoped_3D") then
return is_stats[stat]
end
end)
if GetConVar("sv_tfa_sprint_enabled") == nil then
CreateReplConVar("sv_tfa_sprint_enabled", "1", "Enable sprinting? Disabling this allows shooting while IN_SPEED.")
end
if GetConVar("sv_tfa_reloads_enabled") == nil then
CreateReplConVar("sv_tfa_reloads_enabled", "1", "Enable reloading? Disabling this allows shooting from ammo pool.")
end
if GetConVar("sv_tfa_attachments_enabled") == nil then
CreateReplConVar("sv_tfa_attachments_enabled", "1", "Display attachment picker?")
end
if GetConVar("sv_tfa_attachments_alphabetical") == nil then
CreateReplConVar("sv_tfa_attachments_alphabetical", "0", "Override weapon attachment order to be alphabetical.")
end
if GetConVar("sv_tfa_jamming") == nil then
CreateReplConVar("sv_tfa_jamming", "1", "Enable jamming mechanics?")
end
if GetConVar("sv_tfa_jamming_mult") == nil then
CreateReplConVar("sv_tfa_jamming_mult", "1", "Multiply jam chance by this value. You really should modify sv_tfa_jamming_factor_inc rather than this.")
end
if GetConVar("sv_tfa_jamming_factor") == nil then
CreateReplConVar("sv_tfa_jamming_factor", "1", "Multiply jam factor by this value")
end
if GetConVar("sv_tfa_jamming_factor_inc") == nil then
CreateReplConVar("sv_tfa_jamming_factor_inc", "1", "Multiply jam factor gain by this value")
end
if GetConVar("sv_tfa_nearlyempty") == nil then
CreateReplConVar("sv_tfa_nearlyempty", "1", "Enable nearly-empty sounds")
end
if GetConVar("sv_tfa_fixed_crosshair") == nil then
CreateReplConVar("sv_tfa_fixed_crosshair", "0", "Fix crosshair position on center of the screen (CS:GO style)")
end
if GetConVar("sv_tfa_crosshair_showplayer") == nil then
CreateReplConVar("sv_tfa_crosshair_showplayer", "1", "Crosshair team color option reveals players")
end
if GetConVar("sv_tfa_crosshair_showplayerteam") == nil then
CreateReplConVar("sv_tfa_crosshair_showplayerteam", engine.ActiveGamemode() == "terrortown" and "0" or "1", "Crosshair team color option reveals players's team")
end
if GetConVar("sv_tfa_weapon_weight") == nil then
CreateReplConVar("sv_tfa_weapon_weight", "1", "Disabling this WILL break certain SWEPs and Mechanics. You were warned.")
end
if GetConVar("sv_tfa_fov_sprintmod") == nil then
CreateReplConVar("sv_tfa_fov_sprintmod", "1", "Enable sprint FOV modification")
end
if GetConVar("sv_tfa_first_draw_anim_enabled") == nil then
CreateReplConVar("sv_tfa_first_draw_anim_enabled", "1", "Enable first draw animation")
end

View File

@@ -0,0 +1,26 @@
local TFA_PocketBlock = {}
TFA_PocketBlock["tfa_ammo_357"] = true
TFA_PocketBlock["tfa_ammo_ar2"] = true
TFA_PocketBlock["tfa_ammo_buckshot"] = true
TFA_PocketBlock["tfa_ammo_c4"] = true
TFA_PocketBlock["tfa_ammo_frags"] = true
TFA_PocketBlock["tfa_ammo_ieds"] = true
TFA_PocketBlock["tfa_ammo_nervegas"] = true
TFA_PocketBlock["tfa_ammo_nuke"] = true
TFA_PocketBlock["tfa_ammo_pistol"] = true
TFA_PocketBlock["tfa_ammo_proxmines"] = true
TFA_PocketBlock["tfa_ammo_rockets"] = true
TFA_PocketBlock["tfa_ammo_smg"] = true
TFA_PocketBlock["tfa_ammo_smg1_grenade"] = true
TFA_PocketBlock["tfa_ammo_smg1_grenade_large"] = true
TFA_PocketBlock["tfa_ammo_sniper_rounds"] = true
TFA_PocketBlock["tfa_ammo_stickynades"] = true
TFA_PocketBlock["tfa_ammo_winchester"] = true
local function TFA_PockBlock(ply, wep) --Get it, because cockblock, hehe..... so mature.
if not IsValid(wep) then return end
local class = wep:GetClass()
if TFA_PocketBlock[class] then return false end
end
hook.Add("canPocket", "TFA_PockBlock", TFA_PockBlock)

View File

@@ -0,0 +1,566 @@
-- This file is holding seamless translation of older versions of data to newer
-- versions of data
TFA.LatestDataVersion = 1
TFA.DataVersionMapping = {
[0] = {
{
old_path = "DrawCrosshairIS",
new_path = "DrawCrosshairIronSights",
},
{
old_path = "FiresUnderwater",
new_path = "Primary.FiresUnderwater",
},
{
old_path = "PenetrationMaterials",
new_path = "Primary.PenetrationMaterials",
},
{
old_path = "MaxPenetrationCounter",
new_path = "Primary.MaxSurfacePenetrationCount",
},
{
old_path = "MaxPenetration",
new_path = "Primary.MaxSurfacePenetrationCount",
},
{
old_path = "IronRecoilMultiplier",
new_path = "Primary.IronRecoilMultiplier",
},
{
old_path = "MoveSpeed",
new_path = "RegularMoveSpeedMultiplier",
},
{
old_path = "IronSightsMoveSpeed",
new_path = "AimingDownSightsSpeedMultiplier",
},
{
old_path = "Shotgun",
new_path = "LoopedReload",
},
{
old_path = "ShellTime",
new_path = "LoopedReloadInsertTime",
},
{
old_path = "CrouchPos",
new_path = "CrouchViewModelPosition",
},
{
old_path = "CrouchAng",
new_path = "CrouchViewModelAngle",
},
{
old_path = "data.ironsights",
new_path = "Secondary.IronSightsEnabled",
upgrade = function(value) return value == 1 end,
downgrade = function(value) return value and 1 or 0 end,
},
{
old_path = "Secondary.IronFOV",
new_path = "Secondary.OwnerFOV",
},
{
old_path = "IronViewModelFOV",
new_path = "Secondary.ViewModelFOV",
},
{
old_path = "DoProceduralReload",
new_path = "IsProceduralReloadBased",
},
{
old_path = "ProceduralReloadEnabled",
new_path = "IsProceduralReloadBased",
},
{
old_path = "Akimbo",
new_path = "IsAkimbo",
},
{
old_path = "AkimboHUD",
new_path = "EnableAkimboHUD",
},
{
old_path = "IronInSound",
new_path = "Secondary.IronSightsInSound",
},
{
old_path = "IronOutSound",
new_path = "Secondary.IronSightsOutSound",
},
{
old_path = "DisableChambering",
new_path = "Primary.DisableChambering",
},
{
old_path = "DisplayFalloff",
new_path = "Primary.DisplayFalloff",
},
{
old_path = "SpreadBiasYaw",
new_path = "Primary.SpreadBiasYaw",
},
{
old_path = "SpreadBiasPitch",
new_path = "Primary.SpreadBiasPitch",
},
{
old_path = "VMPos",
new_path = "ViewModelPosition",
},
{
old_path = "VMAng",
new_path = "ViewModelAngle",
},
{
old_path = "VMPos_Additive",
new_path = "AdditiveViewModelPosition",
},
{
old_path = "RunSightsPos",
new_path = "SprintViewModelPosition",
},
{
old_path = "RunSightsAng",
new_path = "SprintViewModelAngle",
},
{
old_path = "IronSightsPos",
new_path = "IronSightsPosition",
},
{
old_path = "IronSightsAng",
new_path = "IronSightsAngle",
},
{
old_path = "Bodygroups_V",
new_path = "ViewModelBodygroups",
},
{
old_path = "Bodygroups_W",
new_path = "WorldModelBodygroups",
},
{
old_path = "CenteredPos",
new_path = "CenteredViewModelPosition",
},
{
old_path = "CenteredAng",
new_path = "CenteredViewModelAngle",
},
{
old_path = "Offset",
new_path = "WorldModelOffset",
},
{
old_path = "ProceduralHolsterPos",
new_path = "ProceduralHolsterPosition",
},
{
old_path = "ProceduralHolsterAng",
new_path = "ProceduralHolsterAngle",
},
{
old_path = "VElements",
new_path = "ViewModelElements",
},
{
old_path = "WElements",
new_path = "WorldModelElements",
},
}
}
local function identity(...) return ... end
for version = 0, #TFA.DataVersionMapping do
for i, data in ipairs(TFA.DataVersionMapping[version]) do
if not isfunction(data.upgrade) then data.upgrade = identity end
if not isfunction(data.downgrade) then data.downgrade = identity end
end
end
TFA.PathParseCache = {}
TFA.PathParseCacheTR = {}
TFA.StatPathRemapCache = {}
TFA.PathParseCacheDirect = {}
TFA.PathParseChildren = {}
local PathParseCache = TFA.PathParseCache
local PathParseCacheTR = TFA.PathParseCacheTR
local PathParseCacheDirect = TFA.PathParseCacheDirect
local StatPathRemapCache = TFA.StatPathRemapCache
local PathParseChildren = TFA.PathParseChildren
local string_Explode = string.Explode
local ipairs = ipairs
local pairs = pairs
local string_sub = string.sub
local tonumber = tonumber
local table_Copy = table.Copy
local table_concat = table.concat
local istable = istable
local string_format = string.format
local function doDowngrade(path, migrations)
for i, data in ipairs(migrations) do
if data.new_path == path then
return data.old_path, data.upgrade
elseif path:StartWith(data.new_path) and path[#data.new_path + 1] == '.' then
return data.old_path .. path:sub(#data.new_path + 1), data.upgrade
end
end
return path
end
local function doUpgrade(path, migrations)
for i, data in ipairs(migrations) do
if data.old_path == path then
return data.new_path, data.downgrade
elseif path:StartWith(data.old_path) and path[#data.old_path + 1] == '.' then
return data.new_path .. path:sub(#data.old_path + 1), data.downgrade
end
end
return path
end
function TFA.RemapStatPath(path, path_version, structure_version)
local cache_path = path
if path_version == nil then path_version = 0 end
if structure_version == nil then structure_version = 0 end
-- version do not match
if path_version ~= structure_version then
cache_path = string_format("%d_%d_%s", path_version, structure_version, path)
end
local get_cache = StatPathRemapCache[cache_path]
if get_cache ~= nil then return get_cache end
if cache_path ~= path then
-- downgrade path
if path_version > structure_version then
for version = path_version, structure_version, -1 do
local mapping = TFA.DataVersionMapping[version]
if istable(mapping) then
path = doDowngrade(path, mapping)
end
end
else -- upgrade path
for version = path_version, structure_version do
local mapping = TFA.DataVersionMapping[version]
if istable(mapping) then
path = doUpgrade(path, mapping)
end
end
end
end
StatPathRemapCache[cache_path] = path
return StatPathRemapCache[cache_path]
end
function TFA.GetStatPathChildren(path, path_version, structure_version)
-- version do not match
if path_version ~= structure_version then
path = TFA.RemapStatPath(path, path_version, structure_version)
end
if not PathParseChildren[path] then
TFA.GetStatPath(path, path_version, structure_version)
end
return PathParseChildren[path].list
end
local function concat_to(tab, to)
local str = tab[1]
for i = 2, to do
str = str .. '.' .. tab[i]
end
return str
end
local function concat_from(tab, from)
local str = tab[from]
for i = from + 1, #tab do
str = str .. '.' .. tab[i]
end
return str
end
function TFA.GetStatPath(path, path_version, structure_version, no_translate)
local cache_path = path
if path_version == nil then path_version = 0 end
if structure_version == nil then structure_version = 0 end
-- version do not match
if path_version ~= structure_version then
cache_path = string_format("%d_%d_%s", path_version, structure_version, path)
end
local _PathParseCache = no_translate and PathParseCacheTR or PathParseCache
local get_cache = _PathParseCache[cache_path]
if get_cache ~= nil then return get_cache[1], get_cache[2], get_cache[3] end
local fn, fnGet
if cache_path ~= path then
-- downgrade
if path_version > structure_version then
for version = path_version, structure_version, -1 do
local mapping = TFA.DataVersionMapping[version]
if istable(mapping) then
path, fnGet = doDowngrade(path, mapping)
if fnGet and fnGet ~= identity then
if not fn then
fn = fnGet
else
local _fn = fn
function fn(...) return fnGet(_fn(...)) end
end
end
end
end
else -- upgrade
for version = path_version, structure_version do
local mapping = TFA.DataVersionMapping[version]
if istable(mapping) then
path, fnGet = doUpgrade(path, mapping)
if fnGet and fnGet ~= identity then
if not fn then
fn = fnGet
else
local _fn = fn
function fn(...) return fnGet(_fn(...)) end
end
end
end
end
end
end
get_cache = string_Explode(".", path, false)
do
local children = PathParseChildren[get_cache[1]]
if not children then
children = {
list = {get_cache[1]},
children = {}
}
PathParseChildren[get_cache[1]] = children
end
local childrens = {children}
for i = 2, #get_cache do
local obj = get_cache[i]
local path2 = concat_to(get_cache, i)
for i3 = 1, #childrens do
local list = childrens[i3].list
local hit = false
for i2 = 1, #list do
if list[i2] == path2 then
hit = true
break
end
end
if not hit then
table.insert(list, path2)
end
end
if not children.children[obj] then
children.children[obj] = {
list = {path2},
children = {}
}
end
if not PathParseChildren[path2] then
PathParseChildren[path2] = {
list = {path2},
children = {}
}
end
children = children.children[obj]
table.insert(childrens, children)
table.insert(childrens, PathParseChildren[path2])
end
end
if not no_translate then
if get_cache[1] == "Primary" then
get_cache[1] = "Primary_TFA"
elseif get_cache[1] == "Secondary" then
get_cache[1] = "Secondary_TFA"
end
end
for k, v in ipairs(get_cache) do
get_cache[k] = tonumber(v) or v
end
_PathParseCache[cache_path] = {get_cache, path, fn or identity}
return get_cache, path, fn or identity
end
function TFA.GetStatPathRaw(path)
local get_cache = PathParseCacheDirect[path]
if get_cache ~= nil then return get_cache end
local t_stbl = string_Explode(".", path, false)
for k, v in ipairs(t_stbl) do
t_stbl[k] = tonumber(v) or v
end
PathParseCacheDirect[path] = t_stbl
return t_stbl
end
local GetStatPathRaw = TFA.GetStatPathRaw
do
local function get(self, path)
local value = self[path[1]]
for i = 2, #path do
if not istable(value) then return end
value = value[path[i]]
end
return value
end
local function set(self, path, val)
if #path == 1 then
if self[path[1]] == nil then
self[path[1]] = val
end
return
end
local value = self[path[1]]
if value == nil then
self[path[1]] = {}
value = self[path[1]]
end
for i = 2, #path - 1 do
if not istable(value) then return end
if value[path[i]] == nil then value[path[i]] = {} end
value = value[path[i]]
end
if istable(value) and value[path[#path]] == nil then
value[path[#path]] = val
elseif not istable(value) then
print('[TFA Base] unable to fill gap for older version in meta structure of ' .. table_concat(path, '.'))
end
end
function TFA.FillMissingMetaValues(SWEP)
for version = TFA.LatestDataVersion, 0, -1 do
local mapping = TFA.DataVersionMapping[version]
if istable(mapping) then
for i, data in ipairs(mapping) do
local getVal = get(SWEP, GetStatPathRaw(data.new_path))
if getVal ~= nil then
set(SWEP, GetStatPathRaw(data.old_path), data.downgrade(getVal))
end
end
end
end
end
end
if CLIENT then
concommand.Add("cl_tfa_data_translatestat", function(ply, cmd, args, argStr)
if #args <= 0 then
print("Usage: " .. cmd .. " <stat name>")
return
end
for _, arg in ipairs(args) do
if string.StartsWith(arg, "SWEP.") or string.StartsWith(arg, "self.") then
arg = string.sub(arg, 6)
end
local _, path, _ = TFA.GetStatPath(arg, 0, TFA.LatestDataVersion)
if path then
print("SWEP." .. arg .. " => SWEP." .. path .. " (at data version " .. TFA.LatestDataVersion .. ")")
else
print("Unable to lookup updated stat path for " .. arg)
end
end
end)
end

View File

@@ -0,0 +1,32 @@
TFA.Effects = TFA.Effects or {}
local Effects = TFA.Effects
Effects.Overrides = Effects.Overrides or {}
local Overrides = Effects.Overrides
function Effects.AddOverride(target, override)
assert(type(target) == "string", "No target effect name or not a string")
assert(type(override) == "string", "No override effect name or not a string")
Overrides[target] = override
end
function Effects.GetOverride(target)
if Overrides[target] then
return Overrides[target]
end
return target
end
local util_Effect = util.Effect
function Effects.Create(effectName, effectData, allowOverride, ignorePredictionOrRecipientFilter)
effectName = Effects.GetOverride(effectName)
util_Effect(effectName, effectData, allowOverride, ignorePredictionOrRecipientFilter)
end
if SERVER then
AddCSLuaFile("tfa/muzzleflash_base.lua")
end

View File

@@ -0,0 +1,77 @@
local EmptyFunc = function() end
local debugInfoTbl = debug.getinfo(EmptyFunc)
local cv_do_check = CreateConVar("sv_tfa_envcheck", "1", {FCVAR_ARCHIVE, FCVAR_NOTIFY, FCVAR_REPLICATED}, "Enable environment sanity checks and warnings?")
local function checkEnv(plyIn)
if not cv_do_check:GetBool() then return end
local function printFunc(msg, ...)
msg = "[TFA Base] " .. msg
if chat and chat.AddText then
return chat.AddText(msg, ...)
end
return print(msg, ...)
end
if game.SinglePlayer() then
if CLIENT then
local found = false
for _, wepDefTable in ipairs(weapons.GetList()) do
if wepDefTable.Spawnable and weapons.IsBasedOn(wepDefTable.ClassName, "tfa_gun_base") then
found = true
break
end
end
if not found then
printFunc("Thank you for installing our weapons base! It appears that you have installed only the base itself, which does not include any weapons by default. Please install some weapons/packs that utilize TFA Base for full experience!")
end
end
local shortsrc = debugInfoTbl.short_src
if shortsrc:StartWith("addons") then -- legacy/unpacked addon
local addonRootFolder = shortsrc:GetPathFromFilename():Replace("lua/tfa/modules/", "")
if not file.Exists(addonRootFolder .. ".git", "GAME") then -- assume unpacked version by missing .git folder, which is ignored by gmad.exe
printFunc("You are using unpacked version of TFA Base.\nWe only provide support for Steam Workshop version.")
end
end
else
local activeGamemode = engine.ActiveGamemode()
local isRP = activeGamemode:find("rp")
or activeGamemode:find("roleplay")
or activeGamemode:find("serious")
if isRP and (SERVER or (IsValid(plyIn) and (plyIn:IsAdmin() or plyIn:IsSuperAdmin()))) then
print("You are running the base on DarkRP or DarkRP-derived gamemode. We can't guarantee that it will work correctly with any possible addons the server might have installed (especially the paid ones), so we don't provide support for RP gamemodes/servers. If you've encountered a conflict error with another addon, it's most likely that addon's fault. DO NOT CONTACT US ABOUT THAT!")
end
end
timer.Simple(0, function()
if not TFA.BASE_LOAD_COMPLETE or not TFA.SWEP_LOAD_COMPLETE then
printFunc("Some of the base's modules have failed to load. You are probably going over Lua files limit. Try disabling some addons until you stop getting this error.")
end
end)
end
if CLIENT then
hook.Add("HUDPaint", "TFA_CheckEnv", function()
local ply = LocalPlayer()
if not IsValid(ply) then return end
hook.Remove("HUDPaint", "TFA_CheckEnv")
checkEnv(ply)
end)
else
--resource.AddWorkshop("2840031720")
hook.Add("InitPostEntity", "TFA_CheckEnv", checkEnv)
end

View File

@@ -0,0 +1,415 @@
local gas_cl_enabled = GetConVar("cl_tfa_fx_gasblur")
local oldshell_cl_enabled = GetConVar("cl_tfa_legacy_shells")
local ScrW, ScrH = ScrW, ScrH
local BindToKeyTBL = {
["ctrl"] = KEY_LCONTROL,
["rctrl"] = KEY_LCONTROL,
["alt"] = KEY_LALT,
["ralt"] = KEY_RALT,
["space"] = KEY_SPACE,
["caps"] = KEY_CAPSLOCK,
["capslock"] = KEY_CAPSLOCK,
["tab"] = KEY_TAB,
["back"] = KEY_BACKSPACE,
["backspace"] = KEY_BACKSPACE,
[0] = KEY_0,
[1] = KEY_1,
[2] = KEY_2,
[3] = KEY_3,
[4] = KEY_4,
[5] = KEY_5,
[6] = KEY_6,
[7] = KEY_7,
[8] = KEY_8,
[9] = KEY_9
}
local alphabet = "abcdefghijklmnopqrstuvwxyz"
for i = 1, string.len(alphabet) do
local sstr = string.sub( alphabet, i, i )
BindToKeyTBL[ sstr ] = string.byte( sstr ) - 86
end
local SoundChars = {
["*"] = "STREAM",--Streams from the disc and rapidly flushed; good on memory, useful for music or one-off sounds
["#"] = "DRYMIX",--Skip DSP, affected by music volume rather than sound volume
["@"] = "OMNI",--Play the sound audible everywhere, like a radio voiceover or surface.PlaySound
[">"] = "DOPPLER",--Left channel for heading towards the listener, Right channel for heading away
["<"] = "DIRECTIONAL",--Left channel = front facing, Right channel = read facing
["^"] = "DISTVARIANT",--Left channel = close, Right channel = far
["("] = "SPATIALSTEREO_LOOP",--Position a stereo sound in 3D space; broken
[")"] = "SPATIALSTEREO",--Same as above but actually useful
["}"] = "FASTPITCH",--Low quality pitch shift
["$"] = "CRITICAL",--Keep it around in memory
["!"] = "SENTENCE",--NPC dialogue
["?"] = "USERVOX"--Fake VOIP data; not that useful
}
local DefaultSoundChar = ")"
local SoundChannels = {
["shoot"] = CHAN_WEAPON,
["shootwrap"] = CHAN_STATIC,
["misc"] = CHAN_AUTO
}
--Scope
local cv_rt
function TFA.RTQuality()
if not cv_rt then
cv_rt = GetConVar("cl_tfa_3dscope_quality")
end
return math.Clamp(cv_rt:GetInt(), cv_rt:GetMin(), cv_rt:GetMax())
end
--Sensitivity
local ss, fov_og, resrat, fov_cv
fov_cv = GetConVar("fov_desired")
function TFA.CalculateSensitivtyScale( fov_target, fov_src, screenscale )
if not LocalPlayer():IsValid() then return 1 end
resrat = ScrW() / ScrH()
fov_og = fov_src or TFADUSKFOV or fov_cv:GetFloat()
ss = screenscale or 1
return math.Clamp(math.atan( resrat * math.tan(math.rad( fov_target / 2 ) ) ) / math.atan( resrat * math.tan( math.rad( fov_og / 2) ) ) / ss, 0, 1)
end
--Ammo
local AmmoTypesByName = {}
local AmmoTypesAdded = {}
function TFA.AddAmmo(id, name)
if not AmmoTypesAdded[id] then
AmmoTypesAdded[id] = true
game.AddAmmoType({
name = id
})
end
if name and language then
language.Add(id .. "_ammo", name)
end
if name then
AmmoTypesByName[name] = AmmoTypesByName[name] or id
return AmmoTypesByName[name]
end
return id
end
--Particles
function TFA.ParticleTracer( name,startPos,endPos,doWhiz,ent,att)
if type(ent) ~= "number" and IsValid(ent) and ent.EntIndex then
ent = ent:EntIndex()
end
if ent then
att = att or -1
return util.ParticleTracerEx(name,startPos,endPos,doWhiz,ent,att)
else
return util.ParticleTracerEx(name,startPos,endPos,doWhiz,0,-1)
end
end
--Binds
function TFA.BindToKey( bind, default )
return BindToKeyTBL[ string.lower( bind ) ] or default or KEY_C
end
--Sounds
function TFA.PatchSound( path, kind )
local pathv
local c = string.sub(path,1,1)
if SoundChars[c] then
pathv = string.sub( path, 2, string.len(path) )
else
pathv = path
end
local kindstr = kind
if not kindstr then
kindstr = DefaultSoundChar
end
if string.len(kindstr) > 1 then
local found = false
for k,v in pairs( SoundChars ) do
if v == kind then
kindstr = k
found = true
break
end
end
if not found then
kindstr = DefaultSoundChar
end
end
return kindstr .. pathv
end
function TFA.AddSound( name, channel, volume, level, pitch, wave, char )
char = char or ""
local SoundData = {
name = name,
channel = channel or CHAN_AUTO,
volume = volume or 1,
level = level or 75,
pitch = pitch or 100
}
if char ~= "" then
if type(wave) == "string" then
wave = TFA.PatchSound(wave, char)
elseif type(wave) == "table" then
local patchWave = table.Copy(wave)
for k, v in pairs(patchWave) do
patchWave[k] = TFA.PatchSound(v, char)
end
wave = patchWave
end
end
SoundData.sound = wave
sound.Add(SoundData)
end
function TFA.AddFireSound( id, path, wrap, kindv )
kindv = kindv or ")"
TFA.AddSound(id, wrap and SoundChannels.shootwrap or SoundChannels.shoot, 1, 120, {97, 103}, path, kindv)
end
function TFA.AddWeaponSound( id, path, kindv )
kindv = kindv or ")"
TFA.AddSound(id, SoundChannels.misc, 1, 80, {97, 103}, path, kindv)
end
--Frametime
--CVar Mediators
function TFA.GetGasEnabled()
local enabled = false
if gas_cl_enabled then
enabled = gas_cl_enabled:GetBool()
end
return enabled
end
function TFA.GetLegacyShellsEnabled()
local enabled = false
if oldshell_cl_enabled then
enabled = oldshell_cl_enabled:GetBool()
end
return enabled
end
local ejectionsmoke_cl_enabled = GetConVar("cl_tfa_fx_ejectionsmoke")
local muzzlesmoke_cl_enabled = GetConVar("cl_tfa_fx_muzzlesmoke")
function TFA.GetMZSmokeEnabled()
local enabled = false
if muzzlesmoke_cl_enabled then
enabled = muzzlesmoke_cl_enabled:GetBool()
end
return enabled
end
function TFA.GetEJSmokeEnabled()
local enabled = false
if ejectionsmoke_cl_enabled then
enabled = ejectionsmoke_cl_enabled:GetBool()
end
return enabled
end
local muzzleflashsmoke_cl_enabled = GetConVar("cl_tfa_fx_muzzleflashsmoke")
function TFA.GetMZFSmokeEnabled()
local enabled = false
if muzzleflashsmoke_cl_enabled then
enabled = muzzleflashsmoke_cl_enabled:GetBool()
end
return enabled
end
local ricofx_cl_enabled = GetConVar("cl_tfa_fx_impact_ricochet_enabled")
function TFA.GetRicochetEnabled()
local enabled = false
if ricofx_cl_enabled then
enabled = ricofx_cl_enabled:GetBool()
end
return enabled
end
--Local function for detecting TFA Base weapons.
function TFA.PlayerCarryingTFAWeapon(ply)
if not ply then
if CLIENT then
if LocalPlayer():IsValid() then
ply = LocalPlayer()
else
return false, nil, nil
end
elseif game.SinglePlayer() then
ply = Entity(1)
else
return false, nil, nil
end
end
if not (IsValid(ply) and ply:IsPlayer() and ply:Alive()) then return end
local wep = ply:GetActiveWeapon()
if IsValid(wep) then
if (wep.IsTFAWeapon) then return true, ply, wep end
return false, ply, wep
end
return false, ply, nil
end
local sv_cheats = GetConVar("sv_cheats")
local host_timescale = GetConVar("host_timescale")
function TFA.FrameTime()
return engine.TickInterval() * game.GetTimeScale() * (sv_cheats:GetBool() and host_timescale:GetFloat() or 1)
end
local buffer = {}
function TFA.tbezier(t, values, amount)
assert(isnumber(t), 't is not a number')
assert(t >= 0 and t <= 1, '0 <= t <= 1!')
assert(#values >= 2, 'at least two values must be provided')
amount = amount or #values
local a, b = values[1], values[2]
-- linear
if amount == 2 then
return a + (b - a) * t
-- square
elseif amount == 3 then
return (1 - t) * (1 - t) * a + 2 * t * (1 - t) * b + t * t * values[3]
-- cube
elseif amount == 4 then
return (1 - t) * (1 - t) * (1 - t) * a + 3 * t * (1 - t) * (1 - t) * b + 3 * t * t * (1 - t) * values[3] + t * t * t * values[4]
end
for point = 1, amount do
local point1 = values[point]
local point2 = values[point + 1]
if not point2 then break end
buffer[point] = point1 + (point2 - point1) * t
end
return TFA.tbezier(t, buffer, amount - 1)
end
if DLib and math.tbezier and isfunction(math.tbezier) then
TFA.tbezier = math.tbezier
end
function TFA.Quintic(t)
return t * t * t * (t * (t * 6 - 15) + 10)
end
function TFA.Cosine(t)
return (1 - math.cos(t * math.pi)) / 2
end
function TFA.Sinusine(t)
return (1 - math.sin(t * math.pi)) / 2
end
function TFA.Cubic(t)
return -2 * t * t * t + 3 * t * t
end
function TFA.UnfoldBaseClass(tableInput, tableOutput)
if tableOutput == nil then tableOutput = tableInput end
if not istable(tableInput) or not istable(tableOutput) then return tableOutput end
if tableInput ~= tableOutput then
for k, v in pairs(tableInput) do
if tableOutput[k] == nil then
tableOutput[k] = v
end
end
end
if istable(tableInput.BaseClass) then
TFA.UnfoldBaseClass(tableInput.BaseClass, tableOutput)
end
return tableOutput
end
if CLIENT then
local cvar_scale = GetConVar("cl_tfa_hud_scale")
local sscache = {}
function TFA.ScaleH(num)
if not sscache[num] then
sscache[num] = num * (ScrH() / 1080) * cvar_scale:GetFloat()
end
return sscache[num]
end
local function EmptyCache()
sscache = {}
end
cvars.AddChangeCallback("cl_tfa_hud_scale", EmptyCache, "TFA_ClearSScaleCache")
hook.Add("OnScreenSizeChanged", "_TFA_DropSScaleCache", EmptyCache)
local color_black = Color(0, 0, 0, 255)
function TFA.DrawTextShadowed(text, font, x, y, color, shadowlength, shadowcolor)
shadowlength = shadowlength or 2
shadowcolor = shadowcolor or ColorAlpha(color_black, color.a)
surface.SetFont(font)
surface.SetTextPos(x + shadowlength, y + shadowlength)
surface.SetTextColor(shadowcolor.r, shadowcolor.g, shadowcolor.b, shadowcolor.a)
surface.DrawText(text)
surface.SetTextPos(x, y)
surface.SetTextColor(color.r, color.g, color.b, color.a)
surface.DrawText(text)
end
end

View File

@@ -0,0 +1,661 @@
local TFA = TFA
TFA.INSPECTION_IMPULSE = 148
TFA.BASH_IMPULSE = 149
TFA.CYCLE_FIREMODE_IMPULSE = 150
TFA.CYCLE_SAFETY_IMPULSE = 151
TFA.INSPECTION_IMPULSE_STRING = "148"
TFA.BASH_IMPULSE_STRING = "149"
TFA.CYCLE_FIREMODE_IMPULSE_STRING = "150"
TFA.CYCLE_SAFETY_IMPULSE_STRING = "151"
local sp = game.SinglePlayer()
local CurTime = CurTime
local ENTITY = FindMetaTable("Entity")
local PLAYER = FindMetaTable("Player")
--[[
Hook: PlayerPostThink
Function: Weapon Logic
Used For: Main weapon "think" logic
]]
--
if CLIENT and sp then
local engine_TickCount = engine.TickCount
hook.Add("PlayerPostThink", "PlayerTickTFA", function(plyv)
local wepv = PLAYER.GetActiveWeapon(plyv)
if not IsValid(wepv) then return end
local wep2 = ENTITY.GetTable(wepv)
if wep2.IsTFAWeapon then
local ply2 = PLAYER.GetTable(plyv)
local tickCount = engine_TickCount()
wep2.PlayerThink(wepv, plyv, ply2.last_tfa_think == tickCount)
ply2.last_tfa_think = tickCount
end
end)
end
if SERVER or not sp then
local IsFirstTimePredicted = IsFirstTimePredicted
hook.Add("FinishMove", "PlayerTickTFA", function(plyv)
local wepv = PLAYER.GetActiveWeapon(plyv)
if not IsValid(wepv) then return end
local wep2 = ENTITY.GetTable(wepv)
if wep2.IsTFAWeapon then
wep2.PlayerThink(wepv, plyv, not IsFirstTimePredicted())
end
end)
end
--[[
Hook: Think
Function: Weapon Logic for NPC
User For: Calling SWEP:Think for NPCs manually
]]
--
if SERVER then
hook.Add("Think", "NPCTickTFA", function()
hook.Run("TFA_NPCWeaponThink")
end)
end
--[[
Hook: Tick
Function: Inspection mouse support
Used For: Enables and disables screen clicker
]]
--
if CLIENT then
local tfablurintensity
local its_old = 0
local ScreenClicker = false
local cl_tfa_inspect_hide = GetConVar("cl_tfa_inspect_hide")
local cl_drawhud = GetConVar("cl_drawhud")
hook.Add("Tick", "TFAInspectionScreenClicker", function()
tfablurintensity = 0
if LocalPlayer():IsValid() and LocalPlayer():GetActiveWeapon():IsValid() then
local w = LocalPlayer():GetActiveWeapon()
if w.IsTFAWeapon then
tfablurintensity = w:GetCustomizing() and 1 or 0
end
end
if tfablurintensity > its_old and not ScreenClicker and not cl_tfa_inspect_hide:GetBool() and cl_drawhud:GetBool() then
gui.EnableScreenClicker(true)
ScreenClicker = true
elseif tfablurintensity < its_old and ScreenClicker then
gui.EnableScreenClicker(false)
ScreenClicker = false
end
its_old = tfablurintensity
end)
local engine_TickCount = engine.TickCount
local tickInterval = engine.TickInterval()
local FrameTime = FrameTime
local math_Clamp = math.Clamp
TFA.TickDeltaLastTick = TFA.TickDeltaLastTick or engine_TickCount() - 1
TFA.TickDelta = 0
hook.Add("Think", "TFABase_PlayerThinkCL", function()
TFA.TickDelta = math_Clamp(TFA.TickDelta + FrameTime() / tickInterval, 0, 1)
local tcount = engine_TickCount()
if TFA.TickDeltaLastTick ~= tcount then
TFA.TickDeltaLastTick = tcount
TFA.TickDelta = 0
end
local ply = LocalPlayer()
if not IsValid(ply) then return end
local weapon = ply:GetActiveWeapon()
if IsValid(weapon) and weapon.IsTFAWeapon and weapon.PlayerThinkCL then
weapon:PlayerThinkCL(ply)
end
end)
end
if CLIENT and sp then
local lastSDLP
local function SyncSDLP()
local ply = LocalPlayer()
if not IsValid(ply) then return end
local SDLP = ply:ShouldDrawLocalPlayer()
if lastSDLP == nil or lastSDLP ~= SDLP then
net.Start("tfaSDLP", true)
net.WriteBool(SDLP)
net.SendToServer()
lastSDLP = SDLP
end
end
hook.Add("Think", "TFABase_ShouldDrawLocalPlayer", SyncSDLP)
end
--[[
Hook: AllowPlayerPickup
Function: Prop holding
Used For: Records last held object
]]
--
hook.Add("AllowPlayerPickup", "TFAPickupDisable", function(plyv, ent)
plyv:SetNW2Entity("LastHeldEntity", ent)
end)
--[[
Hook: PlayerBindPress
Function: Intercept Keybinds
Used For: Alternate attack, inspection, shotgun interrupts, and more
]]
--
local cv_cm = GetConVar("sv_tfa_cmenu")
local cv_cm_key = GetConVar("sv_tfa_cmenu_key")
local keyv
local function GetInspectionKey()
if cv_cm_key and cv_cm_key:GetInt() >= 0 then
keyv = cv_cm_key:GetInt()
else
keyv = TFA.BindToKey(input.LookupBinding("+menu_context", true) or "c", KEY_C)
end
return keyv
end
local function TFAContextBlock()
local plyv = LocalPlayer()
if not plyv:IsValid() or GetViewEntity() ~= plyv then return end
if plyv:InVehicle() and not plyv:GetAllowWeaponsInVehicle() then return end
local wepv = plyv:GetActiveWeapon()
if not IsValid(wepv) then return end
if plyv:GetInfoNum("cl_tfa_keys_customize", 0) > 0 then return end
if GetInspectionKey() == TFA.BindToKey(input.LookupBinding("+menu_context", true) or "c", KEY_C) and wepv.ToggleInspect and cv_cm:GetBool() and not plyv:KeyDown(IN_USE) then return false end
end
hook.Add("ContextMenuOpen", "TFAContextBlock", TFAContextBlock)
if CLIENT then
local kd_old = false
local cl_tfa_keys_customize
local function TFAKPThink()
local plyv = LocalPlayer()
if not plyv:IsValid() then return end
local wepv = plyv:GetActiveWeapon()
if not IsValid(wepv) then return end
if not cl_tfa_keys_customize then
cl_tfa_keys_customize = GetConVar("cl_tfa_keys_customize")
end
if cl_tfa_keys_customize:GetBool() then return end
local key = GetInspectionKey()
local kd = input.IsKeyDown(key)
if IsValid(vgui.GetKeyboardFocus()) then
kd = false
end
if kd ~= kd_old and kd and cv_cm:GetBool() and not plyv:KeyDown(IN_USE) then
RunConsoleCommand("impulse", tostring(TFA.INSPECTION_IMPULSE))
end
kd_old = kd
end
hook.Add("Think", "TFAInspectionMenu", TFAKPThink)
end
local cv_lr = GetConVar("sv_tfa_reloads_legacy")
local reload_threshold = 0.3
local sv_cheats = GetConVar("sv_cheats")
local host_timescale = GetConVar("host_timescale")
local band = bit.band
local bxor = bit.bxor
local bnot = bit.bnot
local GetTimeScale = game.GetTimeScale
local IN_ATTACK2 = IN_ATTACK2
local IN_RELOAD = IN_RELOAD
local function FinishMove(ply, cmovedata)
if ply:InVehicle() and not ply:GetAllowWeaponsInVehicle() then return end
local wepv = ply:GetActiveWeapon()
if not IsValid(wepv) or not wepv.IsTFAWeapon then return end
wepv:TFAFinishMove(ply, cmovedata:GetVelocity(), cmovedata)
local impulse = cmovedata:GetImpulseCommand()
if impulse == TFA.INSPECTION_IMPULSE then
wepv:ToggleInspect()
elseif impulse == TFA.CYCLE_FIREMODE_IMPULSE and wepv:GetStatus() == TFA.Enum.STATUS_IDLE and wepv:GetStatL("SelectiveFire") then
wepv:CycleFireMode()
elseif impulse == TFA.CYCLE_SAFETY_IMPULSE and wepv:GetStatus() == TFA.Enum.STATUS_IDLE then
wepv:CycleSafety()
end
local BashImpulse = cmovedata:GetImpulseCommand() == TFA.BASH_IMPULSE
ply:TFA_SetZoomKeyDown(BashImpulse) -- this may or may not work
if wepv.SetBashImpulse then
wepv:SetBashImpulse(BashImpulse)
end
if cmovedata:GetImpulseCommand() == 100 and (wepv:GetStatL("FlashlightAttachmentName") ~= nil or wepv:GetStatL("FlashlightAttachment", 0) > 0) then
wepv:ToggleFlashlight()
end
local lastButtons = wepv:GetDownButtons()
local buttons = cmovedata:GetButtons()
local stillPressed = band(lastButtons, buttons)
local changed = bxor(lastButtons, buttons)
local pressed = band(changed, bnot(lastButtons), buttons)
local depressed = band(changed, lastButtons, bnot(buttons))
wepv:SetDownButtons(buttons)
wepv:SetLastPressedButtons(pressed)
local time = CurTime()
local cl_tfa_ironsights_toggle = (ply:GetInfoNum("cl_tfa_ironsights_toggle", 0) or 0) >= 1
local cl_tfa_ironsights_resight = (ply:GetInfoNum("cl_tfa_ironsights_resight", 0) or 0) >= 1
local cl_tfa_ironsights_responsive = (ply:GetInfoNum("cl_tfa_ironsights_responsive", 0) or 0) >= 1
local cl_tfa_ironsights_responsive_timer = ply:GetInfoNum("cl_tfa_ironsights_responsive_timer", 0.175) or 0.175
local scale_dividier = GetTimeScale() * (sv_cheats:GetBool() and host_timescale:GetFloat() or 1)
if wepv:GetStatL("Secondary.IronSightsEnabled", false) and not wepv:IsSafety() then
if band(changed, IN_ATTACK2) == IN_ATTACK2 then
local deltaPress = (time - wepv:GetLastIronSightsPressed()) / scale_dividier
-- pressing for first time
if not wepv:GetIronSightsRaw() and band(pressed, IN_ATTACK2) == IN_ATTACK2 then
wepv:SetIronSightsRaw(true)
wepv:SetLastIronSightsPressed(time)
elseif wepv:GetIronSightsRaw() and
((cl_tfa_ironsights_toggle or cl_tfa_ironsights_responsive) and band(pressed, IN_ATTACK2) == IN_ATTACK2 or
not cl_tfa_ironsights_toggle and not cl_tfa_ironsights_responsive and band(depressed, IN_ATTACK2) == IN_ATTACK2)
then
-- get out of iron sights
wepv:SetIronSightsRaw(false)
wepv:SetLastIronSightsPressed(-1)
elseif wepv:GetIronSightsRaw() and cl_tfa_ironsights_responsive and band(depressed, IN_ATTACK2) == IN_ATTACK2 and deltaPress > cl_tfa_ironsights_responsive_timer then
-- we depressed IN_ATTACK2 with it were being held down
wepv:SetIronSightsRaw(false)
wepv:SetLastIronSightsPressed(-1)
end
elseif wepv:GetIronSightsRaw() and not cl_tfa_ironsights_resight and (not TFA.Enum.IronStatus[wepv:GetStatus()] or wepv:GetSprinting()) then
wepv:SetIronSightsRaw(false)
wepv:SetLastIronSightsPressed(-1)
end
end
if
band(depressed, IN_RELOAD) == IN_RELOAD and
not cv_lr:GetBool()
and band(buttons, IN_USE) == 0
and time <= (wepv:GetLastReloadPressed() + reload_threshold * scale_dividier)
then
wepv:SetLastReloadPressed(-1)
wepv:Reload(true)
elseif band(pressed, IN_RELOAD) == IN_RELOAD then
wepv:SetLastReloadPressed(time)
elseif band(buttons, IN_RELOAD) ~= 0 and band(buttons, IN_USE) == 0 and time > (wepv:GetLastReloadPressed() + reload_threshold * scale_dividier) then
wepv:CheckAmmo()
end
if BashImpulse then
if wepv.AltAttack then
wepv:AltAttack()
end
end
end
hook.Add("FinishMove", "TFAFinishMove", FinishMove)
local function TFABashZoom(plyv, cusercmd)
if plyv:InVehicle() and not plyv:GetAllowWeaponsInVehicle() then return end
if plyv:GetInfoNum("cl_tfa_keys_bash", 0) ~= 0 then
if (sp or CLIENT) and plyv.tfa_bash_hack then
cusercmd:SetImpulse(TFA.BASH_IMPULSE)
end
return
end
local zoom = cusercmd:KeyDown(IN_ZOOM)
if zoom then
local wepv = plyv:GetActiveWeapon()
if IsValid(wepv) and wepv.IsTFAWeapon and wepv.AltAttack then
cusercmd:RemoveKey(IN_ZOOM)
cusercmd:SetImpulse(TFA.BASH_IMPULSE)
end
end
end
hook.Add("StartCommand", "TFABashZoom", TFABashZoom)
--[[
Hook: PlayerSpawn
Function: Extinguishes players, zoom cleanup
Used For: Fixes incendiary bullets post-respawn
]]
--
hook.Add("PlayerSpawn", "TFAExtinguishQOL", function(plyv)
if IsValid(plyv) and plyv:IsOnFire() then
plyv:Extinguish()
end
end)
local sv_tfa_weapon_weight = GetConVar("sv_tfa_weapon_weight")
--[[
Hook: SetupMove
Function: Modify movement speed
Used For: Weapon slowdown, ironsights slowdown
]]
--
hook.Add("SetupMove", "tfa_setupmove", function(plyv, movedata, commanddata)
local wepv = plyv:GetActiveWeapon()
if IsValid(wepv) and wepv.IsTFAWeapon and sv_tfa_weapon_weight:GetBool() then
local speedmult = Lerp(wepv:GetIronSightsProgress(), wepv:GetStatL("RegularMoveSpeedMultiplier", 1), wepv:GetStatL("AimingDownSightsSpeedMultiplier", 1))
movedata:SetMaxClientSpeed(movedata:GetMaxClientSpeed() * speedmult)
commanddata:SetForwardMove(commanddata:GetForwardMove() * speedmult)
commanddata:SetSideMove(commanddata:GetSideMove() * speedmult)
end
end)
--[[
Hook: InitPostEntity
Function: Patches or removes other hooks that breaking or changing behavior of our weapons in a negative way
Used For: Fixing our stuff
]]
--
local function FixInvalidPMHook()
if not CLIENT then return end
local hookTable = hook.GetTable()
if hookTable["PostDrawViewModel"] and hookTable["PostDrawViewModel"]["Set player hand skin"] then
local targetFunc = hookTable["PostDrawViewModel"]["Set player hand skin"]
if not targetFunc then return end
local cv_shouldfix = GetConVar("cl_tfa_fix_pmhands_hook") or CreateClientConVar("cl_tfa_fix_pmhands_hook", "1", true, false, "Fix hands skin hook for CaptainBigButt's (and others) playermodels (Change requires map restart)")
if not cv_shouldfix:GetBool() then return end
print("[TFA Base] The playermodels you have installed breaks the automatic rig parenting for Insurgency and CS:GO weapons. The fix is applied but it's more of a band-aid, the solution would be to either fix this properly on author's side or to uninstall the addon.")
if CLIENT and debug and debug.getinfo then
local funcPath = debug.getinfo(targetFunc).short_src
print("Type whereis " .. funcPath .. " in console to see the conflicting addon.")
end
hook.Remove("PostDrawViewModel", "Set player hand skin")
hook.Add("PreDrawPlayerHands", "Set player hand skin BUT FIXED", function(hands, vm, ply, weapon)
if hands:SkinCount() == ply:SkinCount() then
hands:SetSkin(ply:GetSkin())
end
end)
end
end
local function PatchSiminovSniperHook()
if not CLIENT then return end -- that hook is clientside only
local hookTable = hook.GetTable()
if hookTable["CreateMove"] and hookTable["CreateMove"]["SniperCreateMove"] then
local SniperCreateMove = hookTable["CreateMove"]["SniperCreateMove"] -- getting the original function
if not SniperCreateMove then return end
local cv_shouldfix = GetConVar("cl_tfa_fix_siminov_scopes") or CreateClientConVar("cl_tfa_fix_siminov_scopes", "1", true, false, "Patch Siminov's sniper overlay hook with weapon base check (Change requires map restart)")
if not cv_shouldfix:GetBool() then return end
local PatchedSniperCreateMove = function(cmd) -- wrapping their function with our check
local ply = LocalPlayer()
if IsValid(ply) and IsValid(ply:GetActiveWeapon()) and ply:GetActiveWeapon().IsTFAWeapon then
return
end
SniperCreateMove(cmd)
end
hook.Remove("CreateMove", "SniperCreateMove") -- removing original hook
hook.Add("CreateMove", "SniperCreateMove_PatchedByTFABase", PatchedSniperCreateMove) -- creating new hook with wrap
end
end
hook.Add("InitPostEntity", "tfa_unfuckeverything", function()
FixInvalidPMHook()
PatchSiminovSniperHook()
end)
--[[
Hook: PlayerSwitchFlashlight
Function: Flashlight toggle
Used For: Switching flashlight on weapon and blocking HEV flashlight
]]
--
hook.Add("PlayerSwitchFlashlight", "tfa_toggleflashlight", function(plyv, toEnable)
if CLIENT then return end -- this is serverside hook GO AWAY
-- fuck you source
-- where is fucking prediction??!??!?!?/
if not IsValid(plyv) or not toEnable then return end -- allow disabling HEV flashlight
local wepv = plyv:GetActiveWeapon()
if IsValid(wepv) and wepv.IsTFAWeapon and (wepv:GetStatL("FlashlightAttachmentName") ~= nil or wepv:GetStatL("FlashlightAttachment", 0) > 0) then
-- wepv:ToggleFlashlight()
return false
end
end)
--[[
Hook: SetupMove
Function: Update players NW2 variable
Used For: Walking animation NW2 var
]]
--
hook.Add("SetupMove", "tfa_checkforplayerwalking", function(plyv, mvdatav, cmdv)
if not IsValid(plyv) or not mvdatav then return end
if mvdatav:GetForwardSpeed() ~= 0 or mvdatav:GetSideSpeed() ~= 0 then
if not plyv:GetNW2Bool("TFA_IsWalking") then
plyv:SetNW2Bool("TFA_IsWalking", true)
end
elseif plyv:GetNW2Bool("TFA_IsWalking") then
plyv:SetNW2Bool("TFA_IsWalking", false)
end
end)
--[[
Hook: PreDrawOpaqueRenderables
Function: Calls SWEP:PreDrawOpaqueRenderables()
Used For: whatever draw stuff you need lol
]]
--
hook.Add("PreDrawOpaqueRenderables", "tfaweaponspredrawopaque", function()
for _, v in ipairs(player.GetAll()) do
local wepv = v:GetActiveWeapon()
if IsValid(wepv) and wepv.IsTFAWeapon and wepv.PreDrawOpaqueRenderables then
wepv:PreDrawOpaqueRenderables()
end
end
end)
--[[
Hook: PreDrawViewModel
Function: Calculating viewmodel offsets
Used For: Viewmodel sway, offset and flip
]]
--
if CLIENT then
local vec = Vector()
local ang = Angle()
local IsGameUIVisible = gui and gui.IsGameUIVisible
local FrameTime = FrameTime
local FrameNumber = FrameNumber
local lastframe = FrameNumber() - 1
hook.Add("PreDrawViewModel", "TFACalculateViewmodel", function(vm, plyv, wepv)
if lastframe == FrameNumber() then return end
lastframe = FrameNumber()
if not IsValid(wepv) or not wepv.IsTFAWeapon then return end
local wep2 = wepv:GetTable()
if sp and IsGameUIVisible and IsGameUIVisible() then return end
wep2.UpdateEngineBob(wepv)
local delta = FrameTime()
wep2.Sway(wepv, vec, ang, delta)
wep2.CalculateViewModelOffset(wepv, delta)
wep2.CalculateViewModelFlip(wepv)
wep2.UpdateProjectedTextures(wepv, true)
end)
end
--[[
Hook: EntityTakeDamage
Function: Applies physics damage to Combine Turrets
Used For: Knocking up Combine Turrets with TFA Base weapons
]]
--
hook.Add("EntityTakeDamage", "TFA_TurretPhysics", function(entv, dmg)
if entv:GetClass() == "npc_turret_floor" then
entv:TakePhysicsDamage(dmg)
end
end)
--[[
Hook: HUDPaint
Function: Calls another hook
Used For: Hook that notifies when player is fully loaded.
]]
--
hook.Add("HUDPaint", "TFA_TRIGGERCLIENTLOAD", function()
if LocalPlayer():IsValid() then
hook.Remove("HUDPaint", "TFA_TRIGGERCLIENTLOAD")
hook.Run("TFA_ClientLoad")
end
end)
--[[
Hook: InitPostEntity
Function: Wraps SWEP:Think functions
Used For: Patching old, broken weapons that override SWEP:Think without calling baseclass
]]
--
local PatchClassBlacklisted = {
tfa_gun_base = true,
tfa_melee_base = true,
tfa_bash_base = true,
tfa_bow_base = true,
tfa_knife_base = true,
tfa_nade_base = true,
tfa_sword_advanced_base = true,
tfa_cssnade_base = true,
tfa_shotty_base = true,
tfa_akimbo_base = true,
tfa_3dbash_base = true,
tfa_3dscoped_base = true,
tfa_scoped_base = true,
}
local cv_shouldpatchthink = GetConVar("sv_tfa_backcompat_patchswepthink") or CreateConVar("sv_tfa_backcompat_patchswepthink", "1", CLIENT and {FCVAR_REPLICATED} or {FCVAR_REPLICATED, FCVAR_ARCHIVE, FCVAR_NOTIFY}, "Enable patching of old weapons that override SWEP:Think function to work with newer version of the base?\n\tDISABLING THIS IS NOT RECOMMENDED AND MAY LEAD TO NON-FUNCTIONING WEAPONS!")
hook.Add("InitPostEntity", "TFA_PatchThinkOverride", function()
if not cv_shouldpatchthink:GetBool() then return end
if not debug or not debug.getinfo then return end
for _, wepRefTable in ipairs(weapons.GetList()) do
local class = wepRefTable.ClassName
if PatchClassBlacklisted[class] or not weapons.IsBasedOn(class, "tfa_gun_base") then
goto THINK1FOUND
end
local wepRealTbl = weapons.GetStored(class)
if wepRealTbl.Think then
local info = debug.getinfo(wepRealTbl.Think, "S")
if not info or not info.linedefined or not info.lastlinedefined then goto THINK1FOUND end
local src = info.short_src
if src:StartWith("addons/") then
src = src:gsub("^addons/[^%0:/]+/", "")
end
local luafile = file.Read(src:sub(5), "LUA")
if not luafile or luafile == "" then goto THINK1FOUND end
local lua = luafile:gsub("\r\n","\n"):gsub("\r","\n"):Split("\n")
for i = info.linedefined, info.lastlinedefined do
local line = lua[i]
if not line or line:find("BaseClass%s*.%s*Think%s*%(") then
goto THINK1FOUND
end
end
print(("[TFA Base] Weapon %s (%s) is overriding SWEP:Think() function without calling baseclass!"):format(wepRefTable.ClassName, info.short_src))
local BaseClass = baseclass.Get(wepRealTbl.Base)
wepRealTbl.ThinkFuncUnwrapped = wepRealTbl.ThinkFuncUnwrapped or wepRealTbl.Think
function wepRealTbl:Think(...)
self:ThinkFuncUnwrapped(...)
return BaseClass.Think(self, ...)
end
end
::THINK1FOUND::
end
end)

View File

@@ -0,0 +1,234 @@
TFA._KeyBindTable = TFA._KeyBindTable or {}
local KeyBindTable = TFA._KeyBindTable
local cv_prefix = "cl_tfa_keys_"
local sp = game.SinglePlayer()
if sp then -- THANK YOU GARRY FOR HIGH QUALITY PREDICTION IN SINGLEPLAYER
if SERVER then
util.AddNetworkString("TFA_KB_State")
util.AddNetworkString("TFA_KB_Think")
end
if CLIENT then
net.Receive("TFA_KB_State", function()
local ply = LocalPlayer()
local bind = net.ReadString()
local state = net.ReadBool()
local data = KeyBindTable[bind]
if data and data.state ~= state then
data.state = state
if state then
data.onpress(ply)
else
data.onrelease(ply)
end
end
end)
net.Receive("TFA_KB_Think", function()
local ply = LocalPlayer()
local bind = net.ReadString()
local data = KeyBindTable[bind]
if data and data.think and data.state then
data.think(ply)
end
end)
end
end
local function empty()
end
function TFA.RegisterKeyBind(data_in)
assert(type(data_in) == "table", "Data must be a table!")
assert(data_in.bind and type(data_in.bind) == "string", "Invalid bind name!")
-- assert(not TFA._KeyBindTable[data.bind], "Keybind already registered!")
local data = table.Copy(data_in)
if not data.onpress then
data.onpress = empty
elseif type(data.onpress) ~= "function" then
error("data.onpress - function expected, got " .. type(data.onpress))
end
if not data.onrelease then
data.onrelease = empty
elseif type(data.onrelease) ~= "function" then
error("data.onrelease - function expected, got " .. type(data.onrelease))
end
data.state = false
if CLIENT and GetConVar(cv_prefix .. data.bind) == nil then
CreateClientConVar(cv_prefix .. data.bind, 0, true, true, data.desc)
end
hook.Add("PlayerButtonDown", "TFA_KB_KeyDown_" .. data.bind, function(ply, button)
if not IsFirstTimePredicted() then return end
local cv_key = ply:GetInfoNum(cv_prefix .. data.bind, 0)
if cv_key > 0 and cv_key == button and not data.state then
data.state = true
data.onpress(ply)
if sp and SERVER then
net.Start("TFA_KB_State", true)
net.WriteString(data.bind)
net.WriteBool(data.state)
net.Send(ply)
end
end
end)
hook.Add("PlayerButtonUp", "TFA_KB_KeyUp_" .. data.bind, function(ply, button)
if not IsFirstTimePredicted() then return end
local cv_key = ply:GetInfoNum(cv_prefix .. data.bind, 0)
if cv_key > 0 and cv_key == button and data.state then
data.state = false
data.onrelease(ply)
if sp and SERVER then
net.Start("TFA_KB_State", true)
net.WriteString(data.bind)
net.WriteBool(data.state)
net.Send(ply)
end
end
end)
hook.Remove("PlayerPostThink", "TFA_KB_Think_" .. data.bind)
if data.think and type(data.think) == "function" then
hook.Add("PlayerPostThink", "TFA_KB_Think_" .. data.bind, function(ply)
if data.state then
data.think(ply)
if sp and SERVER then
net.Start("TFA_KB_Think", true)
net.WriteString(data.bind)
net.Send(ply)
end
end
end)
end
KeyBindTable[data.bind] = data
end
if CLIENT then -- Populate spawnmenu settings with registered keybinds
local function tfaOptionKeys(panel)
panel:Help("#tfa.keybinds.help.bind")
panel:Help("#tfa.keybinds.help.bound")
panel:Help("#tfa.keybinds.help.unbind")
panel:Help("")
for _, data in pairs(KeyBindTable) do
local cv = GetConVar(cv_prefix .. data.bind)
if cv then
panel:Help("#tfa.keybind." .. data.bind)
local binder = vgui.Create("DBinder")
binder:SetValue(cv:GetInt())
function binder:OnChange(newcode)
cv:SetInt(newcode)
end
panel:AddItem(binder)
panel:Help("")
end
end
end
hook.Add("PopulateToolMenu", "TFA_AddKeyBinds", function()
spawnmenu.AddToolMenuOption("Utilities", "TFA SWEP Base Settings", "TFASwepBaseKeybinds", "#tfa.smsettings.keybinds", "", "", tfaOptionKeys)
end)
end
-- Default keybinds
TFA.RegisterKeyBind({
bind = "bash",
onpress = function(plyv)
if not plyv:IsValid() then return end
plyv.tfa_bash_hack = true
end,
onrelease = function(plyv)
if not plyv:IsValid() then return end
plyv.tfa_bash_hack = false
end
})
TFA.RegisterKeyBind({
bind = "customize",
onpress = CLIENT and function(plyv)
if not plyv:IsValid() then return end
RunConsoleCommand("impulse", TFA.INSPECTION_IMPULSE_STRING)
end
})
TFA.RegisterKeyBind({
bind = "inspect",
onpress = function(plyv)
local wepv = plyv:GetActiveWeapon()
if (IsValid(wepv) and wepv.GetStat) and (wepv:GetActivityEnabled(ACT_VM_FIDGET) or wepv.InspectionActions) and wepv:GetStatus() == TFA.Enum.STATUS_IDLE then
local _, tanim, ttype = wepv:ChooseInspectAnim()
wepv:ScheduleStatus(TFA.Enum.STATUS_FIDGET, wepv:GetActivityLength(tanim, false, ttype))
end
end
})
TFA.RegisterKeyBind({
bind = "firemode",
onpress = CLIENT and function(plyv)
local wepv = plyv:GetActiveWeapon()
if IsValid(wepv) and wepv.GetStat then
if wepv:GetStatL("SelectiveFire") and not wepv:GetOwner():KeyDown(IN_SPEED) then
RunConsoleCommand("impulse", TFA.CYCLE_FIREMODE_IMPULSE_STRING)
elseif wepv:GetOwner():KeyDown(IN_SPEED) then
RunConsoleCommand("impulse", TFA.CYCLE_SAFETY_IMPULSE_STRING)
end
end
end
})
TFA.RegisterKeyBind({
bind = "silencer",
onpress = function(plyv)
local wepv = plyv:GetActiveWeapon()
if (IsValid(wepv) and wepv.GetStat) and wepv:GetStatRawL("CanBeSilenced") and TFA.Enum.ReadyStatus[wepv:GetStatus()] then
local _, tanim, ttype = wepv:ChooseSilenceAnim(not wepv:GetSilenced())
wepv:ScheduleStatus(TFA.Enum.STATUS_SILENCER_TOGGLE, wepv:GetActivityLength(tanim, true, ttype))
end
end
})
-- EXAMPLE KEYBIND:
--[[
TFA.RegisterKeyBind({
bind = "whatever", -- bind id, cvar is cl_tfa_keys_whatever
onpress = function(ply) end, -- function called on key press
onrelease = function(ply) end, -- function called on key release
think = function(ply) end, -- called from PlayerPostThink when key is held down
})
]]

View File

@@ -0,0 +1,205 @@
local CHAR_STRING = {
["\""] = true,
["'"] = true
}
local CHAR_TABLE_OPEN = {
["{"] = true,
["["] = true
}
local CHAR_TABLE_CLOSE = {
["}"] = true,
["]"] = true
}
local CHAR_WHITESPACE = {
[" "] = true,
["\t"] = true,
["\r"] = true,
["\n"] = true
}
local CHAR_NEWLINE = {
["\r"] = true,
["\n"] = true
}
local CHAR_COMMENT = {
["/"] = true,
["-"] = true
}
local KEY_CASE = true
local ORDERED = false
local buffer = ""
local tbl = {}
local tbl_focus
local tbl_tmp
local value, lastvalue
local ignore_next_pop
local escape
local stringtype
local is_comment = false
local f
local strsub = string.sub
local strlow = string.lower
local fread = file.Read
local function strchar( strv, ind )
return strsub( strv, ind, ind)
end
local function ResetValues()
lastvalue = nil
value = nil
end
local function FlushBuffer(write)
if buffer ~= "" or stringtype then
lastvalue = value
if lastvalue and not KEY_CASE then
lastvalue = strlow(lastvalue)
end
value = buffer
buffer = ""
if tbl_focus and (write == nil or write) and lastvalue and value then
if ORDERED then
tbl_focus[ #tbl_focus + 1 ] = { ["key"] = lastvalue, ["value"] = value }
else
tbl_focus[lastvalue] = value
end
ResetValues()
end
end
end
local function PushTable()
FlushBuffer(true)
if value and not KEY_CASE then
value = strlow(value)
end
if value and value ~= "" then
if ORDERED then
tbl_focus[ #tbl_focus + 1 ] = { ["key"] = value, ["value"] = {} }
tbl_focus[ #tbl_focus ].value.__par = tbl_focus
tbl_focus = tbl_focus[ #tbl_focus ].value
else
tbl_focus[value] = istable(tbl_focus[value]) and tbl_focus[value] or {}
tbl_focus[value].__par = tbl_focus
tbl_focus = tbl_focus[value]
end
ignore_next_pop = false
else
ignore_next_pop = true
end
ResetValues()
end
local function PopTable()
if not ignore_next_pop then
FlushBuffer(true)
if tbl_focus.__par then
tbl_tmp = tbl_focus.__par
tbl_focus.__par = nil
tbl_focus = tbl_tmp
end
end
ignore_next_pop = false
ResetValues()
end
function TFA.ParseKeyValues(fn, path, use_escape, keep_key_case, invalid_escape_addslash, ordered )
if use_escape == nil then
use_escape = true
end
if keep_key_case == nil then
keep_key_case = true
end
KEY_CASE = keep_key_case
if invalid_escape_addslash == nil then
invalid_escape_addslash = true
end
if ordered then
ORDERED = true
else
ORDERED = false
end
tbl = {}
tbl_focus = tbl
tbl_tmp = nil
value = nil
lastvalue = nil
escape = false
is_comment = false
stringtype = nil
f = fread(fn, path)
if not f then return tbl end
for i = 1, #f do
local char = strchar(f,i)
if not char then
FlushBuffer()
break
end
if is_comment then
if CHAR_NEWLINE[char] then
is_comment = false
end
elseif escape then
if char == "t" then
buffer = buffer .. "\t"
elseif char == "n" then
buffer = buffer .. "\n"
elseif char == "r" then
buffer = buffer
else
if invalid_escape_addslash then
buffer = buffer .. "\\"
end
buffer = buffer .. char
end
escape = false
elseif char == "\\" and use_escape then
escape = true
elseif CHAR_STRING[char] then
if not stringtype then
FlushBuffer()
stringtype = char
elseif stringtype == char then
FlushBuffer()
stringtype = nil
elseif stringtype then
buffer = buffer .. char
end
elseif stringtype then
buffer = buffer .. char
elseif CHAR_COMMENT[char] then
if CHAR_COMMENT[ strchar(f,i + 1,i + 1 ) ] then
is_comment = true
else
buffer = buffer .. char
end
elseif CHAR_WHITESPACE[char] then
if buffer ~= "" then
FlushBuffer()
end
elseif CHAR_TABLE_OPEN[char] then
PushTable()
elseif CHAR_TABLE_CLOSE[char] then
PopTable()
else
buffer = buffer .. char
end
end
return tbl
end

View File

@@ -0,0 +1,177 @@
local nvec = Vector()
local function SetPlayerColors(ply)
if not IsValid(ply) then return end
local _SetNWVector = ply.SetNW2Vector or ply.SetNWVector
nvec.x = ply:GetInfoNum("cl_tfa_laser_color_r", 255)
nvec.y = ply:GetInfoNum("cl_tfa_laser_color_g", 0)
nvec.z = ply:GetInfoNum("cl_tfa_laser_color_b", 0)
_SetNWVector(ply, "TFALaserColor", nvec)
nvec.x = ply:GetInfoNum("cl_tfa_reticule_color_r", 255)
nvec.y = ply:GetInfoNum("cl_tfa_reticule_color_g", 0)
nvec.z = ply:GetInfoNum("cl_tfa_reticule_color_b", 0)
_SetNWVector(ply, "TFAReticuleColor", nvec)
end
hook.Add("PlayerSpawn", "TFANetworkColors_Spawn", SetPlayerColors)
concommand.Add("sv_tfa_apply_player_colors", SetPlayerColors)
if not matproxy then return end
matproxy.Add({
name = "PlayerWeaponColorStatic",
init = function(self, mat, values)
self.ResultTo = values.resultvar
end,
bind = function(self, mat, ent)
if (not IsValid(ent)) then return end
local owner = ent:GetOwner()
if (not IsValid(owner) or not owner:IsPlayer()) then return end
local col = owner:GetWeaponColor()
if (not isvector(col)) then return end
mat:SetVector(self.ResultTo, col * 1)
end
})
local cvec = Vector()
matproxy.Add({
name = "TFALaserColor",
init = function(self, mat, values)
self.ResultTo = values.resultvar
end,
bind = function(self, mat, ent)
local owner
if (IsValid(ent)) then
owner = ent:GetOwner()
if not IsValid(owner) then
owner = ent:GetParent()
end
if IsValid(owner) and owner:IsWeapon() then
owner = owner:GetOwner() or owner:GetOwner()
end
if not (IsValid(owner) and owner:IsPlayer()) then
owner = GetViewEntity()
end
else
owner = GetViewEntity()
end
if (not IsValid(owner) or not owner:IsPlayer()) then return end
local c
if owner.GetNW2Vector then
c = owner:GetNW2Vector("TFALaserColor") or cvec
else
c = owner:GetNWVector("TFALaserColor") or cvec
end
cvec.x = math.sqrt(c.r / 255) --sqrt for gamma
cvec.y = math.sqrt(c.g / 255)
cvec.z = math.sqrt(c.b / 255)
mat:SetVector(self.ResultTo, cvec)
end
})
local cvec_r = Vector()
matproxy.Add({
name = "TFAReticuleColor",
init = function(self, mat, values)
self.ResultTo = values.resultvar
end,
bind = function(self, mat, ent)
local owner
if (IsValid(ent)) then
owner = ent:GetOwner()
if not IsValid(owner) then
owner = ent:GetParent()
end
if IsValid(owner) and owner:IsWeapon() then
owner = owner:GetOwner() or owner:GetOwner()
end
if not (IsValid(owner) and owner:IsPlayer()) then
owner = GetViewEntity()
end
else
owner = GetViewEntity()
end
if (not IsValid(owner) or not owner:IsPlayer()) then return end
local c
if owner.GetNW2Vector then
c = owner:GetNW2Vector("TFAReticuleColor") or cvec_r
else
c = owner:GetNWVector("TFAReticuleColor") or cvec_r
end
cvec_r.x = c.r / 255
cvec_r.y = c.g / 255
cvec_r.z = c.b / 255
mat:SetVector(self.ResultTo, cvec_r)
end
})
matproxy.Add({
name = "TFA_RTScope",
init = function(self, mat, values)
self.RTMaterial = Material("!tfa_rtmaterial")
end,
bind = function(self, mat, ent)
if not self.RTMaterial then
self.RTMaterial = Material("!tfa_rtmaterial")
end
mat:SetTexture("$basetexture", self.RTMaterial:GetTexture("$basetexture"))
end
})
local Lerp = Lerp
local RealFrameTime = RealFrameTime
local vector_one = Vector(1, 1, 1)
matproxy.Add({
name = "TFA_CubemapTint",
init = function(self, mat, values)
self.ResultVar = values.resultvar or "$envmaptint"
self.MultVar = values.multiplier
end,
bind = function(self, mat, ent)
local tint = vector_one
if IsValid(ent) then
local mult = self.MultVar and mat:GetVector(self.MultVar) or vector_one
tint = Lerp(RealFrameTime() * 10, mat:GetVector(self.ResultVar), mult * render.GetLightColor(ent:GetPos()))
end
mat:SetVector(self.ResultVar, tint)
end
})
-- VMT Example:
--[[
$envmapmultiplier "[1 1 1]" // Lighting will be multiplied by this value
Proxies
{
TFA_CubemapTint
{
resultvar $envmaptint // Write final output to $envmaptint
multiplier $envmapmultiplier // Use our value for default envmap tint
}
}
]]

View File

@@ -0,0 +1,232 @@
local vector_origin = Vector()
local timed_blocking_cv = GetConVar("sv_tfa_melee_blocking_timed")
local angle_mult_cv = GetConVar("sv_tfa_melee_blocking_anglemult")
local deflect_cv = GetConVar("sv_tfa_melee_blocking_deflection")
local stun_cv = GetConVar("sv_tfa_melee_blocking_stun_enabled")
local stuntime_cv = GetConVar("sv_tfa_melee_blocking_stun_time")
local bul = {
HullSize = 5,
Num = 1,
Tracer = 1,
AmmoType = "",
TracerName = "Tracer",
Spread = Vector(0.05,0.05,0),
Distance = 56756
}
local function CanDeflect()
return true
end
local function DeflectBullet( ent, dmginfo, olddmg )
if dmginfo:IsDamageType( DMG_BULLET ) and CanDeflect() and ent.FireBullets then
bul.Src = ent:GetShootPos()
bul.Dir = ent:EyeAngles():Forward()
bul.Damage = olddmg
bul.Force = olddmg / 10
local atk = dmginfo:GetAttacker()
if IsValid( atk ) and atk.TFALastTracer then
bul.Tracer = atk.TFALastTracer
end
ent:FireBullets( bul, false )
dmginfo:ScaleDamage(0)
end
end
local stuntime = 0.65
local function StunNPC( npc, ply )
if stun_cv and not stun_cv:GetBool() then return end
if ( not IsValid( npc ) ) or ( not npc:IsNPC() ) then
return
end
if npc.ClearSchedule then
npc:ClearSchedule()
end
if npc.SetEnemy then
npc:SetEnemy(nil)
end
if npc.AddEntityRelationship and IsValid(ply) then
local oldrel = npc.GetRelationship and npc:GetRelationship(ply) or D_HT
npc:AddEntityRelationship( ply, D_NU, 99)
stuntime = stuntime_cv:GetFloat()
timer.Simple( stuntime , function()
if IsValid(npc) and npc:IsNPC() and IsValid(ply) then
npc:AddEntityRelationship( ply, oldrel, 99)
end
end)
end
if npc.ClearEnemyMemory then
npc:ClearEnemyMemory()
end
end
local function BlockDamageNew( ent, dmginfo )
if not ent:IsPlayer() then return end
if dmginfo:IsDamageType( DMG_DROWNRECOVER ) or dmginfo:IsDamageType(DMG_DIRECT) then return end
local wep
wep = ent:GetActiveWeapon()
if (wep.IsTFAWeapon and wep.BlockDamageTypes and wep:GetStatus() == TFA.Enum.STATUS_BLOCKING) then
local canblock = false
for _,v in ipairs(wep.BlockDamageTypes) do
if dmginfo:IsDamageType(v) then canblock = true end
end
local retVal = hook.Run("TFA_MeleeCanBlockDamage", wep, ent, dmginfo, canblock)
if retVal ~= nil then
canblock = retVal
end
if canblock then
local damageinflictor, blockthreshold
damageinflictor = dmginfo:GetInflictor()
if (not IsValid(damageinflictor)) then
damageinflictor = dmginfo:GetAttacker()
end
blockthreshold = ( wep.BlockCone or 135 ) / 2
if angle_mult_cv then
blockthreshold = blockthreshold * angle_mult_cv:GetFloat()
end
if ( IsValid(damageinflictor) and ( math.abs(math.AngleDifference( ent:EyeAngles().y, ( damageinflictor:GetPos() - ent:GetPos() ):Angle().y )) <= blockthreshold)) then
local fac = math.Clamp( ( CurTime() - wep:GetBlockStart() - wep.BlockTimeWindow ) / wep.BlockTimeFade, 0, 1)
local dmgscale
if ( not timed_blocking_cv ) or timed_blocking_cv:GetBool() then
dmgscale = Lerp(fac, wep.BlockDamageMaximum, wep.BlockDamageMinimum)
else
dmgscale = wep.BlockDamageMaximum
end
local olddmg = dmginfo:GetDamage()
dmgscale = math.min( dmgscale, wep.BlockDamageCap / dmginfo:GetDamage() )
--print(fac)
dmginfo:ScaleDamage(dmgscale)
dmginfo:SetDamagePosition(vector_origin)
dmginfo:SetDamageType( bit.bor( dmginfo:GetDamageType(), DMG_DROWNRECOVER ) )
wep:EmitSound(wep.BlockSound or "")
if wep.ChooseBlockAnimation then
wep:ChooseBlockAnimation()
end
if deflect_cv and deflect_cv:GetInt() == 2 then
DeflectBullet( ent, dmginfo, olddmg )
end
if dmginfo:GetDamage() < 1 then
if deflect_cv and deflect_cv:GetInt() == 1 and wep.BlockCanDeflect then
DeflectBullet( ent, dmginfo, olddmg )
end
StunNPC( dmginfo:GetAttacker(), ent )
return true
end
return
end
end
end
end
hook.Add("EntityFireBullets","TFA_Melee_LogTracer",function(ent,bulv) --Record tracer for blocking
ent.TFALastTracer = bulv.TracerName or ""
end)
local npc_dmg_scale_cv = GetConVar("sv_tfa_melee_damage_npc")
local ply_dmg_scale_cv = GetConVar("sv_tfa_melee_damage_ply")
hook.Add("EntityTakeDamage", "TFA_Melee_Scaling", function( ent, dmginfo )
local wep = dmginfo:GetInflictor()
if not IsValid(wep) then return end
if wep:IsPlayer() then wep = wep:GetActiveWeapon() end
if not IsValid(wep) or not wep:IsWeapon() or not wep.IsTFAWeapon or not wep.IsMelee then return end
if ent:IsNPC() then
dmginfo:ScaleDamage( npc_dmg_scale_cv:GetFloat() )
elseif ent:IsPlayer() then
dmginfo:ScaleDamage( ply_dmg_scale_cv:GetFloat() )
end
end) --Cancel
hook.Add("EntityTakeDamage", "aaa_TFA_Melee_Block", function( ent, dmginfo )
return BlockDamageNew( ent, dmginfo )
end) --Cancel
hook.Add("ScalePlayerDamage", "aaa_TFA_Melee_Block", function( ent, _, dmginfo ) --Cancel
return BlockDamageNew( ent, dmginfo )
end)
game.AddAmmoType({
name = "TFMSwordHitGenericSlash",
dmgtype = DMG_SLASH,
tracer = TRACER_NONE
})
local function TFMPlayerSpawn(ply)
ply:SetNW2Vector("TFM_SwordPosition", Vector(1, 1, 1))
ply:SetNW2Vector("TFM_SwordNormal", Vector(1, 1, 1))
ply:SetNW2Bool("TFM_IsSprinting", false)
ply:SetNW2Bool("TFM_IsBlocking", false)
ply:SetNW2Bool("TFM_IsSwinging", false)
ply:SetNW2Float("TFM_SwingStart", CurTime())
end
hook.Add("PlayerSpawn", "TFM_PlayerSpawn", TFMPlayerSpawn)
hook.Add("EntityTakeDamage", "TFM_Block", function(ent, dmginfo) --Legacy
if ent:IsPlayer() then
local wep
wep = ent:GetActiveWeapon()
if (wep.IsTFAWeapon and wep.BlockAngle) and (dmginfo:IsDamageType(DMG_SLASH) or dmginfo:IsDamageType(DMG_CLUB) or (wep.NinjaMode and wep.NinjaMode == true and (dmginfo:IsDamageType(DMG_CRUSH) or dmginfo:IsDamageType(DMG_BULLET)))) and wep:GetIronSights() then
local damageinflictor, blockthreshold
damageinflictor = dmginfo:GetInflictor()
if (not IsValid(damageinflictor)) then
damageinflictor = dmginfo:GetAttacker()
end
blockthreshold = wep.BlockAngle / 2 or 90
if (IsValid(damageinflictor) and (math.abs((ent:GetAimVector():Angle() - (damageinflictor:GetPos() - ent:GetPos()):Angle()).y) <= blockthreshold)) or (math.abs((ent:GetAimVector():Angle() - (dmginfo:GetDamagePosition() - ent:GetPos()):Angle()).y) <= blockthreshold) then
local fac = math.Clamp((CurTime() - wep:GetBlockStart() - wep.BlockWindow) / wep.BlockFadeTime, 0, 1)
local dmgscale
if ( not timed_blocking_cv ) or timed_blocking_cv:GetBool() then
dmgscale = Lerp(fac, wep.BlockMaximum, wep.BlockMinimum)
else
dmgscale = wep.BlockMaximum
end
--print(fac)
dmginfo:ScaleDamage(dmgscale)
dmginfo:SetDamagePosition(vector_origin)
wep:EmitSound(wep.Primary.Sound_Impact_Metal)
if wep.BlockAnim then
wep:BlockAnim()
end
end
end
end
end)
--Getting the position and angle of an attachment and sending it back to the server is wayyy too laggy. Must be pre-coded.
--[[
if SERVER then
util.AddNetworkString( "TFM_SAPacket" )
net.Receive("TFM_SAPacket", function()
local ply;
ply = net.ReadEntity()
local pos;
pos = net.ReadVector()
local norm;
norm = net.ReadNormal()
if IsValid(ply) then
if pos and norm then
ply:SetNW2Vector("TFM_SwordPosition",pos)
ply:SetNW2Vector("TFM_SwordNormal",norm)
end
end
end)
end
]]
--

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