mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 13:53:45 +03:00
414 lines
12 KiB
Lua
414 lines
12 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.
|
|
|
|
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
|