Files
VnUtest/garrysmod/addons/tfa_base/lua/tfa/modules/tfa_attachments.lua
2026-03-31 10:27:04 +03:00

409 lines
10 KiB
Lua

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})