├── 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 |
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 |
--------------------------------------------------------------------------------