Files
wnsrc/addons/tfa-base/lua/weapons/tfa_gun_base/common/utils.lua
lifestorm 94063e4369 Upload
2024-08-04 22:55:00 +03:00

1203 lines
31 KiB
Lua

--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
-- Copyright (c) 2018-2020 TFA Base Devs
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
TFA.RangeFalloffLUTStep = 0.01
TFA.RangeFalloffLUTStepInv = 1 / TFA.RangeFalloffLUTStep
SWEP.AmmoRangeTable = {
["SniperPenetratedRound"] = 2,
["SniperPenetratedBullet"] = 2,
["buckshot"] = 0.5,
["ar2"] = 1,
["smg1"] = 0.7,
["pistol"] = 0.33,
["def"] = 1
}
local IsValid = IsValid
function SWEP:AmmoRangeMultiplier()
return self.AmmoRangeTable[self.Primary_TFA.Ammo or "def"] or self.AmmoRangeTable["def"] or 1
end
function SWEP:MetersToUnits(x)
return x * 39.3701 * 4 / 3
end
function SWEP:GetLastSequenceString()
if not IsValid(self.OwnerViewModel) then return "" end
if self:GetLastSequence() < 0 then return "" end
return self.OwnerViewModel:GetSequenceName(self:GetLastSequence())
end
local cv_3dmode = GetConVar("cl_tfa_scope_sensitivity_3d")
SWEP.SensitivtyFunctions = {
[0] = function() return 1 end,
[1] = function(self, ...)
if self:GetStatL("Secondary.ScopeZoom") then
return TFA.CalculateSensitivtyScale(90 / self:GetStatL("Secondary.ScopeZoom"), self:GetStatL("Secondary.OwnerFOV"), self.Secondary_TFA.ScopeScreenScale or 0.392592592592592)
else
return self.SensitivtyFunctions[2](self, ...)
end
end,
[2] = function(self, ...)
if self:GetStatL("RTScopeFOV") then
return TFA.CalculateSensitivtyScale(self:GetStatL("RTScopeFOV"), self:GetStatL("Secondary.OwnerFOV"), self.Secondary_TFA.ScopeScreenScale or 0.392592592592592)
else
return self.SensitivtyFunctions[0](self, ...)
end
end,
[3] = function(self, ...)
if self:GetStatL("RTScopeFOV") then
return TFA.CalculateSensitivtyScale(self:GetStatL("RTScopeFOV"), self:GetStatL("Secondary.OwnerFOV"), 1)
else
return self.SensitivtyFunctions[0](self, ...)
end
end
}
function SWEP:Get3DSensitivity()
local f = self.SensitivtyFunctions[cv_3dmode:GetInt()]
return f(self)
end
function SWEP:GetSeed()
local sd = math.floor(self:Clip1() + self:Ammo1() + self:Clip2() + self:Ammo2() + self:GetLastActivity()) + self:GetNextIdleAnim() + self:GetNextPrimaryFire() + self:GetNextSecondaryFire()
return math.Round(sd)
end
function SWEP:GetSeedIrradical()
return math.floor(self:Clip1() + self:Ammo1() + self:Clip2() + self:Ammo2() + self:GetLastActivity()) + self:GetNextIdleAnim() + self:GetNextPrimaryFire() + self:GetNextSecondaryFire()
end
SWEP.SharedRandomValues = {}
local seed
--math.random equivalent
function SWEP:SharedRandom(min, max, id)
if min and not max then
max = min
min = 1
end
min = math.Round(min)
max = math.Round(max)
local key = (id or "Weapon") .. min .. max
seed = self:GetSeed()
local val = math.floor(util.SharedRandom(id or "Weapon", min, max + 1, seed))
if self.SharedRandomValues[key] and self.SharedRandomValues[key] == val then
if min < val and max > val then
math.randomseed(seed)
if (math.Rand(0, 1) < 0.5) then
math.randomseed(seed + 1)
val = math.random(min, val - 1)
else
math.randomseed(seed + 1)
val = math.random(val + 1, max)
end
elseif min < val then
math.randomseed(seed + 1)
val = math.random(min, val - 1)
elseif max > val then
math.randomseed(seed + 1)
val = math.random(val + 1, max)
end
end
if IsFirstTimePredicted() then
timer.Simple(0, function()
if IsValid(self) then
self.SharedRandomValues[key] = val
end
end)
end
return val
end
local oiv = nil
local holding_result_cached = false
local last_held_check = -1
local sp = game.SinglePlayer()
local slo, sqlo
local nm
--[[
local sqind
function SWEP:TranslateSequenceActivityTable( tbl )
if not self:VMIV() then return end
for k,v in pairs(tbl) do
if type(k) == "string" then
sqind = self.OwnerViewModel:GetSequenceActivity( self.OwnerViewModel:LookupSequence( k ) or -1 ) or -1
tbl[ sqind ] = tbl[sqind] or v
end
tbl[k] = nil
end
end
]]
--
--, seq )
function SWEP:GetActivityLengthRaw(tanim, status, animType)
local vm = self:VMIVNPC()
if not vm then return 0 end
if tanim == nil then
-- we already track last sequence so, we can account sequence
tanim = self:GetLastSequence()
animType = TFA.Enum.ANIMATION_SEQ
end
if tanim < 0 then return 0 end
if animType == nil or animType == TFA.Enum.ANIMATION_ACT then
nm = vm:GetSequenceName(vm:SelectWeightedSequenceSeeded(tanim, self:GetSeedIrradical()))
else
nm = vm:GetSequenceName(tanim)
end
local sqlen
if animType == TFA.Enum.ANIMATION_SEQ then
sqlen = vm:SequenceDuration(tanim)
elseif tanim == vm:GetSequenceActivity(vm:GetSequence()) then
sqlen = vm:SequenceDuration(vm:GetSequence())
else
sqlen = vm:SequenceDuration(vm:SelectWeightedSequenceSeeded(math.max(tanim or 1, 1), self:GetSeedIrradical()))
end
slo = self:GetStatL("StatusLengthOverride." .. nm) or self:GetStatL("StatusLengthOverride." .. (tanim or "0"))
sqlo = self:GetStatL("SequenceLengthOverride." .. nm) or self:GetStatL("SequenceLengthOverride." .. (tanim or "0"))
if status and slo then
sqlen = slo
elseif sqlo then
sqlen = sqlo
end
return sqlen
end
function SWEP:GetActivityLength(tanim, status, animType)
if not self:VMIVNPC() then return 0 end
local sqlen = self:GetActivityLengthRaw(tanim, status, animType)
if sqlen <= 0 then return 0 end
return sqlen / self:GetAnimationRate(tanim)
end
function SWEP:GetHolding()
if CurTime() > last_held_check + 0.2 then
last_held_check = CurTime()
holding_result_cached = nil
end
if holding_result_cached == nil then
holding_result_cached = false
if not IsValid(self:GetOwner()) or not self:GetOwner():IsPlayer() then
holding_result_cached = false
return false
end
local ent = self:GetOwner():GetNW2Entity("LastHeldEntity")
if not IsValid(ent) then
holding_result_cached = false
return false
end
if ent.IsPlayerHolding then
ent:SetNW2Bool("PlayerHolding", ent:IsPlayerHolding())
end
if ent:GetNW2Bool("PlayerHolding") then
holding_result_cached = true
return true
end
end
return holding_result_cached
end
function SWEP:CanInterruptShooting()
return self:GetStatL("Primary.RPM") > 160 and not self:GetStatL("BoltAction") and not self:GetStatL("BoltAction_Forced")
end
function SWEP:ReloadCV()
if rlcv then
if (not rlcv:GetBool()) and (not self.Primary_TFA.ClipSize_PreEdit) then
self.Primary_TFA.ClipSize_PreEdit = self.Primary_TFA.ClipSize
self.Primary_TFA.ClipSize = -1
self:ClearStatCache()
elseif rlcv:GetBool() and self.Primary_TFA.ClipSize_PreEdit then
self.Primary_TFA.ClipSize = self.Primary_TFA.ClipSize_PreEdit
self.Primary_TFA.ClipSize_PreEdit = nil
self:ClearStatCache()
end
end
end
function SWEP:OwnerIsValid()
if oiv == nil then
oiv = IsValid(self:GetOwner())
end
return oiv
end
function SWEP:NullifyOIV()
if oiv ~= nil then
self:GetHolding()
oiv = nil
end
return self:VMIV()
end
function SWEP:VMIVNPC()
local ply = self:GetOwner()
if ply:IsPlayer() then return self:VMIV() end
if ply:IsNPC() then
return self
end
return false
end
function SWEP:VMIV()
local owent = self:GetOwner()
if not IsValid(self.OwnerViewModel) then
if IsValid(owent) and owent.GetViewModel then
self.OwnerViewModel = owent:GetViewModel()
end
return false
else
if not IsValid(owent) or not owent.GetViewModel then
self.OwnerViewModel = nil
return false
end
return self.OwnerViewModel
end
end
function SWEP:CanChamber()
if self.C_CanChamber ~= nil then
return self.C_CanChamber
else
self.C_CanChamber = not self:GetStatL("BoltAction") and not self:GetStatL("LoopedReload") and not self.Revolver and not self:GetStatL("Primary.DisableChambering")
return self.C_CanChamber
end
end
function SWEP:GetPrimaryClipSize(calc)
local targetclip = self:GetStatL("Primary.ClipSize")
if self:CanChamber() and not (calc and self:Clip1() <= 0) then
targetclip = targetclip + (self:GetStatL("IsAkimbo") and 2 or 1)
end
return math.max(targetclip, -1)
end
function SWEP:GetPrimaryClipSizeForReload(calc)
local targetclip = self:GetStatL("Primary.ClipSize")
if self:CanChamber() and not (calc and self:Clip1() <= 0) and not self:IsJammed() then
targetclip = targetclip + (self:GetStatL("IsAkimbo") and 2 or 1)
end
return math.max(targetclip, -1)
end
function SWEP:GetSecondaryClipSize(calc)
local targetclip = self:GetStatL("Secondary.ClipSize")
return math.max(targetclip, -1)
end
--[[
function SWEP:GetPrimaryAmmoType()
return self:GetStatL( "Primary.Ammo" ) or ""
end
function SWEP:GetPrimaryAmmoTypeC()
return self:GetStatL( "Primary.Ammo" ) or self:GetPrimaryAmmoType()
end
function SWEP:Ammo1()
return self:GetOwner():GetAmmoCount( self:GetPrimaryAmmoTypeC() or 0 )
end
function SWEP:GetSecondaryAmmoType()
return self:GetStatL( "Secondary.Ammo" ) or ""
end
function SWEP:GetSecondaryAmmoTypeC()
return self:GetStatL( "Secondary.Ammo" ) or self:GetSecondaryAmmoType()
end
function SWEP:Ammo2()
return self:GetOwner():GetAmmoCount( self:GetSecondaryAmmoTypeC() or -1 )
end
]]
--
local at
function SWEP:GetPrimaryAmmoTypeC()
at = self:GetStatL("Primary.Ammo")
if at and at ~= self.Primary_TFA.Ammo then
return at
elseif self.GetPrimaryAmmoTypeOld then
return self:GetPrimaryAmmoTypeOld()
else
return self:GetPrimaryAmmoType()
end
end
function SWEP:GetSecondaryAmmoTypeC()
at = self:GetStatL("Secondary.Ammo")
if at and at ~= self.Secondary_TFA.Ammo then
return at
elseif self.GetSecondaryAmmoTypeOld then
return self:GetSecondaryAmmoTypeOld()
else
return self:GetSecondaryAmmoType()
end
end
function SWEP:Ammo1()
if not self:GetOwner():IsValid() then return false end
if self:GetOwner():IsNPC() then return 9999 end
return self:GetOwner():GetAmmoCount(self:GetPrimaryAmmoTypeC() or 0)
end
function SWEP:Ammo2()
if not self:GetOwner():IsValid() then return false end
if self:GetOwner():IsNPC() then return 9999 end
return self:GetOwner():GetAmmoCount(self:GetSecondaryAmmoTypeC() or -1)
end
function SWEP:TakePrimaryAmmo(num, pool)
-- Doesn't use clips
if self:GetStatL("Primary.ClipSize") < 0 or pool then
if (self:Ammo1() <= 0) then return end
if not self:GetOwner():IsPlayer() then return end
self:GetOwner():RemoveAmmo(math.min(self:Ammo1(), num), self:GetPrimaryAmmoTypeC())
return
end
self:SetClip1(math.max(self:Clip1() - num, 0))
end
function SWEP:TakeSecondaryAmmo(num, pool)
-- Doesn't use clips
if self:GetStatL("Secondary.ClipSize") < 0 or pool then
if (self:Ammo2() <= 0) then return end
if not self:GetOwner():IsPlayer() then return end
self:GetOwner():RemoveAmmo(math.min(self:Ammo2(), num), self:GetSecondaryAmmoTypeC())
return
end
self:SetClip2(math.max(self:Clip2() - num, 0))
end
function SWEP:IsEmpty1()
return self:GetStatL("Primary.ClipSize") > 0 and self:Clip1() == 0 or
self:GetStatL("Primary.ClipSize") <= 0 and self:Ammo1() == 0
end
function SWEP:IsEmpty2()
return self:GetStatL("Secondary.ClipSize") > 0 and self:Clip2() == 0 or
self:GetStatL("Secondary.ClipSize") <= 0 and self:Ammo2() == 0
end
SWEP.TakeAmmo1 = SWEP.TakePrimaryAmmo
SWEP.TakeAmmo2 = SWEP.TakeSecondaryAmmo
function SWEP:GetFireDelay()
if self:GetMaxBurst() > 1 and self:GetStatL("Primary.RPM_Burst") and self:GetStatL("Primary.RPM_Burst") > 0 then
return 60 / self:GetStatL("Primary.RPM_Burst")
elseif self:GetStatL("Primary.RPM_Semi") and not self.Primary_TFA.Automatic and self:GetStatL("Primary.RPM_Semi") and self:GetStatL("Primary.RPM_Semi") > 0 then
return 60 / self:GetStatL("Primary.RPM_Semi")
elseif self:GetStatL("Primary.RPM") and self:GetStatL("Primary.RPM") > 0 then
return 60 / self:GetStatL("Primary.RPM")
else
return self:GetStatL("Primary.Delay") or 0.1
end
end
function SWEP:GetBurstDelay(bur)
if not bur then
bur = self:GetMaxBurst()
end
if bur <= 1 then return 0 end
if self:GetStatL("Primary.BurstDelay") then return self:GetStatL("Primary.BurstDelay") end
return self:GetFireDelay() * 3
end
local tickrate = engine.TickInterval()
function SWEP:GetNextCorrectedPrimaryFire(delay)
local nextfire = self:GetNextPrimaryFire()
local delta = CurTime() - nextfire
if delta < 0 or delta > tickrate then
nextfire = CurTime()
end
return nextfire + delay
end
function SWEP:GetNextCorrectedSecondaryFire(delay)
local nextfire = self:GetNextSecondaryFire()
local delta = CurTime() - nextfire
if delta < 0 or delta > tickrate then
nextfire = CurTime()
end
return nextfire + delay
end
--[[
Function Name: IsSafety
Syntax: self:IsSafety().
Returns: Are we in safety firemode.
Notes: Non.
Purpose: Utility
]]
--
function SWEP:IsSafety()
if not self:GetStatL("FireModes") then return false end
local fm = self:GetStatL("FireModes")[self:GetFireMode()]
local fmn = string.lower(fm and fm or self:GetStatL("FireModes")[1])
if fmn == "safe" or fmn == "holster" then
return true
else
return false
end
end
function SWEP:UpdateMuzzleAttachment()
if not self:VMIV() then return end
local vm = self.OwnerViewModel
if not IsValid(vm) then return end
self.MuzzleAttachmentRaw = nil
if not self.MuzzleAttachmentSilenced then
self.MuzzleAttachmentSilenced = (vm:LookupAttachment("muzzle_silenced") <= 0) and self.MuzzleAttachment or "muzzle_silenced"
end
if self:GetSilenced() and self.MuzzleAttachmentSilenced then
self.MuzzleAttachmentRaw = vm:LookupAttachment(self.MuzzleAttachmentSilenced)
if not self.MuzzleAttachmentRaw or self.MuzzleAttachmentRaw <= 0 then
self.MuzzleAttachmentRaw = nil
end
end
if not self.MuzzleAttachmentRaw and self.MuzzleAttachment then
self.MuzzleAttachmentRaw = vm:LookupAttachment(self.MuzzleAttachment)
if not self.MuzzleAttachmentRaw or self.MuzzleAttachmentRaw <= 0 then
self.MuzzleAttachmentRaw = 1
end
end
local mzm = self:GetStatL("MuzzleAttachmentMod", 0)
if mzm then
if isstring(mzm) then
self.MuzzleAttachmentRaw = vm:LookupAttachment(mzm)
elseif mzm > 0 then
self.MuzzleAttachmentRaw = mzm
end
end
end
function SWEP:UpdateConDamage()
if not IsValid(self) then return end
if not self.DamageConVar then
self.DamageConVar = GetConVar("sv_tfa_damage_multiplier")
end
if self.DamageConVar and self.DamageConVar.GetFloat then
self.ConDamageMultiplier = self.DamageConVar:GetFloat()
end
end
--[[
Function Name: IsCurrentlyScoped
Syntax: self:IsCurrentlyScoped().
Returns: Is the player scoped in enough to display the overlay? true/false, returns a boolean.
Notes: Change SWEP.ScopeOverlayThreshold to change when the overlay is displayed.
Purpose: Utility
]]
--
function SWEP:IsCurrentlyScoped()
return (self:GetIronSightsProgress() > self:GetStatL("ScopeOverlayThreshold")) and self:GetStatL("Scoped")
end
--[[
Function Name: IsCurrently3DScoped
Syntax: self:IsCurrently3DScoped().
Returns: Is player aiming down the sights while having a RT-enabled scope equipped?
Notes:
Purpose: Utility
]]
--
function SWEP:IsCurrently3DScoped()
return (self:GetStatL("RTDrawEnabled") or self.RTCode ~= nil) and self:GetIronSights()
end
--[[
Function Name: IsHidden
Syntax: self:IsHidden().
Returns: Should we hide self?.
Notes:
Purpose: Utility
]]
--
function SWEP:GetHidden()
if not self:VMIV() then return true end
if self.DrawViewModel ~= nil and not self.DrawViewModel then return true end
if self.ShowViewModel ~= nil and not self.ShowViewModel then return true end
if self:GetHolding() then return true end
return self:IsCurrentlyScoped()
end
--[[
Function Name: IsFirstPerson
Syntax: self:IsFirstPerson().
Returns: Is the owner in first person.
Notes: Broken in singplayer because gary.
Purpose: Utility
]]
--
function SWEP:IsFirstPerson()
if not IsValid(self) or not self:OwnerIsValid() then return false end
if self:GetOwner():IsNPC() then return false end
if CLIENT and (not game.SinglePlayer()) and self:GetOwner() ~= GetViewEntity() then return false end
if sp and SERVER then return not self:GetOwner().TFASDLP end
if self:GetOwner().ShouldDrawLocalPlayer and self:GetOwner():ShouldDrawLocalPlayer() then return false end
if LocalPlayer and hook.Call("ShouldDrawLocalPlayer", GAMEMODE, self:GetOwner()) then return false end
return true
end
--[[
Function Name: GetMuzzlePos
Syntax: self:GetMuzzlePos( hacky workaround that doesn't work anyways ).
Returns: The AngPos for the muzzle attachment.
Notes: Defaults to the first attachment, and uses GetFPMuzzleAttachment
Purpose: Utility
]]
--
local fp
function SWEP:GetMuzzleAttachment()
local vmod = self.OwnerViewModel
local att = math.max(1, self.MuzzleAttachmentRaw or (sp and vmod or self):LookupAttachment(self.MuzzleAttachment))
if self:GetStatL("IsAkimbo") then
att = 1 + self:GetAnimCycle()
end
return att
end
function SWEP:GetMuzzlePos(ignorepos)
fp = self:IsFirstPerson()
local vm = self.OwnerViewModel
if not IsValid(vm) then
vm = self
end
-- Avoid returning strings inside MuzzleAttachmentMod, since this would decrease performance
-- Better call :UpdateMuzzleAttachment() or return number in MuzzleAttachmentMod
local obj = self:GetStatL("MuzzleAttachmentMod") or self.MuzzleAttachmentRaw or vm:LookupAttachment(self.MuzzleAttachment)
if type(obj) == "string" then
obj = tonumber(obj) or vm:LookupAttachment(obj)
end
local muzzlepos
obj = math.Clamp(obj or 1, 1, 128)
if fp then
muzzlepos = vm:GetAttachment(obj)
else
muzzlepos = self:GetAttachment(obj)
end
return muzzlepos
end
function SWEP:FindEvenBurstNumber()
local burstOverride = self:GetStatL("BurstFireCount")
if burstOverride then
return burstOverride
end
if (self:GetStatL("Primary.ClipSize") % 3 == 0) then
return 3
elseif (self:GetStatL("Primary.ClipSize") % 2 == 0) then
return 2
else
local i = 4
while i <= 7 do
if self:GetStatL("Primary.ClipSize") % i == 0 then return i end
i = i + 1
end
end
return nil
end
function SWEP:GetFireModeName()
local fm = self:GetFireMode()
local fmn = string.lower(self:GetStatL("FireModes")[fm])
if fmn == "safe" or fmn == "holster" then return language.GetPhrase("tfa.firemode.safe") end
if self:GetStatL("FireModeName") then return language.GetPhrase(self:GetStatL("FireModeName")) end
if fmn == "auto" or fmn == "automatic" then return language.GetPhrase("tfa.firemode.auto") end
if fmn == "semi" or fmn == "single" then
if self:GetStatL("Revolver") then
if (self:GetStatL("BoltAction")) then
return language.GetPhrase("tfa.firemode.single")
else
return language.GetPhrase("tfa.firemode.revolver")
end
else
if (self:GetStatL("BoltAction")) then
return language.GetPhrase("tfa.firemode.bolt")
else
if self:GetStatL("LoopedReload") and self:GetStatL("Primary.RPM") < 250 then
return language.GetPhrase("tfa.firemode.pump")
else
return language.GetPhrase("tfa.firemode.semi")
end
end
end
end
local bpos = string.find(fmn, "burst")
if bpos then return language.GetPhrase("tfa.firemode.burst"):format(string.sub(fmn, 1, bpos - 1)) end
return ""
end
SWEP.BurstCountCache = {}
function SWEP:GetMaxBurst()
local fm = self:GetFireMode()
if not self.BurstCountCache[fm] then
local fmt = self:GetStatL("FireModes")
local fmn = string.lower(fmt[fm])
local bpos = string.find(fmn, "burst")
if bpos then
self.BurstCountCache[fm] = tonumber(string.sub(fmn, 1, bpos - 1))
else
self.BurstCountCache[fm] = 1
end
end
return self.BurstCountCache[fm]
end
--[[
Function Name: CycleFireMode
Syntax: self:CycleFireMode()
Returns: Nothing.
Notes: Cycles to next firemode.
Purpose: Feature
]]
--
local l_CT = CurTime
SWEP.FireModesAutomatic = {
["Automatic"] = true,
["Auto"] = true,
}
SWEP.FireModeSound = Sound("Weapon_AR2.Empty") -- firemode toggle sound
function SWEP:CycleFireMode()
local ct = l_CT()
local fm = self:GetFireMode()
fm = fm + 1
if fm >= #self:GetStatL("FireModes") then
fm = 1
end
self:SetFireMode(fm)
local success, tanim, ttype = self:ChooseROFAnim()
if success then
self:SetNextPrimaryFire(ct + self:GetActivityLength(tanim, false, ttype))
else
self:EmitSound(self:GetStatL("FireModeSound"))
self:SetNextPrimaryFire(ct + math.max(self:GetFireDelay(), 0.25))
end
self.BurstCount = 0
self:SetIsCyclingSafety(false)
self:SetStatus(TFA.Enum.STATUS_FIREMODE, self:GetNextPrimaryFire())
self.Primary.Automatic = self:GetStatL("FireModesAutomatic." .. self:GetStatL("FireModes." .. fm)) ~= nil
self.Primary_TFA.Automatic = self.Primary.Automatic
end
--[[
Function Name: CycleSafety
Syntax: self:CycleSafety()
Returns: Nothing.
Notes: Toggles safety
Purpose: Feature
]]
--
function SWEP:CycleSafety()
local ct = l_CT()
local fm = self:GetFireMode()
local fmt = self:GetStatL("FireModes")
self.BurstCount = 0
self:SetIsCyclingSafety(true)
self:SetIronSightsRaw(false)
if fm ~= #fmt then
self.LastFireMode = fm
self:SetFireMode(#fmt)
else
self:SetFireMode(self.LastFireMode or 1)
end
local success, tanim, ttype = self:ChooseROFAnim()
if success then
self:SetSafetyCycleAnimated(true)
self:SetNextPrimaryFire(ct + self:GetActivityLength(tanim, false, ttype))
else
self:SetSafetyCycleAnimated(false)
self:EmitSound(self:GetStatL("FireModeSound"))
self:SetNextPrimaryFire(ct + math.max(self:GetFireDelay(), 0.25))
end
self:SetStatus(TFA.Enum.STATUS_FIREMODE, self:GetNextPrimaryFire())
if self:IsSafety() then
self.Primary.Automatic = false
self.Primary_TFA.Automatic = false
else
self.Primary.Automatic = self:GetStatL("FireModesAutomatic." .. self:GetStatL("FireModes." .. self:GetFireMode())) ~= nil
self.Primary_TFA.Automatic = self.Primary.Automatic
end
end
--[[
Function Name: 1FireMode
Syntax: self:ProcessFireMode()
Returns: Nothing.
Notes: Processes fire mode changing and whether the swep is auto or not.
Purpose: Feature
]]
--
function SWEP:ProcessFireMode()
local owner = self.GetOwner and self:GetOwner()
if !owner or owner and !IsValid(owner) then return end
if owner:IsNPC() then return end
if owner.GetInfoNum and owner:GetInfoNum("cl_tfa_keys_firemode", 0) > 0 then
return
end
if self:OwnerIsValid() and self:KeyPressed(IN_RELOAD) and self:KeyDown(IN_USE) and self:GetStatus() == TFA.Enum.STATUS_IDLE and (SERVER or not sp) then
if self:GetStatL("SelectiveFire") and not self:KeyDown(IN_SPEED) then
self:CycleFireMode()
elseif owner:KeyDown(IN_SPEED) then
self:CycleSafety()
end
end
end
--[[
Function Name: Unload
Syntax: self:Unload()
Returns: Nothing.
Notes: Returns Clip1 ammo to reserve.
Purpose: Utility
]]
--
function SWEP:Unload()
local amm = self:Clip1()
self:SetClip1(0)
if self.OwnerIsValid and self:OwnerIsValid() and self.Owner.GiveAmmo then
self:GetOwner():GiveAmmo(amm, self:GetPrimaryAmmoType(), true)
end
end
--[[
Function Name: Unload
Syntax: self:Unload()
Returns: Nothing.
Notes: Returns Clip1 ammo to reserve.
Purpose: Utility
]]
--
function SWEP:Unload2()
local amm = self:Clip2()
self:SetClip2(0)
if self.OwnerIsValid and self:OwnerIsValid() and self.Owner.GiveAmmo then
self:GetOwner():GiveAmmo(amm, self:GetSecondaryAmmoType(), true)
end
end
local penetration_hitmarker_cvar = GetConVar("sv_tfa_penetration_hitmarker")
function SWEP:SendHitMarker(ply, traceres, dmginfo)
if CLIENT or not penetration_hitmarker_cvar:GetBool() then return end
if not IsValid(ply) or not ply:IsPlayer() then return end
local hm3d = ply:GetInfoNum("cl_tfa_hud_hitmarker_3d_all", 0) > 0
local hm3d_sg = ply:GetInfoNum("cl_tfa_hud_hitmarker_3d_shotguns", 0) > 0 and self:GetStatL("Primary.NumShots") > 1
if hm3d or hm3d_sg then
net.Start("tfaHitmarker3D", true)
net.WriteVector(traceres.HitPos)
net.Send(ply)
else
net.Start("tfaHitmarker", true)
net.Send(ply)
end
end
SWEP.VMSeqCache = {}
local vm -- are you fucking kidding me
function SWEP:CheckVMSequence(seqname)
if not IsValid(self) then return false end
vm = self.OwnerViewModel
if not IsValid(vm) then return false end
local mdl = vm:GetModel()
if not mdl then return false end
self.VMSeqCache[mdl] = self.VMSeqCache[mdl] or {}
if self.VMSeqCache[mdl][seqname] == nil then
self.VMSeqCache[mdl][seqname] = vm:LookupSequence(seqname) >= 0
end
return self.VMSeqCache[mdl][seqname]
end
do
local function sorter(a, b)
return a.range < b.range
end
local function linear(a) return a end
function SWEP:BuildFalloffTable(input, step)
if step == nil then step = TFA.RangeFalloffLUTStep end
table.sort(input.lut, sorter)
if input.lut[1].range > 0 then
for i = #input.lut, 1, -1 do
input.lut[i + 1] = input.lut[i]
end
input.lut[1] = {range = 0, damage = 1}
end
local div = (input.units == "hammer" or input.units == "inches" or input.units == "inch" or input.units == "hu") and 1 or 0.0254
local build = {}
local minimal = input.lut[1].range
local maximal = input.lut[#input.lut].range
local fnrange = isfunction(input.range_func) and input.range_func or
input.range_func == "quintic" and TFA.Quintic or
input.range_func == "cubic" and TFA.Cubic or
input.range_func == "cosine" and TFA.Cosine or
input.range_func == "sinusine" and TFA.Sinusine or
linear
if input.bezier then
local build_range = {}
local build_damage = {}
for _, data in ipairs(input.lut) do
table.insert(build_range, data.range / div)
table.insert(build_damage, data.damage)
end
for i = 0, 1, step do
local value = fnrange(i)
table.insert(build, {TFA.tbezier(value, build_range), TFA.tbezier(value, build_damage)})
end
else
local current, next = input.lut[1], input.lut[2]
local nextindex = 1
for i = 0, 1, step do
local value = fnrange(i)
local interp = Lerp(value, minimal, maximal)
if next.range < interp then
nextindex = nextindex + 1
current, next = input.lut[nextindex], input.lut[nextindex + 1]
end
if not current or not next then break end -- safeguard
table.insert(build, {interp / div, Lerp(1 - (next.range - interp) / (next.range - current.range), current.damage, next.damage)})
end
end
return build
end
end
function SWEP:IncreaseRecoilLUT()
if not self:HasRecoilLUT() then return end
local self2 = self:GetTable()
local time = CurTime()
if not self:GetRecoilThink() then
self:SetRecoilThink(true)
end
if not self:GetRecoilLoop() then
local newvalue = self:GetRecoilInProgress() + self2.Primary_TFA.RecoilLUT["in"].increase
self:SetRecoilInProgress(math.min(1, newvalue))
self:SetRecoilInWait(time + self2.Primary_TFA.RecoilLUT["in"].wait)
if self:GetRecoilInProgress() >= 1 then
self:SetRecoilLoop(true)
self:SetRecoilLoopProgress(math.Clamp(newvalue % 1, 0, 1))
self:SetRecoilLoopWait(time + self2.Primary_TFA.RecoilLUT["loop"].wait)
end
return
end
local sub = 0
if self:GetRecoilOutProgress() ~= 0 then
local prev = self:GetRecoilOutProgress()
local newvalue = math.max(0, prev - self2.Primary_TFA.RecoilLUT["out"].increase)
self:SetRecoilOutProgress(newvalue)
self:SetRecoilLoopWait(time + self2.Primary_TFA.RecoilLUT["loop"].wait)
if newvalue ~= 0 then
return
end
sub = self2.Primary_TFA.RecoilLUT["out"].increase - prev
end
local newvalue = (self:GetRecoilLoopProgress() + self2.Primary_TFA.RecoilLUT["loop"].increase + sub) % 1
self:SetRecoilLoopProgress(newvalue)
self:SetRecoilLoopWait(time + self2.Primary_TFA.RecoilLUT["loop"].wait)
end
function SWEP:HasRecoilLUT()
return self.Primary_TFA.RecoilLUT ~= nil
end
do
local function linear(a) return a end
local function getfn(input)
return isfunction(input.func) and input.func or
input.func == "quintic" and TFA.Quintic or
input.func == "cubic" and TFA.Cubic or
input.func == "cosine" and TFA.Cosine or
input.func == "sinusine" and TFA.Sinusine or
linear
end
function SWEP:GetRecoilLUTAngle()
if not self:GetRecoilThink() then
return Angle()
end
local self2 = self:GetTable()
local isp = 1 - self:GetIronSightsProgress() * self2.GetStatL(self, "Primary.RecoilLUT_IronSightsMult")
if not self:GetRecoilLoop() then
-- currently, we only playing IN animation
local t = getfn(self2.Primary_TFA.RecoilLUT["in"])(self:GetRecoilInProgress())
local pitch = TFA.tbezier(t, self2.Primary_TFA.RecoilLUT["in"].points_p)
local yaw = TFA.tbezier(t, self2.Primary_TFA.RecoilLUT["in"].points_y)
return Angle(pitch * isp, yaw * isp)
end
local out = getfn(self2.Primary_TFA.RecoilLUT["out"])(self:GetRecoilOutProgress())
local loop = getfn(self2.Primary_TFA.RecoilLUT["loop"])(self:GetRecoilLoopProgress())
local pitch = TFA.tbezier(loop, self2.Primary_TFA.RecoilLUT["loop"].points_p)
local yaw = TFA.tbezier(loop, self2.Primary_TFA.RecoilLUT["loop"].points_y)
if out ~= 0 then
-- cooling out
self2.Primary_TFA.RecoilLUT["out"].points_p[1] = pitch
self2.Primary_TFA.RecoilLUT["out"].points_y[1] = yaw
local pitch2 = TFA.tbezier(out, self2.Primary_TFA.RecoilLUT["out"].points_p)
local yaw2 = TFA.tbezier(out, self2.Primary_TFA.RecoilLUT["out"].points_y)
return Angle(pitch2 * isp, yaw2 * isp)
end
return Angle(pitch * isp, yaw * isp)
end
end
local sv_tfa_recoil_legacy = GetConVar("sv_tfa_recoil_legacy")
function SWEP:GetAimVector()
return self:GetAimAngle():Forward()
end
function SWEP:GetAimAngle()
local ang = self:GetOwner():GetAimVector():Angle()
if sv_tfa_recoil_legacy:GetBool() and self:GetOwner():IsPlayer() then
ang:Add(self:GetOwner():GetViewPunchAngles())
elseif self:HasRecoilLUT() then
ang:Add(self:GetRecoilLUTAngle())
else
ang.p = ang.p + self:GetViewPunchP()
ang.y = ang.y + self:GetViewPunchY()
end
ang:Normalize()
return ang
end
function SWEP:EmitSoundNet(sound, ifp)
if ifp == nil then ifp = IsFirstTimePredicted() end
if not ifp then return end
if CLIENT and sp then return end
if CLIENT or sp then
self:EmitSound(sound)
return
end
local filter = RecipientFilter()
filter:AddPAS(self:GetPos())
if IsValid(self:GetOwner()) and self:GetOwner():IsPlayer() then
filter:RemovePlayer(self:GetOwner())
end
if filter:GetCount() == 0 then return end
net.Start("tfaSoundEvent", true)
net.WriteEntity(self)
net.WriteString(sound)
net.Send(filter)
end
function SWEP:StopSoundNet(sound, ifp)
if ifp == nil then ifp = IsFirstTimePredicted() end
if not ifp then return end
if CLIENT and sp then return end
if CLIENT or sp then
self:StopSound(sound)
return
end
local filter = RecipientFilter()
filter:AddPAS(self:GetPos())
if IsValid(self:GetOwner()) and self:GetOwner():IsPlayer() then
filter:RemovePlayer(self:GetOwner())
end
if filter:GetCount() == 0 then return end
net.Start("tfaSoundEventStop", true)
net.WriteEntity(self)
net.WriteString(sound)
net.Send(filter)
end