Files
wnsrc/addons/tfa-base/lua/weapons/tfa_gun_base/common/stat.lua

414 lines
12 KiB
Lua
Raw Normal View History

2024-08-05 18:40:29 +03:00
--[[
| 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.
local tableCopy = table.Copy
function SWEP:GetStatRecursive(srctbl, stbl, ...)
local val = srctbl[stbl[1]]
for i = 2, #stbl do
if (val) then
val = val[stbl[i]]
else
return true, ...
end
end
if val == nil then
return true, ...
end
if istable(val) and val.functionTable then
local currentStat, isFinal, nocache, nct
nocache = false
for i = 1, #val do
local v = val[i]
if isfunction(v) then
if currentStat == nil then
currentStat, isFinal, nct = v(self, ...)
else
currentStat, isFinal, nct = v(self, currentStat)
end
nocache = nocache or nct
if isFinal then break end
elseif v then
currentStat = v
end
end
if currentStat ~= nil then
return false, currentStat, nocache
end
return true, ...
end
return false, val
end
SWEP.StatCache_Blacklist = {
["ViewModelBoneMods"] = true,
["WorldModelBoneMods"] = true,
["MaterialTable"] = true,
["MaterialTable_V"] = true,
["MaterialTable_W"] = true,
["ViewModelBodygroups"] = true,
["Bodygroups_V"] = true,
["WorldModelBodygroups"] = true,
["Skin"] = true
}
SWEP.StatCache = {}
SWEP.StatCache2 = {}
SWEP.StatStringCache = {}
SWEP.LastClearStatCache = 0
SWEP.ClearStatCacheWarnCount = 0
SWEP.ClearStatCacheWarned = false
local IdealCSCDeltaTime = engine.TickInterval() * 2
local LatestDataVersion = TFA.LatestDataVersion
function SWEP:ClearStatCache(vn)
return self:ClearStatCacheVersioned(vn, 0)
end
function SWEP:ClearStatCacheL(vn)
return self:ClearStatCacheVersioned(vn, LatestDataVersion)
end
local trigger_lut_rebuild = {
FalloffMetricBased = true,
Range = true,
RangeFalloff = true,
}
function SWEP:ClearStatCacheVersioned(vn, path_version)
local self2 = self:GetTable()
self2.ignore_stat_cache = true
local getpath, getpath2
if isstring(vn) then
vn = TFA.RemapStatPath(vn, path_version, self.TFADataVersion)
end
if not vn and not self2.ClearStatCacheWarned then
local ct = CurTime()
local delta = ct - self2.LastClearStatCache
if delta < IdealCSCDeltaTime and debug.traceback():find("Think2") then
self2.ClearStatCacheWarnCount = self2.ClearStatCacheWarnCount + 1
if self2.ClearStatCacheWarnCount >= 5 then
self2.ClearStatCacheWarned = true
print(("[TFA Base] Weapon %s (%s) is abusing ClearStatCache function from Think2! This will lead to really bad performance issues, tell weapon's author to fix it ASAP!"):format(self2.PrintName, self:GetClass()))
end
elseif self2.ClearStatCacheWarnCount > 0 then
self2.ClearStatCacheWarnCount = 0
end
self2.LastClearStatCache = ct
end
if vn then
local list = TFA.GetStatPathChildren(vn, path_version, self.TFADataVersion)
for i = 1, #list do
self2.StatCache[list[i]] = nil
self2.StatCache2[list[i]] = nil
end
getpath2 = self2.GetStatPath(self, vn)
getpath = getpath2[1]
else
table.Empty(self2.StatCache)
table.Empty(self2.StatCache2)
end
if vn == "Primary" or not vn then
table.Empty(self2.Primary)
local temp = {}
setmetatable(self2.Primary, {
__index = function(self3, key)
return self2.GetStatVersioned(self, "Primary." .. key, self2.TFADataVersion)
end,
__newindex = function() end
})
for k in pairs(self2.Primary_TFA) do
if isstring(k) then
temp[k] = self2.GetStatVersioned(self, "Primary." .. k, self2.TFADataVersion)
end
end
setmetatable(self2.Primary, nil)
for k, v in pairs(temp) do
self2.Primary[k] = v
end
if self2.Primary_TFA.RangeFalloffLUT_IsConverted then
self2.Primary_TFA.RangeFalloffLUT = nil
self2.AutoDetectRange(self)
end
local getLUT = self2.GetStatL(self, "Primary.RangeFalloffLUT", nil, true)
if getLUT then
self2.Primary.RangeFalloffLUTBuilt = self:BuildFalloffTable(getLUT)
end
if self2.Primary_TFA.RecoilLUT then
if self2.Primary_TFA.RecoilLUT["in"] then
self2.Primary_TFA.RecoilLUT["in"].points_p = {0}
self2.Primary_TFA.RecoilLUT["in"].points_y = {0}
for _, point in ipairs(self2.Primary_TFA.RecoilLUT["in"].points) do
table.insert(self2.Primary_TFA.RecoilLUT["in"].points_p, point.p)
table.insert(self2.Primary_TFA.RecoilLUT["in"].points_y, point.y)
end
end
if self2.Primary_TFA.RecoilLUT["loop"] then
self2.Primary_TFA.RecoilLUT["loop"].points_p = {}
self2.Primary_TFA.RecoilLUT["loop"].points_y = {}
for _, point in ipairs(self2.Primary_TFA.RecoilLUT["loop"].points) do
table.insert(self2.Primary_TFA.RecoilLUT["loop"].points_p, point.p)
table.insert(self2.Primary_TFA.RecoilLUT["loop"].points_y, point.y)
end
table.insert(self2.Primary_TFA.RecoilLUT["loop"].points_p, self2.Primary_TFA.RecoilLUT["loop"].points[1].p)
table.insert(self2.Primary_TFA.RecoilLUT["loop"].points_y, self2.Primary_TFA.RecoilLUT["loop"].points[1].y)
end
if self2.Primary_TFA.RecoilLUT["out"] then
self2.Primary_TFA.RecoilLUT["out"].points_p = {0}
self2.Primary_TFA.RecoilLUT["out"].points_y = {0}
for _, point in ipairs(self2.Primary_TFA.RecoilLUT["out"].points) do
table.insert(self2.Primary_TFA.RecoilLUT["out"].points_p, point.p)
table.insert(self2.Primary_TFA.RecoilLUT["out"].points_y, point.y)
end
table.insert(self2.Primary_TFA.RecoilLUT["out"].points_p, 0)
table.insert(self2.Primary_TFA.RecoilLUT["out"].points_y, 0)
end
end
elseif getpath == "Primary_TFA" and isstring(getpath2[2]) then
if trigger_lut_rebuild[getpath2[2]] and self2.Primary_TFA.RangeFalloffLUT_IsConverted then
self2.Primary_TFA.RangeFalloffLUT = nil
self2.AutoDetectRange(self)
end
self2.Primary[getpath[2]] = self2.GetStatVersioned(self, vn, path_version)
end
if vn == "Secondary" or not vn then
table.Empty(self2.Secondary)
local temp = {}
setmetatable(self2.Secondary, {
__index = function(self3, key)
return self2.GetStatVersioned(self, "Secondary." .. key, self2.TFADataVersion)
end,
__newindex = function() end
})
for k in pairs(self.Secondary_TFA) do
if isstring(k) then
temp[k] = self2.GetStatVersioned(self, "Secondary." .. k, self2.TFADataVersion)
end
end
setmetatable(self2.Secondary, nil)
for k, v in pairs(temp) do
self2.Secondary[k] = v
end
elseif getpath == "Secondary_TFA" and isstring(getpath2[2]) then
self2.Secondary[getpath[2]] = self2.GetStatVersioned(self, vn, path_version)
end
if CLIENT then
self:RebuildModsRenderOrder()
end
self2.ignore_stat_cache = false
hook.Run("TFA_ClearStatCache", self)
end
local ccv = GetConVar("cl_tfa_debug_cache")
function SWEP:GetStatPath(stat, path_version)
return TFA.GetStatPath(stat, path_version or 0, self.TFADataVersion)
end
function SWEP:RemapStatPath(stat, path_version)
return TFA.RemapStatPath(stat, path_version or 0, self.TFADataVersion)
end
function SWEP:GetStatPathRaw(stat)
return TFA.GetStatPathRaw(stat)
end
function SWEP:GetStatRaw(stat, path_version)
local self2 = self:GetTable()
local path = TFA.GetStatPath(stat, path_version or 0, self2.TFADataVersion)
local value = self2[path[1]]
for i = 2, #path do
if not istable(value) then return end
value = value[path[i]]
end
return value
end
function SWEP:GetStatRawL(stat)
return self:GetStatRaw(stat, LatestDataVersion)
end
function SWEP:SetStatRaw(stat, path_version, _value)
local self2 = self:GetTable()
local path = TFA.GetStatPath(stat, path_version or 0, self2.TFADataVersion)
if #path == 1 then
self2[path[1]] = _value
return self
end
local value = self2[path[1]]
for i = 2, #path - 1 do
if not istable(value) then return self end
value = value[path[i]]
end
if istable(value) then
value[path[#path]] = _value
end
return self
end
function SWEP:SetStatRawL(stat, _value)
return self:SetStatRaw(stat, LatestDataVersion, _value)
end
function SWEP:GetStat(stat, default, dontMergeTables)
return self:GetStatVersioned(stat, 0, default, dontMergeTables)
end
function SWEP:GetStatL(stat, default, dontMergeTables)
return self:GetStatVersioned(stat, LatestDataVersion, default, dontMergeTables)
end
function SWEP:GetStatVersioned(stat, path_version, default, dontMergeTables)
local self2 = self:GetTable()
local statPath, currentVersionStat, translate = self2.GetStatPath(self, stat, path_version)
if self2.StatCache2[currentVersionStat] ~= nil then
local finalReturn
if self2.StatCache[currentVersionStat] ~= nil then
finalReturn = self2.StatCache[currentVersionStat]
else
local isDefault, retval = self2.GetStatRecursive(self, self2, statPath)
if retval ~= nil then
if not isDefault then
self2.StatCache[currentVersionStat] = retval
end
finalReturn = retval
else
finalReturn = istable(default) and tableCopy(default) or default
end
end
--local getstat = hook.Run("TFA_GetStat", self, currentVersionStat, finalReturn)
--if getstat ~= nil then return translate(getstat) end
return translate(finalReturn)
end
if not self2.OwnerIsValid(self) then
local finalReturn = default
if IsValid(self) then
local _
_, finalReturn = self2.GetStatRecursive(self, self2, statPath, istable(default) and tableCopy(default) or default)
end
--local getstat = hook.Run("TFA_GetStat", self, currentVersionStat, finalReturn)
--if getstat ~= nil then return translate(getstat) end
return translate(finalReturn)
end
local isDefault, statSelf = self2.GetStatRecursive(self, self2, statPath, istable(default) and tableCopy(default) or default)
local isDefaultAtt, statAttachment, noCache = self2.GetStatRecursive(self, self2.AttachmentTableCache, statPath, istable(statSelf) and tableCopy(statSelf) or statSelf)
local shouldCache = not noCache and
not (self2.StatCache_Blacklist_Real or self2.StatCache_Blacklist)[currentVersionStat] and
not (self2.StatCache_Blacklist_Real or self2.StatCache_Blacklist)[statPath[1]] and
not (ccv and ccv:GetBool())
if istable(statAttachment) and istable(statSelf) and not dontMergeTables then
statSelf = table.Merge(tableCopy(statSelf), statAttachment)
else
statSelf = statAttachment
end
if shouldCache and not self2.ignore_stat_cache then
if not isDefault or not isDefaultAtt then
self2.StatCache[currentVersionStat] = statSelf
end
self2.StatCache2[currentVersionStat] = true
end
--local getstat = hook.Run("TFA_GetStat", self, currentVersionStat, statSelf)
--if getstat ~= nil then return translate(getstat) end
return translate(statSelf)
end