├── addons ├── igs-core │ ├── lua │ │ ├── igs │ │ │ ├── modules │ │ │ │ ├── pushes │ │ │ │ │ ├── _main.lua │ │ │ │ │ ├── init_sv.lua │ │ │ │ │ ├── actions_sv.lua │ │ │ │ │ └── akupol_sv.lua │ │ │ │ ├── inv_log │ │ │ │ │ ├── _main.lua │ │ │ │ │ ├── integrator_sv.lua │ │ │ │ │ ├── core_cl.lua │ │ │ │ │ ├── core_sv.lua │ │ │ │ │ ├── README.MD │ │ │ │ │ └── interface_cl.lua │ │ │ │ └── extra │ │ │ │ │ ├── init_cl.lua │ │ │ │ │ ├── _main.lua │ │ │ │ │ ├── error_logging_sv.lua │ │ │ │ │ ├── useful_functions.lua │ │ │ │ │ ├── installation_check_sv.lua │ │ │ │ │ ├── new_items_notify_cl.lua │ │ │ │ │ ├── init_sh.lua │ │ │ │ │ └── don_grace_sv.lua │ │ │ ├── extensions │ │ │ │ ├── evolve.lua │ │ │ │ ├── pointshop2.lua │ │ │ │ ├── fadmin.lua │ │ │ │ ├── levels.lua │ │ │ │ ├── shwhitelist.lua │ │ │ │ ├── badmin.lua │ │ │ │ ├── bwhitelist.lua │ │ │ │ ├── serverguard.lua │ │ │ │ ├── xadmin.lua │ │ │ │ ├── infammo.lua │ │ │ │ ├── sam.lua │ │ │ │ ├── ulx.lua │ │ │ │ ├── darkrp.lua │ │ │ │ └── extra.lua │ │ │ ├── dependencies │ │ │ │ ├── resources.lua │ │ │ │ ├── plurals.lua │ │ │ │ ├── chatprint.lua │ │ │ │ ├── stack.lua │ │ │ │ ├── dash │ │ │ │ │ └── misc.lua │ │ │ │ ├── bib.lua │ │ │ │ ├── scc.lua │ │ │ │ ├── matex.lua │ │ │ │ ├── lolib.lua │ │ │ │ └── permasents.lua │ │ │ ├── utils │ │ │ │ ├── ut_cl.lua │ │ │ │ ├── ut_sv.lua │ │ │ │ └── ut_sh.lua │ │ │ ├── interface │ │ │ │ ├── vgui │ │ │ │ │ ├── igs_wmat.lua │ │ │ │ │ ├── igs_html.lua │ │ │ │ │ ├── igs_button.lua │ │ │ │ │ ├── igs_sidebar.lua │ │ │ │ │ ├── igs_panels_layout_list.lua │ │ │ │ │ ├── igs_group.lua │ │ │ │ │ ├── igs_multipanel.lua │ │ │ │ │ ├── igs_frame.lua │ │ │ │ │ ├── igs_tabbar.lua │ │ │ │ │ ├── igs_panels_layout.lua │ │ │ │ │ ├── igs_table.lua │ │ │ │ │ └── igs_iteminfo.lua │ │ │ │ ├── windows │ │ │ │ │ ├── group_content.lua │ │ │ │ │ ├── modals.lua │ │ │ │ │ └── item_info.lua │ │ │ │ ├── activities │ │ │ │ │ ├── purchases.lua │ │ │ │ │ ├── inventory.lua │ │ │ │ │ ├── profile.lua │ │ │ │ │ └── main.lua │ │ │ │ └── skin.lua │ │ │ ├── servers │ │ │ │ ├── serv_sh.lua │ │ │ │ └── serv_sv.lua │ │ │ ├── objects │ │ │ │ ├── shop_group.lua │ │ │ │ └── level.lua │ │ │ ├── launcher.lua │ │ │ ├── repeater.lua │ │ │ ├── processor_sv.lua │ │ │ └── network │ │ │ │ ├── nw_sh.lua │ │ │ │ └── net_cl.lua │ │ ├── entities │ │ │ ├── npc_igs │ │ │ │ ├── shared.lua │ │ │ │ ├── init.lua │ │ │ │ └── cl_init.lua │ │ │ └── ent_igs │ │ │ │ ├── shared.lua │ │ │ │ ├── cl_init.lua │ │ │ │ └── init.lua │ │ └── autorun │ │ │ └── l_ingameshop.lua │ └── README.md └── igs-modification │ └── lua │ ├── igs │ └── settings │ │ ├── config_sv.lua │ │ ├── sh_addlevels.lua │ │ └── config_sh.lua │ └── autorun │ └── l_ingameshopmod.lua ├── luapack ├── tableex.lua ├── main.lua ├── stringex.lua └── file.lua ├── .gitignore ├── .github └── workflows │ └── release_superfile.yml └── README.md /addons/igs-core/lua/igs/modules/pushes/_main.lua: -------------------------------------------------------------------------------- 1 | IGS.sv("akupol_sv.lua") 2 | IGS.sv("init_sv.lua") 3 | IGS.sv("actions_sv.lua") 4 | -------------------------------------------------------------------------------- /luapack/tableex.lua: -------------------------------------------------------------------------------- 1 | function table.Add(targ, source) 2 | for _,v in ipairs(source) do 3 | targ[#targ + 1] = v 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/modules/inv_log/_main.lua: -------------------------------------------------------------------------------- 1 | IGS.sv("core_sv.lua") 2 | IGS.sv("integrator_sv.lua") 3 | IGS.cl("core_cl.lua") 4 | IGS.cl("interface_cl.lua") 5 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/modules/extra/init_cl.lua: -------------------------------------------------------------------------------- 1 | list.Set("DesktopWindows", "IGS",{ 2 | title = "Автодонат", 3 | icon = "icon16/money_add.png", 4 | init = function() 5 | IGS.UI() 6 | end 7 | }) 8 | 9 | -- IGS.UI() 10 | -------------------------------------------------------------------------------- /addons/igs-core/lua/entities/npc_igs/shared.lua: -------------------------------------------------------------------------------- 1 | ENT.Base = "base_ai" 2 | ENT.Type = "ai" 3 | ENT.PrintName = "Донат NPC" 4 | ENT.Author = "GMDonate" 5 | ENT.Category = "IGS" 6 | ENT.Spawnable = true 7 | 8 | ENT.RenderGroup = RENDERGROUP_TRANSLUCENT 9 | -------------------------------------------------------------------------------- /addons/igs-modification/lua/igs/settings/config_sv.lua: -------------------------------------------------------------------------------- 1 | -- Вот так просто! :) 2 | 3 | -- ID проекта в системе 4 | IGS.C.ProjectID = 0 5 | 6 | -- Секретный ключ проекта. Никому не сообщайте. 7 | -- С этим ключом можно запрашивать и изменять данные ваших донатеров 8 | IGS.C.ProjectKey = "" 9 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/modules/extra/_main.lua: -------------------------------------------------------------------------------- 1 | IGS.sv("don_grace_sv.lua") 2 | IGS.sv("error_logging_sv.lua") 3 | IGS.sv("installation_check_sv.lua") 4 | IGS.sv("useful_functions.lua") 5 | IGS.cl("new_items_notify_cl.lua") 6 | 7 | IGS.sh("init_sh.lua") 8 | IGS.sv("init_sv.lua") 9 | IGS.cl("init_cl.lua") 10 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/extensions/evolve.lua: -------------------------------------------------------------------------------- 1 | local ITEM = MT_IGSItem 2 | 3 | function ITEM:SetEvolveRank(rank) 4 | return self:SetInstaller(function(pl) 5 | evolve.PlayerInfo[pl:UniqueID()]["Rank"] = rank 6 | end):SetValidator(function(pl) 7 | return evolve.PlayerInfo[pl:UniqueID()]["Rank"] == rank 8 | end):SetMeta("ev_rank", rank) 9 | end 10 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/dependencies/resources.lua: -------------------------------------------------------------------------------- 1 | -- FontAwesome 32 2 | -- https://steamcommunity.com/sharedfiles/filedetails/?id=1562487365 3 | -- /materials/icons/fa32/{icon_name}.png 4 | resource.AddWorkshop("1562487365") 5 | 6 | -- resource.AddWorkshop("577994158") -- модели подарков 7 | -- "models/christmas_gift2/christmas_gift.mdl" 8 | -- "models/christmas_gift2/christmas_gift2.mdl" -------------------------------------------------------------------------------- /addons/igs-core/lua/entities/ent_igs/shared.lua: -------------------------------------------------------------------------------- 1 | ENT.Type = "anim" 2 | ENT.Base = "base_anim" 3 | ENT.PrintName = "Донат итем" 4 | ENT.Author = "GMDonate" 5 | ENT.Category = "IGS" 6 | 7 | ENT.RenderGroup = RENDERGROUP_TRANSLUCENT 8 | 9 | function ENT:SetupDataTables() 10 | self:NetworkVar("Entity", 0, "owning_ent") 11 | self:NetworkVar("String", 0, "UID") 12 | end 13 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/extensions/pointshop2.lua: -------------------------------------------------------------------------------- 1 | local STORE_ITEM = MT_IGSItem 2 | 3 | function STORE_ITEM:SetPremiumPoints(iAmount) 4 | return self:SetInstaller(function(pl) 5 | pl:PS2_AddPremiumPoints(iAmount) 6 | end):SetMeta("ps2_prempoints", iAmount) 7 | end 8 | 9 | function STORE_ITEM:SetPoints(iAmount) 10 | return self:SetInstaller(function(pl) 11 | pl:PS2_AddStandardPoints(iAmount, "/donate") 12 | end):SetMeta("ps2_points", iAmount) 13 | end 14 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/extensions/fadmin.lua: -------------------------------------------------------------------------------- 1 | -- Кто-то использует FAdmin, как отдельный аддон к другим гейммодам? О_о 2 | 3 | local STORE_ITEM = MT_IGSItem 4 | 5 | function STORE_ITEM:SetFAdminGroup(sGroup, iWeight) 6 | return self:SetInstaller(function(pl) 7 | FAdmin.Access.PlayerSetGroup(pl, sGroup) 8 | pl.IGSFAdminWeight = iWeight 9 | end):SetValidator(function(pl) 10 | if pl.IGSFAdminWeight then 11 | return iWeight < pl.IGSFAdminWeight 12 | end 13 | 14 | return pl:IsUserGroup(sGroup) 15 | end) 16 | end 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | 4 | # Compiled Lua sources 5 | luac.out 6 | 7 | # luarocks build files 8 | *.src.rock 9 | *.zip 10 | *.tar.gz 11 | 12 | # Object files 13 | *.o 14 | *.os 15 | *.ko 16 | *.obj 17 | *.elf 18 | 19 | # Precompiled Headers 20 | *.gch 21 | *.pch 22 | 23 | # Libraries 24 | *.lib 25 | *.a 26 | *.la 27 | *.lo 28 | *.def 29 | *.exp 30 | 31 | # Shared objects (inc. Windows DLLs) 32 | *.dll 33 | *.so 34 | *.so.* 35 | *.dylib 36 | 37 | # Executables 38 | *.exe 39 | *.out 40 | *.app 41 | *.i*86 42 | *.x86_64 43 | *.hex 44 | 45 | -------------------------------------------------------------------------------- /luapack/main.lua: -------------------------------------------------------------------------------- 1 | require("file") 2 | require("stringex") 3 | 4 | local source, save_to = arg[1], arg[2] 5 | source, save_to = source or "source/lua/igs", save_to or "superfile.json" 6 | 7 | 8 | local superfile = {} 9 | 10 | local index = file.Index(source) 11 | for _,path in ipairs(index) do 12 | local pref_pat = string.PatternSafe(source) 13 | local path_sub = path:match(pref_pat .. "/(.+)$") 14 | superfile[path_sub] = file.Read(path) 15 | end 16 | 17 | local json = require("json") 18 | local superfile_json = json.stringify(superfile) 19 | file.AdvWrite(save_to, superfile_json) 20 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/extensions/levels.lua: -------------------------------------------------------------------------------- 1 | --[[------------------------------------------------------------------------- 2 | Поддержка вот этого говнокода: 3 | https://github.com/vrondakis/Leveling-System 4 | ---------------------------------------------------------------------------]] 5 | local STORE_ITEM = MT_IGSItem 6 | 7 | function STORE_ITEM:SetLevels(iAmount) 8 | return self:SetInstaller(function(pl) 9 | pl:addLevels(iAmount) 10 | end) 11 | end 12 | 13 | function STORE_ITEM:SetEXP(iAmount) 14 | return self:SetInstaller(function(pl) 15 | pl:addXP(iAmount) 16 | end) 17 | end 18 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/dependencies/plurals.lua: -------------------------------------------------------------------------------- 1 | local plural_type = function(i) 2 | return i % 10 == 1 and i % 100 ~= 11 and 1 3 | or (i % 10 >= 2 and i % 10 <= 4 and (i % 100 < 10 or i % 100 >= 20) and 2 4 | or 3 5 | ) 6 | end 7 | 8 | function util.formatPlural(plurals, num) 9 | local type = plural_type(num) 10 | local suffix = plurals[type] 11 | return num .. " " .. suffix, suffix 12 | end 13 | 14 | function PLUR(plurals) 15 | return function(num) 16 | return util.formatPlural(plurals, num) 17 | end 18 | end 19 | 20 | -- local P = PLUR({"пост", "поста", "постов"}) 21 | -- PRINT(P(1), P(2), P(5)) 22 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/extensions/shwhitelist.lua: -------------------------------------------------------------------------------- 1 | -- Автор @Joch 2 | -- Тут и пример использования 3 | -- https://forum.gm-donate.net/t/2129/7 4 | 5 | local ITEM = MT_IGSItem 6 | 7 | function ITEM:SetSHWhitelist(team_cmd) 8 | return self:SetCanActivate(function(pl) 9 | if SH_WHITELIST:CanBecomeJob(pl, DarkRP.getJobByCommand(team_cmd)) then 10 | return "Вы в вайтлисте" 11 | end 12 | end):AddHook("SH_WHITELIST.CanBecomeJob", function(pl, job) 13 | if job.command == team_cmd then return true end 14 | end):SetValidator(function(pl) 15 | return SH_WHITELIST:CanBecomeJob(pl, DarkRP.getJobByCommand(team_cmd)) 16 | end) 17 | end 18 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/modules/extra/error_logging_sv.lua: -------------------------------------------------------------------------------- 1 | hook.Add("IGS.OnApiError", "LogError", function(sMethod, error_uid, tParams) 2 | if error_uid == "http_error" then 3 | IGS.prints(Color(255, 0, 0), "", "CEPBEPA GMD BPEMEHHO HE9OCTynHbI. y}{e PEWAEM nPO6JIEMy") 4 | end 5 | 6 | local sparams = "\n" 7 | for k,v in pairs(tParams) do 8 | sparams = sparams .. ("\t%s = %s\n"):format(k,v) 9 | end 10 | 11 | local split = string.rep("-",50) 12 | local err_log = 13 | os.date("%Y-%m-%d %H:%M\n") .. 14 | split .. 15 | "\nMethod: " .. sMethod .. 16 | "\nError: " .. error_uid .. 17 | "\nParams: " .. sparams .. 18 | split .. "\n\n\n" 19 | 20 | file.Append("igs_errors.txt",err_log) 21 | IGS.dprint(Color(250, 50, 50), err_log) 22 | end) 23 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/dependencies/chatprint.lua: -------------------------------------------------------------------------------- 1 | if chat and chat.AddTextSV then return end 2 | 3 | 4 | if SERVER then 5 | util.AddNetworkString("ChatPrintColor") 6 | 7 | 8 | local function sayColor(targ, ...) 9 | local args = {...} 10 | -- PrintTable(args) 11 | net.Start("ChatPrintColor") 12 | net.WriteTable(IsColor(args[1]) and args or args[1]) 13 | net[targ and "Send" or "Broadcast"](targ) 14 | end 15 | 16 | local PLAYER = FindMetaTable("Player") 17 | function PLAYER:ChatPrintColor(...) 18 | sayColor(self, ...) 19 | end 20 | 21 | chat = chat or {} 22 | function chat.AddTextSV(...) 23 | sayColor(nil, ...) 24 | end 25 | 26 | else 27 | net.Receive("ChatPrintColor",function() 28 | chat.AddText(unpack(net.ReadTable())) 29 | end) 30 | end 31 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/modules/extra/useful_functions.lua: -------------------------------------------------------------------------------- 1 | -- Функции, которые могут быть полезны, но не используются в самом ядре 2 | 3 | -- Списать деньги у SteamID и отправить другому. 4 | -- Если SteamID в сети, то баланс обновится сразу. 5 | -- Впервые использовалось в коде "аукциона". 6 | function IGS.PayP2P(s64Who, s64Targ, iSum, sNote, fCallback) 7 | local pWho = player.GetBySteamID64(s64Who) 8 | local pTarg = player.GetBySteamID64(s64Targ) 9 | 10 | local fCreateTxWho = pWho and pWho.AddIGSFunds or IGS.Transaction 11 | local fCreateTxTarg = pTarg and pTarg.AddIGSFunds or IGS.Transaction 12 | 13 | fCreateTxWho(pWho or s64Who, -iSum, sNote, function() 14 | fCreateTxTarg(pTarg or s64Targ, iSum, sNote, fCallback) -- tx id in callback 15 | end) 16 | end 17 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/utils/ut_cl.lua: -------------------------------------------------------------------------------- 1 | -- Норм название сервера по его ИД 2 | -- Если вернут "-", значит сервер скорее всего, отключен 3 | function IGS.ServerName(iID) 4 | local serv_name = iID == 0 and "Откл." or IGS.SERVERS(iID) 5 | serv_name = serv_name or "Глоб." -- IGS.SERVERS(iID) вернул nil = везде 6 | -- serv_name = serv_name[1]:upper() .. serv_name:sub(2) -- апперкейсит первую букву 7 | 8 | return serv_name 9 | end 10 | 11 | 12 | function IGS.ProcessActivate(dbID, cb) 13 | IGS.Activate(dbID,function(ok, iPurchID, sMsg_) 14 | sMsg_ = sMsg_ or "Предмет активирован. Спасибо вам!" 15 | IGS.ShowNotify(sMsg_, ok and "Успешная активация" or "Ошибка активации") 16 | 17 | if cb then 18 | cb(ok, iPurchID, sMsg_) 19 | end 20 | end) 21 | end 22 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/modules/pushes/init_sv.lua: -------------------------------------------------------------------------------- 1 | local log = kupol.log 2 | 3 | local function handleUpdate(upd) 4 | if log.level == 1 or log.level == 2 then 5 | PrintTable(upd) 6 | end 7 | 8 | if not upd.ok then 9 | log.warning("GMD вернул ошибку: {}", upd.error) 10 | return 11 | end 12 | 13 | -- {cp = true/false, data = {}, ok = true, method = "payment"} 14 | hook.Run("IGS.IncomingMessage", upd.data, upd.method, upd) 15 | end 16 | 17 | hook.Add("IGS.Loaded", "polling", function() 18 | if IGS.POLLING then 19 | log.debug("Stopping previous poller") 20 | IGS.POLLING.stop() 21 | end 22 | 23 | log.info("Start polling") 24 | local uid = string.format("gmd_%s_%s", IGS.C.ProjectID, IGS.C.ProjectKey) -- antinil 25 | IGS.POLLING = kupol.new("https://poll.gmod.app/", uid, 30).start(handleUpdate) 26 | end) 27 | -------------------------------------------------------------------------------- /addons/igs-core/lua/entities/npc_igs/init.lua: -------------------------------------------------------------------------------- 1 | IGS.sh("shared.lua") 2 | IGS.cl("cl_init.lua") 3 | 4 | function ENT:Initialize() 5 | self:SetModel(IGS_NPC_MODEL or "models/gman_high.mdl") 6 | self:SetHullType( HULL_HUMAN ) 7 | self:SetHullSizeNormal() 8 | self:SetSolid( SOLID_BBOX ) 9 | self:CapabilitiesAdd( CAP_ANIMATEDFACE ) 10 | self:CapabilitiesAdd( CAP_TURN_HEAD ) 11 | self:DropToFloor() 12 | self:SetMoveType( MOVETYPE_NONE ) 13 | self:SetCollisionGroup( COLLISION_GROUP_PLAYER ) 14 | self:SetUseType( SIMPLE_USE ) 15 | 16 | local phys = self:GetPhysicsObject() 17 | if IsValid(phys) then 18 | phys:EnableMotion(false) 19 | end 20 | end 21 | 22 | function ENT:PlayerUse(pl) 23 | IGS.UI(pl) 24 | end 25 | 26 | function ENT:AcceptInput(name, activator, pl, data) 27 | if name == "Use" then 28 | self:PlayerUse(pl) 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/vgui/igs_wmat.lua: -------------------------------------------------------------------------------- 1 | -- 2025.01.03 сделан невероятный рефакторинг, который упростил код до размера мухи 2 | 3 | local PANEL = {} 4 | 5 | local default_mater = Material("models/effects/portalrift_sheet") 6 | function PANEL:Paint(w, h) 7 | surface.SetDrawColor( IGS.col.ICON ) 8 | surface.SetMaterial( self.material or (self.url and matex.now(self.url)) or matex.now(IGS.C.DefaultIcon) or default_mater ) 9 | surface.DrawTexturedRect(0, 0, w, h) 10 | end 11 | 12 | -- Для SetIcon mode == "material", например 13 | function PANEL:SetMaterial(sMaterial) -- "models/debug/debugwhite" 14 | self.material = sMaterial and Material(sMaterial, "noclamp smooth") or nil 15 | end 16 | 17 | -- Укажите nil/false для сброса 18 | function PANEL:SetURL(sUrl) 19 | self.url = sUrl 20 | end 21 | 22 | vgui.Register("igs_wmat", PANEL, "Panel") 23 | -- IGS.UI() 24 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/extensions/badmin.lua: -------------------------------------------------------------------------------- 1 | local STORE_ITEM = MT_IGSItem 2 | 3 | function STORE_ITEM:SetBAdminGroup(rank) 4 | return self:SetInstaller(function(pl) 5 | assert(rank, "IGS Rank expected, got " .. type(rank)) 6 | local RANK = assert(ba.ranks.Get(rank), "IGS Rank " .. rank .. " invalid") 7 | pl:SetNetVar("UserGroup", RANK:GetID()) 8 | end):AddHook("IGS.PlayerPurchasesLoaded", function(pl, purchases) -- #TODO упростить хук. sam использует тот же 9 | if CLIENT or not purchases then return end 10 | 11 | local priority_item = self 12 | 13 | for uid in pairs(purchases) do 14 | local ITEM = IGS.GetItemByUID(uid) 15 | if ITEM:GetMeta("bagroup") and ITEM.id > self.id then 16 | priority_item = ITEM 17 | end 18 | end 19 | 20 | if priority_item == self then 21 | self:Setup(pl) 22 | 23 | end 24 | end):SetMeta("bagroup", rank) 25 | end 26 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/vgui/igs_html.lua: -------------------------------------------------------------------------------- 1 | local PANEL = {} 2 | 3 | function PANEL:Init() 4 | local c = IGS.col.HIGHLIGHT_INACTIVE 5 | self.text_color = Color(c.r,c.g,c.b,c.a) 6 | end 7 | 8 | local LOADING_TEXT = "Загрузочка..." 9 | local ICO = Material("materials/icons/fa32/usd.png", "smooth") 10 | 11 | function PANEL:Paint(w,h) 12 | self.text_color.a = Lerp( (math.sin(CurTime() * 5) + 1) / 2 ,0,255) -- Alpha 13 | 14 | -- ИКОНКА 15 | surface.SetDrawColor(self.text_color) 16 | surface.SetMaterial(ICO) 17 | local y = (h - 50) / 2 18 | surface.DrawTexturedRect((w - 50) / 2,y - 10,50,50) 19 | y = y + 50 -- центральная точка между иконкой и текстом 20 | 21 | -- ТЕКСТ 22 | surface.SetTextColor(self.text_color) 23 | surface.SetFont("igs.24") -- 40 24 | local tw = surface.GetTextSize(LOADING_TEXT) 25 | surface.SetTextPos((w - tw) / 2,y + 10) 26 | surface.DrawText(LOADING_TEXT) 27 | end 28 | 29 | vgui.Register("igs_html",PANEL,"HTML") 30 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/vgui/igs_button.lua: -------------------------------------------------------------------------------- 1 | local PANEL = {} 2 | 3 | function PANEL:Init() 4 | self:SetTextColor(IGS.col.HIGHLIGHTING) 5 | self:SetFont("igs.18") 6 | end 7 | 8 | -- Как блять по человечески оверрайднуть эту хуетоту? 9 | -- function PANEL:SetText(text) 10 | -- self.BaseClass:SetText(" " .. text .. " ") -- чтобы при SizeToContent было не вплотную к стенкам 11 | -- end 12 | 13 | function PANEL:SetActive(bActive) 14 | self.active = bActive 15 | 16 | self:SetTextColor(self.active and IGS.col.TEXT_ON_HIGHLIGHT or IGS.col.HIGHLIGHTING) 17 | end 18 | 19 | function PANEL:IsActive() 20 | return self.active 21 | end 22 | 23 | function PANEL:Paint(w,h) 24 | draw.RoundedBox(4,0,0,w,h,IGS.col.HIGHLIGHTING) -- outline 25 | 26 | if not self.active then 27 | draw.RoundedBox(4,1,1,w - 2,h - 2,IGS.col.PASSIVE_SELECTIONS) -- bg TODO изменить, сделав как-то прозрачным 28 | end 29 | end 30 | 31 | vgui.Register("igs_button",PANEL,"DButton") 32 | 33 | -- IGS.UI() 34 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/vgui/igs_sidebar.lua: -------------------------------------------------------------------------------- 1 | local PANEL = {} 2 | 3 | function PANEL:Init() 4 | -- Херня справа от лэйаута с услугами http://joxi.ru/52aQQ8Efzov120 5 | -- Вид без нее: http://joxi.ru/eAO44lGcXORlro 6 | self.sidebar = uigs.Create("Panel", function(sbar) 7 | sbar:Dock(FILL) 8 | end, self) 9 | 10 | -- Верхняя часть http://joxi.ru/5mdWW05tzW6Wr1 11 | self.header = uigs.Create("Panel", function(header) 12 | header:SetTall(40) 13 | header:Dock(TOP) 14 | end, self.sidebar) 15 | end 16 | 17 | -- Заголовок сайдбара "Последние покупки" и т.д. 18 | function PANEL:SetTitle(sTitle) 19 | self.title = self.title or uigs.Create("DLabel", function(title) 20 | title:Dock(BOTTOM) 21 | title:SetTall(24) 22 | title:SetFont("igs.19") 23 | title:SetTextColor(IGS.col.TEXT_HARD) 24 | title:SetContentAlignment(8) 25 | end, self.header) 26 | 27 | self.title:SetText(sTitle) 28 | 29 | return self.title 30 | end 31 | 32 | vgui.Register("igs_sidebar",PANEL,"Panel") 33 | --IGS.UI() 34 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/vgui/igs_panels_layout_list.lua: -------------------------------------------------------------------------------- 1 | local PANEL = {} 2 | 3 | function PANEL:Init() 4 | -- categ_name > categpan 5 | self.list = {} 6 | end 7 | 8 | -- Одиночное добавление 9 | function PANEL:Add(panel,sCategory) 10 | local cat = sCategory or "Разное" 11 | 12 | if not self.list[cat] then 13 | self.list[cat] = uigs.Create("igs_panels_layout", self) 14 | self.list[cat]:SetWide(650) 15 | self.list[cat]:SetName(sCategory) 16 | self.list[cat]:DisableAlignment(self.disabled_align) 17 | 18 | self:AddItem(self.list[cat]) 19 | end 20 | 21 | self.list[cat]:Add(panel) 22 | 23 | return self.list[cat] 24 | end 25 | 26 | -- Отключает центрирование эллементов во всех панелях лэйаута 27 | function PANEL:DisableAlignment(bDisable) 28 | self.disabled_align = bDisable 29 | end 30 | 31 | function PANEL:Clear() 32 | for _,panel in pairs(self.list) do -- categ 33 | panel:Remove() 34 | end 35 | 36 | self:Init() 37 | end 38 | 39 | vgui.Register("igs_panels_layout_list", PANEL, "igs_scroll") 40 | -- IGS.UI() 41 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/modules/inv_log/integrator_sv.lua: -------------------------------------------------------------------------------- 1 | hook.Add("IGS.PlayerPurchasedItem", "IL.Integration", function(owner, ITEM, invDbID) 2 | if not IGS.C.Inv_Enabled then return end -- #todo сделать inv_log модулем и убрать проверку (mb nil if inv disabled) 3 | IGS.IL.Log(invDbID, ITEM:UID(), owner:SteamID64(), owner:SteamID64(), IGS.IL.NEW) 4 | end) 5 | 6 | -- ранее было на playeractivatedite. но там iPurchID 7 | hook.Add("IGS.PlayerActivatedInventoryItem", "IL.Integration", function(owner, ITEM, invDbID) 8 | IGS.IL.Log(invDbID, ITEM:UID(), owner:SteamID64(), owner:SteamID64(), IGS.IL.ACT) 9 | end) 10 | 11 | hook.Add("IGS.PlayerDroppedGift", "IL.Integration", function(owner, UID, invDbID) 12 | IGS.IL.Log(invDbID, UID, owner:SteamID64(), owner:SteamID64(), IGS.IL.DROP) 13 | end) 14 | 15 | hook.Add("IGS.PlayerPickedGift", "IL.Integration", function(owner, UID, invDbID, picker) 16 | if not IsValid(owner) then return end -- самодельный гифт 17 | IGS.IL.Log(invDbID, UID, owner:SteamID64(), picker:SteamID64(), IGS.IL.PICK) 18 | end) 19 | -------------------------------------------------------------------------------- /addons/igs-core/lua/entities/ent_igs/cl_init.lua: -------------------------------------------------------------------------------- 1 | IGS.sh("shared.lua") 2 | 3 | local UPPER_TEXT = Color(255,255,255) 4 | local LOWER_TEXT = Color(20, 150, 200) 5 | local OUTLINE = Color(0,0,0) 6 | 7 | local font = "igs.40" 8 | local function drawSide(pos,ang,t1,t2) 9 | cam.Start3D2D(pos, ang, .1) 10 | draw.SimpleTextOutlined(t1, font, 0, -385, UPPER_TEXT, TEXT_ALIGN_CENTER, nil, 1, OUTLINE) 11 | draw.SimpleTextOutlined(t2, font, 0, -350, LOWER_TEXT, TEXT_ALIGN_CENTER, nil, 1, OUTLINE) 12 | cam.End3D2D() 13 | end 14 | 15 | 16 | 17 | -- в Draw изменять осторожно 18 | -- (может быть куча итемов, которые ускоряют анимацию при неправильном коде) 19 | local ang = Angle(0,0,90) 20 | function ENT:Draw() 21 | ang.y = -180 * math.Remap(CurTime() % 3, 0,3, 0,1) -- минус крутить в другую сторону 22 | 23 | self:DrawModel() 24 | 25 | local pos = self:GetPos() 26 | 27 | local ITEM = IGS.GetItemByUID(self:GetUID()) 28 | local t1 = ITEM:Name() 29 | local t2 = "Действует " .. IGS.TermToStr(ITEM:Term()) 30 | 31 | drawSide(pos,ang,t1,t2) 32 | ang:RotateAroundAxis(ang:Right(), 180) 33 | drawSide(pos,ang,t1,t2) 34 | end 35 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/modules/extra/installation_check_sv.lua: -------------------------------------------------------------------------------- 1 | local function isMounted(path) 2 | return file.Exists(path, "LUA") 3 | end 4 | 5 | local function isWorkshopped(path) 6 | return file.Exists("lua/" .. path, "WORKSHOP") 7 | end 8 | 9 | local function isDownloaded(path) 10 | return IGS_MOUNT and IGS_MOUNT[path] 11 | end 12 | 13 | 14 | 15 | local function isUnpacked(path) 16 | return isMounted(path) and not (isWorkshopped(path) or isDownloaded(path)) 17 | end 18 | 19 | hook.Add("IGS.Initialized", "installation_check", function() 20 | local path = "igs/launcher.lua" 21 | if isUnpacked(path) then 22 | IGS.prints(Color(250, 100, 100), "Похоже, что автодонат распакован в /addons. ", "Автоматические обновления не работают 🚨") 23 | end 24 | 25 | if isWorkshopped(path) and isDownloaded(path) then 26 | IGS.prints("Удалите автодонат из вашей коллекции в воркшопе. Обновления работают через GitHub") 27 | end 28 | end) 29 | 30 | -- PRINT(file.Find("*", "LUA")) -- mediaplayer, wire 31 | -- PRINT(file.Find("lua/*", "THIRDPARTY")) -- mediaplayer, wire 32 | 33 | 34 | -- print(isUnpacked("wire/wireshared.lua")) 35 | -------------------------------------------------------------------------------- /luapack/stringex.lua: -------------------------------------------------------------------------------- 1 | local pattern_escape_replacements = { 2 | ["("] = "%(", 3 | [")"] = "%)", 4 | ["."] = "%.", 5 | ["%"] = "%%", 6 | ["+"] = "%+", 7 | ["-"] = "%-", 8 | ["*"] = "%*", 9 | ["?"] = "%?", 10 | ["["] = "%[", 11 | ["]"] = "%]", 12 | ["^"] = "%^", 13 | ["$"] = "%$", 14 | ["\0"] = "%z" 15 | } 16 | 17 | function string.PatternSafe(str) 18 | return (string.gsub(str, ".", pattern_escape_replacements)) 19 | end 20 | 21 | function string.Explode(separator, str, withpattern) 22 | if separator == "" then return string.ToTable(str) end 23 | if withpattern == nil then withpattern = false end 24 | 25 | local ret = {} 26 | local current_pos = 1 27 | 28 | for i = 1, string.len(str) do 29 | local start_pos, end_pos = string.find(str, separator, current_pos, not withpattern) 30 | if not start_pos then break end 31 | ret[i] = string.sub(str, current_pos, start_pos - 1) 32 | current_pos = end_pos + 1 33 | end 34 | 35 | ret[#ret + 1] = string.sub(str, current_pos) 36 | 37 | return ret 38 | end 39 | 40 | function string.Split(str, delimiter) 41 | return string.Explode(delimiter, str) 42 | end 43 | 44 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/extensions/bwhitelist.lua: -------------------------------------------------------------------------------- 1 | IGS.ITEMS.Whitelist = IGS.ITEMS.Whitelist or {} 2 | 3 | local STORE_ITEM = MT_IGSItem 4 | 5 | local function team_id(team_cmd) 6 | return DarkRP.getJobByCommand(team_cmd).team 7 | end 8 | 9 | function STORE_ITEM:SetBWhitelist(team_cmd) 10 | self:SetCanActivate(function(pl) 11 | if GAS.JobWhitelist:IsWhitelisted(pl, team_id(team_cmd)) then 12 | return "Вы в вайтлисте" 13 | end 14 | end) 15 | self:SetInstaller(function(pl) 16 | GAS.JobWhitelist:AddToWhitelist(team_id(team_cmd), GAS.JobWhitelist.LIST_TYPE_STEAMID, pl:SteamID()) 17 | end) 18 | self:SetValidator(function(pl) 19 | return GAS.JobWhitelist:IsWhitelisted(pl, team_id(team_cmd)) 20 | end) 21 | 22 | self.whitelist = self:Insert(IGS.ITEMS.Whitelist, team_cmd) -- not team_id из-за DarkRP = nil на этом этапе 23 | return self 24 | end 25 | 26 | if SERVER then 27 | hook.Add("IGS.PlayerPurchasesLoaded", "IGS.bWhitelist", function(pl) 28 | for team_cmd,_ in pairs(IGS.ITEMS.Whitelist) do 29 | GAS.JobWhitelist:RemoveFromWhitelist(team_id(team_cmd), GAS.JobWhitelist.LIST_TYPE_STEAMID, pl:SteamID()) 30 | end 31 | end) 32 | end 33 | -------------------------------------------------------------------------------- /.github/workflows/release_superfile.yml: -------------------------------------------------------------------------------- 1 | name: Release superfile 2 | 3 | on: 4 | push: 5 | branches: [ 'main' ] 6 | tags: [ '*' ] 7 | 8 | jobs: 9 | superfile: 10 | runs-on: ubuntu-24.04 11 | if: startsWith(github.ref, 'refs/tags/') 12 | steps: 13 | - id: checkout 14 | uses: actions/checkout@v4 15 | - id: changelog 16 | uses: zhaojh329/auto-changelog@master 17 | with: 18 | token: ${{ secrets.GITHUB_TOKEN }} 19 | - name: Download lua 20 | uses: leafo/gh-actions-lua@v10 21 | with: 22 | luaVersion: "5.3.5" 23 | - id: superfile 24 | run: | 25 | cd luapack 26 | lua main.lua ../addons/igs-core/lua superfile.json 27 | - id: igsmod 28 | run: | 29 | cd addons 30 | zip -x "*.DS_Store" -r igs-mod.zip igs-modification/ 31 | - id: release 32 | uses: softprops/action-gh-release@v2 33 | with: 34 | body: ${{steps.changelog.outputs.changelog}} 35 | # tag_name: asdasd 36 | #draft: true 37 | prerelease: false 38 | files: | 39 | luapack/superfile.json 40 | addons/igs-mod.zip 41 | env: 42 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 43 | -------------------------------------------------------------------------------- /addons/igs-core/README.md: -------------------------------------------------------------------------------- 1 | # ⚠️ ВНИМАНИЕ ⚠️ 2 | 3 | ### Не скачивайте эту папку и не помещайте ее в addons 4 | 5 | > Мы не несем ответственности за igs-core, который установлен в addons и не оказываем в таких случаях бесплатную [поддержку](https://gm-donate.net/support). Вам также не будут приходить автоматические обновления. 6 | 7 | Код из этой папки скачивается автоматически, если вы установили автодонат [по инструкции](https://gm-donate.net/instructions). 8 | 9 | Правильная установка это igs-modification в addons **без igs-core**. Если без igs-core автодонат не запускается, значит, вероятнее всего, блокируется RunString. Так это или нет, можно узнать, если внимательно смотреть в консоль во время запуска игрового сервера. 10 | 11 | Если есть ошибка _[IGSmod] RunString doesn’t work_, значит нужно ввести в консоль `lua_run PrintTable(debug.getinfo(RunString))` и в консоли будет написан файл, в котором блокируется RunString. Затем открыть найденный файл и удалить строки, связанные с RunString, CompileString и тд 12 | 13 | --- 14 | 15 | Если возникли вопросы или проблемы с правильной установкой, то вы можете задать их [здесь](https://forum.gm-donate.net/c/support/troubleshooting/15). Рекомендуем сразу приложить console.log и `garrysmod/data/igs_errors.txt` 16 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/servers/serv_sh.lua: -------------------------------------------------------------------------------- 1 | IGS.SERVERS = --[[ IGS.SERVERS or --]] setmetatable({ 2 | ID = function() return IGS.SERVERS.CURRENT end, 3 | Name = function() return IGS.SERVERS.MAP[IGS.SERVERS.CURRENT] end, 4 | 5 | -- Отправляет на клиент таблицу IGS.SERVERS 6 | Broadcast = function() 7 | -- нельзя nil 8 | IGS.nw.SetGlobal("igs_servers",true) 9 | end, 10 | 11 | MAP = {}, -- id, name 12 | 13 | -- CURRENT -- int 14 | -- LOADED -- bool 15 | TOTAL = 0, -- для считывания серверов в nw 16 | ENABLED = 0, -- для подсчета скидки 17 | },{ 18 | __call = function(self,id) 19 | return IGS.SERVERS.MAP[id] 20 | end 21 | }) 22 | 23 | 24 | IGS.nw.Register("igs_servers") 25 | :Write(function() 26 | net.WriteUInt(IGS.SERVERS.CURRENT,16) -- 65535 27 | net.WriteUInt(IGS.SERVERS.TOTAL,8) -- 256 28 | net.WriteUInt(IGS.SERVERS.ENABLED,8) -- 256 29 | 30 | for id,name in pairs(IGS.SERVERS.MAP) do 31 | net.WriteUInt(id,16) 32 | net.WriteString(name) 33 | end 34 | end) 35 | :Read(function() 36 | IGS.SERVERS.CURRENT = net.ReadUInt(16) 37 | IGS.SERVERS.TOTAL = net.ReadUInt(8) 38 | IGS.SERVERS.ENABLED = net.ReadUInt(8) 39 | 40 | for i = 1,IGS.SERVERS.TOTAL do 41 | IGS.SERVERS.MAP[net.ReadUInt(16)] = net.ReadString() 42 | end 43 | 44 | return IGS.SERVERS 45 | end) 46 | :SetGlobal():SetHook("IGS.ServersLoaded") 47 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/modules/extra/new_items_notify_cl.lua: -------------------------------------------------------------------------------- 1 | -- bib.setNum("igs:lasttimeitems", 85) 2 | 3 | -- 18, 23, 245 4 | local PL_POYAVILSA = PLUR({"появился","появилось","появилось"}) 5 | local PL_NEW = PLUR({"новый", "новых", "новых"}) 6 | local PL_ITEMS = PLUR({"предмет", "предмета", "предметов"}) 7 | 8 | hook.Add("IGS.Loaded", "NewItemsNotify", function() 9 | -- local ip,port = game.GetIPAddress():match("(.+):(.+)") 10 | -- print(game.GetIPAddress():gsub("%.",""):gsub(":","")) 11 | -- print(util.CRC(game.GetIPAddress())) 12 | 13 | if IGS.C.NotifyAboutNewItems == false then return end 14 | 15 | local crc = util.CRC(game.GetIPAddress()) 16 | 17 | local iItemsNow = #IGS.GetItems() 18 | local iCached = bib.getNum("igs:lasttimeitems:" .. crc) 19 | bib.setNum("igs:lasttimeitems:" .. crc, iItemsNow) 20 | 21 | if iCached and iCached < iItemsNow then 22 | local new = iItemsNow - iCached 23 | 24 | local _,sNew = PL_NEW(new) -- Чисто слово (новый, новых итд) 25 | local _,sItems = PL_ITEMS(new) 26 | local _,sAppear = PL_POYAVILSA(new) 27 | 28 | local message = 29 | "В нашем /donate магазине " .. sAppear .. " " .. new .. " " .. sNew .. " " .. sItems .. ". Желаете взглянуть?" 30 | 31 | IGS.BoolRequest("Пополнение магазина", message, function(aga) 32 | if aga then 33 | IGS.UI() 34 | end 35 | end) 36 | end 37 | end) 38 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/dependencies/stack.lua: -------------------------------------------------------------------------------- 1 | local link = {} 2 | local mt = { 3 | __index = link, 4 | } 5 | 6 | function link:length() 7 | return self.tail - self.head + 1 8 | end 9 | 10 | function link:isempty() 11 | return self:length() == 0 12 | end 13 | 14 | 15 | function link:peek() 16 | return self.data[self.head] 17 | end 18 | 19 | function link:lpush(v) 20 | self.tail = self.tail + 1 21 | self.data[self.tail] = v 22 | end 23 | 24 | function link:rpush(v) 25 | self.head = self.head - 1 26 | self.data[self.head] = v 27 | end 28 | 29 | function link:lpop() 30 | if self.head > self.tail then return nil end 31 | local v = self.data[self.tail] 32 | self.data[self.tail] = nil 33 | self.tail = self.tail - 1 34 | return v 35 | end 36 | 37 | function link:rpop() 38 | if self.head > self.tail then return nil end 39 | local v = self.data[self.head] 40 | self.data[self.head] = nil 41 | self.head = self.head + 1 42 | return v 43 | end 44 | 45 | function IGStack(...) 46 | return setmetatable({ 47 | head = 1, 48 | tail = select("#",...), 49 | data = {...}, 50 | }, mt) 51 | end 52 | 53 | 54 | -- local S = IGStack() 55 | -- S:lpush(1) S:lpush(2) S:lpush(3) -- 3 < 2 < 1 56 | -- PRINT({len = S:length(), head = S:rpop(), tail = S:lpop()}) -- 1 3 57 | -- S:rpush(1) S:lpush(3) S:lpush(4) -- вернули, что забрали и добавили слева 4 58 | -- PRINT({len = S:length(), head = S:rpop(), all = S:table()}) 59 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/vgui/igs_group.lua: -------------------------------------------------------------------------------- 1 | local PANEL = {} 2 | 3 | local PL_VARIANTS = PLUR({"вариант", "варианта", "вариантов"}) 4 | function PANEL:SetGroup(ITEM_GROUP) 5 | self.group = ITEM_GROUP 6 | 7 | if ITEM_GROUP:ICON() then 8 | self:SetIcon(ITEM_GROUP:ICON()) 9 | end 10 | 11 | if ITEM_GROUP.highlight then 12 | self:SetTitleColor(ITEM_GROUP.highlight) 13 | end 14 | 15 | local visible_items = {} 16 | for _,GROUP_ITEM in ipairs(ITEM_GROUP:Items()) do 17 | if GROUP_ITEM.item:CanSee( LocalPlayer() ) then 18 | table.insert(visible_items, GROUP_ITEM) 19 | end 20 | end 21 | 22 | self:SetName(ITEM_GROUP:Name()) 23 | self:SetSign(PL_VARIANTS(#visible_items)) 24 | 25 | local min,max = math.huge,0 -- минимальная и максимальная цены итемов 26 | for _,v in ipairs(visible_items) do 27 | local price = v.item:GetPrice( LocalPlayer() ) 28 | 29 | if price < min then 30 | min = price 31 | end 32 | 33 | if price > max then 34 | max = price 35 | end 36 | end 37 | 38 | if min == max then 39 | self:SetBottomText("Все по " .. IGS.SignPrice(min)) 40 | else 41 | self:SetBottomText("От " .. min .. " до " .. IGS.SignPrice(max)) 42 | end 43 | 44 | return self 45 | end 46 | 47 | function PANEL:DoClick() 48 | if not IsValid(self.list_bg) then 49 | self.list_bg = IGS.WIN.Group(self.group:UID()) 50 | end 51 | end 52 | 53 | 54 | vgui.Register("igs_group",PANEL,"igs_item") 55 | -- IGS.UI() 56 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/modules/extra/init_sh.lua: -------------------------------------------------------------------------------- 1 | -- Для предложения совершения покупок в определенных ситуациях 2 | -- /igsitem group_premium_30d 3 | 4 | if SERVER then 5 | local function RunCommand(c) 6 | return function(pl, arg) pl:RunSCC(c, arg) end 7 | end 8 | 9 | IGS.WIN = IGS.WIN or {} 10 | IGS.WIN.Item = RunCommand("IGSItem") 11 | IGS.WIN.Group = RunCommand("IGSGroup") 12 | IGS.WIN.Deposit = RunCommand("IGSDeposit") 13 | end 14 | 15 | scc.addClientside("IGSItem", function(_, arg) IGS.WIN.Item(arg) end) 16 | scc.addClientside("IGSDeposit", function(_, arg) IGS.WIN.Deposit(arg) end) 17 | scc.addClientside("IGSGroup", function(_, arg) IGS.WIN.Group(arg) end) 18 | 19 | 20 | 21 | 22 | IGS.PermaSaveFeature("npc_igs") 23 | 24 | local function runAfterhooks() -- #todo перенести эти выполнения в модули или вызывать локально if CODEMOUNT 25 | if (not IGS_MOUNT) then return end 26 | 27 | IGS.dprint("Выполнение 'опоздавших' хуков и spawnmenu_reload") 28 | if CLIENT then -- костыль, но другого способа не вижу 29 | hook.GetTable()["InitPostEntity"]["IGS.nw.InitPostEntity"]() 30 | hook.GetTable()["DarkRPFinishedLoading"]["SupressDarkRPF1"]() 31 | RunConsoleCommand("spawnmenu_reload") -- npc_igs 32 | -- else 33 | -- hook.GetTable()["InitPostEntity"]["IGS.PermaSents"]() 34 | -- "InitPostEntity", "InitializePermaProps" 35 | end 36 | end 37 | 38 | -- IGS.Loaded выполняется при условии IGS.nw.InitPostEntity 39 | hook.Add("IGS.Initialized", "afterhooks", function() 40 | timer.Simple(.1, runAfterhooks) -- энтити грузятся вроде шагом позже 41 | end) 42 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/extensions/serverguard.lua: -------------------------------------------------------------------------------- 1 | local STORE_ITEM = MT_IGSItem 2 | 3 | local is_on_sale_ranks = {} 4 | 5 | function STORE_ITEM:SetSGGroup(sUserGroup) 6 | is_on_sale_ranks[sUserGroup] = true 7 | 8 | return self:SetInstaller(function(pl) 9 | local rankData = serverguard.ranks:GetRank(sUserGroup) 10 | assert(rankData, "IGS: В SetSGGroup указана несуществующая группа") 11 | serverguard.player:SetRank(pl, sUserGroup, 0) 12 | serverguard.player:SetImmunity(pl, rankData.immunity) 13 | serverguard.player:SetTargetableRank(pl, rankData.targetable) 14 | serverguard.player:SetBanLimit(pl, rankData.banlimit) 15 | end):SetMeta("sggroup", sUserGroup) 16 | end 17 | 18 | if CLIENT then return end 19 | 20 | -- addhook не подойдет (не будет автоснятия. Можно и отдельно, конечно) 21 | hook.Add("IGS.PlayerPurchasesLoaded", "sggroup", function(pl, purchases) 22 | if not serverguard then hook.Remove("IGS.PlayerPurchasesLoaded", "sggroup") return end 23 | if hook.Run("IGS.SkipSGRestore", pl) then return end 24 | 25 | local prior 26 | 27 | for uid in pairs(purchases or {}) do 28 | local ITEM = IGS.GetItemByUID(uid) 29 | if ITEM:GetMeta("sggroup") and (not prior or ITEM.id >= prior.id) then 30 | prior = ITEM 31 | end 32 | end 33 | 34 | if prior then 35 | prior:Setup(pl) 36 | else -- ни один не куплен (срок истек?) 37 | local player_rank = serverguard.player:GetRank(pl) -- default: user 38 | if is_on_sale_ranks[player_rank] then -- но ранг, который у игрока продается 39 | serverguard.player:SetRank(pl, "user", 0) -- снимаем 40 | end 41 | end 42 | end) 43 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/windows/group_content.lua: -------------------------------------------------------------------------------- 1 | local function getSpacePanel() 2 | return uigs.Create("Panel", function(self) 3 | self:Dock(TOP) 4 | self:SetTall(3) 5 | end) 6 | end 7 | 8 | function IGS.WIN.Group(sGroupUID) 9 | local GROUP = IGS.GetGroup(sGroupUID) 10 | assert(GROUP, "Incorrect group: " .. tostring(sGroupUID)) 11 | 12 | surface.PlaySound("ambient/weather/rain_drip1.wav") 13 | 14 | return uigs.Create("igs_frame", function(bg) 15 | bg:SetTitle(GROUP:Name()) 16 | bg:MakePopup() 17 | 18 | local cellW,cellH -- не изменяется в зависимости от контента 19 | function bg:AddIGSItem(ITEM, nameInGroup) 20 | local it = uigs.Create("igs_item"):SetItem(ITEM) 21 | it:SetName(nameInGroup or ITEM:Name()) 22 | 23 | if not cellW then 24 | cellW,cellH = it:GetSize() 25 | end 26 | 27 | it:SetSize(cellW * 1.3, cellH) 28 | it.DoClick = function() 29 | -- bg:Close() 30 | IGS.WIN.Item(ITEM:UID()) 31 | end 32 | 33 | bg.scroll:AddItem(it) 34 | end 35 | 36 | bg.scroll = uigs.Create("igs_scroll", bg) 37 | bg.scroll:Dock(FILL) 38 | bg.scroll:SetPadding(6) 39 | 40 | 41 | bg.scroll:AddItem( getSpacePanel() ) -- из-за паддинга #1 42 | for _,v in pairs(GROUP:Items()) do 43 | local ITEM = v.item 44 | if v.item:CanSee( LocalPlayer() ) then -- еще в main_cl 45 | bg:AddIGSItem(ITEM, v.name) 46 | end 47 | end 48 | bg.scroll:AddItem( getSpacePanel() ) -- из-за паддинга #2 49 | 50 | -- or: https://t.me/c/1353676159/21116 51 | bg:SetSize((cellW or 220) * 1.3, 300) 52 | bg:RememberLocation("igs_group") 53 | end) 54 | end 55 | -- IGS.UI() 56 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/vgui/igs_multipanel.lua: -------------------------------------------------------------------------------- 1 | --[[------------------------------------------------------------------------- 2 | Панель, на которой можно разместить несколько других и переключаться между ними 3 | Используется в igs_tabbar и может использоваться по отдельности 4 | 5 | Обращаем внимание на pnl:OnOpen() 6 | ---------------------------------------------------------------------------]] 7 | 8 | local PANEL = {} 9 | 10 | function PANEL:Init() 11 | self.Panels = {} 12 | end 13 | 14 | function PANEL:AddPanel(panel,bActive) 15 | panel:SetSize(self:GetWide(),self:GetTall()) 16 | panel:SetVisible(false) 17 | panel:SetParent(self) 18 | -- panel.Paint = function(s,w,h) end -- АХТУНГ. Уже дважды на грабли встал 19 | -- Не понимал, почему не добавляется панель. Потом дошло, что этот хук ее просто прячет 20 | 21 | panel.ID = table.insert(self.Panels,panel) 22 | 23 | if (bActive) then 24 | self:SetActivePanel(panel.ID) 25 | end 26 | 27 | return panel.ID 28 | end 29 | 30 | function PANEL:SetActivePanel(iID) 31 | for i,pnl in ipairs(self.Panels) do 32 | pnl.Active = iID == i 33 | 34 | if (pnl:IsVisible()) then 35 | pnl:Dock(NODOCK) 36 | pnl:SetVisible(false) 37 | end 38 | 39 | if (iID == i) then 40 | pnl:SetVisible(true) 41 | pnl:DockMargin(0, 0, 0, 0) 42 | pnl:Dock(FILL) 43 | 44 | if pnl.OnOpen then 45 | pnl:OnOpen() 46 | end 47 | end 48 | end 49 | end 50 | 51 | function PANEL:GetActivePanel() 52 | for i,pnl in ipairs(self.Panels) do 53 | if pnl.Active then 54 | return pnl 55 | end 56 | end 57 | end 58 | 59 | vgui.Register("igs_multipanel", PANEL, "Panel") 60 | -- IGS.UI() 61 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/dependencies/dash/misc.lua: -------------------------------------------------------------------------------- 1 | if dash then return end 2 | -- Thanks to SuperiorServers.co 3 | 4 | local Start = net.Start 5 | local Send = SERVER and net.Send or net.SendToServer 6 | function net.Ping(msg, recipients) 7 | Start(msg) 8 | Send(recipients) 9 | end 10 | 11 | 12 | if CLIENT then 13 | local surface_SetFont = surface.SetFont 14 | local surface_GetTextSize = surface.GetTextSize 15 | local string_Explode = string.Explode 16 | local ipairs = ipairs 17 | 18 | function string.Wrap(font, text, width) 19 | surface_SetFont(font) 20 | 21 | local sw = surface_GetTextSize(' ') 22 | local ret = {} 23 | 24 | local w = 0 25 | local s = '' 26 | 27 | local t = string_Explode('\n', text) 28 | for i = 1, #t do 29 | local t2 = string_Explode(' ', t[i], false) 30 | for i2 = 1, #t2 do 31 | local neww = surface_GetTextSize(t2[i2]) 32 | 33 | if (w + neww >= width) then 34 | ret[#ret + 1] = s 35 | w = neww + sw 36 | s = t2[i2] .. ' ' 37 | else 38 | s = s .. t2[i2] .. ' ' 39 | w = w + neww + sw 40 | end 41 | end 42 | ret[#ret + 1] = s 43 | w = 0 44 | s = '' 45 | end 46 | 47 | if (s ~= '') then 48 | ret[#ret + 1] = s 49 | end 50 | 51 | return ret 52 | end 53 | 54 | local formathex = '%%%02X' 55 | function string:URLEncode() 56 | return string.gsub(string.gsub(string.gsub(self, '\n', '\r\n'), '([^%w ])', function(c) 57 | return string.format(formathex, string.byte(c)) 58 | end), ' ', '+') 59 | end 60 | 61 | local surface_DrawRect = surface.DrawRect 62 | local surface_SetDrawColor = surface.SetDrawColor 63 | function draw.Box(x, y, w, h, col) 64 | surface_SetDrawColor(col) 65 | surface_DrawRect(x, y, w, h) 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/utils/ut_sv.lua: -------------------------------------------------------------------------------- 1 | 2 | local col_lime = Color(100,210,40) 3 | local col_light = Color(228,228,228) 4 | local col_red = Color(250,30,90) 5 | 6 | function IGS.NotifyAll(...) 7 | chat.AddTextSV( 8 | col_lime, "[IGS]", 9 | col_red, " > ", 10 | col_light,... 11 | ) 12 | end 13 | 14 | 15 | function IGS.Notify(pl, ...) 16 | pl:ChatPrintColor( 17 | col_lime, "[IGS]", 18 | col_red, " > ", 19 | col_light,... 20 | ) 21 | end 22 | 23 | -- IGS.MUTE_NOTIFY = IGS.MUTE_NOTIFY or {} 24 | -- function IGS.NotifyOpt(pl, uid, ...) 25 | -- if IGS.MUTE_NOTIFY[uid] then return end 26 | -- return IGS.Notify(pl, ...) 27 | -- end 28 | 29 | 30 | -- todo реальный логгинг 31 | function IGS.LogError(err) 32 | error(err) 33 | end 34 | 35 | 36 | --[[--------------------------- 37 | API 38 | -----------------------------]] 39 | -- Проще https://qweqwe.ovh/9vAdB 40 | local limit_per_query = 255 41 | local function getTxsNoLimit(cb, s64, am_, _tmp_) 42 | _tmp_ = _tmp_ or {} 43 | am_ = am_ or math.huge 44 | local left = am_ - #_tmp_ 45 | -- print("need, done, left", am_, #_tmp_, left) 46 | IGS.GetTransactions(function(data) 47 | for _,tr in ipairs(data) do 48 | local i = table.insert(_tmp_,tr) 49 | if am_ and i == am_ then cb(_tmp_) return end -- собрали нужное кол-во 50 | end 51 | 52 | if #data < limit_per_query then cb(_tmp_) -- Последняя страница 53 | else getTxsNoLimit(cb, s64, am_, _tmp_) 54 | end 55 | end, s64, true, math.min(limit_per_query, left), #_tmp_) 56 | end 57 | 58 | IGS.GetPlayerTransactionsBypassingLimit = getTxsNoLimit 59 | 60 | -- getTxsNoLimit(function(all) 61 | -- for i,tx in ipairs(all) do 62 | -- print(i, DateTime(tx.Time), tx.Note) 63 | -- end 64 | -- print("#all", #all) 65 | -- end, AMD():SteamID(), 20) -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/extensions/xadmin.lua: -------------------------------------------------------------------------------- 1 | IGS.ITEMS.XADMIN_USERS = IGS.ITEMS.XADMIN_USERS or {} -- by kip https://t.me/c/1353676159/1673 2 | 3 | local STORE_ITEM = MT_IGSItem 4 | 5 | function STORE_ITEM:SetXAdminGroup(sUserGroup) 6 | return self:SetInstaller(function(pl) 7 | xAdmin.UserGroups[pl:SteamID()] = sUserGroup 8 | pl:SetUserGroup(sUserGroup) 9 | IGS.ITEMS.XADMIN_USERS[pl:SteamID()] = true 10 | end):SetValidator(function() 11 | return false 12 | end) 13 | end 14 | 15 | hook.Add("PlayerDisconnected", "IGS.XAdminRemovePerm", function(pl) 16 | if IGS.ITEMS.XADMIN_USERS[pl:SteamID()] then 17 | xAdmin.UserGroups[pl:SteamID()] = nil 18 | IGS.ITEMS.XADMIN_USERS[pl:SteamID()] = nil 19 | end 20 | end) 21 | 22 | 23 | -- Есть xAdmin.SetGroup(pl, sUserGroup) 24 | -- но тогда будет сложнее контроллировать снятие прав, тк идет запись в БД 25 | function STORE_ITEM:SetXAdmin2Group(sUserGroup) 26 | return self:SetInstaller(function(pl) 27 | local sid64 = pl:SteamID64() 28 | local oldgroup = pl:GetUserGroup() 29 | 30 | xAdmin.UserData[sid64] = xAdmin.UserData[sid64] or {SteamID = sid64} 31 | xAdmin.UserData[sid64].UserGroup = sUserGroup 32 | 33 | pl:SetUserGroup(sUserGroup) 34 | 35 | xAdmin.UpdateGroupMembers(player.GetAll(), sUserGroup) 36 | 37 | hook.Run("xAdminUserGroupUpdated", pl, sUserGroup, oldgroup) 38 | end):AddHook("IGS.PlayerPurchasesLoaded", function(pl, purchases) 39 | if CLIENT or not purchases then return end 40 | 41 | local priority_item = self 42 | 43 | for uid in pairs(purchases) do 44 | local ITEM = IGS.GetItemByUID(uid) 45 | if ITEM:GetMeta("badmin2group") and ITEM.id > self.id then 46 | priority_item = ITEM 47 | end 48 | end 49 | 50 | if priority_item == self then 51 | self:Setup(pl) 52 | end 53 | end):SetMeta("badmin2group", sUserGroup) 54 | end 55 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/extensions/infammo.lua: -------------------------------------------------------------------------------- 1 | -- источник и альтернативные решения: 2 | -- https://forum.gm-donate.net/t/usluga-beskonechnye-patrony/633/10?u=gmd 3 | 4 | -- IGS("Бесконечные патроны", "infammo", 100) 5 | -- :SetInfAmmo() 6 | -- :SetTerm(10) 7 | 8 | local ITEM = MT_IGSItem 9 | 10 | local function setInfAmmo(pl) 11 | local weapon = pl:GetActiveWeapon() 12 | if not IsValid(weapon) then return end 13 | 14 | local maxClip = weapon:GetMaxClip1() 15 | local maxClip2 = weapon:GetMaxClip2() 16 | local primAmmoType = weapon:GetPrimaryAmmoType() 17 | local secAmmoType = weapon:GetSecondaryAmmoType() 18 | 19 | if maxClip == -1 and maxClip2 == -1 then 20 | maxClip = 100 21 | maxClip2 = 100 22 | end 23 | 24 | if maxClip <= 0 and primAmmoType ~= -1 then 25 | maxClip = 1 26 | end 27 | 28 | if maxClip2 == -1 and secAmmoType ~= -1 then 29 | maxClip2 = 1 30 | end 31 | 32 | if maxClip > 0 then 33 | weapon:SetClip1(maxClip) 34 | end 35 | 36 | if maxClip2 > 0 then 37 | weapon:SetClip2(maxClip2) 38 | end 39 | 40 | if primAmmoType ~= -1 then 41 | pl:SetAmmo( maxClip, primAmmoType, true) 42 | end 43 | 44 | if secAmmoType ~= -1 and secAmmoType ~= primAmmoType then 45 | pl:SetAmmo( maxClip2, secAmmoType, true) 46 | end 47 | end 48 | 49 | 50 | local infammo_players = {} 51 | 52 | timer.Create("igs_infammo", 3.3, 0, function() 53 | if not infammo_players[1] then return end 54 | 55 | for i = #infammo_players, 0, -1 do -- reversed ipairs 56 | local pl = infammo_players[i] 57 | if IsValid(pl) then 58 | setInfAmmo(pl) 59 | else 60 | table.remove(infammo_players, i) 61 | end 62 | end 63 | end) 64 | 65 | function ITEM:SetInfAmmo() 66 | return self:SetInstaller(function(pl) 67 | if not table.HasValue(infammo_players, pl) then 68 | table.insert(infammo_players, pl) 69 | end 70 | end):SetValidator(function(pl) 71 | return false 72 | end) 73 | end 74 | -------------------------------------------------------------------------------- /addons/igs-core/lua/entities/npc_igs/cl_init.lua: -------------------------------------------------------------------------------- 1 | IGS.sh("shared.lua") 2 | 3 | -- https://forum.gm-donate.net/t/igs-izmenenie-modeli-nadpisi-npc/2268 4 | ENT.TextAboveNPC = "Донат услуги" 5 | 6 | -- #todo сделать такое же для подарка? 7 | 8 | local COL_TEXT = Color(255,255,255) 9 | local COL_BG = Color(0,0,0,150) 10 | local FONT = "igs.40" 11 | 12 | local function textPlate(text,y) 13 | surface.SetFont(FONT) 14 | local tw,th = surface.GetTextSize(text) 15 | local bx,by = -tw / 2 - 10, y - 5 16 | local bw,bh = tw + 10 + 10, th + 10 + 10 17 | 18 | -- Background 19 | surface.SetDrawColor(COL_BG) 20 | surface.DrawRect(bx,by, bw,bh) 21 | surface.SetDrawColor(COL_TEXT) 22 | surface.DrawRect(bx, by + bh - 4, bw, 4) 23 | 24 | -- text 25 | surface.SetTextColor(COL_TEXT) 26 | surface.SetTextPos(-tw / 2,y) 27 | surface.DrawText(text) 28 | end 29 | 30 | local function drawInfo(ent, text, dist) 31 | dist = dist or EyePos():DistToSqr(ent:GetPos()) 32 | 33 | if dist < 60000 then 34 | surface.SetAlphaMultiplier( math.Clamp(3 - (dist / 20000), 0, 1) ) 35 | 36 | local _,max = ent:GetRotatedAABB(ent:OBBMins(), ent:OBBMaxs() ) 37 | local rot = (ent:GetPos() - EyePos()):Angle().yaw - 90 38 | local sin = math.sin(CurTime() + ent:EntIndex()) / 3 + .5 -- EntIndex дает разницу в движении 39 | local center = ent:LocalToWorld(ent:OBBCenter()) 40 | 41 | cam.Start3D2D(center + Vector(0, 0, math.abs(max.z / 2) + 12 + sin), Angle(0, rot, 90), 0.13) 42 | textPlate(text,15) 43 | cam.End3D2D() 44 | 45 | surface.SetAlphaMultiplier(1) 46 | end 47 | end 48 | 49 | -- https://vk.com/gim143836547?msgid=46147&q=рендер&sel=88943099 50 | IGS_NPC_HIDE_ON_DISTANCE = nil -- 100000 51 | function ENT:Draw() 52 | local dist = EyePos():DistToSqr(self:GetPos()) 53 | if IGS_NPC_HIDE_ON_DISTANCE and dist > IGS_NPC_HIDE_ON_DISTANCE then return end -- не отрисовывать 54 | 55 | self:DrawModel() 56 | drawInfo(self, self.TextAboveNPC, dist) 57 | end 58 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/extensions/sam.lua: -------------------------------------------------------------------------------- 1 | local STORE_ITEM = MT_IGSItem 2 | 3 | function STORE_ITEM:SetSAMGroup(sUserGroup) 4 | IGS.SAM_GROUPS = IGS.SAM_GROUPS or {} 5 | 6 | self:SetInstaller(function(pl) 7 | pl:sam_set_rank(sUserGroup) 8 | end):SetMeta("samgroup", sUserGroup) 9 | 10 | self:Insert(IGS.SAM_GROUPS, sUserGroup) -- #todo insert возвращает значение.. 11 | return self 12 | end 13 | 14 | -- #todo IGS.Filter ? 15 | local function fl_filter(t, func) 16 | local res = {} 17 | for i,v in ipairs(t) do 18 | if func(v) then 19 | res[#res + 1] = v 20 | end 21 | end 22 | return res 23 | end 24 | 25 | hook.Add("IGS.PlayerPurchasesLoaded", "IGS_SAM", function(pl, purchases_) 26 | if CLIENT or not IGS.SAM_GROUPS then return end 27 | 28 | local purchased_groups = {} 29 | if purchases_ then 30 | local purchases_list = table.GetKeys(purchases_) 31 | purchased_groups = fl_filter(purchases_list, function(uid) 32 | return IGS.GetItemByUID(uid):GetMeta("samgroup") 33 | end) 34 | end 35 | 36 | -- У игрока среди покупок нет SAM групп 37 | -- Но его ранг не дефолтный и продается 38 | -- Значит снимаем 39 | if not purchased_groups[1] then 40 | local current_pl_rank = 41 | (pl.sam_getrank and pl:sam_getrank()) or -- до версии SAM 143 была такая функция. Потом пропала 42 | (pl.sam_get_nwvar and pl:sam_get_nwvar("rank", "user")) -- обратная совместимость: https://forum.gm-donate.net/t/7730/9 43 | or pl:GetUserGroup() 44 | 45 | if current_pl_rank ~= "user" and IGS.SAM_GROUPS[current_pl_rank] then 46 | pl:sam_set_rank("user") -- используется sam.player.set_rank(ply, rank, length), не путать с PLAYER:sam_setrank(name) 47 | end 48 | 49 | return 50 | end 51 | 52 | -- У игрока куплена минимум 1 SAM группа 53 | -- Та, что в sh_additems ниже, та важнее 54 | local priority_item = IGS.GetItemByUID( purchased_groups[1] ) 55 | for _,uid in ipairs(purchased_groups) do 56 | local ITEM = IGS.GetItemByUID(uid) 57 | if ITEM.id > priority_item.id then 58 | priority_item = ITEM 59 | end 60 | end 61 | 62 | -- Самую важную из купленных и выставляем 63 | priority_item:Setup(pl) 64 | end) 65 | -------------------------------------------------------------------------------- /luapack/file.lua: -------------------------------------------------------------------------------- 1 | require("stringex") -- Split 2 | require("tableex") -- Add 3 | 4 | file = {} 5 | 6 | function file.Find(path, pattern) 7 | local f = io.popen(string.format("ls -dp1 %s/%s", path, pattern or "*"), "r") 8 | local data = f:read("*all") 9 | f:close() 10 | local files, dirs = {}, {} 11 | for _, line in pairs(string.Split(data, "\n")) do 12 | local name = string.sub(line, #path + 2, #line) 13 | if #name > 0 then 14 | local is_dir = string.byte(name, #name) == 0x2f 15 | table.insert(is_dir and dirs or files, is_dir and string.sub(name, 1, #name - 1) or name) 16 | end 17 | end 18 | return files, dirs 19 | end 20 | 21 | -- function file.IsDir(path) return os.execute(string.format("bash -c '[ -d %q ]'", path)) == 0 end 22 | -- function file.IsFile(path) return os.execute(string.format("bash -c '[ -f %q ]'", path)) == 0 end 23 | 24 | -- function file.Exists(path) 25 | -- return file.IsDir(path) or file.IsFile(path) 26 | -- end 27 | 28 | function file.Read(path) 29 | local f = io.open(path, "r") 30 | if not f then return nil end 31 | local data = f:read("*all") 32 | f:close() 33 | return data 34 | end 35 | 36 | function file.Write(path, data) 37 | local f = io.open(path, "wb") 38 | if not f then return false end 39 | f:write(data) 40 | f:close() 41 | return true 42 | end 43 | 44 | function file.CreateDir(path) 45 | local f = io.popen(string.format("mkdir -p %q 2>&1 echo $?", path), "r") 46 | local data = string.Split(f:read("*all"), "\n") 47 | local msg, code = data[1], tonumber(data[2]) 48 | f:close() 49 | -- os.execute("rm -r 0/ echo/") 50 | return code == nil, code ~= nil and msg or nil 51 | end 52 | 53 | 54 | 55 | 56 | function file.Index(path) 57 | local list = {} 58 | local files,dirs = file.Find(path) 59 | for _,f in ipairs(files) do 60 | list[#list + 1] = path .. "/" .. f 61 | end 62 | for _,d in ipairs(dirs) do 63 | local res = file.Index(path .. "/" .. d) 64 | table.Add(list, res) 65 | end 66 | return list 67 | end 68 | 69 | function file.AdvWrite(sPath, sData) 70 | -- bla2/bla3/kek.txt > bla2/bla3 71 | file.CreateDir(sPath:match("(.+)/")) 72 | return file.Write(sPath, sData) 73 | end 74 | 75 | 76 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/dependencies/bib.lua: -------------------------------------------------------------------------------- 1 | bib = bib or {} -- Не знаю, почему я решил дать ему именно такое название 2 | 3 | function bib.set(key,value) 4 | sql.Query([[ 5 | REPLACE INTO `bib`(`key`,`value`) 6 | VALUES (]] .. sql.SQLStr(key) .. "," .. sql.SQLStr(value) .. [[) 7 | ]]) 8 | end 9 | 10 | function bib.get(key,fallback) 11 | return sql.QueryValue([[ 12 | SELECT `value` 13 | FROM `bib` 14 | WHERE `key` = ]] .. sql.SQLStr(key) 15 | ) or fallback 16 | end 17 | 18 | function bib.delete(key) 19 | sql.Query([[ 20 | DELETE FROM `bib` 21 | WHERE `key` = ]] .. sql.SQLStr(key) 22 | ) 23 | end 24 | 25 | function bib.getAll() 26 | local t = sql.Query([[ 27 | SELECT `key`,`value` 28 | FROM `bib` 29 | ]]) or {} 30 | 31 | local kv = {} 32 | for _,v in ipairs(t) do 33 | kv[v.key] = v.value 34 | end 35 | 36 | return kv 37 | end 38 | 39 | function bib.reset() 40 | sql.Query([[ 41 | DELETE FROM `bib` 42 | ]]) 43 | end 44 | 45 | sql.Query([[ 46 | CREATE TABLE IF NOT EXISTS `bib` ( 47 | `key` TEXT NOT NULL UNIQUE, 48 | `value` TEXT, 49 | PRIMARY KEY(key) 50 | ); 51 | ]]) 52 | 53 | 54 | 55 | 56 | 57 | --[[------------------------------------------------------------------------- 58 | Bools 59 | ---------------------------------------------------------------------------]] 60 | function bib.getBool(k, bFallback) 61 | local v = bib.get(k) 62 | if v == nil then 63 | return bFallback 64 | end 65 | 66 | return v == "t" 67 | end 68 | 69 | function bib.setBool(k,b) 70 | bib.set(k,b and "t" or "f") 71 | end 72 | 73 | -- local function g() return bib.getBool("foo") end 74 | -- local function s(b) bib.setBool("foo",b) end 75 | -- s(nil) print("false",g()) 76 | -- s(true) print("true",g()) 77 | -- s(false) print("false",g()) 78 | -- s(1) print("true",g()) 79 | 80 | 81 | --[[------------------------------------------------------------------------- 82 | Numbers 83 | ---------------------------------------------------------------------------]] 84 | function bib.getNum(k,iFallback) 85 | return tonumber(bib.get(k,iFallback)) 86 | end 87 | 88 | function bib.setNum(k,i) 89 | bib.set(k,i) 90 | end 91 | 92 | function bib.increment(k, i_) 93 | local i = bib.getNum(k, 0) + (i_ or 1) 94 | bib.setNum(k, i) 95 | return i 96 | end 97 | 98 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/dependencies/scc.lua: -------------------------------------------------------------------------------- 1 | -- Simple Chat Commands 2 | -- Special for IGS by _AMD_ 3 | -- 2019.11.08 01:47 4 | 5 | scc = { 6 | commands = {}, 7 | } 8 | 9 | if SERVER then 10 | util.AddNetworkString("scc.run") 11 | end 12 | 13 | function scc.add(command, callback) 14 | scc.commands[command:lower()] = callback 15 | end 16 | 17 | function scc.run(pl, command, args) 18 | local callback = scc.commands[command:lower()] 19 | callback(pl, unpack(args or {})) 20 | end 21 | 22 | 23 | if SERVER then 24 | hook.Add("PlayerSay", "scc", function(pl, text) 25 | text = text:Trim() 26 | if text[1] == "/" then 27 | local pieces = text:Split(" ") 28 | local command = pieces[1]:sub(2):lower() 29 | 30 | if scc.commands[command] then 31 | local args = {} 32 | for i = 2,#pieces do 33 | args[#args + 1] = pieces[i] 34 | end 35 | 36 | scc.run(pl, command, args) 37 | return "" 38 | end 39 | end 40 | end) 41 | end 42 | 43 | 44 | --[[------------------------------------------------------------------------- 45 | ---------------------------------------------------------------------------]] 46 | 47 | local PLAYER = FindMetaTable("Player") 48 | function PLAYER:RunSCC(command, ...) 49 | scc.run(self, command, {...}) 50 | end 51 | 52 | -- function scc.addWithCooldown(cooldown, command, callback) 53 | -- local runIfNotCooldown = function(pl, ...) 54 | -- if not pl.sccLastRun then pl.sccLastRun = {} end 55 | -- if CurTime() - (pl.sccLastRun[command] or 0) >= cooldown then 56 | -- pl.sccLastRun[command] = CurTime() 57 | -- callback(pl, ...) 58 | -- end 59 | -- end 60 | 61 | -- scc.add(command, runIfNotCooldown) 62 | -- end 63 | 64 | if SERVER then 65 | util.AddNetworkString("scc.run") 66 | else 67 | net.Receive("scc.run", function() 68 | local command = net.ReadString() 69 | local args = {} 70 | for i = 1, net.ReadUInt(4) do 71 | args[i] = net.ReadString() 72 | end 73 | 74 | scc.run(LocalPlayer(), command, args) 75 | end) 76 | end 77 | 78 | function scc.addClientside(command, callback) 79 | local runOnClient = SERVER and function(pl, ...) 80 | local args = {...} 81 | net.Start("scc.run") 82 | net.WriteString(command) 83 | net.WriteUInt(#args, 4) 84 | for _,arg in ipairs(args) do 85 | net.WriteString(arg) 86 | end 87 | net.Send(pl) 88 | end or callback 89 | 90 | scc.add(command, runOnClient) 91 | end 92 | -------------------------------------------------------------------------------- /addons/igs-core/lua/entities/ent_igs/init.lua: -------------------------------------------------------------------------------- 1 | IGS.sh("shared.lua") 2 | IGS.cl("cl_init.lua") 3 | 4 | function ENT:Initialize() 5 | -- self:SetModel("models/props_junk/Shoe001a.mdl") -- ботинок 6 | -- self:SetModel("models/christmas_gift2/christmas_gift2.mdl") -- подарок 7 | 8 | self:SetModel(IGS_GIFT_MODEL or "models/dav0r/hoverball.mdl") 9 | self:SetModelScale(1.5) 10 | self:SetAngles(Angle(90, 0, 0)) 11 | 12 | self:PhysicsInit(SOLID_VPHYSICS) 13 | self:SetMoveType(MOVETYPE_VPHYSICS) 14 | self:SetSolid(SOLID_VPHYSICS) 15 | self:SetUseType(SIMPLE_USE) 16 | self:PhysWake() 17 | end 18 | 19 | -- Чтобы нельзя было убить NPC 20 | function ENT:OnTakeDamage() 21 | return 0 22 | end 23 | 24 | function ENT:Use(_, caller) 25 | if caller:IsPlayer() then 26 | self:PlayerUse(caller) 27 | end 28 | end 29 | 30 | function ENT:PlayerUse(pl) 31 | if IGS.IsInventoryOverloaded(pl) then 32 | IGS.Notify(pl, "У вас слишком много предметов в инвентаре") 33 | return 34 | end 35 | 36 | if self.Busy or self.Removed then -- хз нужно ли именно здесь, но я добавил 37 | -- https://vk.com/gim143836547?sel=383010676&msgid=90338 38 | if CurTime() - self.Busy > 5 then 39 | IGS.Notify(pl, "Предмет в процессе перемещения в инвентарь") 40 | IGS.Notify(pl, "Если процесс бесконечный, то поскорее сделайте доказательства и сообщите администратору") 41 | end 42 | return 43 | end 44 | self.Busy = CurTime() 45 | 46 | local UID = self:GetUID() 47 | IGS.AddToInventory(pl, UID, function(invDbID) 48 | self.Removed = true 49 | self:Remove() 50 | 51 | IGS.Notify(pl, "Предмет помещен в /donate инвентарь") 52 | 53 | -- вставлять новый ID не совсем корректно 54 | -- Думаю, надо кешировать тот ИД, что был при покупке 55 | hook.Run("IGS.PlayerPickedGift", self.Getowning_ent and self:Getowning_ent(), UID, invDbID, pl) 56 | end) 57 | end 58 | 59 | function IGS.SpawnGift(sUid, vPos) 60 | assert(sUid, "Item UID expected") 61 | 62 | local ent = ents.Create("ent_igs") 63 | ent:SetUID(sUid) 64 | 65 | if vPos then 66 | ent:SetPos(vPos) 67 | ent:Spawn() 68 | end 69 | 70 | return ent 71 | end 72 | 73 | -- Обратная совместимость 74 | -- https://forum.gm-donate.net/t/spavn-donata-cherez-konsol/438/4?u=gmd 75 | function IGS.CreateGift(sUid, plOwner, vPos) 76 | local ent = IGS.SpawnGift(sUid, vPos) 77 | if ent.Setowning_ent then 78 | ent:Setowning_ent(plOwner) 79 | end 80 | return ent 81 | end 82 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/activities/purchases.lua: -------------------------------------------------------------------------------- 1 | hook.Add("IGS.CatchActivities","purchases",function(activity,sidebar) 2 | local bg = sidebar:AddPage("Активные покупки") 3 | 4 | 5 | --[[------------------------------------------------------------------------- 6 | Основная часть фрейма 7 | ---------------------------------------------------------------------------]] 8 | uigs.Create("igs_table", function(pnl) 9 | pnl:Dock(FILL) 10 | pnl:DockMargin(5,5,5,5) 11 | 12 | pnl:SetTitle("Активные покупки") 13 | 14 | local multisv = IGS.SERVERS.TOTAL > 1 15 | if multisv then 16 | pnl:AddColumn("Сервер",100) 17 | else 18 | pnl:AddColumn("#",40) 19 | end 20 | 21 | pnl:AddColumn("Предмет") 22 | pnl:AddColumn("Куплен",90) 23 | pnl:AddColumn("Истечет",90) 24 | 25 | 26 | IGS.GetMyPurchases(function(d) 27 | if not IsValid(pnl) then return end -- Долго данные получались, фрейм успели закрыть 28 | 29 | IGS.AddTextBlock(bg.side,"Что тут?", 30 | #d == 0 and 31 | "Здесь будут отображаться ваши активные покупки\n\n" .. 32 | "Не самое ли подходящее время, чтобы совершить первую?\n\n" .. 33 | "Табличка сразу станет красивее. Честно-честно" 34 | or 35 | "Слева отображаются ваши активные услуги.\n\n" .. 36 | "Чем больше услуг, тем красивее эта табличка выглядит, а администрация более счастливая ;)" 37 | ) 38 | 39 | IGS.AddButton(bg.side,"Купить плюшку",function() 40 | if #IGS.GetItems() == 0 then -- если NULL уберу 41 | LocalPlayer():ChatPrint("Настройте предметы автодоната в sh_additems.lua") 42 | return 43 | end 44 | 45 | while true do 46 | local random_ITEM = table.Random(IGS.GetItems()) 47 | if random_ITEM:CanSee( LocalPlayer() ) then 48 | IGS.WIN.Item(random_ITEM:UID()) 49 | break 50 | end 51 | end 52 | end) 53 | 54 | for i,v in ipairs(d) do 55 | local sv_name = IGS.ServerName(v.server) 56 | local ITEM = IGS.GetItemByUID(v.item) 57 | local sName = ITEM.isnull and v.item or ITEM:Name() 58 | 59 | pnl:AddLine( 60 | -- v.id, 61 | multisv and sv_name or #d - i + 1, 62 | sName, 63 | IGS.TimestampToDate(v.purchase) or "Никогда", 64 | IGS.TimestampToDate(v.expire) or "Никогда" 65 | ):SetTooltip("Имя сервера: " .. sv_name .. "\nID в системе: " .. v.id .. "\nОригинальное название: " .. v.item) 66 | end 67 | end) 68 | end, bg) 69 | 70 | activity:AddTab("Покупки",bg,"materials/icons/fa32/reorder.png") 71 | end) 72 | 73 | -- IGS.UI() 74 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/modules/inv_log/core_cl.lua: -------------------------------------------------------------------------------- 1 | IGS.IL = IGS.IL or {} 2 | 3 | local callbacks,last,MAX = {},0,7 4 | function IGS.IL.GetLog(fCb, iPage, s64_owner, gift_uid) 5 | net.Start("IGS.InvLog") 6 | net.WriteBool(s64_owner) 7 | if s64_owner then 8 | net.WriteString(s64_owner) 9 | end 10 | 11 | net.WriteBool(gift_uid) 12 | if gift_uid then 13 | net.WriteString(gift_uid) 14 | end 15 | 16 | net.WriteBool(iPage) 17 | if iPage then 18 | net.WriteUInt(iPage,8) 19 | end 20 | 21 | last = (last + 1) % MAX + 1 22 | callbacks[last] = fCb 23 | 24 | net.WriteUInt(last,3) 25 | net.SendToServer() 26 | end 27 | -- IGS.IL.GetLog(PRINT, 1, nil, "chat_prefix") 28 | 29 | net.Receive("IGS.InvLog", function() 30 | local cb_id = net.ReadUInt(3) 31 | local cb = callbacks[cb_id] 32 | -- prt({callbacks = callbacks,cb_id = cb_id}) 33 | assert(cb,"No callback with id " .. cb_id) 34 | 35 | local data = {[0] = net.ReadUInt(22)} 36 | for i = 1,net.ReadUInt(6) do 37 | data[i] = { 38 | owner = net.ReadString(), 39 | inflictor = net.ReadString(), 40 | gift_uid = net.ReadString(), 41 | gift_id = net.ReadUInt(22), 42 | action = net.ReadUInt(3), 43 | action_id = net.ReadUInt(22), 44 | date = net.ReadUInt(32), 45 | } 46 | end 47 | 48 | cb(data) 49 | callbacks[cb_id] = nil 50 | end) 51 | 52 | 53 | 54 | local sid_to_name_cache = {} 55 | function IGS.IL.NameRequest(fCb, s64) 56 | if sid_to_name_cache[s64] then -- cached or queued {} 57 | local cached = sid_to_name_cache[s64][0] 58 | if cached ~= nil then 59 | fCb(cached) -- mb false 60 | else -- добавляем еще одного желающего получить результат запроса 61 | table.insert(sid_to_name_cache[s64], fCb) 62 | end 63 | else 64 | -- скок же я проебался, забыв добавить в таблицу fCb 65 | sid_to_name_cache[s64] = {fCb} 66 | net.Start("IGS.NameRequest") 67 | net.WriteString(s64) 68 | net.SendToServer() 69 | end 70 | end 71 | 72 | net.Receive("IGS.NameRequest", function() 73 | local requested_s64 = net.ReadString() 74 | local name_ = net.ReadBool() and net.ReadString() 75 | 76 | local cbs = sid_to_name_cache[requested_s64] 77 | for i = #cbs,1,-1 do 78 | cbs[i](name_ or false) 79 | cbs[i] = nil 80 | end 81 | cbs[0] = name_ -- cache 82 | end) 83 | -- IGS.IL.NameRequest(PRINT, AMD():SteamID64()) 84 | -- IGS.IL.NameRequest(PRINT, "76561198109429966") 85 | 86 | 87 | function IGS.DeactivateItem(iPurchID) 88 | net.Start("IGS.DeactivateItem") 89 | net.WriteUInt(iPurchID, IGS.BIT_PURCH_ID) 90 | net.SendToServer() 91 | end 92 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/objects/shop_group.lua: -------------------------------------------------------------------------------- 1 | --[[------------------------------------------------------------------------- 2 | В магазине есть категории, а есть группы товаров. 3 | 4 | Категория - это группа подобных товаров. 5 | Например, випы и премиумы 6 | 7 | Группа это может быть разновидность одного товара. 8 | Например группа вип прав содержит в себе вип на неделю, месяц, навсегда и т.д. 9 | 10 | Этот файл представляет собой регистратор ГРУПП 11 | ---------------------------------------------------------------------------]] 12 | 13 | local ITEM_GROUP = {} 14 | ITEM_GROUP.__index = ITEM_GROUP 15 | ITEM_GROUP.__tostring = function(self) 16 | -- "ITEM GROUP (Name Of Group) [i]" 17 | return "IGS GROUP (" .. self:Name() .. ")[" .. #self:Items() .. "]" 18 | end 19 | -- MT_IGSGroup = getmetatable( IGS.NewGroup("") ) 20 | 21 | 22 | 23 | -- Fade, Tiger, Damascus 24 | function ITEM_GROUP:AddItem(STORE_ITEM,sNameOverride) 25 | if self.items.STORED[STORE_ITEM:UID()] then 26 | return self.items.STORED[STORE_ITEM:UID()] 27 | end 28 | 29 | local dat = { 30 | item = STORE_ITEM, 31 | name = sNameOverride or STORE_ITEM:Name() 32 | } 33 | 34 | local ID = #self.items.MAP + 1 35 | self.items.MAP[ID] = dat 36 | self.items.STORED[STORE_ITEM:UID()] = dat 37 | 38 | STORE_ITEM.group = self 39 | 40 | return self.items.MAP[ID] 41 | end 42 | 43 | function ITEM_GROUP:SetIcon(sIconUrl) 44 | self.icon_url = sIconUrl 45 | return self 46 | end 47 | 48 | function ITEM_GROUP:SetHighlightColor(color) 49 | if CLIENT then 50 | self.highlight = color 51 | end 52 | return self 53 | end 54 | 55 | 56 | 57 | 58 | function ITEM_GROUP:Name() 59 | return self.name 60 | end 61 | 62 | function ITEM_GROUP:UID() -- TODO 63 | return self:Name() 64 | end 65 | 66 | function ITEM_GROUP:Items() 67 | return self.items.MAP 68 | end 69 | 70 | function ITEM_GROUP:ICON() 71 | return self.icon_url 72 | end 73 | 74 | 75 | 76 | 77 | IGS.GROUPS = IGS.GROUPS or {} 78 | 79 | -- Flip Knifes 80 | function IGS.NewGroup(sName) 81 | if IGS.GROUPS[sName] then 82 | return IGS.GROUPS[sName] 83 | end 84 | 85 | local group = setmetatable({ 86 | name = sName, 87 | items = { 88 | MAP = {}, -- iter, STORED 89 | STORED = {} -- STORE_ITEM:UID(), 90 | }, 91 | },ITEM_GROUP) 92 | 93 | IGS.GROUPS[sName] = group 94 | 95 | return group 96 | end 97 | 98 | function IGS.GetGroups() 99 | return IGS.GROUPS 100 | end 101 | 102 | function IGS.GetGroup(name) 103 | return IGS.GROUPS[name] 104 | end 105 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/dependencies/matex.lua: -------------------------------------------------------------------------------- 1 | -- TRIGON.IM 12 dec 2021 2 | -- Упрощенная версия texture либы от dash 3 | -- 2024.12.27 dec 2024 добавлена проверка is_normal_image, чтобы всякие 429 и 403 от imgur не кешировали говно 4 | 5 | matex = matex or {} 6 | 7 | file.CreateDir("matex") 8 | 9 | local PNG_START = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A} 10 | local PNG_TRAIL = {0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82} 11 | local is_png = function(raw) 12 | for i = 1, 8 do 13 | if PNG_START[i] ~= string.byte(raw, i) then return false end 14 | if PNG_TRAIL[i] ~= string.byte(raw, -(9 - i)) then return false end 15 | end 16 | return true 17 | end 18 | 19 | 20 | local JPG_START = {0xFF, 0xD8, 0xFF} 21 | local JPG_TRAIL = {0xFF, 0xD9} 22 | local is_jpg = function(raw) 23 | for i = 1, 3 do 24 | if JPG_START[i] ~= string.byte(raw, i) then return false end 25 | if i == 3 then break end 26 | if JPG_TRAIL[i] ~= string.byte(raw, -(3 - i)) then return false end 27 | end 28 | return true 29 | end 30 | 31 | 32 | -- https://mimesniff.spec.whatwg.org/#matching-an-image-type-pattern 33 | -- https://www.garykessler.net/library/file_sigs.html 34 | local function is_normal_image(raw) 35 | local png, jpg = is_png(raw), is_jpg(raw) 36 | return png or jpg 37 | end 38 | 39 | function matex.download(url, callback, useproxy) 40 | local id = util.CRC(url) 41 | 42 | local filepath = "matex/" .. id .. ".png" 43 | local matpath = "../data/matex/" .. id .. ".png" 44 | 45 | if file.Exists(filepath, "DATA") then 46 | callback( Material(matpath, "noclamp smooth") ) 47 | return 48 | end 49 | 50 | local baseurl = useproxy and "https://proxy.duckduckgo.com/iu/?u=" .. url or url 51 | http.Fetch(baseurl, function(body) 52 | if is_normal_image(body) then file.Write(filepath, body) end 53 | callback( Material(matpath, "noclamp smooth") ) 54 | end, function() 55 | if useproxy then callback( Material("nil") ) return end 56 | matex.download(url, callback, true) 57 | end) 58 | end 59 | 60 | local cache = {} 61 | function matex.now(url) 62 | if cache[url] then return cache[url].material end 63 | cache[url] = {material = nil} 64 | matex.download(url, function(material) cache[url].material = material end) 65 | return cache[url].material 66 | end 67 | 68 | 69 | --[[ 70 | -- example: 71 | hook.Add("HUDPaint", "mater", function() 72 | local mater = matex.now("https://i.imgur.com/TZcJ1CK.png") 73 | if mater then 74 | surface.SetDrawColor(color_white) 75 | surface.SetMaterial(mater) 76 | surface.DrawTexturedRect(35, 35, 570, 460) 77 | end 78 | end) 79 | -- hook.Remove("HUDPaint", "mater") 80 | --]] 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 | # InGameShop (IGS) 8 | **Внутриигровой магазин от gm-donate.net** — аддон для Garry's Mod, который добавляет на сервер F1 меню, через которое игроки могут сделать пожертвование и обменять полученные баллы на виртуальные привилегии: группы прав, оружие, модельки, хвосты, транспорт и т.д. 9 | 10 | 11 | 12 | ## Инструкция по установке 13 | 14 | 1. Скачайте `igs-modification`: [скачать](https://github.com/GM-DONATE/IGS/releases/latest/download/igs-mod.zip) и распакуйте его в папку /addons на сервере. `igs-core` скачивать не нужно. Этот скрипт используется для настройки автодоната, а также сам его обновляет 15 | 2. В файле `config_sv.lua` укажите ID проекта и секретный ключ, который указан на странице проекта [на сайте](https://gm-donate.net/panel/) (проект нужно создать) 16 | 17 | Напишите нам [в поддержку](https://gm-donate.net/support), если возникли какие-то вопросы, а наш живой оператор вам поможет. Также вы можете обратиться [на форум](https://forum.gm-donate.net). 18 | 19 | ## Главная ошибка при установке 20 | 21 | > ❗ **Папки `igs-core` не должно быть в addons**. `igs-modification` сама проследит и скачает все необходимые файлы. 22 | 23 | В 99% случаев `igs-core` распаковывают в addons, чтобы что-то сделать, что можно сделать без распаковки. Просто спросите у поддержки, либо на форуме, а мы вам поможем не сделать глупость. 24 | 25 | `igs-core` в addons лишает вас автоматических обновлений и исправлений, если очередная обнова в гмоде снова все сломает, как с `debug.getregistry()` 26 | 27 | ## Полезные ресурсы 28 | 29 | - 🔥 [Более подробная инструкция по установке](http://gm-donate.net/instructions) 30 | - 📰 [VK](https://vk.com/public143836547): Главная группа VK с основными новостями 31 | - 💬 [Forum](https://forum.gm-donate.net): Форум гмоддеров. Ответы и помощь с любыми гмод вопросами, включая GMD 32 | - 📣 [GMD Stream](https://t.me/notafaq): Telegram канал с информацией, которая мало известна, но может быть полезна 33 | - 📣 [GMD Mods](https://t.me/gmodder): Telegram канал с модами и различными публикациями, которых нет в группе ВК 34 | - 🔧 [Настройка итемов](https://gm-donate.net/docs): информация про методы, которые наделяют донат предметы функционалом 35 | -------------------------------------------------------------------------------- /addons/igs-modification/lua/igs/settings/sh_addlevels.lua: -------------------------------------------------------------------------------- 1 | --[[------------------------------------------------------------------------- 2 | Цены в .Add указываются в рублях 3 | ---------------------------------------------------------------------------]] 4 | 5 | -- Уровни сработают только, если не произойдет ошибки при пополнении счета 6 | -- Правда я не представляю что нужно сделать, чтобы произошла ошибка (Не пришел сигнал PAY с автодоната) 7 | IGS.LVL.Add(1, "Новичок") 8 | :SetBonus(function(pl) 9 | local bonus = pl:IGSFunds() * .1 10 | pl:AddIGSFunds(bonus,"Бонус за первое пополнение") 11 | IGS.Notify(pl,"Вы получили " .. PL_MONEY(bonus) .. "\nв качестве бонуса за первое пополнение счета") 12 | end) 13 | :SetDescription("При первом пополнении счета получите 10% в подарок автоматически и бесплатно") -- выше в catchDSHints еще 14 | 15 | 16 | IGS.LVL.Add(100, "Стартанувший") 17 | IGS.LVL.Add(500, "В теме") 18 | 19 | IGS.LVL.Add(1000, "Бывалый") 20 | :SetDescription("Позволяет получить уникальный статус \"Мегалодон\" на форуме") 21 | 22 | IGS.LVL.Add(1500, "Вроде не бомж") 23 | :SetDescription("Скоро новый бонус") 24 | 25 | 26 | IGS.LVL.Add(2000, "Точно не бомж") 27 | :SetDescription("Еще капельку и бонус. Следующий лвл") 28 | 29 | 30 | IGS.LVL.Add(2500, "При деньгах") 31 | :SetDescription("Бонус 20% на пополнение счета") 32 | :SetBonus(function(pl) 33 | local bonus = pl:IGSFunds() * .2 -- на самом деле бонус начислит на всю имеющуюся сумму, а не сумму пополнения. Так что ахтунг 34 | pl:AddIGSFunds(bonus,"Бонус за 2500 руб транзакций") 35 | IGS.Notify(pl,"Вы получили " .. PL_MONEY(bonus) .. "\nв качестве бонуса за новый бизнес ЛВЛ") 36 | end) 37 | 38 | 39 | IGS.LVL.Add(3000, "Щедрый") 40 | :SetDescription("Премиум поддержка от правительства") 41 | 42 | IGS.LVL.Add(4000, "Очень щедрый") 43 | :SetDescription("На след. лвл новый бонус") 44 | 45 | IGS.LVL.Add(5000, "Пиздец щедрый") 46 | :SetDescription("Статус Убердон на форуме") 47 | 48 | IGS.LVL.Add(6000, "Мажор") 49 | :SetDescription("Предложение о сотрудничестве") 50 | 51 | 52 | IGS.LVL.Add(7000, "Супермажик") 53 | IGS.LVL.Add(8000, "Гипермажик") 54 | IGS.LVL.Add(9000, "Убермажор") 55 | IGS.LVL.Add(10000, "Миллионер") 56 | :SetDescription("Премиум поддержка от основателя в любое время суток") 57 | 58 | IGS.LVL.Add(12000, "МультиМиллионер") 59 | IGS.LVL.Add(15000, "Миллиардер") 60 | IGS.LVL.Add(20000, "МультиМиллиардер") 61 | IGS.LVL.Add(25000, "Кыш с дороги") 62 | IGS.LVL.Add(30000, "Сядь в лужу, я пройду") 63 | IGS.LVL.Add(35000, "Я тебя куплю") 64 | IGS.LVL.Add(40000, "Я куплю тебя и твою семью") 65 | IGS.LVL.Add(50000, "Бог один и это Я") 66 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/launcher.lua: -------------------------------------------------------------------------------- 1 | IGS.C = IGS.C or {} -- config 2 | 3 | local function sh(path) return IGS.sh("igs/" .. path) end 4 | local function sv(path) return IGS.sv("igs/" .. path) end 5 | local function cl(path) return IGS.cl("igs/" .. path) end 6 | 7 | local function dir(path, fIncluder) return IGS.include_files("igs/" .. path, fIncluder) end 8 | local function mods(path) return IGS.load_modules("igs/" .. path) end 9 | 10 | 11 | sh("dependencies/plurals.lua") 12 | sh("dependencies/chatprint.lua") 13 | sv("dependencies/stack.lua") 14 | sh("dependencies/scc.lua") 15 | sv("dependencies/resources.lua") -- иконки, моделька дропнутого итема 16 | sh("dependencies/bib.lua") 17 | sh("dependencies/permasents.lua") 18 | cl("dependencies/matex.lua") 19 | 20 | -- #todo сделать через require 21 | -- lua/includes/modules отсюда 22 | -- уберет костыль внутри kupol 23 | -- +при фетче оверрайд require 24 | sh("dependencies/lolib.lua") -- должна быть перед kupol 25 | -- sh("dependencies/kupol.lua") -- решил поставлять с модулем 26 | 27 | -- Антиконфликт с https://trello.com/c/3ti6xIjW/ 28 | sh("dependencies/dash/nw.lua") 29 | 30 | -- if not dash then 31 | sh("dependencies/dash/misc.lua") 32 | 33 | sh("settings/config_sh.lua") 34 | sv("settings/config_sv.lua") -- для фетча project key (Генерация подписи) 35 | 36 | -- Метаобъекты 37 | sh("objects/level.lua") 38 | sh("objects/shop_group.lua") 39 | sh("objects/shop_item.lua") 40 | 41 | sh("network/nw_sh.lua") -- для igs_servers в serv_sv.lua 42 | 43 | sv("core_sv.lua") -- для фетча подписи 44 | 45 | sv("repeater.lua") 46 | sv("apinator.lua") 47 | 48 | -- После датапровайдера, хотя сработают все равно после первого входа игрока 49 | sh("servers/serv_sh.lua") 50 | sv("servers/serv_sv.lua") 51 | 52 | 53 | 54 | --[[------------------------------------------------------------------------- 55 | Второй "этап" (для работы требовал загрузку серверов) 56 | ---------------------------------------------------------------------------]] 57 | sh("utils/ut_sh.lua") 58 | sv("utils/ut_sv.lua") 59 | cl("utils/ut_cl.lua") 60 | 61 | 62 | -- Нельзя ниже sh_additems 63 | dir("extensions", IGS.sh) 64 | 65 | sh("settings/sh_additems.lua") 66 | sh("settings/sh_addlevels.lua") 67 | 68 | sv("network/net_sv.lua") 69 | cl("network/net_cl.lua") 70 | 71 | 72 | cl("interface/skin.lua") 73 | -- cl("core_cl.lua") 74 | 75 | -- Подключение VGUI компонентов 76 | dir("interface/vgui", IGS.cl) 77 | 78 | cl("interface/core.lua") 79 | 80 | dir("interface/activities", IGS.cl) 81 | dir("interface/windows", IGS.cl) 82 | 83 | mods("modules") 84 | 85 | sv("processor_sv.lua") -- начинаем обработку всего серверного в конце 86 | 87 | 88 | hook.Add("IGS.ServersLoaded", "IGS.Loaded", function() 89 | hook.Run("IGS.Loaded") 90 | end) 91 | 92 | hook.Run("IGS.Initialized") -- можно создавать итемы 93 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/objects/level.lua: -------------------------------------------------------------------------------- 1 | IGS.LVL = IGS.LVL or setmetatable({ 2 | MAP = {}, 3 | STORED = {} 4 | },{ 5 | __call = function(self,...) 6 | return self.Add(...) 7 | end 8 | }) 9 | 10 | 11 | 12 | local LVL = {} 13 | LVL.__index = LVL 14 | 15 | function LVL:SetBonus(fOnReach) 16 | self.bonus = fOnReach -- ply in args 17 | return self 18 | end 19 | 20 | function LVL:SetName(sName) -- OUTDATED (удалено 2018.03.12) 21 | self.name = sName 22 | return self 23 | end 24 | 25 | function LVL:SetDescription(sDesc) 26 | self.description = sDesc 27 | return self 28 | end 29 | 30 | function LVL:Name() 31 | return self.name 32 | end 33 | 34 | function LVL:Description() 35 | return self.description 36 | end 37 | 38 | function LVL:LVL() 39 | return self.lvl 40 | end 41 | 42 | function LVL:Cost() 43 | return self.cost 44 | end 45 | 46 | function LVL:GetNext() -- may be nil 47 | local ilvl,OBJ = next(IGS.LVL.MAP,self.lvl) 48 | return OBJ,ilvl 49 | end 50 | 51 | -- Разница между уровнями 52 | -- function LVL:NeedToRaise() -- nil on last lvl 53 | -- local n = self:GetNext() 54 | -- return n and n.cost - self.cost 55 | -- end 56 | 57 | function IGS.LVL.Add(iNeedSum, sName) 58 | local OBJ = setmetatable({ 59 | cost = iNeedSum, 60 | name = sName 61 | },LVL) 62 | 63 | IGS.LVL.STORED[iNeedSum] = OBJ 64 | IGS.LVL.Rearrange() 65 | 66 | return OBJ 67 | end 68 | 69 | function IGS.LVL.Get(iLVL) 70 | return IGS.LVL.MAP[iLVL] 71 | end 72 | 73 | -- Дает объект лвл, соответствующий указанной сумме 74 | function IGS.LVL.GetByCost(iRealCost) 75 | for lvl = 1,#IGS.LVL.MAP do 76 | if not IGS.LVL.MAP[lvl + 1] or IGS.LVL.MAP[lvl + 1].cost > iRealCost then 77 | return IGS.LVL.MAP[lvl] 78 | end 79 | end 80 | end 81 | 82 | -- Уровни в диапазоне стоимости. 83 | -- Начало юзаться при выдаче бонусов за пополнение: https://trello.com/c/EjmPOeso/476-- 84 | -- function IGS.LVL.GetRange(iFromCost, iToCost) 85 | -- local t = {} 86 | 87 | -- for iLVL,lvl in ipairs(IGS.LVL.MAP) do 88 | -- if lvl.cost >= iFromCost and lvl.cost <= iToCost then 89 | -- table.insert(t,lvl) 90 | -- end 91 | -- end 92 | 93 | -- return t 94 | -- end 95 | 96 | -- Перестраивает кэш порядка левелов 97 | -- Нужно на случай, если сначала добавили 30 лвл, а потом 10 98 | -- то чтобы не считалось, что 30 ниже 10 99 | function IGS.LVL.Rearrange() 100 | IGS.LVL.MAP = {} -- reset 101 | 102 | local i = 0 103 | for sum in SortedPairs(IGS.LVL.STORED) do 104 | -- долго объяснять. Короче сортедпэирс по ходу копирует объект перед тем, 105 | -- как вернуть его в цикл и нельзя сделать for sum,OBJ 106 | local OBJ = IGS.LVL.STORED[sum] 107 | i = i + 1 108 | OBJ.lvl = i 109 | 110 | IGS.LVL.MAP[i] = OBJ 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/vgui/igs_frame.lua: -------------------------------------------------------------------------------- 1 | local PANEL = {} 2 | 3 | function PANEL:Init() 4 | self:DockPadding(0,24,0,0) 5 | 6 | self.lblTitle:SetPos(5,3) 7 | self.lblTitle:SetColor(IGS.col.TEXT_HARD) 8 | 9 | self.btnClose:SetTextColor(IGS.col.HIGHLIGHTING) 10 | self.btnClose:SetText("✕") 11 | self.btnClose:SetSize(30, 24) 12 | self.btnClose.Paint = function() end 13 | -- self.btnClose.DoClick = function() self:Close() end 14 | 15 | -- self:SetBackgroundBlur(false) 16 | self:SetTitle("") 17 | 18 | -- self.btnClose:SetVisible(false) 19 | self.btnMaxim:SetVisible(false) 20 | self.btnMinim:SetVisible(false) 21 | 22 | self.lblTitle:SetFont("igs.20") 23 | end 24 | 25 | local locations = {} 26 | function PANEL:SaveLocation(panel_uid) 27 | locations[panel_uid] = {self:GetPos()} 28 | end 29 | 30 | function PANEL:RestoreLocation(panel_uid) 31 | if locations[panel_uid] then 32 | local x,y = unpack(locations[panel_uid]) 33 | self:SetPos( 34 | math.Clamp(x,0,ScrW() - 10), 35 | math.Clamp(y,0,ScrH() - 10) 36 | ) -- на случай, если уменьшат разрешение, чтобы не исчезло у краев 37 | locations[panel_uid] = nil 38 | else 39 | self:Center() 40 | end 41 | end 42 | 43 | function PANEL:RememberLocation(panel_uid) 44 | self.remember_uid = panel_uid 45 | end 46 | 47 | 48 | function PANEL:Close(...) 49 | surface.PlaySound("ambient/water/rain_drip3.wav") 50 | self.BaseClass.Close(self, ...) 51 | if self.remember_uid then 52 | self:SaveLocation(self.remember_uid) 53 | end 54 | end 55 | 56 | function PANEL:GetTitleHeight() 57 | return 24 -- close button H 58 | end 59 | 60 | function PANEL:Paint(w,h) 61 | if self.m_bBackgroundBlur then 62 | Derma_DrawBackgroundBlur(self, self.m_fCreateTime) 63 | end 64 | 65 | IGS.S.Frame(self,w,h) 66 | return true 67 | end 68 | 69 | function PANEL:PaintOver(w,h) 70 | IGS.S.Outline(self,w,h) -- через = не работало 71 | end 72 | 73 | function PANEL:Focus() 74 | local panels = {} 75 | self:SetBackgroundBlur(true) 76 | for _, v in ipairs(vgui.GetWorldPanel():GetChildren()) do 77 | if v:IsVisible() and (v ~= self) then 78 | panels[#panels + 1] = v 79 | v:SetVisible(false) 80 | end 81 | end 82 | self._OnClose = self.OnClose 83 | self.OnClose = function() 84 | for _, v in ipairs(panels) do 85 | if IsValid(v) then 86 | v:SetVisible(true) 87 | end 88 | end 89 | 90 | self:_OnClose() 91 | end 92 | end 93 | 94 | function PANEL:PerformLayout() 95 | self.lblTitle:SizeToContents() 96 | self.btnClose:SetPos(self:GetWide() - 30, 0) 97 | 98 | if self.remember_uid and not self.restored then 99 | self.restored = true 100 | self:RestoreLocation(self.remember_uid) 101 | end 102 | end 103 | 104 | vgui.Register("igs_frame",PANEL,"DFrame") 105 | -- IGS.UI() 106 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/repeater.lua: -------------------------------------------------------------------------------- 1 | -- Алгоритм и описание репитера: https://gist.github.com/5e5afdff55cc1f70f1c1a459b8487943 2 | 3 | IGS.REPEATER = IGS.REPEATER or IGStack() 4 | local R = IGS.REPEATER 5 | 6 | function R:IsEmpty() -- внешка 7 | return self:isempty() 8 | end 9 | -------------------------------------------------------- 10 | 11 | function R:Query(REQ) 12 | IGS.WrapQuery(REQ[1], REQ[2], REQ[3]) 13 | end 14 | 15 | function R:ProcessNextQuery() 16 | local NEXT_REQ = self:lpop() 17 | if NEXT_REQ then 18 | self:Query(NEXT_REQ) -- один за другим, пока не закончатся 19 | else -- ставится ok для IGS 20 | hook.Run("IGS.RepeaterEmpty") 21 | end 22 | end 23 | 24 | function R:SafeQuery(sMethod, tParams, fOnSuccess) 25 | local REQ = {sMethod, tParams, fOnSuccess} 26 | 27 | if self:IsEmpty() then 28 | self:Query(REQ) 29 | else 30 | self:rpush(REQ) 31 | end 32 | end 33 | 34 | -------------------------------------------------------- 35 | 36 | -- func > iter 37 | -- Сколько раз функция перевыполнялась 38 | local repeats_counter = setmetatable({}, {__mode = "k"}) 39 | 40 | local SHOULD_REPEAT_CASES = { 41 | ["too_many_requests"] = true, 42 | ["http_error"] = true, 43 | 44 | -- стоит ли? Что если метод сломался на постоянке? 45 | ["invalid_response_format"] = true, 46 | } 47 | 48 | -- Нужно, чтобы запросы без колбэка не выдавали ошибку 49 | -- https://t.me/c/1353676159/12486 50 | local function getBlankCallback(sMethod) 51 | return function() 52 | IGS.prints("", "Редкое сообщение. Выполнился ", sMethod, " без колбэка, который сначала 'упал'") 53 | end 54 | end 55 | 56 | -- Переносит запрос в конец очереди или отбрасывает его 57 | hook.Add("IGS.OnApiError", "repeater", function(sMethod, error_uid, tParams, fOnSuccess) 58 | if SHOULD_REPEAT_CASES[error_uid] then 59 | fOnSuccess = fOnSuccess or getBlankCallback(sMethod) 60 | 61 | local try = (repeats_counter[fOnSuccess] or 0) + 1 62 | repeats_counter[fOnSuccess] = try 63 | 64 | if try <= 15 then 65 | -- Следующие запросы теперь будут добавляться в очередь, а не выполняться 66 | -- Ниже таймер, который скоро начнет их обработку 67 | R:rpush({sMethod, tParams, fOnSuccess}) 68 | end 69 | 70 | -- Был burst запросов и хук вызвался дважды 71 | if not timer.Exists("IGS_REPEATER") then 72 | IGS.dprint(Color(250, 50, 50), "Ошибка выполнения запроса: ", error_uid, ". Запущен репитер") 73 | timer.Create("IGS_REPEATER", 10, 1, function() R:ProcessNextQuery() end) 74 | end 75 | else 76 | -- Антизависание при 77 | -- [успешный > неуспешный > успешный] 78 | -- https://img.qweqwe.ovh/1565285856105.png 79 | R:ProcessNextQuery() 80 | end 81 | end) 82 | 83 | -- Вырубает таймер, выполняет последовательно оставшиеся запросы 84 | hook.Add("IGS.OnApiSuccess", "repeater", function() 85 | -- Это каллбэк внутри успешного хука, тоесть GMD заработал 86 | -- и по логике остальные запросы выполнятся корректно 87 | -- Если какой-то все же даст сбой, то снова заработает таймер 88 | R:ProcessNextQuery() -- one by one 89 | end) 90 | -------------------------------------------------------------------------------- /addons/igs-modification/lua/igs/settings/config_sh.lua: -------------------------------------------------------------------------------- 1 | 2 | -- \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ 3 | --[[------------------------------------------------------------------------- 4 | ПРЕДМЕТЫ ДОБАВЛЯЮТСЯ В sh_additems.lua 5 | ---------------------------------------------------------------------------]] 6 | -- /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ 7 | 8 | 9 | --[[------------------------------------------------------------------------- 10 | Настройки валюты 11 | ---------------------------------------------------------------------------]] 12 | IGS.C.CURRENCY_SIGN = "Alc" 13 | 14 | -- Множественные названия валюты. 15 | -- Пример 1: Доллар, Доллара, Долларов 16 | -- Пример 2: Поинт, Поинта, Поинтов 17 | IGS.C.CurrencyPlurals = { 18 | "алкобакс", -- 1 алкобакс 19 | "алкобакса", -- 3 алкобакса 20 | "алкобаксов" -- 5 алкобаксов 21 | } 22 | 23 | 24 | --[[------------------------------------------------------------------------- 25 | Настройки активации интерфейса 26 | ---------------------------------------------------------------------------]] 27 | -- На какую кнопку будет открываться донат менюшка 28 | -- https://wiki.facepunch.com/gmod/Enums/KEY 29 | IGS.C.MENUBUTTON = KEY_F1 30 | 31 | 32 | -- /команда для открытия донат менюшки 33 | IGS.C.COMMANDS = { 34 | ["donate"] = true, 35 | ["донат"] = true, 36 | } 37 | 38 | 39 | --[[------------------------------------------------------------------------- 40 | Донат инвентарь 41 | ---------------------------------------------------------------------------]] 42 | -- Если отключить, вкладка инвентаря исчезнет, а предметы при покупке сразу будут активироваться 43 | -- Станут недоступны некоторые методы, вроде :SetItems(, так как используют инвентарь 44 | IGS.C.Inv_Enabled = true 45 | 46 | -- Разрешить выбрасывать предметы с инвентаря на пол 47 | -- Это позволит игрокам покупать донат подарки для друзей или вам делать донат раздачи 48 | IGS.C.Inv_AllowDrop = true 49 | 50 | 51 | 52 | if SERVER then return end -- не смотрите так на меня :) 53 | 54 | 55 | -- Показывать ли уведомление о новых предметах в донат меню 56 | -- Выглядит вот так https://img.qweqwe.ovh/1526574184864.png 57 | IGS.C.NotifyAboutNewItems = true 58 | 59 | 60 | -- Эта иконка будет отображена для предмета, если для него не будет установлена кастомная через :SetIcon() 61 | -- Отображается вот тут: https://img.qweqwe.ovh/1494088609445.png 62 | -- Хостинг картинок: https://t.me/cfr2bot 63 | IGS.C.DefaultIcon = "https://file.def.pm/42Hr9k9U.jpeg" 64 | 65 | 66 | 67 | 68 | -- Уберите "--" с начала строки, чтобы отключить появление определенного фрейма 69 | IGS.C.DisabledFrames = { 70 | -- ["faq_and_help"] = true, -- Чат бот (страница помощи) 71 | -- ["profile"] = true, -- Страница профиля игрока (с транзакциями) 72 | -- ["purchases"] = true, -- Активные покупки 73 | } 74 | 75 | 76 | -- Оставьте так, если не уверены 77 | -- Инфо: https://vk.cc/6xaFOe 78 | IGS.C.DATE_FORMAT = "%d.%m.%y %H:%M:%S" 79 | IGS.C.DATE_FORMAT_SHORT = "%d.%m.%y" 80 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/processor_sv.lua: -------------------------------------------------------------------------------- 1 | local function giveLvlBonuses(pl, from_lvl, to_lvl) 2 | for i = from_lvl,to_lvl do 3 | local lvl = IGS.LVL.Get(i) 4 | 5 | if lvl.bonus then 6 | lvl.bonus(pl) 7 | end 8 | 9 | IGS.NotifyAll(pl:Name() .. " получил новый (" .. i .. ") бизнес уровень - " .. lvl:Name()) 10 | end 11 | end 12 | 13 | local function recalcTransactionsAndBonuses(pl, bGiveBonuses) 14 | -- Сумма операций 15 | IGS.GetPlayerTransactionsBypassingLimit(function(dat) 16 | local tt = 0 17 | for _,v in ipairs(dat) do 18 | tt = v.Sum > 0 and tt + v.Sum or tt 19 | end 20 | 21 | local prev_lvl = IGS.PlayerLVL(pl) or 0 -- не двигать под igs_lvl! 22 | pl:SetIGSVar("igs_lvl",IGS.LVL.GetByCost( tt ):LVL()) 23 | pl:SetIGSVar("igs_total_transactions",tt) 24 | 25 | if bGiveBonuses and IGS.PlayerLVL(pl) > prev_lvl then 26 | giveLvlBonuses(pl, prev_lvl + 1, IGS.PlayerLVL(pl)) 27 | end 28 | end, pl:SteamID64()) 29 | end 30 | 31 | 32 | -- Указать bGiveBonuses, если нужно пересчитать бонусы 33 | local function updateBalance(pl, fOnFinish, bGiveBonuses) 34 | IGS.GetPlayer(pl:SteamID64(), function(pld_) 35 | if not IsValid(pl) then return end 36 | local now_igs_ = pld_ and pld_.Balance 37 | local now_score_ = pld_ and pld_.Score 38 | 39 | local was_igs = pl:IGSFunds() -- fallback = 0 даже для тех, кто никогда не донил (фундаментальный проеб) 40 | local diff = (now_igs_ or 0) - was_igs 41 | 42 | if diff ~= 0 then -- транзакция или есть стартовый баланс 43 | pl:SetIGSVar("igs_balance", now_igs_) -- Должно выполняться ТОЛЬКО на донатеров (не на обычных игроках) 44 | end 45 | 46 | pl.igs_score = now_score_ -- #todo make netvar 47 | 48 | if now_igs_ then -- значит есть транзакции, реальный донатер, а не просто баланс 0 49 | recalcTransactionsAndBonuses(pl, bGiveBonuses) 50 | end 51 | 52 | if fOnFinish then 53 | fOnFinish(now_igs_, diff) 54 | end 55 | end) 56 | end 57 | 58 | 59 | hook.Add("PlayerInitialSpawn", "IGS.LoadPlayer", function(pl) 60 | if pl:IsBot() then return end 61 | 62 | -- Устанавливаем баланс донат счета и донат уровень игрока 63 | updateBalance(pl, function(now_igs_) 64 | if (not IsValid(pl)) then return end 65 | 66 | IGS.LoadPlayerPurchases(pl) 67 | IGS.LoadInventory(pl) 68 | 69 | if now_igs_ and now_igs_ > 0 then 70 | IGS.UpdatePlayerName(pl:SteamID64(), pl:Name()) 71 | end 72 | end) 73 | end) 74 | 75 | local function repairBrokenPurchases(pl, purchases) 76 | for uid in pairs(purchases) do -- , count 77 | local ITEM = IGS.GetItemByUID(uid) 78 | 79 | if ITEM:IsValid(pl) == false then 80 | ITEM:Setup(pl) -- не первый раз, скипаем onactivate 81 | end 82 | end 83 | end 84 | 85 | -- Восстановление слетевших прав 86 | hook.Add("IGS.PlayerPurchasesLoaded", "RestorePex", function(pl, purchases) 87 | if purchases then 88 | repairBrokenPurchases(pl, purchases) 89 | end 90 | end) 91 | 92 | hook.Add("IGS.PaymentStatusUpdated", "NoRejoiningCharge", function(pl, dat) 93 | if dat.method ~= "pay" then return end 94 | 95 | updateBalance(pl, function(new_bal, diff) 96 | hook.Run("IGS.PlayerDonate", pl, diff, new_bal) 97 | end, true) -- updateBalance with bGiveBonuses 98 | end) 99 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/modules/pushes/actions_sv.lua: -------------------------------------------------------------------------------- 1 | --[[------------------------------------------------------------------------- 2 | Мгновенные изменения 3 | ---------------------------------------------------------------------------]] 4 | local function getPlayer(dat) 5 | return player.GetBySteamID64( dat.SteamID64 ) 6 | end 7 | 8 | -- Обновление статуса платежа 9 | hook.Add("IGS.IncomingMessage","PaymentStatus",function(d, method) 10 | if method ~= "payment.UpdateStatus" then return end 11 | 12 | local pl = getPlayer(d) 13 | if not pl then return end 14 | 15 | -- https://img.qweqwe.ovh/1537567538620.png 16 | -- https://img.qweqwe.ovh/1537568769298.png 17 | hook.Run("IGS.PaymentStatusUpdated",pl,d) 18 | end) 19 | 20 | -- Моментальная выдача услуги 21 | hook.Add("IGS.IncomingMessage","GivePurchase",function(d, method) 22 | if method ~= "purchase.store" then return end 23 | 24 | local pl = getPlayer(d) 25 | if not pl then return end 26 | 27 | local ITEM = IGS.GivePurchase(pl,d.Item) -- выдает покупку без сохранения в БД 28 | IGS.Notify(pl,"Вам выдана новая услуга: " .. ITEM:Name()) 29 | end) 30 | 31 | -- Перенос услуги (в т.ч. отключение) 32 | hook.Add("IGS.IncomingMessage","MovePurchase",function(d, method) 33 | if not (method == "purchase.move" and IGS.SERVERS:ID() == d.ServFrom) then return end 34 | 35 | local pl = getPlayer(d) 36 | if not pl then return end 37 | 38 | -- Просто перезагружаем данные 39 | -- Если перенос был на этот сервер, то услуга будет выдана (или забрана. С :HasPurchase) 40 | IGS.Notify(pl, "Перезагрузка списка покупок из-за переноса или отключения услуг") 41 | IGS.LoadPlayerPurchases(pl,function() 42 | IGS.Notify(pl,"Список перезагружен") 43 | end) 44 | end) 45 | 46 | 47 | local INV_ACTIONS = { 48 | ["inventory.storeItem"] = true, 49 | ["inventory.deleteItem"] = true, 50 | } 51 | 52 | -- Забираем вещь с инвентаря 53 | -- Добавление итема в инвентарь 54 | hook.Add("IGS.IncomingMessage","InventoryActions",function(d, method) 55 | if not INV_ACTIONS[method] then return end 56 | 57 | local pl = getPlayer(d) 58 | if not pl then return end 59 | 60 | IGS.Notify(pl, "Перезагрузка инвентаря") 61 | IGS.LoadInventory(pl,function() 62 | IGS.Notify(pl, "Инвентарь перезагружен") 63 | end) 64 | end) 65 | 66 | -- Отключаем сервер 67 | hook.Add("IGS.IncomingMessage","DisableServer",function(d, method) 68 | if method ~= "servers.disable" then return end 69 | 70 | local endl = "" 71 | if d.Server == IGS.SERVERS:ID() then 72 | -- Относительно тяжелая, но кейс редкий. не критично 73 | -- https://img.qweqwe.ovh/1566303954304.png 74 | -- Можно заюзать lua broadcast переменной даже 75 | SetGlobalBool("IGS_DISABLED", true) 76 | else 77 | endl = " на " .. IGS.SERVERS(d.Server) 78 | end 79 | 80 | IGS.NotifyAll("Автодонат временно отключен" .. endl) 81 | end) 82 | 83 | -- nomr 84 | hook.Add("IGS.IncomingMessage", "nomr", function(d, method) 85 | if method ~= "transactions.create" then return end 86 | 87 | local pl = getPlayer(d) 88 | if not pl then return end 89 | 90 | if IGS.C.DisableAntiMultirun then return end 91 | 92 | if d.Server and d.Server ~= IGS.SERVERS:ID() then 93 | pl:Kick("Транзакция на другом сервере") 94 | end 95 | end) 96 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/extensions/ulx.lua: -------------------------------------------------------------------------------- 1 | IGS.ITEMS.ULX = IGS.ITEMS.ULX or { 2 | GROUPS = {}, 3 | PEX = {} 4 | } 5 | 6 | 7 | local STORE_ITEM = MT_IGSItem 8 | 9 | function STORE_ITEM:SetULXGroup(sUserGroup, iGroupWeight) 10 | self:SetCanActivate(function(pl) -- invDbID 11 | if pl:IsUserGroup(sUserGroup) then 12 | return "У вас уже действует эта услуга" 13 | end 14 | end) 15 | self:SetInstaller(function(pl) 16 | RunConsoleCommand("ulx", "adduserid", pl:SteamID(), sUserGroup) 17 | pl.IGSULXWeight = iGroupWeight 18 | end) 19 | self:SetValidator(function(pl) 20 | local valid = false 21 | if pl.IGSULXWeight then 22 | valid = iGroupWeight <= pl.IGSULXWeight 23 | else 24 | valid = pl:IsUserGroup(sUserGroup) 25 | end 26 | 27 | if not valid then 28 | IGS.NotifyAll("Автовосстановление " .. self:Name() .. " для " .. pl:Name()) 29 | return false 30 | end 31 | end) 32 | 33 | 34 | self.ulx_group = self:Insert(IGS.ITEMS.ULX.GROUPS, sUserGroup) 35 | self.ulx_group_weight = iGroupWeight 36 | return self 37 | end 38 | 39 | -- Есть много ньюансов. Коммит 1 октября 2019 40 | function STORE_ITEM:SetULXCommandAccess(cmd,tag) -- "ulx model","^", например 41 | self:SetInstaller(function(pl) 42 | if not tag then 43 | table.insert(ULib.ucl.authed[ pl:UniqueID() ].allow, cmd) 44 | else 45 | ULib.ucl.authed[ pl:UniqueID() ].allow[cmd] = tag 46 | end 47 | end) 48 | self:SetValidator(function() 49 | return false 50 | end) 51 | 52 | 53 | self.ulx_command = self:Insert(IGS.ITEMS.ULX.PEX, cmd) 54 | return self 55 | end 56 | 57 | 58 | if CLIENT then return end 59 | 60 | local function checkGroups(pl) 61 | local hasAccess = IGS.PlayerHasOneOf(pl, IGS.ITEMS.ULX.GROUPS[ pl:GetUserGroup() ]) 62 | if hasAccess == nil then return end 63 | 64 | if hasAccess then 65 | pl.IGSULXWeight = hasAccess.ulx_group_weight 66 | return -- если имеется хоть одна покупка, то не снимаем права 67 | end 68 | 69 | RunConsoleCommand("ulx","removeuserid",pl:SteamID()) 70 | end 71 | 72 | local function hasPexAccess(pl, cmd) 73 | local hasAccess = IGS.PlayerHasOneOf(pl, IGS.ITEMS.ULX.PEX[cmd]) 74 | return hasAccess ~= false -- nil, если не продается 75 | end 76 | 77 | local function checkPermissions(pl) 78 | local user = ULib.ucl.authed[ pl:UniqueID() ] 79 | if not user then return end 80 | 81 | local changed 82 | -- Вид ucl таблицы https://img.qweqwe.ovh/1523035793058.png 83 | for k,v in pairs(user.allow or {}) do -- не уверен, что allow обязательно есть 84 | local cmd = isnumber(k) and v or k 85 | 86 | if not hasPexAccess(pl, cmd) then 87 | user.allow[k] = nil 88 | changed = true 89 | end 90 | end 91 | 92 | if changed then 93 | ULib.ucl.saveUsers() 94 | end 95 | end 96 | 97 | timer.Simple(.1, function() -- чтобы этот хук обязательно был после RestorePex. История ВК 20 мая с Антон Панченко 98 | hook.Add("IGS.PlayerPurchasesLoaded", "ULXGroupsAndPEX", function(pl) 99 | if next(IGS.ITEMS.ULX.GROUPS) then 100 | checkGroups(pl) 101 | end 102 | 103 | if next(IGS.ITEMS.ULX.PEX) then 104 | checkPermissions(pl) 105 | end 106 | end, HOOK_HIGH) 107 | end) 108 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/vgui/igs_tabbar.lua: -------------------------------------------------------------------------------- 1 | local PANEL = {} 2 | 3 | local barTall = 50 4 | local btnWide = 70 5 | 6 | function PANEL:Init() 7 | self.activity = uigs.Create("igs_multipanel",self) 8 | 9 | self.tabBar = uigs.Create("Panel",self) 10 | self.tabBar:SetTall(barTall) 11 | self.tabBar.Paint = function(_, w, h) 12 | surface.SetDrawColor(IGS.col.TAB_BAR) 13 | surface.DrawRect(0,0,w,h) -- bg 14 | 15 | surface.SetDrawColor(IGS.col.HARD_LINE) 16 | surface.DrawLine(0,0,w,0) -- upper line 17 | end 18 | 19 | self.btnsPan = uigs.Create("DIconLayout", self.tabBar) 20 | self.btnsPan.Paint = function() end 21 | 22 | self.Buttons = {} 23 | end 24 | 25 | function PANEL:SetActiveTab(num) 26 | for i,btn in ipairs(self.Buttons) do 27 | btn.Active = num == i -- для подсветки 28 | end 29 | 30 | self.activity:SetActivePanel(num) 31 | end 32 | 33 | function PANEL:GetActiveTab() 34 | return self.activity:GetActivePanel() 35 | end 36 | 37 | function PANEL:AddTab(sTitle,panel,sIcon,bActive) 38 | local ID = self.activity:AddPanel(panel,bActive) 39 | 40 | local button = uigs.Create("DButton", function(btn) 41 | btn:SetSize(btnWide, 50) 42 | btn:SetText("") 43 | 44 | btn:SetFont("igs.24") 45 | 46 | btn.DoClick = function(s) 47 | self:SetActiveTab(s.ID) 48 | end 49 | 50 | --[[------------------------------------------------------------------------- 51 | TODO Сделать отрисовку скина через скин хук 52 | чтобы можно было юзать компонент не только в IGS без порчи дизайна 53 | В bar.Paint тоже 54 | ---------------------------------------------------------------------------]] 55 | btn.Paint = function(s,w,h) 56 | if s.Active then 57 | surface.SetDrawColor(IGS.col.HIGHLIGHTING) 58 | surface.SetTextColor(IGS.col.HIGHLIGHTING) 59 | else 60 | surface.SetDrawColor(IGS.col.HIGHLIGHT_INACTIVE) 61 | surface.SetTextColor(IGS.col.HIGHLIGHT_INACTIVE) 62 | end 63 | 64 | if sIcon then 65 | surface.SetMaterial( Material(sIcon) ) 66 | surface.DrawTexturedRect(w / 2 - 32 / 2,3,32,32) 67 | end 68 | 69 | if sTitle then 70 | surface.SetFont("igs.15") 71 | 72 | local tw = surface.GetTextSize(sTitle) 73 | surface.SetTextPos(w / 2 - tw / 2,32 + 3) 74 | 75 | surface.DrawText( sTitle ) 76 | end 77 | end 78 | 79 | btn.ID = ID 80 | btn.Tab = panel 81 | btn.Active = bActive 82 | end) 83 | 84 | function button:Name() 85 | return sTitle 86 | end 87 | 88 | self.btnsPan:Add(button) 89 | table.insert(self.Buttons, button) 90 | 91 | -- self:PerformLayout() 92 | self.btnsPan:SetSize(#self.Buttons * btnWide,barTall) 93 | self.btnsPan:SetPos((self:GetWide() - self.btnsPan:GetWide()) / 2) 94 | 95 | 96 | return button 97 | end 98 | 99 | function PANEL:PerformLayout() 100 | self.tabBar:SetWide(self:GetWide()) 101 | self.tabBar:SetPos(0,self:GetTall() - self.tabBar:GetTall()) 102 | 103 | self.activity:SetSize(self.tabBar:GetWide(),self:GetTall() - barTall) 104 | end 105 | 106 | function PANEL:Paint(w,h) 107 | draw.RoundedBox(0,0,0,w,h,IGS.col.ACTIVITY_BG) 108 | end 109 | 110 | vgui.Register("igs_tabbar", PANEL, "Panel") 111 | -- IGS.UI() 112 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/modules/extra/don_grace_sv.lua: -------------------------------------------------------------------------------- 1 | --[[------------------------------------------------------------------------- 2 | Отображение рекордного доната 3 | ---------------------------------------------------------------------------]] 4 | -- Если включить, то в чате начнут появляться сообщения, которые написаны ниже 5 | IGS.C.TopDon_Echo = true 6 | 7 | -- Частота сброса последнего рекорда доната 8 | -- %H - раз в час, %d - раз в день, %u - раз в неделю. %m - раз в месяц 9 | IGS.C.TopDon_Periodicity = "%u" -- %m 10 | 11 | IGS.C.TopDon_TextRecord = "$nick побил рекорд доната в этом месяце, пополнив счет на $sum руб.\nПредыдущий рекорд установил $nick_prev, пополнив счет на $sum_prev руб" 12 | IGS.C.TopDon_TextFirstDon = "$nick стал первым, кто задонатил в этом месяце. $nick умничка. Будь как $nick - /donate" -- доступен шаблон $sum 13 | 14 | 15 | 16 | 17 | --[[------------------------------------------------------------------------- 18 | ---------------------------------------------------------------------------]] 19 | local SUM,TIME,SID,NAME = 20 | "igs:hugecharge_sum", 21 | "igs:hugecharge_time", 22 | "igs:hugecharge_sid", 23 | "igs:hugecharge_name" 24 | 25 | 26 | local function resetHugeCharge() 27 | bib.delete(SUM) 28 | bib.delete(TIME) 29 | bib.delete(SID) 30 | bib.delete(NAME) 31 | 32 | -- cprint("Сбросили") 33 | end 34 | 35 | local function setHugeCharge(sum, sid, nick) 36 | bib.set(SUM, sum) 37 | bib.set(TIME, os.time()) --+ 60*60*24*2) -- +2 дня 38 | bib.set(SID, sid) 39 | bib.set(NAME, nick) 40 | end 41 | 42 | local function getHugeCharge(bSumOnly) 43 | if bSumOnly then -- микрооптимизация 44 | return tonumber(bib.get(SUM)) 45 | end 46 | 47 | return { 48 | sum = tonumber(bib.get(SUM)), 49 | time = tonumber(bib.get(TIME)), 50 | sid = bib.get(SID), 51 | nick = bib.get(NAME) 52 | } 53 | end 54 | 55 | -- print(getHugeCharge()) 56 | -- setHugeCharge(1,AMD():SteamID64(),"_AMD_") 57 | 58 | 59 | 60 | 61 | -- Если есть запись о предыдущем рекорде и сейчас час/день/неделя/месяц меньше, чем было в предыдущей записи 62 | -- значит день/неделя/месяц/год начались сначала и пора удалять эту запись, чтобы начать учет сначала 63 | local prev_rec = getHugeCharge() 64 | -- cprint(os.date(IGS.C.TopDon_Periodicity)) 65 | -- cprint(os.date(IGS.C.TopDon_Periodicity,prev_rec.time)) 66 | 67 | if prev_rec and os.date(IGS.C.TopDon_Periodicity) < os.date(IGS.C.TopDon_Periodicity,prev_rec.time) then 68 | resetHugeCharge() 69 | end 70 | 71 | 72 | 73 | local function charge(pl, sum) 74 | sum = tonumber(sum) 75 | 76 | if sum > (getHugeCharge(true) or 0) then 77 | local pr = getHugeCharge() -- Previous Record 78 | 79 | local s = pr.nick and IGS.C.TopDon_TextRecord or IGS.C.TopDon_TextFirstDon 80 | 81 | -- Не первый донат 82 | if pr.nick then 83 | local s32 = util.SteamIDFrom64(pr.sid) 84 | 85 | s = s 86 | :gsub("$nick_prev", ("%s(%s)"):format(pr.nick,s32) ) 87 | :gsub("$sum_prev",pr.sum) 88 | end 89 | 90 | s = s 91 | :gsub("$nick",pl:Nick()) 92 | :gsub("$sum",sum) 93 | 94 | IGS.NotifyAll(s) 95 | 96 | setHugeCharge(sum,pl:SteamID64(),pl:Nick()) 97 | end 98 | end 99 | 100 | -- charge(AMD(),6) 101 | 102 | 103 | hook.Add("IGS.PlayerDonate","TopDonateEcho",function(pl, sum) 104 | if IGS.C.TopDon_Echo then 105 | charge(pl, sum) 106 | end 107 | end) 108 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/extensions/darkrp.lua: -------------------------------------------------------------------------------- 1 | IGS.ITEMS.DRP = IGS.ITEMS.DRP or { 2 | ITEMS = {}, 3 | JOBS = {} 4 | } 5 | 6 | 7 | local STORE_ITEM = MT_IGSItem 8 | 9 | -- Делает итем в магазине покупаемым только за донат 10 | -- Это может быть ящик оружия, отдельная пушка или даже отдельная энтити 11 | function STORE_ITEM:SetDarkRPItem(sEntClass) 12 | self.dpr_item = self:Insert(IGS.ITEMS.DRP.ITEMS, sEntClass) 13 | return self 14 | end 15 | 16 | -- Доступ к профессиям только тем, кто ее покупал 17 | function STORE_ITEM:SetDarkRPTeams(...) 18 | -- https://trello.com/c/xxGiGpb2/319-улучшить-setdarkrpteam 19 | local tTeams = {...} 20 | for i,team in ipairs(tTeams) do 21 | if isnumber(team) then -- обратная совместимость (20.03.2019) 22 | local TEAM = RPExtraTeams[team] 23 | self:Insert(IGS.ITEMS.DRP.JOBS, TEAM.command) 24 | tTeams[i] = TEAM.command -- заменяем ENUM на cmd для ITEM.dpr_teams 25 | else -- строка (team.command) https://trello.com/c/BcbYbAb7/512 26 | self:Insert(IGS.ITEMS.DRP.JOBS, team) 27 | end 28 | end 29 | self.dpr_teams = tTeams 30 | return self 31 | end 32 | STORE_ITEM.SetDarkRPTeam = STORE_ITEM.SetDarkRPTeams -- обратная совместимость (20.03.2019) 33 | 34 | function STORE_ITEM:SetDarkRPMoney(iSum) 35 | self:SetDescription("Мгновенно и без проблем пополняет баланс игровой валюты на " .. string.Comma(iSum) .. " валюты") 36 | self:SetInstaller(function(pl) pl:addMoney(iSum,"IGS") end) 37 | self:SetStackable() 38 | 39 | self.dpr_money = iSum 40 | return self 41 | end 42 | 43 | hook.Add("canBuyShipment", "IGS", function(pl, tItem) 44 | local ITEM = IGS.PlayerHasOneOf(pl, IGS.ITEMS.DRP.ITEMS[tItem.entity]) 45 | if ITEM ~= nil then -- донатный итем 46 | local allow, message = hook.Run("IGS.canBuyShipment", pl, tItem) 47 | return allow or tobool(ITEM), false, message or "Это для донатеров (/donate)" 48 | end 49 | end) 50 | 51 | hook.Add("canBuyPistol", "IGS", function(pl, tItem) 52 | local ITEM = IGS.PlayerHasOneOf(pl, IGS.ITEMS.DRP.ITEMS[tItem.entity]) 53 | if ITEM ~= nil then -- донатный итем 54 | local allow, message = hook.Run("IGS.canBuyPistol", pl, tItem) 55 | return allow or tobool(ITEM), false, message or "Это для донатеров (/donate)" 56 | end 57 | end) 58 | 59 | -- в DarkRP.hooks нет такого 60 | -- https://img.qweqwe.ovh/1528097550183.png 61 | hook.Add("canBuyCustomEntity", "IGS", function(pl, tItem) 62 | local ITEM = IGS.PlayerHasOneOf(pl, IGS.ITEMS.DRP.ITEMS[tItem.ent]) 63 | if ITEM ~= nil then -- донатный итем 64 | local allow, message = hook.Run("IGS.canBuyCustomEntity", pl, tItem) 65 | return allow or tobool(ITEM), false, message or "Это для донатеров (/donate)" 66 | end 67 | end) 68 | 69 | -- нет suppress 70 | -- print(IGS.PlayerHasOneOf(AMD(), IGS.ITEMS.DRP.JOBS[TEAM_LOCK])) 71 | hook.Add("playerCanChangeTeam", "IGS", function(pl, iTeam, bForce) 72 | local TEAM = RPExtraTeams[iTeam] 73 | local ITEM = IGS.PlayerHasOneOf(pl, IGS.ITEMS.DRP.JOBS[TEAM.command]) 74 | if ITEM ~= nil then -- донатный итем 75 | local allow, message = hook.Run("IGS.playerCanChangeTeam", pl, iTeam, bForce) 76 | return allow or tobool(ITEM), message or "Это для донатеров (/donate)" 77 | end 78 | end) 79 | 80 | 81 | -- Hunger 82 | function STORE_ITEM:DisablePlayerHunger() 83 | return self:SetInstaller(function(pl) 84 | pl.igs_disable_hunger = true 85 | end):SetValidator(function(pl) 86 | return false 87 | end) 88 | end 89 | 90 | hook.Add("hungerUpdate", "IGS", function(pl) 91 | if pl.igs_disable_hunger then 92 | pl:setSelfDarkRPVar("Energy", 100) 93 | return true 94 | end 95 | end) 96 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/vgui/igs_panels_layout.lua: -------------------------------------------------------------------------------- 1 | -- http://joxi.ru/Vm6bbvlipXRZmZ 2 | 3 | -- TODO переписать. СНОВА. Чтобы норм вставляло эллементы с разной шириной, не выходя за пределы строки 4 | local PANEL = {} 5 | 6 | function PANEL:Init() 7 | self.name = "Untitled" 8 | self.panels = {} -- line, panels 9 | 10 | self.current_line = 1 11 | 12 | --self.elements_tall = 80 13 | end 14 | 15 | function PANEL:SetName(sName) 16 | if not sName then return end 17 | 18 | self.name = sName 19 | 20 | self.title = self.title or uigs.Create("DLabel", function(title) 21 | title:SetSize(self:GetWide() - 10 - 10,30) 22 | title:SetPos(10,10) 23 | title:SetFont("igs.24") 24 | 25 | -- 30 is label height, 10 is margins 26 | self.last_y = 10 + 30 27 | end, self) 28 | 29 | self.title:SetText(self.name) 30 | 31 | return self.title 32 | end 33 | 34 | function PANEL:GetLineWide(iLine) 35 | local w = 0 36 | for _,pan in ipairs(self.panels[iLine]) do 37 | w = w + pan:GetWide() 38 | end 39 | 40 | -- 10 - размер отступа 41 | return w + (#self.panels[iLine] - 1) * 10 42 | end 43 | 44 | -- Возвращает панели в строке 45 | -- function PANEL:GetLine(iLine) 46 | -- return self.panels[iLine] 47 | -- end 48 | 49 | function PANEL:GetCurrentLine() 50 | return self.panels[self.current_line] 51 | end 52 | 53 | -- Y для следующего ряда панелек 54 | function PANEL:GetY() 55 | local y = (#self.panels - 1) * (self.elements_tall + 10) -- в #self.panels кол-во линий 56 | 57 | return self.title and (y + 10 + 30) or y 58 | end 59 | 60 | function PANEL:Add(panel) 61 | panel:SetParent(self) 62 | 63 | -- Все эллементы в лэйауте должны быть одинаковой высоты, хотя могут быть разной ширины 64 | self.elements_tall = self.elements_tall or panel:GetTall() 65 | 66 | self.panels[self.current_line] = self.panels[self.current_line] or {} 67 | table.insert(self.panels[self.current_line],panel) 68 | 69 | local line_wide = self:GetLineWide(self.current_line) 70 | 71 | local borders = not self.disabled_align and (self:GetWide() - line_wide) / 2 -- отступы по сторонам 72 | local line_panels = self:GetCurrentLine() 73 | 74 | for i,pan in ipairs(line_panels) do 75 | 76 | -- Если первая панель, то делаем отступ, чтобы в конце все было по середине 77 | if i == 1 then 78 | pan:SetPos(self.disabled_align and 10 or borders, self:GetY()) 79 | else 80 | local x,y = line_panels[i - 1]:GetPos() 81 | pan:SetPos(x + line_panels[i - 1]:GetWide() + 10,y) 82 | end 83 | end 84 | 85 | -- Если размер существующих компонентов + еще один <= размер лэйаута, то вставляем эллемент 86 | -- if panel.tag then 87 | -- print("\n\n") 88 | -- print("self:GetWide()",self:GetWide()) 89 | -- print("panel:GetWide()",panel:GetWide()) 90 | -- print("line_wide",line_wide) 91 | -- print(panel) 92 | -- end 93 | if line_wide + 10 + panel:GetWide() > self:GetWide() then 94 | -- if panel.tag then 95 | -- PrintTable(self.panels) 96 | -- print("Перешли на след. ряд") 97 | -- --panel:SetPos(self.disabled_align and 10 or borders, self:GetY()) 98 | -- end 99 | self.current_line = self.current_line + 1 100 | end 101 | 102 | self:PerformLayout() 103 | end 104 | 105 | function PANEL:PerformLayout() 106 | self:SetHeight(self:GetY() + self.elements_tall) -- + 10 margin 107 | end 108 | 109 | -- Отключает центрирование эллементов 110 | function PANEL:DisableAlignment(bDisable) 111 | self.disabled_align = bDisable 112 | end 113 | 114 | 115 | vgui.Register("igs_panels_layout", PANEL, "Panel") 116 | --IGS.UI() 117 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/dependencies/lolib.lua: -------------------------------------------------------------------------------- 1 | -- 2021.01.18 Logging library 2 | -- Author https://amd-nick.me/about 3 | 4 | lolib = {} 5 | 6 | -- https://colorswall.com/palette/3/ 7 | local COLOR_DEBUG = Color(2, 117, 216) 8 | local COLOR_FADED = Color(247, 247, 247) 9 | local COLOR_INFO = Color(91, 192, 222) 10 | local COLOR_WARNING = Color(255, 255, 0) 11 | local COLOR_ERROR = Color(240, 50, 50) 12 | 13 | lolib.LEVELS = { 14 | DISABLE = 0, 15 | DEBUG = 1, 16 | INFO = 2, 17 | WARNING = 3, 18 | ERROR = 4, 19 | 20 | {"D", COLOR_DEBUG, COLOR_DEBUG}, 21 | {"I", COLOR_INFO, COLOR_FADED}, 22 | {"W", COLOR_WARNING, COLOR_FADED}, 23 | {"E", COLOR_ERROR, COLOR_WARNING}, 24 | } 25 | 26 | local function echo(pref_col, pref, text_col, text) 27 | MsgC(pref_col, pref, text_col, " " .. text .. "\n") 28 | end 29 | 30 | local function fp(a) 31 | local func = a[1] 32 | return function(...) 33 | return func(unpack(a, 2), ...) 34 | end 35 | end 36 | 37 | function lolib.new() 38 | local logger = { 39 | level = lolib.default_level or 3, 40 | pattern = "{message}" 41 | } 42 | 43 | logger.build_message = function(fmt, ...) 44 | local args = {...} 45 | local count = 0 46 | return fmt:gsub("{}", function() 47 | count = count + 1 48 | local replacement_val = (args[count] ~= nil) and args[count] 49 | if istable(replacement_val) then 50 | return util.TableToJSON(replacement_val) 51 | else 52 | return tostring(replacement_val) 53 | end 54 | end) 55 | end 56 | 57 | logger.format = function(fmt, ...) 58 | local text = logger.build_message(fmt, ...) 59 | 60 | -- invalid capture index error. "foo %9 bar" > "foo %%9 bar" 61 | text = text:gsub("%%", "%%%%%") 62 | -- text = text:gsub("%%(%d)", "%%%%%1") 63 | 64 | return logger.pattern 65 | :gsub("{time}", os.date("%H:%M:%S")) 66 | :gsub("{message}", text) 67 | end 68 | 69 | logger.filter = function(tRecord) 70 | if tRecord.level < logger.level or logger.level == 0 then return end 71 | return true 72 | end 73 | 74 | logger.log = function(iLevel, fmt, ...) 75 | if not logger.filter({level = iLevel}) then return end 76 | 77 | local text = logger.format(fmt, ...) 78 | 79 | local level_data = lolib.LEVELS[iLevel] 80 | echo(level_data[2], "[" .. level_data[1] .. "]", level_data[3], text) 81 | end 82 | 83 | logger.debug = fp{logger.log, lolib.LEVELS.DEBUG} 84 | logger.info = fp{logger.log, lolib.LEVELS.INFO} 85 | logger.warning = fp{logger.log, lolib.LEVELS.WARNING} 86 | logger.error = fp{logger.log, lolib.LEVELS.ERROR} 87 | 88 | logger.setLevel = function(iLevel) 89 | logger.level = iLevel 90 | end 91 | 92 | logger.setCvar = function(sVarName, iDefaultValue) 93 | local cvar = CreateConVar(sVarName, iDefaultValue or 0, FCVAR_ARCHIVE) 94 | 95 | cvars.AddChangeCallback(sVarName, function(_, _, new) 96 | local level = tonumber(new) or 0 97 | assert(level >= 0 and level <= 5) 98 | logger.setLevel(level) 99 | MsgN("logging level has been changed. New level is: " .. level) 100 | end, "lolib") 101 | 102 | logger.setLevel(cvar:GetInt()) 103 | 104 | return cvar 105 | end 106 | 107 | logger.setFormat = function(sPattern) 108 | logger.pattern = sPattern 109 | end 110 | 111 | return logger 112 | end 113 | 114 | --[[ 115 | local log = lolib.new() 116 | log.setLevel(lolib.LEVELS.INFO) 117 | 118 | log.debug("debug value: {}", 123) 119 | log.info("info value: {}", 123) 120 | log.warning("warning value: {}", 123) 121 | log.error("error value: {}", 123) 122 | ]] 123 | 124 | -- return lolib 125 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/dependencies/permasents.lua: -------------------------------------------------------------------------------- 1 | --[[------------------------------------------------------------------------- 2 | Для веб загрузки не подходит PermaProps аддон с воркшопа, 3 | поскольку он работает на InitPostEntity, который нельзя вызвать после веб загрузки 4 | 5 | Вообще-то можно, но я решил так 6 | ---------------------------------------------------------------------------]] 7 | 8 | local function getIndex() 9 | return util.JSONToTable(cookie.GetString("perma_index", "")) or {} 10 | end 11 | 12 | local function updateIndex(uid, class) 13 | local map = getIndex() 14 | map[uid] = class 15 | cookie.Set("perma_index", util.TableToJSON(map)) 16 | end 17 | 18 | local function SpawnSent(class, pos, ang) 19 | local ent = ents.Create(class) 20 | ent:SetPos(pos) 21 | ent:SetAngles(ang) 22 | ent:Spawn() 23 | ent:Activate() 24 | 25 | local phys = ent:GetPhysicsObject() 26 | if phys:IsValid() then 27 | phys:EnableMotion(false) 28 | end 29 | 30 | return ent 31 | end 32 | 33 | 34 | function IGS.PermaSaveFeature(class) 35 | properties.Add(class .. "_perma_add", { 36 | MenuLabel = "Сохранить на карте", 37 | Order = 855, 38 | MenuIcon = "icon16/bullet_disk.png", 39 | 40 | Filter = function(self, ent, pl) 41 | return IsValid(ent) and pl:IsSuperAdmin() and ent:GetClass() == class 42 | end, 43 | Action = function(self, ent) 44 | self:MsgStart() 45 | net.WriteEntity( ent ) 46 | self:MsgEnd() 47 | end, 48 | Receive = function(self, length, pl) 49 | local ent = net.ReadEntity() 50 | if not self:Filter(ent, pl) then return end 51 | 52 | if ent.permaSentUID then 53 | pl:ChatPrint("Эта энтити уже сохранена. Удалите и сохраните заново, если хотите переместить") 54 | return 55 | end 56 | 57 | local uid = math.random(0xFFFF) 58 | cookie.Set("perma_" .. class .. "_" .. uid, util.TableToJSON({ent:GetPos(), ent:GetAngles()})) 59 | pl:ChatPrint("Позиция сохранена под UID " .. uid) 60 | ent.permaSentUID = uid 61 | 62 | updateIndex(uid, class) 63 | end 64 | }) 65 | 66 | properties.Add(class .. "_perma_delete", { 67 | MenuLabel = "Удалить с карты", 68 | Order = 856, 69 | MenuIcon = "icon16/bin_closed.png", 70 | 71 | Filter = function(self, ent, pl) 72 | return IsValid(ent) and pl:IsSuperAdmin() and ent:GetClass() == class 73 | end, 74 | Action = function(self, ent) 75 | self:MsgStart() 76 | net.WriteEntity( ent ) 77 | self:MsgEnd() 78 | end, 79 | Receive = function(self, length, pl) 80 | local ent = net.ReadEntity() 81 | if not self:Filter(ent, pl) then return end 82 | 83 | if not ent.permaSentUID then 84 | pl:ChatPrint("Эта энтити не перманентная") 85 | return 86 | end 87 | 88 | local uid = ent.permaSentUID 89 | cookie.Set("perma_" .. class .. "_" .. uid, nil) 90 | pl:ChatPrint("Объект удален. UID был " .. uid) 91 | ent:Remove() 92 | 93 | updateIndex(uid, nil) 94 | end 95 | }) 96 | end 97 | 98 | if SERVER then 99 | hook.Add("IGS.Loaded", "IGS.PermaSents", function() 100 | timer.Simple(30, function() 101 | for _, ent in ipairs(ents.GetAll()) do 102 | if ent.permaSentUID then 103 | ent:Remove() 104 | end 105 | end 106 | 107 | local map = getIndex() 108 | for uid, class in pairs(map) do 109 | if scripted_ents.GetStored(class) then 110 | local dat = util.JSONToTable(cookie.GetString("perma_" .. class .. "_" .. uid)) 111 | local ent = SpawnSent(class, util.StringToType(dat[1], "Vector"), util.StringToType(dat[2], "Angle")) 112 | ent.permaSentUID = uid 113 | 114 | IGS.dprint("PermaSents: Заспавнили ", class) 115 | else 116 | IGS.dprint("PermaSents: ", class, " не существует на сервере") 117 | end 118 | end 119 | end) 120 | end) 121 | end 122 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/activities/inventory.lua: -------------------------------------------------------------------------------- 1 | local function loadTab(activity,sidebar,dat) 2 | local bg = sidebar:AddPage("Действия над итемом") 3 | IGS.AddTextBlock(bg.side, nil, #dat > 0 and 4 | "Выберите предмет, чтобы получить по нему список действий" or 5 | 6 | "Купленные предметы будут находится здесь." .. 7 | "\n\nБлагодаря инвентарю вы можете поделиться покупкой со своим другом, у которого не хватает денег на покупку услуги. " .. 8 | "Просто купите ее вместо него и бросьте на пол. После активации предмета он появится у него в инвентаре." .. 9 | "\n\nДобрые саморитяне используют инвентарь для устраивания классных конкурсов. " .. 10 | "Они набивают свой инвентарь предметами, а затем при каких-то условиях их раздают" 11 | ) 12 | 13 | bg.OnRemove = function() 14 | hook.Remove("IGS.PlayerPurchasedItem","UpdateInventoryView") 15 | end 16 | 17 | -- local act_tall = activity:GetTall() - activity.tabBar:GetTall() 18 | 19 | local infpan = uigs.Create("igs_iteminfo", function(p) 20 | -- p:SetSize(300,act_tall) -- \/ 21 | -- p:SetPos(0,0) 22 | p:Dock(LEFT) p:SetWide(300) 23 | p:SetIcon(IGS.C.DefaultIcon) 24 | p:SetName("") 25 | p:SetDescription("Здесь будет отображена информация о вашей покупке, когда вы ее сделаете") 26 | end, bg) 27 | 28 | local scr = uigs.Create("igs_scroll", bg) 29 | scr:Dock(FILL) scr:SetWide(activity:GetWide() - infpan:GetWide()) 30 | -- scr:SetSize(activity:GetWide() - infpan:GetWide(),act_tall) 31 | -- scr:SetPos(infpan:GetWide(),0) 32 | 33 | IGS.AddTextBlock(scr, "Ваш инвентарь", "Что-то тут пустовато...") 34 | 35 | scr:AddItem( uigs.Create("DIconLayout", function(icons) 36 | icons:SetWide(scr:GetWide()) 37 | icons:SetSpaceX(2) 38 | icons:SetSpaceY(2) 39 | icons.Paint = function() end 40 | 41 | local function removeFromCanvas(itemPan) 42 | if IsValid(itemPan) then -- не закрыли окно 43 | bg.side:Reset() 44 | infpan:Reset() 45 | itemPan:Remove() 46 | end 47 | end 48 | 49 | function icons:AddItem(ITEM, dbID) 50 | local item = icons:Add("igs_item") 51 | item:SetSize(icons:GetWide(),60) 52 | item:SetIcon(ITEM:ICON()) 53 | item:SetName(ITEM:Name()) 54 | item:SetSign("Действует " .. IGS.TermToStr(ITEM:Term())) 55 | item.DoClick = function() 56 | infpan:Reset() 57 | infpan:SetIcon(ITEM:ICON()) 58 | infpan:SetName(ITEM:Name()) 59 | infpan:SetImage(ITEM:IMG()) 60 | infpan:SetSubNameButton(ITEM:Group() and ITEM:Group():Name(), function() 61 | IGS.WIN.Group(ITEM:Group():UID()) 62 | end) 63 | infpan:SetDescription(ITEM:Description()) 64 | infpan:SetInfo(IGS.FormItemInfo(ITEM, LocalPlayer())) -- lp для GetPrice 65 | 66 | 67 | bg.side:Reset() 68 | 69 | local act_btn = IGS.AddButton(bg.side, "",function() 70 | IGS.ProcessActivate(dbID, function(ok) -- iPurchID, sMsg_ 71 | if not ok then return end 72 | 73 | removeFromCanvas(item) 74 | end) 75 | end).button 76 | act_btn:SetActive(true) 77 | act_btn:SetText("Активировать") 78 | 79 | if IGS.C.Inv_AllowDrop then 80 | IGS.AddButton(bg.side,"Бросить на пол",function() 81 | IGS.DropItem(dbID,function() 82 | removeFromCanvas(item) 83 | end) 84 | end) 85 | end 86 | end 87 | end 88 | 89 | for _,v in ipairs(dat) do 90 | icons:AddItem(v.item, v.id) 91 | end 92 | 93 | hook.Add("IGS.PlayerPurchasedItem","UpdateInventoryView",function(_, ITEM, invDbID) 94 | icons:AddItem(ITEM, invDbID) 95 | end) 96 | 97 | 98 | end) ) 99 | 100 | scr:AddItem(uigs.Create("Panel", function(end_margin) 101 | end_margin:SetTall(5) 102 | end)) 103 | 104 | activity:AddTab("Инвентарь",bg,"materials/icons/fa32/cart-arrow-down.png") 105 | end 106 | 107 | hook.Add("IGS.CatchActivities","inventory",function(activity,sidebar) 108 | if not IGS.C.Inv_Enabled then return end 109 | 110 | IGS.GetInventory(function(items) 111 | if not IsValid(sidebar) then return end 112 | loadTab(activity,sidebar,items) 113 | end) 114 | end) 115 | 116 | -- IGS.UI() 117 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/windows/modals.lua: -------------------------------------------------------------------------------- 1 | function IGS.BoolRequest(title, text, cback) 2 | local m = uigs.Create("igs_frame", function(self) 3 | self:SetTitle(title) 4 | self:ShowCloseButton(false) 5 | self:SetWide(ScrW() * .2) 6 | self:MakePopup() 7 | end) 8 | 9 | local txt = string.Wrap("igs.18", text, m:GetWide() - 10) 10 | local y = m:GetTitleHeight() + 5 11 | 12 | for _,line in ipairs(txt) do 13 | uigs.Create("DLabel", function(self, p) 14 | self:SetText(line) 15 | self:SetFont("igs.18") 16 | self:SetTextColor(IGS.col.TEXT_HARD) 17 | self:SizeToContents() 18 | self:SetPos((p:GetWide() - self:GetWide()) / 2, y) 19 | y = y + self:GetTall() + 2 20 | end, m) 21 | end 22 | 23 | y = y + 5 24 | m.btnOK = uigs.Create("igs_button", function(self, p) 25 | self:SetText("Да") 26 | self:SetPos(5, y) 27 | self:SetSize(p:GetWide() / 2 - 7.5, 25) 28 | self.DoClick = function() 29 | p:Close() 30 | cback(true) 31 | end 32 | end, m) 33 | 34 | m.btnCan = uigs.Create("igs_button", function(self, p) 35 | self:SetText("Нет") 36 | self:SetPos(p.btnOK:GetWide() + 10, y) 37 | self:SetSize(p.btnOK:GetWide(), 25) 38 | self:RequestFocus() 39 | self.DoClick = function() 40 | p:Close() 41 | cback(false) 42 | end 43 | y = y + self:GetTall() + 5 44 | end, m) 45 | 46 | m:SetTall(y) 47 | m:Center() 48 | 49 | m:Focus() 50 | return m 51 | end 52 | 53 | function IGS.StringRequest(title, text, default, cback) 54 | local m = uigs.Create("igs_frame", function(self) 55 | self:SetTitle(title) 56 | self:ShowCloseButton(false) 57 | self:SetWide(ScrW() * .3) 58 | self:MakePopup() 59 | end) 60 | 61 | local txt = string.Wrap("igs.18", text, m:GetWide() - 10) 62 | local y = m:GetTitleHeight() + 5 63 | 64 | for _, v in ipairs(txt) do 65 | uigs.Create("DLabel", function(self, p) 66 | self:SetText(v) 67 | self:SetFont("igs.18") 68 | self:SetTextColor(IGS.col.TEXT_HARD) 69 | self:SizeToContents() 70 | self:SetPos((p:GetWide() - self:GetWide()) / 2, y) 71 | y = y + self:GetTall() 72 | end, m) 73 | end 74 | 75 | y = y + 5 76 | local tb = uigs.Create("DTextEntry", function(self, p) 77 | self:SetPos(5, y + 5) 78 | self:SetSize(p:GetWide() - 10, 25) 79 | self:SetValue(default or '') 80 | y = y + self:GetTall() + 10 81 | self.OnEnter = function() 82 | p:Close() 83 | cback(self:GetValue()) 84 | end 85 | end, m) 86 | 87 | local btnOK = uigs.Create("igs_button", function(self, p) 88 | self:SetText("ОК") 89 | self:SetPos(5, y) 90 | self:SetSize(p:GetWide() / 2 - 7.5, 25) 91 | self:SetActive(true) 92 | self.DoClick = function() 93 | p:Close() 94 | cback(tb:GetValue()) 95 | end 96 | end, m) 97 | 98 | uigs.Create("igs_button", function(self) 99 | self:SetText("Отмена") 100 | self:SetPos(btnOK:GetWide() + 10, y) 101 | self:SetSize(btnOK:GetWide(), 25) 102 | self:RequestFocus() 103 | self.DoClick = function() 104 | m:Close() 105 | end 106 | y = y + self:GetTall() + 5 107 | end, m) 108 | 109 | m:SetTall(y) 110 | m:Center() 111 | 112 | m:Focus() 113 | return m 114 | end 115 | 116 | 117 | local null = function() end 118 | function IGS.ShowNotify(sText, sTitle, fOnClose) 119 | local m = IGS.BoolRequest(sTitle or "[IGS] Оповещение", sText, fOnClose or null) 120 | m.btnCan:Remove() -- оставляем только 1 кнопку 121 | 122 | local _,y = m.btnOK:GetPos() 123 | m.btnOK:SetText("ОК") 124 | m.btnOK:SetPos((m:GetWide() - m.btnOK:GetWide()) / 2, y) 125 | 126 | return m 127 | end 128 | 129 | function IGS.WIN.ActivateCoupon() 130 | IGS.StringRequest("Активация купона", 131 | "Если у вас есть донат купон, то введите его ниже", 132 | nil,function(val) 133 | IGS.UseCoupon(val,function(errMsg) 134 | if errMsg then 135 | IGS.ShowNotify(errMsg, "Ошибка активации купона") 136 | else 137 | IGS.ShowNotify("Деньги начислены на ваш счет. Можете посмотреть на это в транзакциях, переоткрыв донат меню", "Успешная активации купона") 138 | end 139 | end) 140 | end) 141 | end 142 | 143 | 144 | -- IGS.ShowNotify(("test "):rep(10), nil, function() 145 | -- print("Нотификашка закрылась") 146 | -- end) 147 | 148 | IGS.OpenURL = gui.OpenURL 149 | 150 | -- IGS.UI() 151 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/network/nw_sh.lua: -------------------------------------------------------------------------------- 1 | IGS.nw.Register("igs_lvl") 2 | :Write(net.WriteUInt,7) -- 127 3 | :Read(function() 4 | return IGS.LVL.Get(net.ReadUInt(7)) 5 | end) 6 | :SetLocalPlayer() 7 | 8 | 9 | IGS.nw.Register("igs_balance") -- Должно быть ТОЛЬКО у клиентов! 10 | :Write(net.WriteDouble) 11 | :Read(net.ReadDouble) 12 | :SetLocalPlayer() --:SetHook("OnIGSBalanceChanged") 13 | 14 | 15 | IGS.nw.Register("igs_total_transactions") 16 | :Write(net.WriteUInt,17) -- 131071 17 | :Read(net.ReadUInt,17) 18 | :SetLocalPlayer() 19 | 20 | -- До 16 фев 2022 передавались чисто ID предметов 21 | -- Но веб лоад приводит к тому, что еще до загрузки самого игрока на сервер ему могли 22 | -- передаваться ID предметов, которые на клиенте не успели создаться (например создавались в хуке IGS.Loaded) 23 | IGS.nw.Register("igs_purchases"):Write(function(networked_purchases) 24 | local flatten = {} 25 | for uid, am in pairs(networked_purchases) do 26 | local s = #flatten 27 | flatten[s + 1] = uid 28 | flatten[s + 2] = am 29 | end 30 | 31 | net.WriteUInt(#flatten / 2, 8) -- 255 32 | 33 | for i = 1,#flatten,2 do 34 | net.WriteString(flatten[i]) 35 | net.WriteUInt(flatten[i + 1], 9) 36 | end 37 | end):Read(function() 38 | local res = {} 39 | for _ = 1,net.ReadUInt(8) do 40 | res[net.ReadString()] = net.ReadUInt(9) 41 | end 42 | return res 43 | end):SetLocalPlayer():SetHook("IGS.PlayerPurchasesLoaded") 44 | 45 | 46 | --[[-------------- 47 | CONSTANTS 48 | ----------------]] 49 | IGS.BIT_TX = 8 -- max транз в нетворке (255) 50 | IGS.BIT_LATEST_PURCH = 6 -- 63 51 | 52 | -- Размер ячейки 53 | IGS.BIT_PURCH_ID = 32 -- 4294967295 54 | IGS.BIT_INV_ID = 32 55 | IGS.BIT_TX_ID = 32 56 | 57 | 58 | --[[-------------- 59 | .net Helpers 60 | ----------------]] 61 | function net.WriteIGSItem(ITEM) net.WriteString(ITEM:UID()) end 62 | function net.ReadIGSItem() return IGS.GetItemByUID(net.ReadString()) end 63 | -- function net.WriteIGSGroup(GROUP) net.WriteString(GROUP:Name()) end 64 | -- function net.ReadIGSGroup() return IGS.GetGroup(net.ReadString()) end 65 | 66 | local function writeIf(value, fWrite, arg_) 67 | -- Server = 0 записывал false, хоть это значение 68 | -- Для отлова бага была такая штука: 69 | -- https://img.qweqwe.ovh/1567089471715.png 70 | net.WriteBool(value ~= nil) 71 | if value then 72 | fWrite(value, arg_) 73 | end 74 | end 75 | 76 | local function readIf(fRead, arg_) 77 | return net.ReadBool() and fRead(arg_) 78 | end 79 | 80 | function net.WriteIGSPurchase(p) 81 | net.WriteUInt(p.ID,IGS.BIT_PURCH_ID) 82 | net.WriteString(p.Item) 83 | 84 | writeIf(p.Server, net.WriteUInt, 6) -- 63 85 | writeIf(p.Purchase,net.WriteUInt, 32) 86 | writeIf(p.Expire, net.WriteUInt, 32) 87 | writeIf(p.Nick, net.WriteString) 88 | end 89 | 90 | function net.ReadIGSPurchase() 91 | return { 92 | id = net.ReadUInt(IGS.BIT_PURCH_ID), 93 | item = net.ReadString(), 94 | 95 | server = readIf(net.ReadUInt, 6), 96 | purchase = readIf(net.ReadUInt, 32), 97 | expire = readIf(net.ReadUInt, 32), 98 | nick = readIf(net.ReadString), 99 | } 100 | end 101 | 102 | function net.WriteIGSTx(tx) 103 | net.WriteUInt(tx.ID, IGS.BIT_TX_ID) 104 | writeIf(tx.Server, net.WriteUInt, 6) -- 63 105 | net.WriteDouble(tx.Sum) 106 | net.WriteUInt(tx.Time, 32) -- dohuya 107 | writeIf(tx.Note, net.WriteString) 108 | end 109 | 110 | function net.ReadIGSTx() 111 | return { 112 | id = net.ReadUInt(IGS.BIT_TX_ID), 113 | server = readIf(net.ReadUInt, 6), 114 | sum = net.ReadDouble(), 115 | date = net.ReadUInt(32), -- timestamp 116 | note = readIf(net.ReadString), 117 | } 118 | end 119 | 120 | function net.WriteIGSInventoryItem(inv_it) 121 | net.WriteUInt(inv_it.ID, IGS.BIT_INV_ID) 122 | net.WriteString(inv_it.Item) 123 | end 124 | 125 | function net.ReadIGSInventoryItem() 126 | return { 127 | id = net.ReadUInt(IGS.BIT_INV_ID), 128 | item = IGS.GetItemByUID(net.ReadString()), 129 | } 130 | end 131 | 132 | function net.WriteIGSMessage(sErr) 133 | writeIf(sErr, net.WriteString) 134 | end 135 | 136 | function net.ReadIGSMessage() 137 | return net.ReadBool() and net.ReadString() 138 | end 139 | 140 | net.WriteIGSError = net.WriteIGSMessage 141 | net.ReadIGSError = net.ReadIGSMessage 142 | -------------------------------------------------------------------------------- /addons/igs-modification/lua/autorun/l_ingameshopmod.lua: -------------------------------------------------------------------------------- 1 | file.CreateDir("igs") 2 | 3 | -- Вы можете сделать форк основного репозитория, сделать там изменения и указать его имя здесь 4 | -- Таким образом IGS будет грузиться у всех с вашего репозитория 5 | IGS_REPO = "GM-DONATE/IGS" -- "AMD-NICK/IGS-1" 6 | if not IGS_REPO or file.Exists("autorun/l_ingameshop.lua", "LUA") then return end -- force lua 7 | 8 | 9 | local function checkRunString() 10 | RunString("IGS_Test_RS = true", "IGS_Test_RS") 11 | assert(IGS_Test_RS, "RunString не работает: https://forum.gm-donate.net/t/1663") 12 | IGS_Test_RS = nil 13 | end 14 | 15 | checkRunString() -- сразу может быть, а потом пропасть 16 | 17 | -- http либа работает не сразу 18 | local fetchDelayed = function(delay, url, fOk, fErr, tHeaders) 19 | timer.Simple(delay, function() 20 | http.Fetch(url, fOk, fErr, tHeaders) 21 | end) 22 | end 23 | 24 | local replaceGithubUrl = function(original) 25 | return original 26 | :gsub("^https://api.github.com", "https://gh.gm-donate.net/api") 27 | :gsub("^https://github.com", "https://gh.gm-donate.net") 28 | end 29 | 30 | local function wrapFetch(url, cb, retry_) 31 | local retry3Times = function() 32 | retry_ = retry_ or 1 33 | if retry_ < 3 then 34 | wrapFetch(url, cb, retry_ + 1) 35 | elseif retry_ == 3 then -- last chance 36 | local newurl = replaceGithubUrl(url) 37 | wrapFetch(newurl or url, cb, retry_ + 1) 38 | else 39 | return true 40 | end 41 | end 42 | 43 | local patt = "IGS Не может выполнить HTTP запрос и загрузить скрипт\nURL: %s\nError: %s\n" 44 | fetchDelayed((retry_ or 0) * 5, url, cb, function(err) -- timeout, unsuccessful 45 | local fault = retry3Times() 46 | if not fault then return end -- пытается дальше 47 | -- попытки исчерпались 48 | 49 | error(patt:format(url, err)) 50 | end) 51 | end 52 | 53 | 54 | local function downloadSuperfile(version, cb, _failure) 55 | local url = "https://github.com/" .. IGS_REPO .. "/releases/download/" .. version .. "/superfile.json" 56 | if _failure then ErrorNoHalt("[IGS] #" .. _failure .. " повторение загрузки", url) end 57 | 58 | wrapFetch(url, function(superfile) 59 | local dat = util.JSONToTable(superfile) 60 | if not dat and (_failure or 0) < 3 then 61 | downloadSuperfile(version, cb, (_failure or 0) + 1) 62 | return 63 | end 64 | 65 | local err = 66 | not dat and "superfile.json получен не в правильном формате" 67 | or dat.error and ("Ошибка от GitHub: " .. dat.error) 68 | 69 | assert(not err, (err or "") .. "\n" .. url .. "\nПопробуйте снова или почитайте тут https://forum.gm-donate.net/t/1663") 70 | 71 | file.Write("igs/superfile.txt", superfile) 72 | cb(superfile) 73 | end) 74 | end 75 | 76 | local function loadFromFile(superfile) 77 | checkRunString() 78 | 79 | local path = "autorun/l_ingameshop.lua" 80 | IGS_MOUNT = util.JSONToTable(superfile) 81 | 82 | RunString(IGS_MOUNT[path], path) 83 | end 84 | 85 | local function findFreshestVersion(cb) 86 | wrapFetch("https://api.github.com/repos/" .. IGS_REPO .. "/releases", function(json) 87 | local releases = util.JSONToTable(json) 88 | table.sort(releases, function(a, b) -- свежайшие версии сначала 89 | return tonumber(a.tag_name) > tonumber(b.tag_name) 90 | end) 91 | 92 | local freshest_version = releases[1] 93 | assert(freshest_version, "Релизов нет. Нужно запустить CI: https://forum.gm-donate.net/t/1663") 94 | 95 | cb(freshest_version.tag_name) 96 | end) 97 | end 98 | 99 | if SERVER then 100 | local superfile = file.Read("igs/superfile.txt") 101 | local version = cookie.GetString("igs_version") 102 | 103 | if superfile and version then -- 2 может не быть, если сервер перенесли без sv.db 104 | loadFromFile(superfile) 105 | 106 | elseif not version then 107 | findFreshestVersion(function(freshest_version) 108 | cookie.Set("igs_version", freshest_version) 109 | downloadSuperfile(freshest_version, loadFromFile) 110 | end) 111 | 112 | else -- version 113 | downloadSuperfile(version, loadFromFile) 114 | end 115 | 116 | elseif CLIENT then 117 | CreateConVar("igs_version", "", {FCVAR_REPLICATED}) 118 | local version = GetConVar("igs_version"):GetString() 119 | assert(tonumber(version), "cvar igs_version не передался клиенту. " .. version .. ": https://forum.gm-donate.net/t/1663") 120 | downloadSuperfile(version, loadFromFile) 121 | end 122 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/modules/pushes/akupol_sv.lua: -------------------------------------------------------------------------------- 1 | -- 2021.01.18 4:16 2 | -- Polling client for poll.gmod.app 3 | -- Docs: https://blog.amd-nick.me/poll-gmod-app-docs 4 | -- Author: amd-nick.me/about 5 | 6 | -- 2 copy of this file in some cases in different places with different include methods 7 | if not lolib then require("lolib") end 8 | -- require bib 9 | 10 | kupol = kupol or { 11 | log = lolib.new() 12 | } 13 | 14 | 15 | local log = kupol.log 16 | log.setCvar("kupol_logging_level") 17 | 18 | log.format = function(fmt, ...) 19 | fmt = fmt:gsub("{}", "%%s") -- обратная совместимость 20 | return os.date("%H:%M:%S ") .. fmt:format(...):gsub("(app/..)[%w_]*(../)", "%1*****%2") 21 | end 22 | 23 | local safestr = function(str) -- hide uid from logZ 24 | return str:gsub("(..)[%w_]*(..)", "%1*****%2") 25 | end 26 | 27 | local function get_updates(base_url, uid, sleep, ts, fOnResponse) 28 | local url = base_url .. uid .. "/getUpdates?sleep=" .. (sleep or "") .. "&ts=" .. (ts or "") 29 | log.debug("kupol http.Fetch({})", url) 30 | http.Fetch(url, function(json) 31 | log.debug("kupol body: {}", json) 32 | local t = util.JSONToTable(json) 33 | if t and t.ok then 34 | fOnResponse(t) 35 | else 36 | fOnResponse(false, t and t.description or "response is not a json") 37 | end 38 | end, function(http_err) 39 | fOnResponse(false, http_err) 40 | end) 41 | end 42 | 43 | 44 | function kupol.new(sUrl, uid, iTimeout) 45 | local o = {uid = uid, url = sUrl, timeout = iTimeout, handler = false, running = false, stopping = false} 46 | 47 | o.poll = function(ts, fOnResponse) 48 | get_updates(o.url, o.uid, o.timeout, ts, fOnResponse) 49 | end 50 | 51 | local processResponse = function(requested_ts, res) 52 | local remote_ts = res.ts 53 | 54 | local a = remote_ts < requested_ts -- переезд, бэкап, обнуление временем 55 | local b = #res.updates == 0 and requested_ts > remote_ts -- переход с dev на prod, где ts больше 56 | 57 | if a or b then 58 | local log_pattern = a and "ts сервера ({}) меньше локального ({})" 59 | or "Похоже, что на сервере произошел баг или сервер изменился. ts {} prev {}" 60 | 61 | log.warning(log_pattern, remote_ts, requested_ts) 62 | bib.setNum("lp:ts:" .. o.uid, remote_ts) 63 | requested_ts = remote_ts 64 | end 65 | 66 | local ts_diff = remote_ts - requested_ts 67 | if #res.updates > 0 then 68 | log.debug("From uid {} received {} new messages. Ts diff: {} items", safestr(o.uid), #res.updates, ts_diff) 69 | end 70 | 71 | for _,upd in ipairs(res.updates) do 72 | -- возможно проскальзывание https://img.qweqwe.ovh/1609615123638.png 73 | -- bib.setNum("lp:ts", remote_ts) 74 | local i = bib.getNum("lp:ts:" .. o.uid, 0) + 1 75 | bib.setNum("lp:ts:" .. o.uid, i) -- increment 76 | 77 | local _, err = pcall(o.handler, upd) 78 | if err then 79 | log.error("Внутри хендлера произошла ошибка и работа чуть не прекратилась: {}", err) 80 | end 81 | end 82 | 83 | -- https://t.me/c/1353676159/43747 84 | if ts_diff > #res.updates then 85 | log.warning("Апдейты долго не запрашивались и {} шт утеряно", ts_diff - #res.updates) 86 | bib.setNum("lp:ts:" .. o.uid, remote_ts) 87 | end 88 | end 89 | 90 | o.consume_updates = function() 91 | local previous_ts = bib.getNum("lp:ts:" .. o.uid) or 0 92 | 93 | -- log.info("Polling uid: {}. Timeout {} sec. Requested Ts {}", safestr(uid), sleep, previous_ts) 94 | o.poll(previous_ts, function(res, err) 95 | if o.checkStopping() then return end 96 | 97 | if res then 98 | processResponse(previous_ts, res) 99 | o.consume_updates() 100 | 101 | else 102 | log.error("Error: {}. Waiting 5 sec and retrying", err) 103 | timer.Simple(5, o.consume_updates) 104 | end 105 | end) 106 | 107 | return o 108 | end 109 | 110 | o.start = function(fHandler) 111 | local stopping = o.stopping 112 | 113 | o.running = true 114 | o.stopping = false 115 | o.handler = fHandler 116 | 117 | if not stopping then 118 | o.consume_updates() 119 | end 120 | 121 | return o 122 | end 123 | 124 | o.stop = function(fOnStopped) 125 | fOnStopped = fOnStopped or function() end 126 | if not o.running then fOnStopped() return end 127 | o.stopping = fOnStopped 128 | return o 129 | end 130 | 131 | o.checkStopping = function() 132 | local onStopped = o.stopping 133 | if onStopped then 134 | o.stopping = false 135 | o.running = false 136 | onStopped() 137 | return true 138 | end 139 | return false 140 | end 141 | 142 | return o 143 | end 144 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/network/net_cl.lua: -------------------------------------------------------------------------------- 1 | -- Запрашивает покупку итема в инвентарь 2 | function IGS.Purchase(sItemUID, callback) 3 | net.Start("IGS.Purchase") 4 | net.WriteString(sItemUID) 5 | net.SendToServer() 6 | 7 | net.Receive("IGS.Purchase",function() 8 | local errMsg = net.ReadIGSError() 9 | 10 | local ITEM = IGS.GetItemByUID(sItemUID) 11 | if errMsg then 12 | if callback then callback(errMsg) end 13 | hook.Run("IGS.OnFailedPurchase", ITEM, errMsg) 14 | else 15 | local invDbID_ = IGS.C.Inv_Enabled and net.ReadUInt(IGS.BIT_INV_ID) 16 | if callback then callback(nil, invDbID_) end 17 | hook.Run("IGS.PlayerPurchasedItem", LocalPlayer(), ITEM, invDbID_) 18 | end 19 | end) 20 | end 21 | 22 | -- Активирует купленный итем (Только если IGS.C.Inv_Enabled) 23 | function IGS.Activate(iInvID, callback) 24 | net.Start("IGS.Activate") 25 | net.WriteUInt(iInvID, IGS.BIT_INV_ID) 26 | net.WriteBool(callback) 27 | net.SendToServer() 28 | 29 | if not callback then return end 30 | net.Receive("IGS.Activate", function() 31 | local ok = net.ReadBool() 32 | local iPurchID = ok and net.ReadUInt(IGS.BIT_PURCH_ID) 33 | local sMsg_ = net.ReadIGSMessage() 34 | callback(ok, iPurchID, sMsg_) 35 | end) 36 | end 37 | 38 | function IGS.UseCoupon(sCoupon, callback) 39 | net.Start("IGS.UseCoupon") 40 | net.WriteString(sCoupon) 41 | net.SendToServer() 42 | 43 | net.Receive("IGS.UseCoupon", function() 44 | callback(net.ReadIGSError()) 45 | end) 46 | end 47 | 48 | --[[------------------------------------------------------------------------- 49 | Ссылки 50 | ---------------------------------------------------------------------------]] 51 | function IGS.GetPaymentURL(iSum,fCallback) -- url in callback 52 | net.Start("IGS.GetPaymentURL") 53 | net.WriteDouble(iSum) 54 | net.SendToServer() 55 | 56 | net.Receive("IGS.GetPaymentURL",function() 57 | fCallback(net.ReadString()) -- url 58 | end) 59 | end 60 | 61 | 62 | 63 | local cache,last_update = {},0 -- на сервере тоже кэширование 64 | function IGS.GetLatestPurchases(fCallback) 65 | if last_update + 60 >= os.time() then 66 | fCallback(cache) 67 | return 68 | end 69 | 70 | net.Ping("IGS.GetLatestPurchases") 71 | net.Receive("IGS.GetLatestPurchases",function() 72 | local dat = {} 73 | for i = 1,net.ReadUInt(IGS.BIT_LATEST_PURCH) do 74 | dat[i] = net.ReadIGSPurchase() -- id и purchase не юзаются 75 | end 76 | 77 | cache = dat 78 | last_update = os.time() 79 | 80 | fCallback(dat) 81 | end) 82 | end 83 | 84 | -- тут таймаут не нужно. Даже если заспамить net - ничего не произойдет 85 | function IGS.GetMyTransactions(fCallback) 86 | net.Ping("IGS.GetMyTransactions") 87 | 88 | net.Receive("IGS.GetMyTransactions",function() 89 | local dat = {} 90 | for i = 1,net.ReadUInt(IGS.BIT_TX) do 91 | dat[i] = net.ReadIGSTx() 92 | end 93 | 94 | fCallback(dat) 95 | end) 96 | end 97 | 98 | function IGS.GetMyPurchases(fCallback) 99 | net.Ping("IGS.GetMyPurchases") 100 | 101 | net.Receive("IGS.GetMyPurchases",function() 102 | local dat = {} 103 | for i = 1,net.ReadUInt(8) do 104 | dat[i] = net.ReadIGSPurchase() 105 | end 106 | 107 | fCallback(dat) 108 | end) 109 | end 110 | 111 | 112 | 113 | 114 | --[[------------------------------------------------------------------------- 115 | Инвентарь 116 | ---------------------------------------------------------------------------]] 117 | function IGS.GetInventory(fCallback) 118 | net.Ping("IGS.GetInventory") 119 | 120 | net.Receive("IGS.GetInventory",function() 121 | local d = {} 122 | 123 | for i = 1,net.ReadUInt(7) do 124 | d[i] = net.ReadIGSInventoryItem() 125 | end 126 | 127 | fCallback(d) 128 | end) 129 | end 130 | 131 | function IGS.DropItem(iID,fCallback) -- энтити в каллбэке 132 | if not IGS.C.Inv_AllowDrop then 133 | IGS.ShowNotify("Дроп предметов отключен администратором", "Ошибка") 134 | return 135 | end 136 | 137 | net.Start("IGS.DropItem") 138 | net.WriteUInt(iID,IGS.BIT_INV_ID) 139 | net.SendToServer() 140 | 141 | net.Receive("IGS.DropItem",function() 142 | local ent = net.ReadEntity() 143 | 144 | if fCallback then 145 | fCallback(ent) 146 | end 147 | end) 148 | end 149 | 150 | 151 | 152 | 153 | net.Receive("IGS.PaymentStatusUpdated",function() 154 | local t = {} 155 | t.paymentType = net.ReadString() 156 | t.orderSum = net.ReadString() 157 | t.method = net.ReadString() 158 | 159 | if t.method == "error" then 160 | t.errorMessage = net.ReadString() 161 | end 162 | 163 | hook.Run("IGS.PaymentStatusUpdated",t) 164 | end) 165 | 166 | net.Receive("IGS.UI",function() 167 | IGS.UI() 168 | end) 169 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/modules/inv_log/core_sv.lua: -------------------------------------------------------------------------------- 1 | IGS.IL = IGS.IL or { -- InventoryLogger 2 | ["NEW"] = 1, -- покупка итема 3 | ["ACT"] = 2, -- активация 4 | ["DROP"] = 3, 5 | ["PICK"] = 4, 6 | } 7 | 8 | util.AddNetworkString("IGS.InvLog") 9 | util.AddNetworkString("IGS.NameRequest") 10 | util.AddNetworkString("IGS.DeactivateItem") 11 | 12 | local function WriteLog(tLog) 13 | net.WriteUInt(tLog[0],22) -- 4194305 (Кол-во записей всего) 14 | net.WriteUInt(#tLog,6) -- 63 (По 50 лимит на страницу) 15 | 16 | for _,row in ipairs(tLog) do 17 | net.WriteString(row.owner) 18 | net.WriteString(row.inflictor) 19 | net.WriteString(row.gift_uid) 20 | net.WriteUInt(row.gift_id,22) -- 4194305 21 | net.WriteUInt(row.action,3) -- 7 22 | net.WriteUInt(row.action_id,22) -- 4194305 23 | net.WriteUInt(row.date,32) -- 4294967295 24 | end 25 | end 26 | 27 | local function ShitLog() 28 | local log = {} 29 | log[0] = 5 30 | for i = 1,log[0] do 31 | log[i] = { 32 | owner = util.SteamIDTo64("STEAM_0:1:23456789"), 33 | inflictor = util.SteamIDTo64("STEAM_0:1:23456789"), 34 | gift_uid = "Только для SuperAdmin", 35 | gift_id = 1337, 36 | action = 0, 37 | action_id = 1488, 38 | date = os.time(), 39 | } 40 | end 41 | return log 42 | end 43 | 44 | net.Receive("IGS.InvLog",function(_, pl) 45 | -- Или s64_owner или gift_uid 46 | local s64_owner = net.ReadBool() and net.ReadString() 47 | local gift_uid = net.ReadBool() and net.ReadString() 48 | local iPage = net.ReadBool() and net.ReadUInt(8) 49 | local cb_id = net.ReadUInt(3) -- 7 50 | 51 | 52 | local tLog = pl:IsSuperAdmin() and IGS.IL.GetLog(50, iPage, s64_owner, gift_uid) or ShitLog() 53 | net.Start("IGS.InvLog") 54 | net.WriteUInt(cb_id,3) -- 7 55 | WriteLog(tLog) 56 | net.Send(pl) 57 | end) 58 | 59 | net.Receive("IGS.NameRequest",function(_, pl) 60 | if not pl:IsSuperAdmin() then return end 61 | 62 | local s64 = net.ReadString() 63 | IGS.GetPlayer(s64, function(d_) 64 | net.Start("IGS.NameRequest") 65 | net.WriteString(s64) 66 | 67 | if d_ then 68 | net.WriteBool(true) 69 | net.WriteString(Format("%d - %s", d_.Score, d_.Name)) 70 | else 71 | net.WriteBool(false) 72 | end 73 | net.Send(pl) 74 | end) 75 | end) 76 | 77 | net.Receive("IGS.DeactivateItem", function(_, pl) 78 | local iPurchID = net.ReadUInt(IGS.BIT_PURCH_ID) 79 | if not (iPurchID and pl:IsSuperAdmin()) then return end 80 | 81 | IGS.DisablePurchase(iPurchID, function(bUpdated) 82 | IGS.Notify(pl, bUpdated and "Покупка отключена" or "Услуга уже отключена") 83 | end) 84 | end) 85 | 86 | 87 | 88 | 89 | -- Вносит новое значение 90 | function IGS.IL.Log(iGiftOrPurchaseId, sGiftUID, s64_owner, s64_inflictor, iActionId) 91 | sGiftUID = sql.SQLStr(sGiftUID) 92 | 93 | sql.Query([[ 94 | INSERT INTO `igs_inv_log`(`gift_id`,`gift_uid`,`owner`,`inflictor`,`action`,`date`) 95 | VALUES (]] .. 96 | iGiftOrPurchaseId .. [[, ]] .. sGiftUID .. [[, ]] .. s64_owner .. [[, ]] .. s64_inflictor .. [[, ]] .. iActionId .. [[, ]] .. os.time() .. 97 | [[) 98 | ]]) 99 | end 100 | 101 | -- Параметры опциональны 102 | function IGS.IL.GetLog(iLimit, iPage, s64_owner, gift_uid) 103 | sql.Begin() 104 | 105 | local q = "SELECT * FROM `igs_inv_log`" 106 | if s64_owner then 107 | q = q .. " WHERE `owner` = " .. sql.SQLStr(s64_owner) 108 | elseif gift_uid then 109 | q = q .. " WHERE `gift_uid` = " .. sql.SQLStr(gift_uid) 110 | end 111 | 112 | q = q .. " ORDER BY `action_id` DESC" 113 | 114 | if iLimit then 115 | q = q .. " LIMIT " .. iLimit 116 | 117 | if iPage then 118 | q = q .. " OFFSET " .. (iPage * iLimit - iLimit) 119 | end 120 | end 121 | 122 | local q_count = "SELECT COUNT(*) FROM `igs_inv_log`" 123 | if s64_owner then 124 | q_count = q_count .. " WHERE `owner` = " .. sql.SQLStr(s64_owner) 125 | end 126 | 127 | local total = sql.QueryValue(q_count) 128 | local log = sql.Query(q) or {} 129 | log[0] = total 130 | 131 | sql.Commit() 132 | return log 133 | end 134 | -- PRINT(IGS.IL.GetLog(50, 1, AMD():SteamID64())) 135 | -- PRINT(IGS.IL.GetLog(50, 1, nil, "chat_prefix")) 136 | 137 | function IGS.IL.CreateTable() 138 | sql.Begin() 139 | 140 | sql.Query([[ 141 | CREATE TABLE IF NOT EXISTS `igs_inv_log` ( 142 | `action_id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 143 | `gift_id` INTEGER NOT NULL, 144 | `gift_uid` TEXT NOT NULL, 145 | `owner` TEXT NOT NULL, 146 | `inflictor` TEXT NOT NULL, 147 | `action` NUMERIC NOT NULL, 148 | `date` INTEGER NOT NULL 149 | ); 150 | ]]) 151 | 152 | sql.Query("CREATE INDEX `owner` ON `igs_inv_log` (`owner`)") 153 | 154 | sql.Commit() 155 | end 156 | 157 | IGS.IL.CreateTable() 158 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/vgui/igs_table.lua: -------------------------------------------------------------------------------- 1 | local PANEL = {} 2 | 3 | function PANEL:Init() 4 | self.columns = {} 5 | self.lines = {} 6 | 7 | self.header_tall = 0 8 | 9 | -- Колонки, для которых указаны iWidePx 10 | self.nonAdjustableSpace = 0 11 | self.nonAdjustableItems = 0 12 | 13 | self.scroll = uigs.Create("igs_scroll", self) 14 | end 15 | 16 | function PANEL:SetTitle(sText) 17 | self.title = self.title or uigs.Create("DLabel", function(t) 18 | t:Dock(TOP) 19 | t:SetTall(20) 20 | t:SetFont("igs.20") 21 | t:SetTextColor(IGS.col.TEXT_HARD) 22 | t:SetContentAlignment(5) 23 | -- t:SetWrap(true) -- если раскомментить, то не будет работать SetContentAlignment 24 | t:SetAutoStretchVertical(true) 25 | end, self) 26 | 27 | self.title:SetText(sText) 28 | end 29 | 30 | function PANEL:AddColumn(sName,iWidePx) 31 | self.columns_panel = self.columns_panel or uigs.Create("Panel", function(p) 32 | p:SetTall(15) 33 | 34 | self.header_tall = self.header_tall + p:GetTall() 35 | end, self) 36 | 37 | table.insert(self.columns, uigs.Create("Panel", function(clmn) 38 | clmn.staticWide = iWidePx 39 | 40 | -- 10 не обязательно. Просто для заметности при отладке 41 | -- clmn:SetSize(clmn.staticWide or 10, self.columns_panel:GetTall()) 42 | clmn.Paint = function(s,w,h) 43 | surface.SetDrawColor(IGS.col.HARD_LINE) 44 | surface.DrawLine(3,h - 1,w - 2,h - 1) 45 | 46 | surface.DrawLine(0,0,0,h) 47 | surface.DrawLine(w,0,w,h) 48 | 49 | surface.SetFont("igs.15") 50 | surface.SetTextColor(IGS.col.TEXT_SOFT) 51 | surface.SetTextPos((w - surface.GetTextSize(sName)) / 2,0) 52 | surface.DrawText(sName) 53 | end 54 | end, self.columns_panel)) 55 | 56 | if iWidePx then 57 | self.nonAdjustableItems = self.nonAdjustableItems + 1 58 | self.nonAdjustableSpace = self.nonAdjustableSpace + iWidePx 59 | end 60 | 61 | self:PerformLayout() 62 | end 63 | 64 | function PANEL:AddLine(...) 65 | local rows = {...} 66 | 67 | local iKek = table.insert(self.lines, 68 | self.scroll:AddItem(uigs.Create("Panel", function(line) 69 | line:SetTall(18) 70 | line:Dock(TOP) 71 | line.columns = {} 72 | 73 | -- self.columns[i]:GetWide() 74 | for i,val in ipairs(rows) do 75 | line.columns[i] = uigs.Create("DButton", function(row) -- Было Panel 76 | row:SetText(val) 77 | row.DoClick = function() if line.DoClick then line.DoClick(row) end end 78 | row:SetCursor("arrow") 79 | row:SetTall( line:GetTall() ) 80 | -- row:SetPos(self.columns[i]:GetPos()) 81 | row.Paint = function(s,w,h) 82 | surface.SetDrawColor(IGS.col.HARD_LINE) 83 | surface.DrawLine(3,h - 1,w - 2,h - 1) 84 | 85 | surface.DrawLine(0,0,0,h) 86 | surface.DrawLine(w,0,w,h) 87 | 88 | surface.SetFont("igs.18") 89 | surface.SetTextColor(s.text_color or IGS.col.TEXT_HARD) 90 | surface.SetTextPos((w - surface.GetTextSize(s:GetText())) / 2,0) 91 | surface.DrawText(s:GetText()) 92 | return true -- override 93 | end 94 | end, line) 95 | end 96 | end)) 97 | ) 98 | 99 | -- self:PerformLayout() 100 | return self.lines[iKek] -- вставленная строка 101 | end 102 | 103 | function PANEL:Clear() 104 | -- for i,linePan in ipairs(self.lines) do 105 | -- linePan:Remove() 106 | -- table.remove(self.lines,i) 107 | -- end 108 | 109 | for i = #self.lines,1,-1 do 110 | local linePan = self.lines[i] 111 | linePan:Remove() 112 | table.remove(self.lines,i) 113 | end 114 | end 115 | 116 | PANEL.Paint = IGS.S.TablePanel 117 | PANEL.PaintOver = IGS.S.Outline 118 | 119 | function PANEL:PerformLayout() 120 | if self.title then 121 | self.header_tall = self.title:GetTall() 122 | end 123 | 124 | if self.columns_panel then 125 | self.columns_panel:SetPos(0,self.header_tall) 126 | self.columns_panel:SetWide(self:GetWide()) 127 | 128 | --PrintTable(self.columns) 129 | 130 | local x = 0 131 | local cell_wide = (self:GetWide() - self.nonAdjustableSpace) / (#self.columns - self.nonAdjustableItems) 132 | -- print("#self.columns",#self.columns) 133 | -- print("self.nonAdjustableItems",self.nonAdjustableItems) 134 | -- print("self.nonAdjustableSpace",self.nonAdjustableSpace) 135 | for _,v in ipairs(self.columns) do 136 | -- print(v,v:GetPos()) 137 | -- print(v:GetSize()) 138 | v:SetPos(x,0) 139 | v:SetSize(v.staticWide or cell_wide, self.columns_panel:GetTall()) 140 | 141 | x = x + (v.staticWide or v:GetWide()) 142 | end 143 | 144 | self.header_tall = self.header_tall + self.columns_panel:GetTall() 145 | end 146 | 147 | self.scroll:SetPos(0,self.header_tall) 148 | self.scroll:SetSize(self:GetWide(),self:GetTall() - self.header_tall) 149 | 150 | for _,linePan in ipairs(self.lines) do 151 | for i,rowPan in ipairs(linePan.columns) do 152 | rowPan:SetWide(self.columns[i]:GetWide()) 153 | rowPan:SetPos(self.columns[i]:GetPos()) 154 | end 155 | end 156 | end 157 | 158 | vgui.Register("igs_table", PANEL, "Panel") 159 | -- IGS.UI() 160 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/windows/item_info.lua: -------------------------------------------------------------------------------- 1 | local m 2 | 3 | local function purchase(ITEM, buy_button) 4 | IGS.Purchase(ITEM:UID(), function(errMsg,dbID) 5 | if not IsValid(buy_button) then return end 6 | 7 | if errMsg then 8 | IGS.ShowNotify(errMsg, "Ошибка покупки") 9 | surface.PlaySound("ambient/voices/citizen_beaten1.wav") -- еще есть 10 | return 11 | end 12 | 13 | buy_button.purchased = buy_button.purchased or 0 14 | buy_button.purchased = buy_button.purchased + 1 15 | 16 | 17 | if ITEM:IsStackable() then 18 | buy_button:SetText("Куплено " .. buy_button.purchased .. " шт") 19 | else 20 | if IsValid(m) then 21 | m:Close() 22 | end 23 | 24 | if not IGS.C.Inv_Enabled then 25 | IGS.ShowNotify("Спасибо за покупку. Это было просто, правда? :)", "Успешная покупка") 26 | return 27 | end 28 | 29 | IGS.BoolRequest("Успешная покупка", 30 | "Спасибо за покупку. Она находится в вашем /donate инвентаре.\n\nАктивировать ее сейчас?", 31 | function(yes) 32 | if not yes then return end 33 | 34 | IGS.ProcessActivate(dbID) 35 | end) 36 | end 37 | 38 | surface.PlaySound("ambient/office/coinslot1.wav") 39 | end) 40 | end 41 | 42 | 43 | 44 | 45 | 46 | 47 | local function move(f, x, sp, cb) 48 | local _,y = f:GetPos() 49 | f:MoveTo(x,y, sp,nil,nil,cb) 50 | end 51 | 52 | local function shakeFrame(f, amplitude, speed, cb) 53 | if not IsValid(f) then return end 54 | 55 | local x = f:GetPos() 56 | move(f, x + amplitude, speed, function() 57 | move(f, x - amplitude, speed, function() 58 | move(f, x, speed, cb) 59 | end) 60 | end) 61 | end 62 | 63 | function IGS.WIN.Item(uid) 64 | local ITEM = IGS.GetItemByUID(uid) 65 | if IsValid(m) then 66 | if m.item_uid == uid then -- попытка повторного открытия того же фрейма 67 | 68 | m:MoveToFront() 69 | shakeFrame(m, 20, .1) 70 | 71 | return 72 | end 73 | 74 | -- Открытия другого 75 | m:Close() 76 | m = nil 77 | end 78 | 79 | surface.PlaySound("ambient/weather/rain_drip1.wav") 80 | 81 | m = uigs.Create("igs_frame", function(self) 82 | self:SetSize(330,550) 83 | self:RememberLocation("igs_item") 84 | self:MakePopup() 85 | self:SetTitle(ITEM:Name()) 86 | 87 | self.item_uid = uid -- для предотвращения повторного открытия двух одинаковых фреймов 88 | end) 89 | 90 | 91 | uigs.Create("igs_iteminfo", function(p) 92 | 93 | --[[------------------------------------------------------------------------- 94 | Очень не красивый, но очень полезный код 95 | Заставляет ползунок помигать для заметности 96 | ---------------------------------------------------------------------------]] 97 | local viewed = tonumber( bib.get("igs:items_viewed",0) ) 98 | bib.set("igs:items_viewed",viewed + 1) 99 | 100 | -- Если мигали 3+ раза, то больше не надо 101 | if viewed < 3 then 102 | local oldThink = p.scroll.scrollBar.Think 103 | timer.Simple(.5,function() -- 0.5 = время, которое скролл будет мигать 104 | if not IsValid(p) then return end 105 | 106 | p.scroll.scrollBar.Think = oldThink 107 | end) 108 | 109 | p.scroll.scrollBar.Think = function() -- \/ скорость мигания 110 | p.scroll.scrollBar.addWidth = (math.sin( CurTime() * 20 ) + 1) / 2 * 8 -- 8 лимит ширины скролла 111 | p.scroll.scrollBar:InvalidateLayout() 112 | end 113 | end 114 | ----------------------------------------------------------------------------- 115 | 116 | 117 | 118 | p:Dock(FILL) 119 | p:SetIcon(ITEM:ICON()) 120 | p:SetName("Действует " .. IGS.TermToStr(ITEM:Term())) 121 | p:SetImage(ITEM:IMG()) 122 | p:SetSubNameButton(ITEM:Group() and ITEM:Group():Name(), function() 123 | IGS.WIN.Group(ITEM:Group():UID()) 124 | end) 125 | p:SetDescription( ITEM:Description() ) 126 | p:SetInfo(IGS.FormItemInfo(ITEM, LocalPlayer())) -- lp для GetPrice 127 | 128 | m.act = p:CreateActivity() -- панелька для кастом эллементов 129 | m.buy = uigs.Create("igs_button", function(buy) 130 | local price = ITEM:GetPrice( LocalPlayer() ) 131 | 132 | buy:Dock(TOP) 133 | buy:SetTall(20) 134 | buy:SetText( "Купить за " .. PL_MONEY(price) ) 135 | buy:SetActive( IGS.CanAfford(LocalPlayer(), price) ) 136 | buy.DoClick = function(s) 137 | if not s:IsActive() then 138 | local need = price - LocalPlayer():IGSFunds() 139 | 140 | surface.PlaySound("ambient/voices/citizen_beaten1.wav") -- еще есть 141 | IGS.BoolRequest( 142 | "Недостаточно денег", 143 | ("Вам не хватает %s для покупки %s.\nЖелаете мгновенно пополнить счет?"):format( PL_MONEY(need), ITEM:Name()), 144 | function(yes) 145 | if yes then 146 | IGS.WIN.Deposit(price, true) 147 | surface.PlaySound("vo/npc/male01/yeah02.wav") 148 | end 149 | end 150 | ):ShowCloseButton(true) 151 | 152 | return 153 | end 154 | 155 | purchase(ITEM, s) 156 | end 157 | end, m.act) 158 | end, m) 159 | 160 | hook.Run("IGS.OnItemInfoOpen", ITEM, m) 161 | end 162 | -- IGS.WIN.Item("permission_model_30d") 163 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/servers/serv_sv.lua: -------------------------------------------------------------------------------- 1 | local function getHostPort() 2 | return tonumber( game.GetIPAddress():match(":(.+)$") ) 3 | end 4 | 5 | local function dprint(...) 6 | if IGS.DEBUG and IGS.DEBUG <= 2 then -- debug, info, [warning, error] 7 | IGS.dprint("🐛 ", "", ...) 8 | end 9 | end 10 | 11 | -- После вызова этой функции загружается вторая часть скрипта 12 | -- Т.е. не вызвать функцию - не запустится скрипт 13 | -- Она не вызывается, если сервер отключен или произошла ошибка в ходе выполнения запроса на получение списка серверов 14 | local function onReady() 15 | dprint("Мы готовы. ", "Запускаем IGS 🚀") 16 | IGS.SERVERS.Broadcast() 17 | hook.Run("IGS.ServersLoaded") 18 | IGS.SetServerVersion( cookie.GetString("igs_version", "123") ) 19 | end 20 | 21 | local function addServerLocally(id, serv_name, enabled) 22 | if true then IGS.SERVERS.TOTAL = IGS.SERVERS.TOTAL + 1 end 23 | if enabled then IGS.SERVERS.ENABLED = IGS.SERVERS.ENABLED + 1 end 24 | 25 | IGS.SERVERS.MAP[id] = serv_name 26 | end 27 | 28 | local function addCurrentServerLocally(id, serv_name) 29 | IGS.SERVERS.CURRENT = id 30 | addServerLocally(id, serv_name, true) 31 | 32 | -- было в registerCurrentServer до https://t.me/c/1353676159/17695 33 | bib.set("igs:serverid", id) 34 | end 35 | 36 | local function registerCurrentServer(local_ip,port, fOnSuccess) 37 | IGS.AddServer(local_ip, port, function(id) 38 | IGS.prints( 39 | "CEPBEP 3APEruCTPuPOBAH nOg ig: ", id, "\n" .. 40 | "HACTPOuKu B ", "gm-donate.net/panel/projects/" .. IGS.C.ProjectID 41 | ) 42 | 43 | local serv_name = GetConVar("hostname"):GetString() 44 | addCurrentServerLocally(id, serv_name) -- нужно снаружи для IGS.SERVERS:ID() 45 | IGS.SetServerName( serv_name ) 46 | end) 47 | end 48 | 49 | local function loadServersOrRegisterCurrent(d, local_ip) 50 | local serv_port = getHostPort() 51 | 52 | -- reset 53 | IGS.SERVERS.TOTAL = 0 54 | IGS.SERVERS.ENABLED = 0 55 | 56 | local maxVisibleServerId = 0 -- больший ид может быть архивированным 57 | local isCurrentDisabled 58 | for _,v in ipairs(d) do -- -- `ID`,`Name`,`IP`,`Port`,`Disabled` 59 | local disabled = tobool(v.Disabled) 60 | maxVisibleServerId = math.max(v.ID, maxVisibleServerId) 61 | 62 | -- Текущий сервер 63 | if v.IP == local_ip and v.Port == serv_port then 64 | if disabled then isCurrentDisabled = true end 65 | addCurrentServerLocally(v.ID, v.Name) 66 | dprint("📍 ВКЛ: ", (disabled and "❌" or "✅"), " ID: ", v.ID, ". Название: ", v.Name) 67 | else 68 | addServerLocally(v.ID, v.Name, not disabled) 69 | dprint("📤 ВКЛ: ", (disabled and "❌" or "✅"), " ID: ", v.ID, ". Название: ", v.Name) 70 | end 71 | end 72 | 73 | dprint("ID самого младшего сервера: ", maxVisibleServerId) 74 | 75 | -- limit 50 76 | if maxVisibleServerId > 40 then 77 | IGS.prints(Color(255, 50, 50), "", 78 | "y IIpoekTa ", maxVisibleServerId, " 3arerucTpuPoBaHHbIx cepBepoB.\n" .. 79 | "IIo gocTu}{eHuIO 50 cepBepoB HoBbIe IIepectaHyT co3gaBaTbC9 u 3tot He 3arpy3uTc9.\n" .. 80 | "O6HoBJI9uTe IP IIpowJIbIX uJIu co3gauTe HoBbIu IIpoeKT" 81 | ) 82 | end 83 | 84 | if isCurrentDisabled then 85 | IGS.prints(Color(255, 50, 50), "", "3TOT CEPBEP OTKJII04EH. 3ArPy3KA nPEKPAwEHA") 86 | return -- не даем выполнить onReady() 87 | end 88 | 89 | -- Сервер не зарегистрирован 90 | if not IGS.SERVERS.CURRENT then 91 | local id_before = bib.getNum("igs:serverid") 92 | if id_before and IGS.SERVERS(id_before) then 93 | IGS.prints("IIOXO}{E 3TOT CEPBEP IIEPEEXAJI (CMEHA IP)") 94 | IGS.UpdateServerAddress(id_before, local_ip, serv_port, function() 95 | IGS.GetServers(function(dat) 96 | loadServersOrRegisterCurrent(dat, local_ip) 97 | end, true) 98 | end) 99 | 100 | else 101 | IGS.prints("3TOT CEPBEP HE 3APEruCTPuPOBAH. CO39AEM!") 102 | registerCurrentServer(local_ip,serv_port, onReady) 103 | end 104 | else 105 | onReady() 106 | end 107 | end 108 | 109 | 110 | local function getAndLoadServers(local_ip) 111 | dprint("Наш IP: ", local_ip, ". Получаем список серверов проекта") 112 | IGS.GetServers(function(dat) 113 | dprint("Получили данные ", #dat, " сервера(ов). Сохраняем в ОЗУ") 114 | loadServersOrRegisterCurrent(dat, local_ip) 115 | end, true) -- include disabled 116 | end 117 | 118 | timer.Simple(0, function() -- фетч заработает только так в этот момент 119 | dprint("Загрузка серверов") 120 | IGS.GetExternalIP(getAndLoadServers) 121 | end) 122 | 123 | hook.Add("IGS.OnApiError","NotifyAboutImpossibleLoading",function(sMethod) 124 | if sMethod == "/servers/get" then 125 | IGS.prints(Color(255,0,0), "", "NEVOZMOZNO ZAGRUZIT SKRIPT. VAZNIE DANNIE NE POLUCHENI") 126 | end 127 | end) 128 | 129 | -- https://t.me/c/1353676159/10880 130 | hook.Add("IGS.OnApiError","DuplicatedServerWarning",function(sMethod, error_uid) 131 | if sMethod == "/servers/get" and error_uid == "server_already_exists" then 132 | IGS.prints(Color(255,0,0), "", "Server s takim IP i PORTom uze zaregistrirovan v paneli. Nuzno izmenit ego tam na drugoy") 133 | end 134 | end) 135 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/extensions/extra.lua: -------------------------------------------------------------------------------- 1 | local STORE_ITEM = MT_IGSItem 2 | 3 | function STORE_ITEM:SetMaxGlobalPurchases(iMax) 4 | IGS.nw.Register("total_purchases_" .. self:UID()) -- только внутри хука юз 5 | :Write(net.WriteUInt, 8) 6 | :Read(net.ReadUInt, 8) 7 | :SetGlobal():SetHook("total_purchases_" .. self:UID()) 8 | 9 | if CLIENT then -- #todo nw не позволяет, но нужно сделать ОДИН клиентский хук 10 | hook.Add("total_purchases_" .. self:UID(), self:UID(), function(purchased) 11 | if self.user_icon == nil then -- чтобы про луа рефреше не стало true 12 | self.user_icon = tobool(self.icon) -- bool вместо ссылки. Нужен, чтобы не оверрайдить юзерские иконки 13 | end 14 | if not self.user_icon then 15 | local left = iMax - purchased 16 | local icon = left <= 0 and " 0 " or left -- потому что 0 сайт не пережевывает 17 | self:SetIcon("https://via.placeholder.com/90x90.png?text=" .. icon) 18 | end 19 | 20 | if purchased >= iMax then 21 | self:SetHidden() 22 | end 23 | end) 24 | else 25 | local purchased = bib.getNum("igs:total_purchases:" .. self:UID()) 26 | IGS.nw.SetGlobal("total_purchases_" .. self:UID(), purchased or 0) 27 | end 28 | 29 | return self:SetMeta("global_limit", iMax) 30 | end 31 | 32 | hook.Add("IGS.CanPlayerBuyItem", "GlobalLimit", function(_, ITEM) 33 | if SERVER and ITEM:GetMeta("global_limit") then 34 | local limit = ITEM:GetMeta("global_limit") 35 | local purchased = bib.getNum("igs:total_purchases:" .. ITEM:UID(), 0) 36 | 37 | if purchased >= limit then 38 | return false, "Этот предмет закончился" 39 | end 40 | end 41 | end) 42 | 43 | hook.Add("IGS.PlayerPurchasedItem", "GlobalLimit", function(_, ITEM) 44 | if SERVER and ITEM:GetMeta("global_limit") then 45 | local limit = ITEM:GetMeta("global_limit") 46 | local purchased = bib.getNum("igs:total_purchases:" .. ITEM:UID(), 0) 47 | 48 | bib.setNum("igs:total_purchases:" .. ITEM:UID(), purchased + 1) 49 | IGS.nw.SetGlobal("total_purchases_" .. ITEM:UID(), purchased + 1) 50 | 51 | if purchased >= limit then 52 | ITEM:SetHidden() 53 | end 54 | end 55 | end) 56 | 57 | 58 | 59 | 60 | -- Возволяет настроить максимальное количество ПОКУПОК одного предмета 61 | -- Полезно для тестовых випок за рубль и тд 62 | function STORE_ITEM:SetMaxPlayerPurchases(iLimit) 63 | return self:SetOnBuy(function(pl) 64 | local limit = self:GetMeta("purchasesLimit") 65 | if limit then 66 | local key = string.format("igs:purchases:%s:%s", pl:UniqueID(), self:UID()) 67 | local now_purchased = bib.getNum(key, 0) + 1 68 | bib.setNum(key, now_purchased) 69 | IGS.Notify(pl, "Вы купили " .. self:Name() .. " " .. now_purchased .. " раз из " .. limit) 70 | end 71 | end):SetMeta("purchasesLimit", iLimit) 72 | end 73 | 74 | -- Причина почему в хуке, а не методе: https://vk.com/gim143836547?sel=273715457&msgid=211938 75 | hook.Add("IGS.CanPlayerBuyItem", "PlayerLimit", function(pl, ITEM) 76 | if SERVER and ITEM:GetMeta("purchasesLimit") then 77 | local limit = ITEM:GetMeta("purchasesLimit") 78 | local key = string.format("igs:purchases:%s:%s", pl:UniqueID(), ITEM:UID()) 79 | if bib.getNum(key, 0) >= limit then 80 | return false, "Этот предмет можно купить только " .. limit .. " раз(а)" 81 | end 82 | end 83 | end) 84 | 85 | 86 | 87 | 88 | -- Глобальные итемы будут активированы на каждом сервере проекта 89 | -- https://img.qweqwe.ovh/1574888071533.png 90 | function STORE_ITEM:SetGlobal(b) 91 | return self:SetMeta("global", b ~= false) 92 | end 93 | 94 | hook.Add("IGS.PlayerActivatedItem", "IGS.GlobalPurchase", function(pl, ITEM) 95 | if SERVER and ITEM:GetMeta("global") then 96 | for sv_id in pairs(IGS.SERVERS.MAP) do 97 | if sv_id ~= IGS.SERVERS.CURRENT then -- еще не выдано 98 | IGS.StorePurchase(pl:SteamID64(), ITEM:UID(), ITEM:Term(), sv_id) 99 | end 100 | end 101 | 102 | IGS.Notify(pl, "Предмет выдан на " .. IGS.SERVERS.TOTAL .. " серверах") 103 | end 104 | end) 105 | 106 | -- Выдает рандомный предмет из указанных (аналог кейсов) 107 | -- https://trello.com/c/hWRihJ1k/564 108 | -- Заметка, почему не нужно делать tItemsUIDs 109 | -- https://img.qweqwe.ovh/1568507799904.png 110 | -- Использовать только с :SetStackable предметами 111 | local function giveRandomItem(pl, tItems) 112 | local WINNED_ITEM = table.Random(tItems) 113 | 114 | IGS.PlayerActivateItem(pl, WINNED_ITEM:UID(), function() 115 | IGS.Notify(pl, "Вы получили " .. WINNED_ITEM:Name()) 116 | end) 117 | end 118 | 119 | -- Сочетается с :SetTerm(0), :SetMaxPurchases() и :SetStackable() 120 | function STORE_ITEM:SetRandom(tItems) 121 | return self:SetInstaller(function(pl) 122 | giveRandomItem(pl, self:GetMeta("random_items")) 123 | end):SetMeta("random_items", tItems) 124 | end 125 | 126 | 127 | local function giveItemsSet(pl, tItems) 128 | local added = 0 129 | for _,ITEM in ipairs(tItems) do 130 | IGS.AddToInventory(pl, ITEM:UID(), function() 131 | added = added + 1 132 | if added == #tItems then 133 | IGS.Notify(pl, "В ваш инвентарь добавлено " .. added .. " предметов") 134 | end 135 | end) 136 | end 137 | end 138 | 139 | -- Ложит В ИНВЕНТАРЬ набор указанных предметов (хотя можно и UID добавить) 140 | -- Например 10 Hidden аптечек (как замена лимиту активаций в инвентаре) 141 | function STORE_ITEM:SetItems(tItems) -- IGS.C.Inv_Enabled 142 | return self:SetInstaller(function(pl) 143 | giveItemsSet(pl, self:GetMeta("items_set")) 144 | end):SetMeta("items_set", tItems) 145 | end 146 | -- local ITEM = IGS("Тайный предмет", "secret", -10):SetStackable():SetHidden():SetOnActivate(fp{PRINT, "YEAH!!"}) 147 | -- IGS("2 тайных предмета", "secret_2", 5):SetStackable():SetItems({ITEM, ITEM}) 148 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/skin.lua: -------------------------------------------------------------------------------- 1 | --[[------------------------------------------------------------------------- 2 | Через этот файл невозможно повсеместно изменить скин. 3 | Да и вообще, все в говно на самом деле, но мне лень уже делать все правильно. 4 | Это куча геммора, который, скорее всего, никому не нужен 5 | ---------------------------------------------------------------------------]] 6 | 7 | IGS.S = IGS.S or {} 8 | 9 | IGS.S.COLORS = { 10 | FRAME_HEADER = Color(255,255,255), -- Фон верхушки фреймов в т.ч. пополнения счета и т.д. https://img.qweqwe.ovh/1491950958825.png 11 | ACTIVITY_BG = Color(255,255,255), -- Фон в каждой вкладке (основной) https://img.qweqwe.ovh/1509370647204.png 12 | TAB_BAR = Color(250,250,250), -- Фон таб бара https://img.qweqwe.ovh/1509370669492.png 13 | 14 | PASSIVE_SELECTIONS = Color(240,240,240), -- Фон панели тегов, цвет кнопки с балансом, верхушки таблиц, не выделенные кнопки https://img.qweqwe.ovh/1509370720597.png 15 | INNER_SELECTIONS = Color(255,255,255), -- Фон иконок на плашках, фон панелек последних покупок... https://img.qweqwe.ovh/1509370766148.png 16 | 17 | SOFT_LINE = Color(240,240,240), -- Линия между секциями, типа "Информация" и "Описание" в инфе об итеме 18 | HARD_LINE = Color(200,200,200), -- Обводки панелей 19 | 20 | HIGHLIGHTING = Color(0,122,255), -- Обводка кнопок, цвет текста не активной кнопки 21 | HIGHLIGHT_INACTIVE = Color(160,160,160), -- Цвет иконки неактивной кнопки таббара, мигающая иконка на фрейме помощи https://img.qweqwe.ovh/1509371884592.png 22 | 23 | TEXT_HARD = Color(0,0,0), -- Заголовки, выделяющиеся тексты https://img.qweqwe.ovh/1509372019687.png 24 | TEXT_SOFT = Color(140,140,150), -- Описания, значения чего-то 25 | TEXT_ON_HIGHLIGHT = Color(255,255,255), -- Цвет текста на выделенных кнопках 26 | 27 | LOG_SUCCESS = Color(76,217,100), -- В логах пополнения цвет успешных операций 28 | LOG_ERROR = Color(220,30,70), -- В логах пополнения цвет ошибок 29 | LOG_NORMAL = Color(0,0,0), -- В логах пополнения обычные записи 30 | 31 | ICON = Color(255,255,255), -- цвет иконок на плашечках 32 | } 33 | 34 | -- Вариант раскраски от Павел Тумачев (vk.com/id240371602) 35 | -- Демо: https://img.qweqwe.ovh/1626714494454.jpg 36 | -- IGS.S.COLORS = { 37 | -- FRAME_HEADER = Color(0,0,0), 38 | -- ACTIVITY_BG = Color(10,10,10,180), 39 | -- TAB_BAR = Color(0,0,0), 40 | 41 | -- PASSIVE_SELECTIONS = Color(0,0,0), 42 | -- INNER_SELECTIONS = Color(0,0,0), 43 | 44 | -- SOFT_LINE = Color(51,128,255), 45 | -- HARD_LINE = Color(51,128,255), 46 | 47 | -- HIGHLIGHTING = Color(51,128,255), 48 | -- HIGHLIGHT_INACTIVE = Color(255,255,255), 49 | 50 | -- TEXT_HARD = Color(255,255,255), 51 | -- TEXT_SOFT = Color(255,255,255), 52 | -- TEXT_ON_HIGHLIGHT = Color(255,255,255), 53 | 54 | -- LOG_SUCCESS = Color(76,217,100), 55 | -- LOG_ERROR = Color(255,45,85), 56 | -- LOG_NORMAL = Color(255,255,255), 57 | 58 | -- ICON = Color(255,255,255), 59 | -- } 60 | 61 | -- Попытки сделать темный скин интерфейса 62 | -- IGS.S.COLORS = { 63 | -- FRAME_HEADER = Color(23,23,23), 64 | -- ACTIVITY_BG = Color(13,13,13), 65 | -- TAB_BAR = Color(23,23,23), 66 | 67 | -- PASSIVE_SELECTIONS = Color(23,23,23), 68 | -- INNER_SELECTIONS = Color(23,23,23), 69 | 70 | -- SOFT_LINE = Color(50,50,50), 71 | -- HARD_LINE = Color(66,66,66), 72 | 73 | -- HIGHLIGHTING = Color(230,130,35), 74 | -- HIGHLIGHT_INACTIVE = Color(130,130,130), 75 | 76 | -- TEXT_HARD = Color(255,255,255), 77 | -- TEXT_SOFT = Color(140,140,150), 78 | -- TEXT_ON_HIGHLIGHT = Color(255,255,255), 79 | 80 | -- LOG_SUCCESS = Color(76,217,100), 81 | -- LOG_ERROR = Color(255,45,85), 82 | -- LOG_NORMAL = Color(140,140,150), 83 | 84 | -- ICON = Color(255,255,255), 85 | -- } 86 | 87 | IGS.col = IGS.S.COLORS 88 | 89 | -- https://img.qweqwe.ovh/1486557631077.png 90 | IGS.S.Panel = function(s,w,h,lL,tL,rL,bL) 91 | draw.RoundedBox(0,0,0,w,h,IGS.col.PASSIVE_SELECTIONS) -- bg 92 | 93 | surface.SetDrawColor(IGS.col.HARD_LINE) -- outline 94 | 95 | if lL then surface.DrawLine(0,0,0,h) end -- left line 96 | if tL then surface.DrawLine(0,0,w,0) end -- top line 97 | if rL then surface.DrawLine(w,0,w,h) end -- right line 98 | if bL then surface.DrawLine(0,h - 1,w,h - 1) end -- bottom line 99 | end 100 | 101 | -- https://img.qweqwe.ovh/1486557676799.png 102 | IGS.S.RoundedPanel = function(s,w,h) 103 | draw.RoundedBox(3,0,0,w,h, IGS.col.HARD_LINE) -- outline 104 | draw.RoundedBox(3,1,1,w - 2,h - 2,IGS.col.INNER_SELECTIONS) -- bg 105 | 106 | return true 107 | end 108 | 109 | -- igs\vgui\igs_frame.lua 110 | IGS.S.Frame = function(s,w,h) 111 | draw.RoundedBox(0,0,0,w,h,IGS.col.ACTIVITY_BG) -- bg 112 | 113 | -- /header 114 | local th = s:GetTitleHeight() 115 | draw.RoundedBox(0,0,0,w,th,IGS.col.FRAME_HEADER) 116 | surface.SetDrawColor(IGS.col.HARD_LINE) 117 | surface.DrawLine(0,th - 1,w,th - 1) 118 | -- \header 119 | end 120 | 121 | -- igs\vgui\igs_table.lua 122 | IGS.S.TablePanel = function(s,w,h) 123 | if s.header_tall then 124 | IGS.S.Panel(s,w,s.header_tall) -- header 125 | end 126 | end 127 | 128 | -- igs_table, igs_frame 129 | IGS.S.Outline = function(s,w,h) 130 | surface.SetDrawColor(IGS.col.HARD_LINE) 131 | 132 | -- https://img.qweqwe.ovh/1486830692390.png 133 | surface.DrawLine(0,h,0,0) 134 | surface.DrawLine(0,0,w,0) 135 | surface.DrawLine(w - 1,0,w - 1,h) 136 | surface.DrawLine(w,h - 1,0,h - 1) 137 | end 138 | -------------------------------------------------------------------------------- /addons/igs-core/lua/autorun/l_ingameshop.lua: -------------------------------------------------------------------------------- 1 | --[[------------------------------------------------------------------------- 2 | Веб загрузчик IGS 13.03.2021 3 | https://blog.amd-nick.me/github-workshop-garrysmod/ 4 | Изначально эта задача представлялась в 3 строки 5 | ---------------------------------------------------------------------------]] 6 | IGS = IGS or {} 7 | 8 | local function log(patt, ...) 9 | if cookie.GetNumber("igs_verbose", 0) == 1 then 10 | print(string.format("[IGS] " .. patt, ...)) -- не менять на printS. Некоторые логи слишком ранние 11 | end 12 | end 13 | 14 | concommand.Add("igs_verbose", function(pl) 15 | if SERVER and IsValid(pl) then return end 16 | 17 | local enable = cookie.GetNumber("igs_verbose", 0) == 0 18 | cookie.Set("igs_verbose", enable and "1" or "0") 19 | IGS.prints("IGS Logging " .. (enable and "enabled" or "disabled")) 20 | end) 21 | 22 | local i = {} -- lua files only 23 | i.sv = SERVER and include or function() end 24 | i.cl = SERVER and AddCSLuaFile or include 25 | i.sh = function(f) return i.cl(f) or i.sv(f) end 26 | 27 | 28 | local function include_mount(sRealm, sAbsolutePath) 29 | if (sRealm == "sh") 30 | or (sRealm == "sv" and SERVER) 31 | or (sRealm == "cl" and CLIENT) then 32 | -- Чистый RunString не воспринимает return внутри файлов 33 | -- Но CompileString 9 апреля 2021 теоретически был причиной ошибок 34 | -- Пока пусть будет RunString без ретурна 35 | -- Заметки: https://t.me/c/1353676159/55852 36 | 37 | -- local executer = CompileString(content, sAbsolutePath) 38 | -- return executer() 39 | 40 | local content = IGS_MOUNT[sAbsolutePath] 41 | RunString(content, sAbsolutePath) 42 | end 43 | end 44 | 45 | -- "Костыль" для работы IGS.sh/sv/cl изнутри модульных _main.lua файлов и энтити 46 | -- с указанием относительного пути 47 | -- не работает с ../file (наверн. Не чекал) 48 | local iam_inside 49 | 50 | local function incl(sRealm, sPath) 51 | -- Не сработает, если например в лаунчере в sh() для файлов убрать приставку "igs/" 52 | local isRelativePath = iam_inside and not sPath:StartWith(iam_inside) 53 | local sAbsolutePath = isRelativePath and iam_inside .. "/" .. sPath or sPath 54 | -- /\ Мб внутри модуля уже указан full путь, а не относительный 55 | -- (обычно путь к _main.lua) 56 | 57 | -- print(sAbsolutePath) 58 | 59 | if IGS_MOUNT and IGS_MOUNT[sAbsolutePath] then -- 1st check for lua load (not web) 60 | log("%s Иклюд с MOUNT. Путь: %s", sRealm, sAbsolutePath) 61 | return include_mount(sRealm, sAbsolutePath) 62 | else 63 | log("%s Иклюд с LUA. Путь: %s", sRealm, sAbsolutePath) 64 | local fIncluder = i[sRealm] 65 | return fIncluder(sAbsolutePath) 66 | end 67 | end 68 | 69 | function IGS.sh(sPath) return incl("sh", sPath) end 70 | function IGS.sv(sPath) return incl("sv", sPath) end 71 | function IGS.cl(sPath) return incl("cl", sPath) end 72 | 73 | local function findKeys(arr, patt) 74 | local found = {} 75 | for key, val in pairs(arr) do 76 | local match = key:match(patt) 77 | if match then 78 | table.insert(found, match) 79 | end 80 | end 81 | return found 82 | end 83 | 84 | -- Тяжелая, но пока в оптимизации не нуждается 85 | -- При выборке модулей и энтити элементы повторяются 86 | local function unique(arr) 87 | local ret = {} 88 | for _, v in ipairs(arr) do 89 | if not table.HasValue(ret, v) then 90 | table.insert(ret, v) 91 | end 92 | end 93 | return ret 94 | end 95 | 96 | local function findInMount(patt) 97 | return IGS_MOUNT and findKeys(IGS_MOUNT, patt) or {} 98 | end 99 | 100 | function IGS.include_files(sPath, fIncluder) -- igs/extensions 101 | local data_files = findInMount("^" .. sPath:PatternSafe() .. "/(.*%.lua)$") 102 | local lua_files = file.Find(sPath .. "/*.lua", "LUA") 103 | table.Add(data_files, lua_files) 104 | 105 | for _, fileName in ipairs(data_files) do 106 | fIncluder(sPath .. "/" .. fileName) 107 | end 108 | end 109 | 110 | function IGS.load_modules(sBasePath) -- igs/modules 111 | local data_modules = findInMount("^" .. sBasePath .. "/([^/]*)/_main%.lua$") 112 | data_modules = unique(data_modules) 113 | local _, lua_modules = file.Find(sBasePath .. "/*", "LUA") 114 | table.Add(data_modules, lua_modules) 115 | 116 | for _, mod in ipairs(data_modules) do 117 | local sModPath = sBasePath .. "/" .. mod 118 | iam_inside = sModPath 119 | IGS.sh(sModPath .. "/_main.lua") -- igs/modules/inv_log/_main.lua 120 | end 121 | iam_inside = nil 122 | end 123 | 124 | function IGS.load_entities() 125 | log("Загрузка энтити") 126 | local entities = findInMount("^entities/([^/]*)/(.*%.lua)$") 127 | entities = unique(entities) -- {ent_igs, npc_igs} 128 | 129 | for _, ent_class in ipairs(entities) do 130 | iam_inside = "entities/" .. ent_class 131 | ENT = {} 132 | ENT.Folder = iam_inside 133 | 134 | if SERVER then IGS.sv("init.lua") 135 | else IGS.cl("cl_init.lua") end 136 | scripted_ents.Register(ENT, ent_class) 137 | 138 | iam_inside = nil 139 | 140 | ENT = nil 141 | end 142 | end 143 | 144 | 145 | concommand.Add("igs_flushversion", function(pl) 146 | if IsValid(pl) then IGS.prints("console only") return end 147 | cookie.Delete("igs_version") 148 | IGS.prints("OK. После перезагрузки сервер скачает новую версию") 149 | end) 150 | 151 | -- Используется только для передачи версии клиенту. На сервере использовать cookie.GetString("igs_version") 152 | local igs_version = CreateConVar("igs_version", "", {FCVAR_NOTIFY, FCVAR_REPLICATED, FCVAR_SERVER_CAN_EXECUTE}) 153 | 154 | if SERVER and igs_version:GetString() == "" then 155 | local version = cookie.GetString("igs_version") 156 | igs_version:SetString(version or "777") -- "or" for case when igsmod isn't ran (core hosted locally) 157 | end 158 | 159 | IGS.sh("igs/launcher.lua") 160 | IGS.load_entities() 161 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/activities/profile.lua: -------------------------------------------------------------------------------- 1 | -- Профиль, где транзакции и прогресс до след. бонуса, а также место в топе донатеров за месяц, бизнес левел 2 | -- Прототип https://pp.vk.me/c638927/v638927381/1ec19/nQnaytZDxls.jpg (Вышло совсем не как задумано) 3 | -- Стоимость услуги по текущему курсу 4 | 5 | local LP 6 | hook.Add("IGS.CatchActivities","profile",function(activity,sidebar) 7 | LP = LP or LocalPlayer() 8 | 9 | local bg = sidebar:AddPage("Информация профиля") 10 | local ava_bg = uigs.Create("Panel", function(self) 11 | local y = 5 12 | 13 | -- Аватар 14 | local sw = bg.side:GetWide() -- 220 15 | local margin = (sw - 184) / 2 16 | uigs.Create("AvatarImage", function(ava) 17 | ava:SetSize(184,184) 18 | ava:SetPos(margin,y) 19 | ava:SetPlayer(LP,184) 20 | 21 | y = y + 184 22 | end, self) 23 | 24 | -- Ник 25 | uigs.Create("DLabel", function(nick) 26 | nick:SetSize(sw,24) 27 | nick:SetPos(0,y) 28 | nick:SetFont("igs.24") 29 | nick:SetTextColor(IGS.col.HIGHLIGHTING) 30 | nick:SetText(LP:Nick()) 31 | nick:SetContentAlignment(5) 32 | 33 | y = y + nick:GetTall() 34 | end, self) 35 | 36 | -- Стимайди 37 | uigs.Create("DLabel", function(sid) 38 | sid:SetSize(sw,18) 39 | sid:SetPos(0,y) 40 | sid:SetFont("igs.18") 41 | sid:SetTextColor(IGS.col.TEXT_SOFT) 42 | sid:SetText("(" .. LP:SteamID() .. ")") 43 | sid:SetContentAlignment(5) 44 | 45 | y = y + sid:GetTall() + 10 46 | end, self) 47 | 48 | function self:AddRow(sName,sVal) 49 | local row_bg = uigs.Create("Panel", self) 50 | row_bg:SetSize(sw,20) -- 20, как и размер шрифта 51 | row_bg:SetPos(0,y) 52 | 53 | -- Key 54 | local n = uigs.Create("DLabel", function(name) 55 | name:SetSize(80,row_bg:GetTall()) 56 | name:SetPos(0,0) 57 | name:SetFont("igs.17") 58 | name:SetTextColor(IGS.col.TEXT_SOFT) 59 | name:SetText(sName) 60 | name:SetContentAlignment(6) 61 | end, row_bg) 62 | 63 | -- Value 64 | for i,line in ipairs(string.Wrap("igs.18",sVal,sw - n:GetWide() - 5)) do 65 | uigs.Create("DLabel", function(val) 66 | val:SetSize(sw - n:GetWide() - 5,n:GetTall()) 67 | val:SetPos(n:GetWide() + 5,(i - 1) * val:GetTall()) 68 | val:SetFont("igs.18") 69 | val:SetTextColor(IGS.col.TEXT_HARD) 70 | val:SetText(line) 71 | 72 | y = y + val:GetTall() 73 | row_bg:SetTall(row_bg:GetTall() + val:GetTall()) 74 | end, row_bg) 75 | end 76 | 77 | self:SetTall(y + 5) 78 | end 79 | 80 | self:SetTall(y + 5) 81 | end) 82 | 83 | local lvl = IGS.PlayerLVL(LP) 84 | local mybal = LP:IGSFunds() 85 | local next_lvl = not lvl and IGS.LVL.MAP[1] or lvl:GetNext() 86 | 87 | ava_bg:AddRow("Статус",lvl and lvl:Name() or "Никто :(") 88 | if next_lvl then 89 | local next_desc = next_lvl:Description() 90 | 91 | ava_bg:AddRow("След. статус", next_lvl:Name() .. (next_desc and ("\n\n%s"):format(next_desc) or "")) 92 | ava_bg:AddRow("Нужно", IGS.SignPrice(next_lvl:Cost() - IGS.TotalTransaction(LP)) ) 93 | end 94 | 95 | bg.side:AddItem(ava_bg) 96 | 97 | --[[------------------------------------------------------------------------- 98 | Основная часть фрейма 99 | ---------------------------------------------------------------------------]] 100 | uigs.Create("igs_table", function(pnl) 101 | pnl:Dock(FILL) 102 | pnl:DockMargin(5,5,5,5) 103 | 104 | pnl:SetTitle("Транзакции") 105 | 106 | local multisv = IGS.SERVERS.TOTAL > 1 107 | if multisv then 108 | pnl:AddColumn("Сервер",100) 109 | else 110 | pnl:AddColumn("#",40) 111 | end 112 | 113 | pnl:AddColumn("Сумма",60) 114 | pnl:AddColumn("Баланс",60) 115 | pnl:AddColumn("Действие") 116 | pnl:AddColumn("Дата",130) 117 | 118 | -- Обновление списка транзакций и информации в сайдбаре 119 | IGS.GetMyTransactions(function(dat) 120 | if not IsValid(pnl) then return end -- Долго данные получались 121 | 122 | local bit_num_limit = 2 ^ IGS.BIT_TX - 1 123 | if #dat == bit_num_limit then 124 | pnl:SetTitle("Последние " .. bit_num_limit .. " транзакций") 125 | end 126 | 127 | for i,v in ipairs(dat) do 128 | v.note = v.note or "-" 129 | 130 | local function name_or_uid(sUid) 131 | local ITEM = IGS.GetItemByUID(sUid) 132 | return ITEM.isnull and sUid or ITEM:Name() 133 | end 134 | 135 | -- Если покупка, то пишем ее название или пишем с чем связана транзакция 136 | local note = 137 | v.note:StartsWith("P: ") and name_or_uid(v.note:sub(4)) or 138 | v.note:StartsWith("A: ") and ("Пополнение счета (" .. v.note:sub(4) .. ")") or 139 | v.note:StartsWith("C: ") and ("Купон " .. v.note:sub(4,13) .. "...") or 140 | v.note 141 | 142 | pnl:AddLine( 143 | -- v.id, 144 | multisv and IGS.ServerName(v.server) or #dat - i + 1, 145 | v.sum, 146 | math.Truncate(mybal,2), -- не представляю как, но временами получались очень большие копейки 147 | note, 148 | IGS.TimestampToDate(v.date,true) 149 | ):SetTooltip(("%s\n\nID транзакции в системе: %i%s"):format( 150 | note, 151 | v.id, 152 | note ~= v.note and ("\nОригинальная отметка: " .. v.note) or "" 153 | )) 154 | 155 | mybal = mybal - v.sum 156 | end 157 | 158 | local spent = IGS.isUser(LP) and (IGS.TotalTransaction(LP) - mybal) or 0 159 | 160 | local first = dat[ #dat ] 161 | ava_bg:AddRow("## Операций",#dat .. " шт.") 162 | ava_bg:AddRow("∑ Операций",IGS.SignPrice(spent)) 163 | ava_bg:AddRow("1 Операция",first and IGS.TimestampToDate(first.date) or "Не было") 164 | 165 | IGS.AddButton(bg.side,"Активировать купон",IGS.WIN.ActivateCoupon) --.button:SetActive(true) 166 | 167 | bg.side:AddItem(uigs.Create("Panel", function(s) 168 | s:SetTall(5) 169 | end)) 170 | 171 | end) 172 | end, bg) 173 | 174 | activity:AddTab("Профиль",bg,"materials/icons/fa32/user.png") 175 | end) 176 | 177 | -- IGS.UI() 178 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/modules/inv_log/README.MD: -------------------------------------------------------------------------------- 1 | ## Это генератор данных для тестов. Стимайди рандомные с БД GMD 2 | 3 | ```lua 4 | local sids = {"76561198097017915", "76561198097089588", "76561198097109705", "76561198097113844", "76561198097186201", "76561198097281623", "76561198097324069", "76561198097348606", "76561198097388616", "76561198097390649", "76561198097443354", "76561198097470957", "76561198097591659", "76561198097631964", "76561198097649046", "76561198097780799", "76561198097785356", "76561198097893726", "76561198097926095", "76561198097989516", "76561198098070152", "76561198098092661", "76561198098098471", "76561198098108559", "76561198098113216", "76561198098173031", "76561198098185859", "76561198098354159", "76561198098371450", "76561198098398449", "76561198098424443", "76561198098487418", "76561198098504237", "76561198098509614", "76561198098645216", "76561198098671936", "76561198098749476", "76561198098842349", "76561198098914970", "76561198098929052", "76561198098958992", "76561198099013703", "76561198099021341", "76561198099146015", "76561198099192639", "76561198099210547", "76561198099218232", "76561198099294865", "76561198099344348", "76561198099377064", "76561198099385267", "76561198099453432", "76561198099465673", "76561198099784931", "76561198100103197", "76561198100269725", "76561198100391036", "76561198100392315", "76561198100415833", "76561198100426566", "76561198100486027", "76561198100509562", "76561198100520042", "76561198100537662", "76561198100542291", "76561198100581112", "76561198100594454", "76561198100610417", "76561198100667823", "76561198100690431", "76561198100705146", "76561198100736444", "76561198100842314", "76561198100858376", "76561198100882179", "76561198100885290", "76561198100886058", "76561198100894252", "76561198100942883", "76561198101014296", "76561198101020337", "76561198101022665", "76561198101028818", "76561198101121560", "76561198101145177", "76561198101162273", "76561198101200455", "76561198101200926", "76561198101459915", "76561198101526062", "76561198101592316", "76561198101624996", "76561198101738316", "76561198101804530", "76561198101869174", "76561198101886913", "76561198101919777", "76561198101931622", "76561198101935940", "76561198101971304", "76561198101982860", "76561198102043218", "76561198102069944", "76561198102075611", "76561198102080188", "76561198102246766", "76561198102253745", "76561198102255963", "76561198102311477", "76561198102694965", "76561198102704591", "76561198102723523", "76561198102750172", "76561198102769298", "76561198102817452", "76561198102835860", "76561198102839583", "76561198102852765", "76561198102853115", "76561198102985526", "76561198102985907", "76561198102987096", "76561198103110701", "76561198103149835", "76561198103159526", "76561198103178594", "76561198103194117", "76561198103248177", "76561198103249893", "76561198103262998", "76561198103274471", "76561198103402336", "76561198103448325", "76561198103473745", "76561198103481035", "76561198103551439", "76561198103671678", "76561198103677593", "76561198103691246", "76561198103698271", "76561198103699819", "76561198103705376", "76561198103706687", "76561198103715744", "76561198103805487", "76561198103824827", "76561198103839472", "76561198103855507", "76561198103873259", "76561198103958816", "76561198103968175", "76561198103989423", "76561198104027650", "76561198104029690", "76561198104031283", "76561198104068654", "76561198104069804", "76561198104083016", "76561198104141703", "76561198104166426", "76561198104227259", "76561198104277161", "76561198104295743", "76561198104319145", "76561198104339799", "76561198104432286", "76561198104471120", "76561198104479506", "76561198104490467", "76561198104502139", "76561198104560081", "76561198104566409", "76561198104580685", "76561198104584961", "76561198104611288", "76561198104635971", "76561198104652285", "76561198104656265", "76561198104715531", "76561198104718351", "76561198104721545", "76561198104737137", "76561198104756146", "76561198104829167", "76561198104863232", "76561198104865798", "76561198104872684", "76561198104919828", "76561198105078313", "76561198105114317", "76561198105147987", "76561198105160758", "76561198105282238", "76561198105305054", "76561198105349835", "76561198105361632", "76561198105474104", "76561198105484331", "76561198105495732", "76561198105547831"} 5 | local datagen = {} -- SID, которые принимают участие и данные для действий 6 | 7 | 8 | -- Отбирает рандом участников теста 9 | local NEED = 500 10 | for i = 1,NEED do 11 | local s64 = table.Random(sids) 12 | datagen[i] = {s64 = s64} 13 | end 14 | 15 | -- Чистим таблицу 16 | if sql.TableExists("igs_inv_log") then 17 | sql.Query("DROP TABLE `igs_inv_log`") 18 | IGS.IL.CreateTable() 19 | end 20 | 21 | -- Создание гифтов 22 | for i,d in ipairs(datagen) do 23 | d.gift_id = i + 100500 24 | d.gift_uid = table.Random(IGS.GetItems()):UID() 25 | end 26 | 27 | -- Дроп или активация гифта 28 | for _,d in ipairs(datagen) do 29 | if not DICE(5) then -- в 1 из 5 случаев игрок купил итем и ничего не делает (холдит) 30 | local action = DICE(5) and IGS.IL.DROP or IGS.IL.ACT -- 1 дроп на 5 активаций 31 | d.second_action = action 32 | end 33 | end 34 | 35 | -- Пик гифта другим человеком 36 | for _,d in ipairs(datagen) do 37 | if DICE(10) then -- в 1 из 10 случаев гифт не поднимаем 38 | local s64_inflictor = table.Random(datagen).s64 39 | d.pick_by = s64_inflictor 40 | end 41 | end 42 | 43 | for _,d in ipairs(datagen) do 44 | timer.Simple(math.Rand(0, 1),function() 45 | IGS.IL.Log(d.gift_id, d.gift_uid, d.s64, d.s64, IGS.IL.NEW) 46 | end) 47 | 48 | if d.second_action then 49 | timer.Simple(math.Rand(.5, 1.5),function() 50 | IGS.IL.Log(d.gift_id, d.gift_uid, d.s64, d.s64, d.second_action) 51 | end) 52 | end 53 | 54 | if d.pick_by then 55 | timer.Simple(math.Rand(1, 2),function() 56 | IGS.IL.Log(d.gift_id, d.gift_uid, d.s64, d.pick_by, IGS.IL.PICK) 57 | end) 58 | end 59 | end 60 | 61 | 62 | -- После всех таймеров 63 | timer.Simple(2.5,function() 64 | local random_search = table.Random(datagen).s64 65 | prt(IGS.IL.GetLog(nil, nil, random_search)) 66 | end) 67 | 68 | ``` 69 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/modules/inv_log/interface_cl.lua: -------------------------------------------------------------------------------- 1 | local actions = setmetatable({},{__index = function() return "Ошибка" end}) 2 | actions[1] = "Покупка" 3 | actions[2] = "Активация" 4 | actions[3] = "Дроп" 5 | actions[4] = "Пик" 6 | 7 | local function beautyS64(s64) 8 | local pl = player.GetBySteamID64(s64) 9 | return pl and pl:Nick() or util.SteamIDFrom64(s64):sub(#"STEAM_0") 10 | end 11 | 12 | local function isSteamID64(s) 13 | return #s == 17 and s:StartWith("7656") 14 | end 15 | 16 | local function anyTo64(sid) 17 | local res = isSteamID64(sid) and sid or util.SteamIDTo64(sid) 18 | return res ~= "0" and res 19 | end 20 | 21 | local function getNameBySid(cb, s64) 22 | IGS.IL.NameRequest(function(name_) 23 | cb(name_) 24 | end, s64) 25 | end 26 | 27 | function IGS.WIN.InvLog() 28 | return uigs.Create("igs_frame", function(bg) 29 | bg:SetTitle("Операции с инвентарем") 30 | bg:SetSize(800, 600) 31 | bg:Center() 32 | bg:MakePopup() 33 | 34 | function bg:SearchSteamID(s64) 35 | bg.table:Clear() 36 | bg:Search(1, s64) 37 | end 38 | 39 | function bg:SearchGiftUID(uid) 40 | bg.table:Clear() 41 | bg:Search(1, nil, uid) 42 | end 43 | 44 | function bg:ResetSearch() 45 | bg.table:Clear() 46 | bg:Search() 47 | end 48 | 49 | function bg:AddLine(sOwner, sInfli, sItem, sAction, sDate, r) 50 | local line = bg.table:AddLine(sOwner, sInfli, sItem, r.gift_id, sAction, sDate) 51 | line:SetTooltip("ID операции: " .. r.action_id .. ". Перед ником глобальный Score игрока") 52 | 53 | local btn_giftid = line.columns[4] 54 | btn_giftid.text_color = IGS_IL_ROW_SOLID_COLOR or HSVToColor(((r.gift_id * 10) * 5) % 360, 1, 1) 55 | 56 | for _,v in ipairs(line.columns) do 57 | v:SetCursor("hand") 58 | end 59 | 60 | for i,s64 in ipairs({r.owner, r.inflictor}) do 61 | if not player.GetBySteamID64(s64) then -- игрок не в сети, ник не получен 62 | getNameBySid(function(name_) 63 | if not name_ then return end 64 | 65 | local btn_owner = line.columns[i] -- 1, 2 колонка 66 | btn_owner:SetText(name_) 67 | end, s64) 68 | end 69 | end 70 | 71 | line.DoClick = function() 72 | local m = DermaMenu(line) 73 | m:AddOption("Копировать SID владельца",function() SetClipboardText(r.owner) end) 74 | m:AddOption("Копировать SID исполнителя",function() SetClipboardText(r.inflictor) end) 75 | m:AddOption("Действия игрока",function() bg:SearchSteamID(r.owner) end) 76 | m:AddOption("Действия с " .. sItem,function() bg:SearchGiftUID(r.gift_uid) end) 77 | if sAction == "Активация" then -- а таких записях написан ID покупки, а не гифта (аве шиткодинг!) 78 | m:AddOption("Отключить", function() 79 | IGS.DeactivateItem(r.gift_id) 80 | end) 81 | end 82 | m:Open() 83 | end 84 | end 85 | 86 | function bg:Search(page, sid_, uid_) -- sid ИЛИ uid (Так сделан SELECT запрос) 87 | page = page or 1 88 | self.prev_page = page 89 | self.prev_sid = sid_ 90 | self.prev_uid = uid_ 91 | 92 | IGS.IL.GetLog(function(tLog) 93 | if not IsValid(bg) then return end -- Долго данные получались 94 | 95 | for _,r in ipairs(tLog) do 96 | local ITEM = IGS.GetItemByUID(r.gift_uid) 97 | 98 | local sDate = IGS.TimestampToDate(r.date, true) 99 | local sAction = actions[r.action] 100 | local sItem = ITEM.isnull and (r.gift_uid .. " (NULL)") or ITEM:Name() 101 | 102 | local sOwner = beautyS64(r.owner) 103 | local sInfli = beautyS64(r.inflictor) 104 | 105 | bg:AddLine(sOwner, sInfli, sItem, sAction, sDate, r) 106 | end 107 | 108 | bg.table:PerformLayout() 109 | bg.load:UpdateLoaded(#bg.table.lines, tLog[0]) 110 | end, page, sid_, uid_) 111 | end 112 | 113 | bg.table = uigs.Create("igs_table", function(pnl) 114 | pnl:Dock(FILL) 115 | pnl:DockMargin(5,5,5,5) 116 | -- pnl:SetSize(790, 565) 117 | 118 | pnl:SetTitle("Действия") 119 | 120 | pnl:AddColumn("Владелец",120) 121 | pnl:AddColumn("Исполнитель",120) 122 | pnl:AddColumn("Предмет") 123 | pnl:AddColumn("ID гифта", 65) 124 | pnl:AddColumn("Действие",110) 125 | pnl:AddColumn("Дата",130) 126 | end, bg) 127 | 128 | local bottom = uigs.Create("Panel", function(self) 129 | self:SetHeight(30) 130 | self:Dock(BOTTOM) 131 | self:DockMargin(5,0,5,5) 132 | end, bg) 133 | 134 | local entry = uigs.Create("DTextEntry", function(self) 135 | self:Dock(LEFT) 136 | self:SetWide(200) 137 | self:SetValue("SteamID или UID итема") 138 | self.OnEnter = function() 139 | local val = self:GetValue():Trim() 140 | local s64 = anyTo64(val) 141 | 142 | if val == "" then 143 | bg:ResetSearch() 144 | elseif s64 then 145 | bg:SearchSteamID(s64) 146 | else 147 | bg:SearchGiftUID(val) 148 | end 149 | end 150 | end, bottom) 151 | 152 | uigs.Create("igs_button", function(self) 153 | self:Dock(LEFT) 154 | self:SetWide(150) 155 | self:DockMargin(5,0,0,0) 156 | self:SetText("Найти") 157 | self.DoClick = entry.OnEnter 158 | end, bottom) 159 | 160 | bg.load = uigs.Create("igs_button", function(self) 161 | self:Dock(RIGHT) 162 | self:SetWide(200) 163 | self.DoClick = function() 164 | -- bg.table:Clear() 165 | bg:Search(bg.prev_page + 1, bg.prev_sid, bg.prev_uid) 166 | end 167 | self.UpdateLoaded = function(_, iLoaded, iTotal) 168 | if iLoaded == iTotal then 169 | self:SetText("Все загружено (" .. iTotal .. ")") 170 | self:SetActive(false) 171 | else 172 | self:SetText("Загрузить еще (" .. iLoaded .. "/" .. iTotal .. ")") 173 | self:SetActive(true) 174 | end 175 | end 176 | end, bottom) 177 | 178 | bg:Search() 179 | bg.load:UpdateLoaded(0, 0) 180 | end) 181 | end 182 | 183 | concommand.Add("igs_invlog",IGS.WIN.InvLog) 184 | 185 | -- for i = 1,1 do 186 | -- local fr = IGS.WIN.InvLog() 187 | -- timer.Simple(10,function() 188 | -- if IsValid(fr) then 189 | -- fr:Remove() 190 | -- end 191 | -- end) 192 | -- end 193 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/utils/ut_sh.lua: -------------------------------------------------------------------------------- 1 | -- #todo нужно переделывать, но только так: 2 | -- https://trello.com/c/WfVYTIOF/544 (комменты) 3 | CreateConVar("igs_debug", "0", FCVAR_NOTIFY) 4 | cvars.AddChangeCallback("igs_debug", function(_, old, new) 5 | IGS.DEBUG = tonumber(new) -- level. 0: disabled, 1: debug, 2: info, 3: warning, 4: error 6 | if IGS.DEBUG == 0 then IGS.DEBUG = nil end 7 | IGS.prints("PEZuM OTJIA9Ku ", (IGS.DEBUG and "AKTuBuPOBAH" or "BbIKJII04EH"), ". Level: ", (IGS.DEBUG or 0)) 8 | end, "main") 9 | 10 | --- @class Player 11 | local PLAYER = FindMetaTable("Player") 12 | 13 | function PLAYER:IGSFunds() 14 | return self:GetIGSVar("igs_balance") or 0 15 | end 16 | 17 | function PLAYER:HasPurchase(sUID) 18 | return IGS.PlayerPurchases(self)[sUID] 19 | end 20 | 21 | -- ITEM, если человек имеет хоть один итем из списка 22 | -- nil, если итем не отслеживается 23 | -- false, если нет права 24 | function IGS.PlayerHasOneOf(pl, tItems) 25 | if not tItems then return end 26 | 27 | for _,ITEM in ipairs(tItems) do 28 | if pl:HasPurchase( ITEM:UID() ) then 29 | return ITEM 30 | end 31 | end 32 | 33 | return false 34 | end 35 | 36 | function IGS.isUser(pl) -- возвращает false, если чел никогда не юзал автодонат 37 | return pl:GetIGSVar("igs_balance") ~= nil 38 | end 39 | 40 | 41 | -- Может ли чел себе позволить покупку итема, ценой в sum IGS? 42 | function IGS.CanAfford(pl,sum,assert) 43 | if sum >= 0 and pl:IGSFunds() - sum >= 0 then 44 | return true 45 | end 46 | 47 | if not assert then 48 | return false 49 | end 50 | 51 | if isfunction(assert) then 52 | assert() 53 | else 54 | if SERVER then 55 | IGS.WIN.Deposit(pl, sum) 56 | else 57 | IGS.WIN.Deposit(sum) 58 | end 59 | end 60 | 61 | return false 62 | end 63 | 64 | -- Список активных покупок игрока 65 | -- uid > amount 66 | function IGS.PlayerPurchases(pl) 67 | return CLIENT and (pl:GetIGSVar("igs_purchases") or {}) or pl:GetVar("igs_purchases",{}) 68 | end 69 | 70 | -- Сумма всех положительных операций по счету игрока 71 | -- (включая пополнения, активацию купонов купоны и выдачу денег администратором) 72 | function IGS.TotalTransaction(pl) 73 | return pl:GetIGSVar("igs_total_transactions") or 0 74 | end 75 | 76 | -- возврат объекта ЛВЛ на клиенте, номера уровня на сервере 77 | function IGS.PlayerLVL(pl) 78 | return pl:GetIGSVar("igs_lvl") 79 | end 80 | 81 | 82 | -- Минимальная сумма пополнения в рублях 83 | function IGS.GetMinCharge() 84 | return 10 -- TODO global var? 85 | end 86 | 87 | -- Не смог загрузиться или выключен в панели, меню открывать нельзя 88 | function IGS.IsLoaded() 89 | return IGS.SERVERS:ID() and not GetGlobalBool("IGS_DISABLED") 90 | end 91 | 92 | 93 | 94 | 95 | local terms = { 96 | [1] = "бесконечно", 97 | [2] = "единоразово", 98 | [3] = "%s" 99 | } 100 | 101 | function IGS.TermType(term) 102 | return 103 | not term and 1 or -- бесконечно 104 | term == 0 and 2 or -- мгновенно 105 | term and 3 -- кол-во дней 106 | end 107 | 108 | function IGS.TermToStr(term) 109 | return terms[ IGS.TermType(term) ]:format(term and PL_DAYS(term)) 110 | end 111 | 112 | function IGS.TimestampToDate(ts,bShowFull) -- в "купил до" 113 | if not ts then return end 114 | return os.date(bShowFull and IGS.C.DATE_FORMAT or IGS.C.DATE_FORMAT_SHORT,ts) 115 | end 116 | 117 | -- TODO: может удалить с sh (используется только на клиенте) 118 | function IGS.FormItemInfo(ITEM, pl) 119 | return { 120 | ["Категория"] = ITEM:Category(), 121 | ["Действует"] = IGS.TermToStr(ITEM:Term()), 122 | ["Цена"] = PL_MONEY(ITEM:GetPrice(pl)), 123 | ["Без скидки"] = ITEM.discounted_from and PL_MONEY(ITEM.discounted_from) or nil, 124 | ["Покупки стакаются"] = ITEM:IsStackable() and "да" or "нет", 125 | } 126 | end 127 | 128 | -- Упрощенная в применении версия IGS.print. Использует 2 цвета: основной и для выделений 129 | -- Если первым указать цвет, то будет изменен цвет выделенного текста 130 | -- Если вторым указать цвет, то будет изменен цвет обычного текста 131 | -- Можно указать цвет текста, а первым аргументом использовать nil 132 | function IGS.prints(...) 133 | local base_color = color_white 134 | local highlight_color = Color(50, 50, 255) 135 | 136 | local input = {...} 137 | if not input[1] then input[1] = highlight_color end -- если кто-то хочет поменять только цвет текста 138 | if istable( input[2] ) then base_color = input[2] table.remove(input, 2) end 139 | if istable( input[1] ) then highlight_color = input[1] table.remove(input, 1) end 140 | 141 | local output = {} 142 | 143 | for i, psc in ipairs(input) do 144 | output[#output + 1] = (i % 2 == 0) and highlight_color or base_color 145 | output[#output + 1] = psc 146 | end 147 | 148 | output[#output + 1] = "\n" 149 | MsgC(Color(50, 200, 255), "[IGS] ", unpack(output)) 150 | end 151 | 152 | -- Результат выполнения: https://file.def.pm/Jw9E7j7a.jpg 153 | -- IGS.prints("одиночный текст базового (белого) цвета") 154 | -- IGS.prints(nil, Color(255, 50, 50), "одиночный выделенным кастомным цветом текст") 155 | -- IGS.prints(Color(255, 50, 50), "", "одиночный выделенным кастомным цветом текст") -- аналогичный вариант верхней записи 156 | -- IGS.prints(Color(255, 50, 50), "обычный, ", "выделение кастомым цветом") 157 | -- IGS.prints("обычный, ", "выделенный, ", "обычный, ", "выделенный") 158 | -- IGS.prints("", "выделенный, ", "обычный, ", "выделенный, ", "обычный") 159 | -- IGS.prints(Color(150, 150, 150), "обычный, ", "выделенный цветной, ", "обычный, ", "выделенный") 160 | -- IGS.prints(Color(150, 150, 150), Color(255, 50, 50), "обычный, но красный, ", "выделенный серый, ", "красный, ", "серый") 161 | -- IGS.prints(nil, Color(255, 50, 50), "обычный, но красный, ", "выделенный стандарт, ", "обычный, ", "выделенный") 162 | 163 | -- #deprecated 01.09.2024, not used 164 | function IGS.print(...) 165 | local args = {...} 166 | if not IsColor(args[1]) then 167 | table.insert(args,1,color_white) 168 | end 169 | 170 | args[#args] = args[#args] .. "\n" 171 | MsgC(Color(50,200,255), "[IGS] ", unpack(args)) 172 | end 173 | 174 | function IGS.dprint(...) 175 | if IGS.DEBUG then 176 | IGS.prints(...) 177 | end 178 | end 179 | 180 | 181 | function IGS.SignPrice(iPrice) -- 10 Alc 182 | return math.Truncate(tonumber(iPrice) or -1, 2) .. " " .. IGS.C.CURRENCY_SIGN 183 | end 184 | 185 | PL_MONEY = PLUR(IGS.C.CurrencyPlurals) 186 | PL_DAYS = PLUR({"день", "дня", "дней"}) 187 | 188 | -- #TODO: ОБРАТНАЯ СОВМЕСТИМОСТЬ. НЕ применяется в core. 189 | -- https://forum.gm-donate.net/t/cryptos-igs/1461/6 190 | PL_IGS = PL_MONEY 191 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/activities/main.lua: -------------------------------------------------------------------------------- 1 | local null = function() end 2 | local etoGlavnayaVkladkaBlya = true 3 | 4 | hook.Add("IGS.CatchActivities","main",function(activity,sidebar) 5 | -- Зона прокрутки последних покупок http://joxi.ru/12MQQBlfzPnw2J 6 | local bg = sidebar:AddPage("Последние покупки") 7 | 8 | -- Панель тегов и готовая кнопка сброса фильтров 9 | local tagspan = uigs.Create("Panel", bg) 10 | tagspan:SetWide(activity:GetWide()) 11 | tagspan.Paint = function(s,w,h) 12 | IGS.S.Panel(s,w,h,nil,nil,nil,true) 13 | end 14 | 15 | -- сетка https://img.qweqwe.ovh/1487714173294.png 16 | bg.tags = uigs.Create("DIconLayout", function(tags) 17 | tags:SetWide(activity:GetWide() - 5 - 5) 18 | tags:SetPos(5,5) 19 | tags:SetSpaceX(10) 20 | tags:SetSpaceY(10) 21 | tags.Paint = null 22 | 23 | function tags:AddTag(sName,doClick) 24 | local tag = uigs.Create("igs_button") 25 | tag:SetTall(18) 26 | tag:SetText(" " .. sName .. " ") -- костыль для расширения кнопки 27 | tag:SizeToContents() 28 | tag.DoClick = doClick 29 | 30 | self:Add(tag) 31 | 32 | tags:InvalidateLayout(true) -- tags:GetTall() 33 | tagspan:SetTall(tags:GetTall() + 5 + 5) 34 | 35 | local y = tagspan:GetTall() 36 | 37 | -- Расхождение вот тут: 38 | -- https://img.qweqwe.ovh/1493840355855.png 39 | -- y = y - 10 -- UPD 2020 t.me/c/1353676159/7888 40 | 41 | bg.categs:SetTall(activity:GetTall() - y - activity.tabBar:GetTall()) 42 | bg.categs:SetPos(0,y) 43 | return tag 44 | end 45 | end, tagspan) 46 | 47 | bg.categs = uigs.Create("igs_panels_layout_list", bg) -- center panel 48 | bg.categs:DisableAlignment(true) 49 | bg.categs:SetWide(activity:GetWide()) 50 | 51 | -- Раскомментить, если захочу убрать теги 52 | -- bg.categs:SetTall(activity:GetTall() - activity.tabBar:GetTall()) 53 | -- bg.categs:SetPos(0,y) 54 | 55 | 56 | -- category = true 57 | local cats = {} 58 | 59 | local function addItems(fItemsFilter,fGroupFilter) 60 | local rows = {} 61 | 62 | for _,GROUP in pairs( IGS.GetGroups() ) do -- name 63 | if not fGroupFilter or fGroupFilter(GROUP) ~= false then 64 | local pnl = uigs.Create("igs_group"):SetGroup(GROUP) 65 | pnl.category = GROUP:Items()[1].item:Category() -- предполагаем, что в одной группе будут итемы одной категории 66 | 67 | table.insert(rows,pnl) 68 | end 69 | end 70 | 71 | local check_skip = function(ITEM) 72 | return ITEM.isnull -- пустышка 73 | or (not ITEM:CanSee( LocalPlayer() )) -- еще в IGS.WIN.Group 74 | or ITEM:Group() -- группированные итемы засунуты в группу выше 75 | or (fItemsFilter and fItemsFilter(ITEM) == false) 76 | end 77 | 78 | -- не (i)pairs, потому что какой-то ID в каком-то очень редком случае может отсутствовать 79 | -- если его кто-то принудительно занилит, чтобы убрать итем например. 80 | -- Хотя маловероятно, но все же 81 | for _,ITEM in pairs(IGS.GetItems()) do 82 | if not check_skip(ITEM) then 83 | local pnl = uigs.Create("igs_item"):SetItem(ITEM) 84 | pnl.category = ITEM:Category() 85 | 86 | table.insert(rows,pnl) 87 | end 88 | end 89 | 90 | for _,pnl in ipairs(rows) do 91 | bg.categs:Add(pnl,pnl.category or "Разное").title:SetTextColor(IGS.col.TEXT_HARD) -- http://joxi.ru/Y2LqqyBh5BODA6 92 | cats[pnl.category or "Разное"] = true 93 | end 94 | end 95 | addItems() 96 | 97 | 98 | 99 | --[[------------------------------------------------------------------------- 100 | Теги (Быстрый выбор категории) 101 | ---------------------------------------------------------------------------]] 102 | bg.tags:AddTag("Сброс фильтров", function() bg.categs:Clear() addItems() end) 103 | :SetActive(true) 104 | 105 | for categ in pairs(cats) do 106 | bg.tags:AddTag(categ,function(self) 107 | bg.categs:Clear() 108 | 109 | -- #todo переписать это говнище 110 | addItems(function(ITEM) 111 | return self.categ == "Разное" and not ITEM:Category() or (ITEM:Category() == self.categ) 112 | end,function(GROUP) 113 | return self.categ == "Разное" and not GROUP:Items()[1].item:Category() or (GROUP:Items()[1].item:Category() == self.categ) 114 | end) 115 | end).categ = categ 116 | end 117 | 118 | 119 | 120 | --[[------------------------------------------------------------------------- 121 | Список последних покупок в сайдбаре 122 | ---------------------------------------------------------------------------]] 123 | IGS.GetLatestPurchases(function(latest_purchases) 124 | if not IsValid(activity) then return end 125 | 126 | local function addPurchasePanel(v) 127 | local b = uigs.Create("Panel") 128 | b:SetTall(IGS.SERVERS.TOTAL > 1 and 100 or 100 - 20) 129 | b:DockPadding(5,5,5,5) 130 | 131 | local pnl = uigs.Create("Panel", b) 132 | pnl:Dock(FILL) 133 | pnl:DockPadding(5,5,5,5) 134 | pnl.Paint = IGS.S.RoundedPanel 135 | function pnl:AddRow(sName,value) 136 | local row = uigs.Create("Panel", pnl) 137 | row:Dock(TOP) 138 | row:SetTall(20) 139 | --:DockMargin(5,5,0,5) 140 | --row.Paint = IGS.S.RoundedPanel 141 | 142 | -- key 143 | uigs.Create("DLabel", function(name) 144 | name:Dock(LEFT) 145 | name:SetWide(55) 146 | name:SetText(sName) 147 | name:SetFont("igs.18") 148 | name:SetTextColor(IGS.col.TEXT_HARD) 149 | name:SetContentAlignment(6) 150 | end, row) 151 | 152 | uigs.Create("DLabel", function(name) 153 | name:Dock(FILL) 154 | name:SetText(value) 155 | name:SetFont("igs.18") 156 | name:SetTextColor(IGS.col.TEXT_SOFT) 157 | name:SetContentAlignment(4) 158 | end, row) 159 | end 160 | 161 | -- Заголовок услуги. Легко превращается в лейбу 162 | uigs.Create("DButton", function(name) 163 | name:Dock(TOP) 164 | name:SetTall(20) 165 | name:SetText(IGS.GetItemByUID(v.item):Name()) 166 | name:SetFont("igs.18") 167 | name:SetTextColor(IGS.col.HIGHLIGHTING) 168 | name:SetContentAlignment(4) 169 | name.Paint = null 170 | name.DoClick = function() 171 | IGS.WIN.Item(v.item) 172 | end 173 | end, b) 174 | 175 | pnl:AddRow("Купил: ",v.nick or "NoName") 176 | if IGS.SERVERS.TOTAL > 1 then 177 | pnl:AddRow("На: ",IGS.ServerName(v.server)) 178 | -- else 179 | -- pnl:AddRow("UID: ",v.item) 180 | end 181 | pnl:AddRow("До: ",IGS.TimestampToDate(v.expire) or "навсегда") 182 | 183 | bg.side:AddItem(b) 184 | end 185 | 186 | for _,purchase in ipairs(latest_purchases) do 187 | local ITEM = IGS.GetItemByUID(purchase.item) 188 | if not ITEM.isnull then 189 | addPurchasePanel(purchase) 190 | end 191 | end 192 | end) 193 | 194 | activity:AddTab("Услуги",bg,"materials/icons/fa32/rub.png",etoGlavnayaVkladkaBlya) 195 | end) 196 | 197 | -- local p = IGS.UI() 198 | -- timer.Simple(3,function() if IsValid(p) then p:Remove() end end) 199 | -------------------------------------------------------------------------------- /addons/igs-core/lua/igs/interface/vgui/igs_iteminfo.lua: -------------------------------------------------------------------------------- 1 | local PANEL = {} 2 | 3 | local dock = 5 4 | 5 | function PANEL:Init() 6 | self.head = uigs.Create("Panel", self) 7 | self.head:SetTall(100) 8 | self.head:Dock(TOP) 9 | self.head:DockPadding(dock,dock,dock,dock) 10 | self.head.Paint = function(s,w,h) 11 | IGS.S.Panel(s,w,h, nil,nil,nil,true) 12 | end 13 | 14 | self.scroll = uigs.Create("igs_scroll", self) 15 | self.scroll:Dock(FILL) 16 | end 17 | 18 | function PANEL:SetName(sName) 19 | self.name = self.name or uigs.Create("DLabel", function(lbl) 20 | lbl:Dock(TOP) 21 | --lbl:SetTall(22) 22 | lbl:SetFont("igs.22") 23 | lbl:SetTextColor(IGS.col.TEXT_HARD) 24 | lbl:SetWrap(true) 25 | lbl:SetAutoStretchVertical(true) 26 | lbl:SizeToContents() 27 | end, self.head) 28 | 29 | self.name:SetText(sName) 30 | end 31 | 32 | -- Добавляет иконку рядом с названием инфо панели 33 | function PANEL:SetIcon(sIco, sMode) -- url or Model path 34 | local bIsModel = sMode == true or sMode == "model" 35 | local bIsMaterial = sMode == "material" 36 | 37 | self.icon_bg = self.icon_bg or uigs.Create("Panel", function(bg) 38 | bg.Paint = IGS.S.RoundedPanel 39 | bg:SetWide(self.head:GetTall() - dock * 2) 40 | bg:Dock(LEFT) 41 | bg:DockPadding(3,3,3,3) 42 | bg:DockMargin(0,0,dock,0) 43 | 44 | bg:InvalidateParent(true) -- true bg:GetSize() 45 | end, self.head) 46 | 47 | self.icon = self.icon or (bIsModel and uigs.Create("DModelPanel", function(mdl) 48 | mdl:SetSize(self.icon_bg:GetSize()) 49 | mdl:SetModel(sIco) 50 | 51 | local mn, mx = mdl.Entity:GetRenderBounds() 52 | local size = 0 53 | size = math.max(size, math.abs(mn.x) + math.abs(mx.x)) 54 | size = math.max(size, math.abs(mn.y) + math.abs(mx.y)) 55 | size = math.max(size, math.abs(mn.z) + math.abs(mx.z)) 56 | 57 | mdl:SetFOV(30) 58 | mdl:SetCamPos(Vector(size, size, size)) 59 | mdl:SetLookAt((mn + mx) * 0.5) 60 | mdl.LayoutEntity = function() return false end 61 | 62 | end, self.icon_bg)) or uigs.Create("igs_wmat", function(ico) 63 | ico:SetSize(self.icon_bg:GetSize()) 64 | ico.AutoSize = false 65 | end, self.icon_bg) -- НЕ моделька (Ссылка на иконку) 66 | 67 | 68 | if bIsModel then 69 | self.icon:SetModel(sIco) 70 | elseif bIsMaterial then 71 | self.icon:SetMaterial(sIco) 72 | else 73 | self.icon:SetURL(sIco) -- nil = сброс 74 | end 75 | end 76 | 77 | -- Кнопка под названием инфо панели 78 | function PANEL:SetSubNameButton(sName,func) 79 | if not sName then return end 80 | 81 | self.sub = self.sub or uigs.Create("DButton", function(btn) 82 | btn:Dock(TOP) 83 | btn:SetTall(20) 84 | btn:SetFont("igs.20") 85 | btn:SetTextColor(IGS.col.TEXT_SOFT) 86 | btn:SetContentAlignment(4) 87 | btn.DoClick = func 88 | btn.Paint = function() end 89 | end, self.head) 90 | 91 | self.sub:SetText(sName .. "⯈") 92 | end 93 | 94 | -- Создает невидимую панельку вот тут: 95 | -- https://img.qweqwe.ovh/1486589180000.png 96 | function PANEL:CreateActivity() 97 | self.activity = self.activity or uigs.Create("Panel", function(btn) 98 | btn:Dock(FILL) 99 | end, self.head) 100 | 101 | return self.activity 102 | end 103 | 104 | -- Описание, информация, картинка 105 | function PANEL:AddPanel(sTitle,panel) 106 | -- https://img.qweqwe.ovh/1486595573943.png 107 | local background = uigs.Create("Panel", function(bg) 108 | bg:DockPadding(10,0,10,0) -- отступы по краям внутри скролла 109 | bg.Paint = function(s,w,h) -- линия снизу посередине панели https://img.qweqwe.ovh/1491948928484.png 110 | surface.SetDrawColor(IGS.col.SOFT_LINE) 111 | surface.DrawLine(10,h - 1,w - 10,h - 1) 112 | end 113 | end) -- для отступов 114 | 115 | local y = panel:GetTall() -- с доками не вышло, пришлось сетить вручную 116 | 117 | uigs.Create("DLabel", function(title) 118 | title:Dock(TOP) 119 | title:SetTall(22) 120 | title:DockMargin(0,10,0,5) 121 | title:SetText(sTitle) 122 | title:SetFont("igs.22") 123 | title:SetTextColor(IGS.col.TEXT_HARD) 124 | 125 | y = y + 22 + 5 + 5 126 | end, background) 127 | 128 | panel:SetParent(background) 129 | panel:Dock(TOP) 130 | 131 | self.scroll:AddItem(background) 132 | 133 | background:SetTall(y + 5 + 10) -- 5 хз че, а 10 - отступ от текста к бортику снизу 134 | end 135 | 136 | function PANEL:Reset() 137 | self.scroll:Reset() 138 | 139 | if self.name then 140 | self.name:SetText("") 141 | end 142 | 143 | if self.sub then 144 | self.sub:SetText("") 145 | end 146 | 147 | if self.icon then 148 | self.icon:SetURL() -- IGS.C.DefaultIcon 149 | end 150 | end 151 | 152 | 153 | --[[------------------------------------------------------------------------- 154 | Не обязательные методы. Их тут не должно быть с точки зрения правильности кода 155 | Но так как я панель больше нигде, кроме IGS не юзаю, то мне так будет удобнее 156 | ---------------------------------------------------------------------------]] 157 | -- Добавляет панель с описанием 158 | function PANEL:SetDescription(sDescription) 159 | if self.description then return end 160 | 161 | local pnl = uigs.Create("Panel") 162 | 163 | self:InvalidateParent(true) 164 | local w = self:GetWide() 165 | local txt = string.Wrap("igs.15", sDescription, w - 10 - 10) 166 | local y = 0 167 | 168 | for _,line in ipairs(txt) do 169 | uigs.Create("DLabel", function(d) 170 | d:SetPos(0,y) 171 | d:SetSize(w,15) 172 | d:SetFont("igs.15") 173 | d:SetTextColor(IGS.col.TEXT_SOFT) 174 | d:SetText(line) 175 | 176 | y = y + 15 177 | end, pnl) 178 | end 179 | 180 | pnl:SetTall(y) 181 | self:AddPanel("Описание",pnl) 182 | end 183 | 184 | function PANEL:SetInfo(tInf) 185 | local pnl = uigs.Create("Panel") 186 | local y = 0 187 | 188 | for k,v in pairs(tInf) do 189 | local line_bg = uigs.Create("Panel", pnl) 190 | line_bg:SetTall(15) 191 | line_bg:Dock(TOP) 192 | 193 | uigs.Create("DLabel", function(key) 194 | key:Dock(LEFT) 195 | key:SetWide(80) 196 | key:SetFont("igs.15") 197 | key:SetTextColor(IGS.col.TEXT_SOFT) 198 | key:SetText(k) 199 | key:SetContentAlignment(6) 200 | end, line_bg) 201 | 202 | uigs.Create("DLabel", function(key) 203 | key:Dock(FILL) 204 | key:SetFont("igs.15") 205 | key:SetTextColor(IGS.col.TEXT_HARD) 206 | key:SetText(" " .. v) 207 | key:SetContentAlignment(4) 208 | end, line_bg) 209 | 210 | y = y + 15 211 | end 212 | 213 | pnl:SetTall(y) 214 | self:AddPanel("Информация",pnl) 215 | end 216 | 217 | -- Добавляет панель с указанным изображением 218 | function PANEL:SetImage(sUrl) 219 | if not sUrl then return end 220 | 221 | self:InvalidateParent(true) -- self:GetWide() 222 | 223 | local pnl = uigs.Create("igs_wmat") 224 | pnl:SetSize(self:GetWide(),self:GetWide() / 5 * 2) -- соотношение 5:2 225 | pnl:SetURL(sUrl) 226 | 227 | self:AddPanel("Изображение",pnl) 228 | end 229 | 230 | vgui.Register("igs_iteminfo",PANEL,"Panel") 231 | 232 | -- IGS.WIN.Item("group_premium_15d") 233 | --------------------------------------------------------------------------------