gmpanel.actions = gmpanel.actions or {} gmpanel.actions.available = gmpanel.actions.available or {} gmpanel.actions.added = gmpanel.actions.added or {} pcall(function() local f = file.Read('gmpanel_actions.dat') if f then gmpanel.actions.added = pon.decode(f) or {} end end) local pan, pAvailable, settings, active, right local colors = CFG.skinColors local function addedLinePaint(self, w, h) local off, off2 = 0, 0 if IsValid(settings) and settings.editUid == self.uid then draw.RoundedBox(4, 0, 0, w, h, colors.g) off, off2 = 1, 2 end if self.sel then draw.RoundedBox(4, off, off, w - off2, h - off2, Color(170,119,102)) elseif self.id % 2 == 1 then draw.RoundedBox(4, off, off, w - off2, h - off2, Color(0,0,0, 35)) end end local function addedLineSelect(self) for _,v in ipairs(active:GetChildren()) do v.sel = nil end self.sel = true end local function onModified(self) local byUid = {} for _,v in ipairs(gmpanel.actions.added) do byUid[v.uid] = v end gmpanel.actions.added = {} for _,v in ipairs(self:GetChildren()) do gmpanel.actions.added[#gmpanel.actions.added + 1] = byUid[v.uid] v.sel = v.uid == settings.editUid or nil end gmpanel.actions.save() end local function addedLineClick(self, mcode) if isfunction(self.oldMP) then self:oldMP(mcode) end if mcode ~= MOUSE_RIGHT then return end self:Select() local saved = gmpanel.actions.added[self.id] if not saved then return end local menu = DermaMenu() menu:AddOption('Экспортировать', function() local data = table.Copy(saved) data.uid = nil SetClipboardText(pon.encode(data)) octolib.notify.show('Код действия скопирован в буфер обмена') end):SetIcon(octolib.icons.silk16('page_go')) menu:AddOption('Редактировать', function() if not IsValid(settings) then return end local action = gmpanel.actions.available[saved.id] if not action then return end pAvailable.dirty = true pAvailable:ClearSelection() for _,v in ipairs(pAvailable:GetLines()) do if v.id == saved.id then pAvailable:SelectItem(v) break end end pAvailable.dirty = nil settings:Clear() action.openSettings(settings, saved) settings.editUid = self.uid gmpanel.actions.insertButtons(saved.id, saved) end):SetIcon('octoteam/icons-16/pencil.png') menu:AddOption('Удалить', function() gmpanel.actions.editAction(self.uid) end):SetIcon('octoteam/icons-16/cross.png') menu:Open() end local function rebuildAdded() if not IsValid(pan) then return end if IsValid(right) then right:Remove() end right = pan:Add('DPanel') right:Dock(RIGHT) right:SetWide(150) right:SetPaintBackground(false) active = octolib.label(right, 'Заготовленные действия') active:DockMargin(0, 0, 0, 5) active:SetContentAlignment(5) active = right:Add('DScrollPanel') active:Dock(FILL) active:DockMargin(5, 0, 0, 0) active = active:Add('DListLayout') active:Dock(FILL) active:MakeDroppable('gmpanel.actions.added') function active:AddPanel(i, data) local cont = self:Add('DPanel') cont:SetTall(20) cont.Paint = addedLinePaint cont:SetMouseInputEnabled(true) cont.id = i cont.uid = data.uid cont.oldMP, cont.OnMouseReleased, cont.Select = cont.OnMouseReleased, addedLineClick, addedLineSelect local icon = cont:Add('DImage') icon:Dock(LEFT) icon:SetWide(16) icon:DockMargin(0, 2, 5, 2) icon:SetImage(data._icon) local name = cont:Add('DLabel') name:Dock(FILL) name:SetText(data._name) return cont end for i,v in ipairs(gmpanel.actions.added) do active:AddPanel(i, v) end active.OnModified = onModified local btnImport = right:Add('DButton') btnImport:Dock(BOTTOM) btnImport:SetTall(20) btnImport:DockMargin(0, 5, 0, 0) btnImport:SetText('Импорт действия') btnImport:SetIcon(octolib.icons.silk16('page_code')) function btnImport:DoClick() Derma_StringRequest('Импорт действия', 'Вставь код действия, полученный при экспорте', '', function(inp) local succ, data = pcall(pon.decode, inp) if not succ or not istable(data) then return octolib.notify.show('warning', 'Кажется, код действия поврежден') end gmpanel.actions.addAction(data.id, data) end) end end gmpanel.actions.save = octolib.func.debounce(function() file.Write('gmpanel_actions.dat', pon.encode(gmpanel.actions.added)) end, 1) function gmpanel.actions.registerAction(id, action) gmpanel.actions.available[id] = action end function gmpanel.actions.addAction(id, actionData) if not id or not gmpanel.actions.available[id] then return end actionData.id = id actionData.uid = octolib.string.uuid():sub(1, 8) gmpanel.actions.added[#gmpanel.actions.added + 1] = actionData gmpanel.actions.save() rebuildAdded() gmpanel.scenarios.updateActions() return true end function gmpanel.actions.editAction(uid, actionData) if not uid then return end local idx for i,v in ipairs(gmpanel.actions.added) do if v.uid == uid then idx = i break end end if not idx then return end if not actionData then table.remove(gmpanel.actions.added, idx) rebuildAdded() else gmpanel.actions.added[idx] = actionData if IsValid(active) then local cont = active:GetChildren()[idx] cont:GetChildren()[1]:SetImage(actionData._icon) cont:GetChildren()[2]:SetText(actionData._name) end end gmpanel.scenarios.updateActions() gmpanel.actions.save() end function gmpanel.actions.close() if IsValid(pan) then pan:Remove() end end function gmpanel.actions.defaultExecute(dataPassed, players) dataPassed = table.Copy(dataPassed) local id = dataPassed.id dataPassed.id, dataPassed._name, dataPassed._icon = nil dataPassed.players = players netstream.Start('dbg-event.execute', id, dataPassed) end function gmpanel.actions.insertButtons(id, data) if not (id and gmpanel.actions.available[id]) then return end local action = gmpanel.actions.available[id] data = data or {} octolib.button(settings, 'Тест', function() local func = action.execute or gmpanel.actions.defaultExecute local data if isfunction(action.getData) then data = action.getData(settings) else data = {} end if data == nil then octolib.notify.show('warning', 'Кажется, не все поля заполнены верно') return end data = istable(data) and data or {value = data} data.id = id data.test = true func(data, {LocalPlayer():SteamID()}) end) local btmPan = settings:Add('DPanel') btmPan:Dock(BOTTOM) btmPan:SetTall(48) btmPan:SetPaintBackground(false) local nameCont = btmPan:Add('DPanel') nameCont:Dock(LEFT) nameCont:SetWide(150) nameCont:SetPaintBackground(false) octolib.label(nameCont, 'Кнопка:') local wrapper = nameCont:Add('DPanel') wrapper:Dock(FILL) wrapper:DockMargin(0, 0, 0, 5) local icon = wrapper:Add('DImageButton') icon:Dock(LEFT) icon:DockMargin(2, 4, 2, 4) icon:SetSize(16, 16) icon:SetIcon(data._icon or 'icon16/control_play.png') function icon:DoClick() octolib.icons.picker(function(val) self:SetImage(val) end, 'materials/' .. self:GetImage(), 'materials/icon16/') end local name = wrapper:Add('DTextEntry') name:Dock(FILL) name:SetPaintBackground(false) name:SetTextColor(color_white) name:SetValue(data._name or '') local b = btmPan:Add('DPanel') b:Dock(RIGHT) b:DockMargin(0, 0, 0, 5) b:SetPaintBackground(false) b:SetWide(100) b = b:Add('DButton') b:Dock(BOTTOM) b:SetTall(30) b:SetText(settings.editUid and 'Сохранить' or 'Добавить') b:SetIcon('icon16/folder.png') function b:DoClick() if string.Trim(name:GetValue()) == '' then octolib.notify.show('Пожалуйста, добавь название действия. Тебе так будет проще') return end local data if isfunction(action.getData) then data = action.getData(settings) else data = {} end if data == nil then octolib.notify.show('warning', 'Кажется, не все поля заполнены верно') return end data = istable(data) and data or {value = data} data._name, data._icon = name:GetValue(), icon:GetImage() if settings.editUid then data.id = id data.uid = settings.editUid gmpanel.actions.editAction(settings.editUid, data) else gmpanel.actions.addAction(id, data) end end end hook.Add('gmpanel.populateActionsMenu', 'gmpanel.actions', function(menu, players) for _,v in ipairs(gmpanel.actions.added) do local action = gmpanel.actions.available[v.id] if action then menu:AddOption(v._name, function() local func = action.execute or gmpanel.actions.defaultExecute func(v, players) end):SetIcon(v._icon) end end menu:AddSpacer() end) local function build() gmpanel.actions.close() pan = vgui.Create('DFrame') pan:SetSize(700, 500) pan:SetTitle('Действия') pan:Center() pan:MakePopup() pan:SetDeleteOnClose(true) pAvailable = pan:Add('DListView') pAvailable:Dock(LEFT) pAvailable:SetWide(175) pAvailable:SetHideHeaders(true) pAvailable:DockMargin(0, 0, 5, 0) pAvailable:AddColumn(''):SetFixedWidth(32) pAvailable:AddColumn(L.title) pAvailable:SetDataHeight(32) pAvailable:SetMultiSelect(false) for k,v in pairs(gmpanel.actions.available) do local icon = vgui.Create('DImage') icon:SetImage(v.icon) pAvailable:AddLine(icon, v.name).id = k end settings = pan:Add('DPanel') settings:Dock(FILL) settings:DockPadding(5, 5, 5, 5) function pAvailable:OnRowSelected(_, row) if self.dirty then return end for _,v in ipairs(active:GetChildren()) do v.sel = nil end settings:Clear() gmpanel.actions.available[row.id].openSettings(settings, {}) settings.editUid = nil gmpanel.actions.insertButtons(row.id, {}) end rebuildAdded() end function gmpanel.actions.open() build() end