This commit is contained in:
lifestorm
2024-08-04 22:55:00 +03:00
parent 0e770b2b49
commit 94063e4369
7342 changed files with 1718932 additions and 14 deletions

265
lua/sam/menu/cl_init.lua Normal file
View File

@@ -0,0 +1,265 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
if SAM_LOADED then return end
local vgui = vgui
local draw = draw
local sam = sam
local sui = sui
local TDLib = sui.TDLib
local config = sam.config
do
local funcs = {
["SAM.ComboBox"] = {
event = "OnSelect",
function(s, _, value)
config.set(s.config_key, value)
end
},
["SAM.TextEntry"] = {
event = "OnEnter",
function(s)
local v = s:GetText()
if s:GetNumeric() then
v = tonumber(v)
end
config.set(s.config_key, v)
end
},
["SAM.ToggleButton"] = {
event = "OnChange",
function(s, v)
config.set(s.config_key, v)
end
}
}
sam.SUI = sam.SUI or sui.new("SAM", true, {
SetConfig = function(s, key, default)
s.config_key = key
local i = config.hook({key}, function(value, old)
local v = config.get(key, default)
s:SetValue(v)
end)
local t = funcs[s:GetName()]
s[t.event] = t[1]
s:On("OnRemove", function()
config.remove_hook(i)
end)
end
})
end
local SUI = sam.SUI
local GetColor = SUI.GetColor
sam.menu = {}
local tabs = {}
function sam.menu.add_tab(icon, func, check, pos)
local tab = {
icon = icon,
func = func,
check = check,
pos = pos
}
for k, v in ipairs(tabs) do
if v.icon == icon then
tabs[k] = tab
return
end
end
table.insert(tabs, tab)
end
function sam.menu.remove_tab(name)
for k, v in ipairs(tabs) do
if v.name == name then
table.remove(tabs, k)
break
end
end
end
SAM_TAB_TITLE_FONT = SUI.CreateFont("TabTitle", "Roboto Bold", 22)
SAM_TAB_DESC_FONT = SUI.CreateFont("TabDesc", "Roboto Medium", 15)
local MENU_LOADING = SUI.CreateFont("MenuLoading", "Roboto", 30)
SUI.AddToTheme("Dark", {
frame = "#181818",
scroll_panel = "#181818",
menu_tabs_title = "#ffffff",
--=--
player_list_titles = "#f2f1ef",
player_list_names = "#eeeeee",
player_list_names_2 = "#ff6347",
player_list_data = "#e8e8e8",
player_list_rank = "#41b9ff",
player_list_console = "#00c853",
player_list_rank_text = "#2c3e50",
player_list_steamid = "#a4a4a4",
--=--
--=--
actions_button = Color(0, 0, 0, 0),
actions_button_hover = Color(200, 200, 200, 60),
actions_button_icon = "#aaaaaa",
actions_button_icon_hover = "#ffffff",
--=--
--=--
page_switch_bg = "#222222",
--=--
})
SUI.SetTheme("Dark")
function SUI.panels.Frame:Paint(w, h)
if GetColor("frame_blur") then
TDLib.BlurPanel(self)
end
draw.RoundedBox(8, 0, 0, w, h, GetColor("frame"))
end
function SUI.panels.Frame:HeaderPaint(w, h)
draw.RoundedBoxEx(8, 0, 0, w, h, GetColor("header"), true, true, false, false)
draw.RoundedBox(0, 0, h - 1, w, 1, GetColor("line"))
end
do
function sam.menu.add_loading_panel(parent)
local is_loading = false
local loading_panel = parent:Add("Panel")
loading_panel:SetVisible(false)
loading_panel:SetZPos(999999)
loading_panel:SetMouseInputEnabled(false)
function loading_panel:Paint(w, h)
draw.RoundedBox(3, 0, 0, w, h, Color(50, 50, 50, 200))
draw.SimpleText(string.rep(".", (CurTime() * 3) % 3), MENU_LOADING, w/2, h/2, Color(200, 200, 200, 200), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
parent:SUI_TDLib()
parent:On("PerformLayout", function(s, w, h)
loading_panel:SetSize(w, h)
end)
local first = true
local toggle_loading = function(bool)
if not IsValid(loading_panel) then return end
is_loading = bool or not is_loading
if is_loading and not first then
loading_panel:SetVisible(is_loading and true or false)
loading_panel:SetMouseInputEnabled(is_loading)
else
timer.Simple(0.2, function()
if not IsValid(loading_panel) then return end
loading_panel:SetVisible(is_loading and true or false)
loading_panel:SetMouseInputEnabled(is_loading)
end)
end
first = false
end
return toggle_loading, function()
return is_loading
end
end
end
local sam_menu
function sam.menu.open_menu()
if IsValid(sam_menu) then
return sam_menu:IsVisible() and sam_menu:Hide() or sam_menu:Show()
-- sam_menu:Remove()
end
sam_menu = vgui.Create("SAM.Frame")
sam_menu:Center()
sam_menu:MakePopup()
sam_menu:SetTitle("SAM")
sam_menu:AddAnimations(800, 600)
sam_menu.close.DoClick = function()
sam_menu:Hide()
end
local sheet = sam_menu:Add("SAM.ColumnSheet")
sheet:Dock(FILL)
sheet:InvalidateParent(true)
sheet:InvalidateLayout(true)
sheet.Paint = nil
local tab_scroller = sheet.tab_scroller
tab_scroller:DockMargin(0, 1, 0, 1)
function tab_scroller:Paint(w, h)
draw.RoundedBoxEx(8, 0, 0, w, h, GetColor("column_sheet_bar"), false, false, true, false)
end
local sheets = {}
for _, v in SortedPairsByMemberValue(tabs, "pos") do
sheets[v.icon] = sheet:AddSheet(v.icon, v.func)
end
tab_scroller = tab_scroller:GetCanvas()
sam_menu:On("Think", function()
for _, v in ipairs(tabs) do
local tab = sheets[v.icon]
if v.check and not v.check() then
if tab:IsVisible() then
tab:SetVisible(false)
if sheet:GetActiveTab() == tab then
sheet:SetActiveTab(sheet.tabs[1])
end
tab_scroller:InvalidateLayout()
end
elseif not tab:IsVisible() then
tab:SetVisible(true)
tab_scroller:InvalidateLayout()
end
end
end)
end
function sam.menu.get()
return sam_menu
end
hook.Add("GUIMouseReleased", "SAM.CloseMenu", function(mouse_code)
local panel = vgui.GetHoveredPanel()
if mouse_code == MOUSE_LEFT and panel == vgui.GetWorldPanel() and IsValid(sam_menu) and sam_menu:HasHierarchicalFocus() then
sam_menu:Hide()
end
end)
for _, f in ipairs(file.Find("sam/menu/tabs/*.lua", "LUA")) do
sam.load_file("sam/menu/tabs/" .. f, "sh")
end

34
lua/sam/menu/sh_init.lua Normal file
View File

@@ -0,0 +1,34 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
if SAM_LOADED then return end
require("sui")
sam.command.new("menu")
:Help("Open admin mod menu")
:MenuHide()
:DisableNotify()
:OnExecute(function(ply)
sam.netstream.Start(ply, "OpenMenu")
end)
:End()
if CLIENT then
sam.netstream.Hook("OpenMenu", function()
sam.menu.open_menu()
end)
end
if SERVER then
for _, f in ipairs(file.Find("sam/menu/tabs/*.lua", "LUA")) do
sam.load_file("sam/menu/tabs/" .. f)
end
end

419
lua/sam/menu/tabs/bans.lua Normal file
View File

@@ -0,0 +1,419 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
if SAM_LOADED then return end
local sam = sam
local SQL = sam.SQL
local SUI = sam.SUI
local netstream = sam.netstream
sam.permissions.add("manage_bans", nil, "superadmin")
local get_pages_count = function(bans_count)
bans_count = bans_count / 35
local i2 = math.floor(bans_count)
return bans_count ~= i2 and i2 + 1 or bans_count
end
if SERVER then
local check = function(ply)
return ply:HasPermission("manage_bans") and ply:sam_check_cooldown("MenuManageBans", 0.1)
end
local limit = 35
local get_page_count = function(res, callback, page, order_by, keyword)
local current_time = os.time()
local query = [[
SELECT
COUNT(`steamid`) AS `count`
FROM
`sam_bans`
WHERE
(`unban_date` >= %d OR `unban_date` = 0)]]
query = query:format(current_time)
if keyword then
query = query .. " AND `steamid` LIKE " .. SQL.Escape("%" .. keyword .. "%")
end
SQL.Query(query, callback, true, {res, page, order_by, keyword, current_time})
end
local resolve_promise = function(data, arguments)
local res = arguments[1]
arguments[1] = data
res(arguments)
end
local get_bans = function(count_data, arguments)
local res, page, order_by, keyword, current_time = unpack(arguments)
local count = count_data.count
local current_page
if page < 1 then
page, current_page = 1, 1
end
local pages_count = get_pages_count(count)
if page > pages_count then
page, current_page = pages_count, pages_count
end
local query = [[
SELECT
`sam_bans`.*,
IFNULL(`p1`.`name`, '') AS `name`,
IFNULL(`p2`.`name`, '') AS `admin_name`
FROM
`sam_bans`
LEFT OUTER JOIN
`sam_players` AS `p1`
ON
`sam_bans`.`steamid` = `p1`.`steamid`
LEFT OUTER JOIN
`sam_players` AS `p2`
ON
`sam_bans`.`admin` = `p2`.`steamid`
WHERE
(`sam_bans`.`unban_date` >= %d OR `sam_bans`.`unban_date` = 0)]]
query = query:format(current_time)
if keyword then
query = query .. " AND `sam_bans`.`steamid` LIKE " .. SQL.Escape("%" .. keyword .. "%")
end
local offset = math.abs(limit * (page - 1))
query = query .. ([[
ORDER BY
`sam_bans`.`id` %s
LIMIT
%d OFFSET %d]]):format(order_by, limit, offset)
SQL.Query(query, resolve_promise, nil, {res, count, current_page})
end
netstream.async.Hook("SAM.GetBans", function(res, ply, page, order_by, keyword)
if not isnumber(page) then return end
if order_by ~= "ASC" and order_by ~= "DESC" then return end
if keyword ~= nil and not sam.isstring(keyword) then return end
get_page_count(res, get_bans, page, order_by, keyword)
end, check)
return
end
local GetColor = SUI.GetColor
local Line = sui.TDLib.LibClasses.Line
local COLUMN_FONT = SUI.CreateFont("Column", "Roboto", 18)
local LINE_FONT = SUI.CreateFont("Line", "Roboto", 16)
local NEXT_FONT = SUI.CreateFont("NextButton", "Roboto", 18)
sam.menu.add_tab("https://raw.githubusercontent.com/Srlion/Addons-Data/main/icons/sam/ban-user.png", function(column_sheet)
local refresh, pages
local current_page, current_order, keyword = nil, "DESC", nil
local bans_body = column_sheet:Add("Panel")
bans_body:Dock(FILL)
bans_body:DockMargin(0, 1, 0, 0)
bans_body:DockPadding(10, 10, 10, 10)
local toggle_loading, is_loading = sam.menu.add_loading_panel(bans_body)
local title = bans_body:Add("SAM.Label")
title:Dock(TOP)
title:SetFont(SAM_TAB_TITLE_FONT)
title:SetText("Bans")
title:SetTextColor(GetColor("menu_tabs_title"))
title:SizeToContents()
local total = bans_body:Add("SAM.Label")
total:Dock(TOP)
total:DockMargin(0, 6, 0, 0)
total:SetFont(SAM_TAB_DESC_FONT)
total:SetText("60 total bans")
total:SetTextColor(GetColor("menu_tabs_title"))
total:SetPos(10, SUI.Scale(40))
total:SizeToContents()
do
local container = bans_body:Add("SAM.Panel")
container:Dock(TOP)
container:DockMargin(0, 6, 10, 0)
container:SetTall(30)
local sort_order = container:Add("SAM.ComboBox")
sort_order:Dock(RIGHT)
sort_order:SetWide(96)
sort_order:SetValue("Desc")
sort_order:AddChoice("Desc")
sort_order:AddChoice("Asc")
function sort_order:OnSelect(_, value)
value = value:upper()
if current_order ~= value then
current_order = value
refresh()
end
end
local search_entry = container:Add("SAM.TextEntry")
search_entry:Dock(LEFT)
search_entry:SetNoBar(true)
search_entry:SetPlaceholder("Search...")
search_entry:SetRadius(4)
search_entry:SetWide(220)
function search_entry:OnEnter()
local value = self:GetValue()
if keyword ~= value then
keyword = value ~= "" and value or nil
refresh()
end
end
end
Line(bans_body, nil, -5, 15, -5, 0)
do
local columns = bans_body:Add("Panel")
columns:Dock(TOP)
columns:DockMargin(0, 10, 0, 0)
local info = columns:Add("SAM.Label")
info:Dock(LEFT)
info:DockMargin(4, 0, 0, 0)
info:SetFont(COLUMN_FONT)
info:SetText("Player")
info:SetTextColor(GetColor("player_list_titles"))
info:SetWide(SUI.Scale(280) + SUI.Scale(34))
info:SizeToContentsY(3)
local time_left = columns:Add("SAM.Label")
time_left:Dock(LEFT)
time_left:DockMargin(-4, 0, 0, 0)
time_left:SetFont(COLUMN_FONT)
time_left:SetText("Time Left")
time_left:SetTextColor(GetColor("player_list_titles"))
time_left:SetWide(SUI.Scale(180))
time_left:SizeToContentsY(3)
local reason = columns:Add("SAM.Label")
reason:Dock(LEFT)
reason:DockMargin(-4, 0, 0, 0)
reason:SetFont(COLUMN_FONT)
reason:SetText("Reason")
reason:SetTextColor(GetColor("player_list_titles"))
reason:SetWide(SUI.Scale(280))
reason:SizeToContentsY(3)
columns:SizeToChildren(false, true)
end
local body = bans_body:Add("SAM.ScrollPanel")
body:Dock(FILL)
body:DockMargin(0, 10, 0, 0)
body:SetVBarPadding(6)
local set_data = function(data)
body:GetCanvas():Clear()
body.VBar.Scroll = 0
local bans, bans_count, current_page_2 = unpack(data)
total:SetText(bans_count .. " total bans")
pages = get_pages_count(bans_count)
current_page.i = pages == 0 and 0 or current_page_2 or current_page.i
current_page:SetText(current_page.i .. "/" .. pages)
body:Line()
for k, v in ipairs(bans) do
local line = body:Add("SAM.PlayerLine")
line:DockMargin(0, 0, 0, 10)
local name = v.name ~= "" and v.name or nil
local admin_name = v.admin_name ~= "" and v.admin_name or nil
line:SetInfo({
steamid = v.steamid,
name = name,
rank = admin_name or (v.admin == "Console" and "Console"),
rank_bg = not admin_name and GetColor("player_list_console")
})
local unban_date = tonumber(v.unban_date)
local time_left = line:Add("SAM.Label")
time_left:Dock(LEFT)
time_left:DockMargin(-3, 0, 0, 0)
time_left:SetFont(LINE_FONT)
time_left:SetText(unban_date == 0 and "Never" or sam.reverse_parse_length((unban_date - os.time()) / 60))
time_left:SetTextColor(GetColor("player_list_data"))
time_left:SetContentAlignment(4)
time_left:SetWide(SUI.Scale(180))
local reason = line:Add("SAM.Label")
reason:Dock(LEFT)
reason:DockMargin(4, 0, 0, 0)
reason:SetFont(LINE_FONT)
reason:SetText(v.reason)
reason:SetTextColor(GetColor("player_list_data"))
reason:SetContentAlignment(4)
reason:SetWrap(true)
reason:SetWide(SUI.Scale(200))
local old_tall = line.size
function reason:PerformLayout()
local _, h = self:GetTextSize()
if old_tall < h then
line:SetTall(h)
end
end
local but = line:Actions()
but:On("DoClick", function()
local dmenu = vgui.Create("SAM.Menu")
dmenu:SetInternal(but)
if name then
dmenu:AddOption("Copy Name", function()
SetClipboardText(name)
end)
end
dmenu:AddOption("Copy SteamID", function()
SetClipboardText(v.steamid)
end)
dmenu:AddOption("Copy Reason", function()
SetClipboardText(v.reason)
end)
dmenu:AddOption("Copy Time Left", function()
SetClipboardText(time_left:GetText())
end)
if v.admin ~= "Console" then
dmenu:AddSpacer()
if admin_name then
dmenu:AddOption("Copy Admin Name", function()
SetClipboardText(admin_name)
end)
end
dmenu:AddOption("Copy Admin SteamID", function()
SetClipboardText(v.admin)
end)
end
if LocalPlayer():HasPermission("unban") then
dmenu:AddSpacer()
dmenu:AddOption("Unban", function()
local user = name and ("%s (%s)"):format(name, v.steamid) or v.steamid
local querybox = vgui.Create("SAM.QueryBox")
querybox:SetWide(350)
querybox:SetTitle(user)
local check = querybox:Add("SAM.Label")
check:SetText(sui.wrap_text("Are you sure that you want to unban\n" .. user, LINE_FONT, SUI.Scale(350)))
check:SetFont(LINE_FONT)
check:SizeToContents()
querybox:Done()
querybox.save:SetEnabled(true)
querybox.save:SetText("UNBAN")
querybox.save:SetContained(false)
querybox.save:SetColors(GetColor("query_box_cancel"), GetColor("query_box_cancel_text"))
querybox.cancel:SetContained(true)
querybox.cancel:SetColors()
querybox:SetCallback(function()
RunConsoleCommand("sam", "unban", v.steamid)
end)
end)
end
dmenu:Open()
end)
body:Line()
end
body:InvalidateLayout(true)
body:GetCanvas():InvalidateLayout(true)
end
refresh = function()
if not is_loading() and LocalPlayer():HasPermission("manage_bans") then
local refresh_query = netstream.async.Start("SAM.GetBans", toggle_loading, current_page.i, current_order, keyword)
refresh_query:done(set_data)
end
end
local bottom_panel = bans_body:Add("SAM.Panel")
bottom_panel:Dock(BOTTOM)
bottom_panel:DockMargin(0, 6, 0, 0)
bottom_panel:SetTall(30)
bottom_panel:Background(GetColor("page_switch_bg"))
local previous_page = bottom_panel:Add("SAM.Button")
previous_page:Dock(LEFT)
previous_page:SetWide(30)
previous_page:SetText("<")
previous_page:SetFont(NEXT_FONT)
previous_page:On("DoClick", function()
if current_page.i <= 1 then return end
current_page.i = current_page.i - 1
refresh()
end)
current_page = bottom_panel:Add("SAM.Label")
current_page:Dock(FILL)
current_page:SetContentAlignment(5)
current_page:SetFont(SAM_TAB_DESC_FONT)
current_page:SetText("loading...")
current_page.i = 1
local next_page = bottom_panel:Add("SAM.Button")
next_page:Dock(RIGHT)
next_page:SetWide(30)
next_page:SetText(">")
next_page:SetFont(NEXT_FONT)
next_page:On("DoClick", function()
if current_page.i == pages then return end
current_page.i = current_page.i + 1
refresh()
end)
function bottom_panel:Think()
next_page:SetEnabled(current_page.i ~= pages)
previous_page:SetEnabled(current_page.i > 1)
end
for k, v in ipairs({"SAM.BannedPlayer", "SAM.BannedSteamID", "SAM.EditedBan", "SAM.UnbannedSteamID"}) do
hook.Add(v, "SAM.MenuBans", function()
if IsValid(body) then
refresh()
end
end)
end
refresh()
return bans_body
end, function()
return LocalPlayer():HasPermission("manage_bans")
end, 4)

View File

@@ -0,0 +1,233 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
if SAM_LOADED then return end
if SERVER then return end
local sam = sam
local SUI = sam.SUI
local type = sam.type
local Line = sui.TDLib.LibClasses.Line
local COMMAND_HELP = SUI.CreateFont("CommandHelp", "Roboto", 14)
local COMMAND_RUN = SUI.CreateFont("CommandRun", "Roboto Medium", 14)
sam.menu.add_tab("https://raw.githubusercontent.com/Srlion/Addons-Data/main/icons/sam/command_window.png", function(column_sheet)
local tab_body = column_sheet:Add("Panel")
tab_body:Dock(FILL)
tab_body:DockMargin(0, 1, 0, 0)
do
local title = tab_body:Add("SAM.Label")
title:Dock(TOP)
title:DockMargin(10, 10, 0, 0)
title:SetFont(SAM_TAB_TITLE_FONT)
title:SetText("Commands")
title:SetTextColor(SUI.GetColor("menu_tabs_title"))
title:SizeToContents()
end
local body = tab_body:Add("Panel")
body:Dock(FILL)
body:DockMargin(10, 5, 10, 10)
Line(body)
local left_body = body:Add("SAM.Panel")
left_body:Dock(LEFT)
left_body:SetWide(148)
local search_entry = left_body:Add("SAM.TextEntry")
search_entry:Dock(TOP)
search_entry:SetNoBar(true)
search_entry:SetPlaceholder("Search...")
search_entry:SetRadius(4)
search_entry:SetTall(27)
local category_list = left_body:Add("SAM.CollapseCategory")
category_list:Dock(FILL)
category_list:DockMargin(0, SUI.Scale(10), 0, 0)
local canvas = category_list:GetCanvas()
local commands_refresh = function()
if not IsValid(category_list) then return end
canvas:Clear()
table.Empty(category_list.items)
table.Empty(category_list.categories)
for k, v in ipairs(sam.command.get_commands()) do
if (v.permission and not LocalPlayer():HasPermission(v.permission)) or v.menu_hide then
continue
end
local item = category_list:add_item(v.name, v.category)
item:InvalidateParent(true)
item.help = v.help
item.command = v
item.names = {v.name:lower()}
for _, aliase in ipairs(v.aliases) do
table.insert(item.names, aliase:lower())
end
end
end
commands_refresh()
do
local hooks = {
"SAM.CommandAdded", "SAM.CommandModified", "SAM.CommandRemoved",
"SAM.RemovedPermission",
{"SAM.ChangedPlayerRank", func = function(ply, rank, old_rank)
if rank == old_rank then return end
if ply == LocalPlayer() then
commands_refresh()
end
end},
{
"SAM.RankPermissionGiven", "SAM.RankPermissionTaken", "SAM.ChangedInheritRank",
func = function(rank)
if rank == LocalPlayer():sam_getrank() then
commands_refresh()
end
end
},
{
"SAM.AddedPermission", "SAM.PermissionModified",
func = function(_, _, rank)
if rank == LocalPlayer():sam_getrank() then
commands_refresh()
end
end
}
}
for _, v in ipairs(hooks) do
if type(v) == "table" then
for _, v2 in ipairs(v) do
hook.Add(v2, "SAM.Menu.RefreshCommands", v.func)
end
else
hook.Add(v, "SAM.Menu.RefreshCommands", commands_refresh)
end
end
end
function search_entry:OnValueChange(text)
category_list:Search(text:lower())
end
do
local line = Line(body, LEFT)
line:DockMargin(10, 0, 10, 0)
line:SetWide(1)
end
local buttons = body:Add("SAM.ScrollPanel")
buttons:Dock(FILL)
local childs = {}
local pos = 0
buttons:GetCanvas():On("OnChildAdded", function(s, child)
child:Dock(TOP)
child:DockMargin(0, 0, 0, 5)
child:SetAlpha(0)
child:SetVisible(false)
table.insert(childs, child)
pos = pos + 1
child:SetZPos(pos)
end)
local run_command = buttons:Add("SAM.Button")
run_command:Dock(TOP)
run_command:SetTall(25)
run_command:SetFont(COMMAND_RUN)
run_command:SetZPos(100)
run_command:SetEnabled(false)
run_command:On("DoClick", function(self)
LocalPlayer():ConCommand("sam\"" .. self:GetText() .. "\"\"" .. table.concat(self.input_arguments, "\"\"") .. "\"")
end)
local help = buttons:Add("SAM.Label")
help:Dock(TOP)
help:SetFont(COMMAND_HELP)
help:SetZPos(101)
help:SetWrap(true)
help:SetAutoStretchVertical(true)
sam.menu.get():On("OnKeyCodePressed", function(s, key_code)
if key_code == KEY_ENTER and IsValid(run_command) and run_command:IsEnabled() and run_command:IsMouseInputEnabled() and tab_body:IsVisible() then
run_command:DoClick()
end
end)
function category_list:item_selected(item)
local arguments = sam.command.get_arguments()
local command = item.command
local command_arguments = command.args
local input_arguments = {}
for i = #childs, 3, -1 do
local v = childs[i]
if not v.no_change or not command:HasArg(v.no_change) then
if v.no_remove ~= true then
v:Remove()
else
v:Hide()
end
end
end
local NIL = {}
for _, v in ipairs(command_arguments) do
local func = arguments[v.name]["menu"]
if not func then continue end
local i = table.insert(input_arguments, NIL)
local p = func(function(allow)
if not IsValid(run_command) then return end
input_arguments[i] = allow == nil and NIL or allow
for i_2 = 1, #input_arguments do
if input_arguments[i_2] == NIL then
run_command:SetEnabled(false)
return
end
end
run_command:SetEnabled(true)
end, body, buttons, v, childs)
if p then
p:AnimatedSetVisible(true)
end
end
if #command_arguments == 0 then
run_command:SetEnabled(true)
end
run_command:SetText(command.name)
run_command:AnimatedSetVisible(true)
run_command.input_arguments = input_arguments
if command.help then
help:SetText(command.help)
help:AnimatedSetVisible(true)
help:SizeToContents()
else
help:AnimatedSetVisible(false)
end
buttons:InvalidateLayout(true)
end
return tab_body
end, nil, 1)

View File

@@ -0,0 +1,107 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
if SAM_LOADED then return end
local sam = sam
local config = sam.config
local tabs = {}
if CLIENT then
function config.add_tab(name, func, check, pos)
local tab = {
name = name,
func = func,
check = check,
pos = pos
}
for k, v in ipairs(tabs) do
if v.name == name then
tabs[k] = tab
return
end
end
table.insert(tabs, tab)
end
end
for _, f in ipairs(file.Find("sam/menu/tabs/config/*.lua", "LUA")) do
sam.load_file("sam/menu/tabs/config/" .. f, "cl_")
end
if SERVER then return end
local SUI = sam.SUI
local GetColor = SUI.GetColor
local Line = sui.TDLib.LibClasses.Line
sam.menu.add_tab("https://raw.githubusercontent.com/Srlion/Addons-Data/main/icons/sam/config.png", function(column_sheet)
local tab_body = column_sheet:Add("Panel")
tab_body:Dock(FILL)
tab_body:DockMargin(0, 1, 0, 0)
do
local title = tab_body:Add("SAM.Label")
title:Dock(TOP)
title:DockMargin(10, 10, 0, 0)
title:SetFont(SAM_TAB_TITLE_FONT)
title:SetText("Config")
title:SetTextColor(GetColor("menu_tabs_title"))
title:SizeToContents()
local total = tab_body:Add("SAM.Label")
total:Dock(TOP)
total:DockMargin(10, 6, 0, 0)
total:SetFont(SAM_TAB_DESC_FONT)
total:SetText("Some settings may require a server restart")
total:SetTextColor(GetColor("menu_tabs_title"))
total:SetPos(10, SUI.Scale(40))
total:SizeToContents()
end
local body = tab_body:Add("Panel")
body:Dock(FILL)
body:DockMargin(10, 5, 10, 10)
Line(body, nil, 0, 0, 0, 10)
local sheet = body:Add("SAM.PropertySheet")
sheet:Dock(FILL)
sheet:InvalidateParent(true)
sheet:InvalidateLayout(true)
local sheets = {}
for _, v in SortedPairsByMemberValue(tabs, "pos") do
sheets[v.name] = sheet:AddSheet(v.name, v.func)
end
local tab_scroller = sheet.tab_scroller:GetCanvas()
function tab_body.Think()
for _, v in ipairs(tabs) do
local tab = sheets[v.name]
if v.check and not v.check() then
if tab:IsVisible() then
tab:SetVisible(false)
if sheet:GetActiveTab() == tab then
sheet:SetActiveTab(sheet.tabs[1])
end
tab_scroller:InvalidateLayout()
end
elseif not tab:IsVisible() then
tab:SetVisible(true)
tab_scroller:InvalidateLayout()
end
end
end
return tab_body
end, function()
return LocalPlayer():HasPermission("manage_config")
end, 5)

View File

@@ -0,0 +1,171 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
if SAM_LOADED then return end
local sam = sam
local config = sam.config
local not_empty = function(s)
return s and s ~= ""
end
local number_entry = function(setting, config_key, default)
local entry = setting:Add("SAM.TextEntry")
entry:SetWide(50)
entry:SetPlaceholder("")
entry:SetBackground(Color(34, 34, 34))
entry:SetNumeric(true)
entry:DisallowFloats()
entry:DisallowNegative()
entry:SetCheck(not_empty)
entry:SetConfig(config_key, default)
return entry
end
config.add_tab("Reports", function(parent)
local body = parent:Add("SAM.ScrollPanel")
body:Dock(FILL)
body:LineMargin(0, 6, 0, 0)
local i = 0
body:GetCanvas():On("OnChildAdded", function(s, child)
i = i + 1
child:SetZPos(i)
if not body.making_line then
body:Line()
end
end)
do
local setting = body:Add("SAM.LabelPanel")
setting:Dock(TOP)
setting:SetLabel("Enable")
setting:DockMargin(8, 6, 8, 0)
local enable = setting:Add("SAM.ToggleButton")
enable:SetConfig("Reports", true)
end
do
local setting = body:Add("SAM.LabelPanel")
setting:Dock(TOP)
setting:SetLabel("Commands")
setting:DockMargin(8, 6, 8, 0)
local entry = setting:Add("SAM.TextEntry")
entry:SetWide(200)
entry:SetNoBar(true)
entry:SetPlaceholder("")
entry:SetMultiline(true)
entry:SetConfig("Reports.Commands")
entry.no_scale = true
function entry:OnValueChange()
self:SetTall(self:GetNumLines() * (sam.SUI.Scale(16) --[[font size]] + 1) + 1 + 2)
end
entry:OnValueChange()
end
do
local setting = body:Add("SAM.LabelPanel")
setting:Dock(TOP)
setting:SetLabel("Max Reports (Number of reports that can show on your screen)")
setting:DockMargin(8, 6, 8, 0)
number_entry(setting, "Reports.MaxReports", 4)
end
do
local setting = body:Add("SAM.LabelPanel")
setting:Dock(TOP)
setting:SetLabel("Auto Close Time (Time to wait before automatically closing claimed reports)")
setting:DockMargin(8, 6, 8, 0)
local entry = setting:Add("SAM.TextEntry")
entry:SetWide(70)
entry:SetNoBar(false)
entry:SetPlaceholder("")
entry:SetCheck(function(time)
time = sam.parse_length(time)
if not time then
return false
end
end)
entry:SetConfig("Reports.AutoCloseTime", "10m")
end
do
local setting = body:Add("SAM.LabelPanel")
setting:Dock(TOP)
setting:SetLabel("Always Show (Show the popups even if you are not on duty)")
setting:DockMargin(8, 6, 8, 0)
local enable = setting:Add("SAM.ToggleButton")
enable:SetConfig("Reports.AlwaysShow", true)
end
do
local setting = body:Add("SAM.LabelPanel")
setting:Dock(TOP)
setting:SetLabel("On Duty Jobs")
setting:DockMargin(8, 6, 8, 0)
local entry = setting:Add("SAM.TextEntry")
entry:SetWide(300)
entry:SetNoBar(true)
entry:SetPlaceholder("")
entry:SetMultiline(true)
entry:SetConfig("Reports.DutyJobs", "")
entry.no_scale = true
function entry:OnValueChange()
self:SetTall(self:GetNumLines() * (sam.SUI.Scale(16) --[[font size]] + 1) + 1 + 2)
end
entry:OnValueChange()
end
do
local setting = body:Add("SAM.LabelPanel")
setting:Dock(TOP)
setting:SetLabel("Position")
setting:DockMargin(8, 6, 8, 0)
local combo = setting:Add("SAM.ComboBox")
combo:SetWide(60)
combo:AddChoice("Left", nil, true)
combo:AddChoice("Right")
combo:SetConfig("Reports.Position", "Left")
end
do
local setting = body:Add("SAM.LabelPanel")
setting:Dock(TOP)
setting:SetLabel("X Padding")
setting:DockMargin(8, 6, 8, 0)
number_entry(setting, "Reports.XPadding", 5)
end
do
local setting = body:Add("SAM.LabelPanel")
setting:Dock(TOP)
setting:SetLabel("Y Padding")
setting:DockMargin(8, 6, 8, 0)
number_entry(setting, "Reports.YPadding", 5)
end
return body
end, function()
return LocalPlayer():HasPermission("manage_config")
end, 2)

View File

@@ -0,0 +1,42 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
if SAM_LOADED then return end
local sam = sam
local config = sam.config
config.add_tab("Server", function(parent)
local server_body = parent:Add("SAM.ScrollPanel")
server_body:Dock(FILL)
server_body:LineMargin(0, 6, 0, 0)
local i = 0
server_body:GetCanvas():On("OnChildAdded", function(s, child)
i = i + 1
child:SetZPos(i)
end)
for k, v in ipairs(sam.config.get_menu_settings()) do
local panel = v.func(server_body)
if ispanel(panel) then
local setting = server_body:Add("SAM.LabelPanel")
setting:DockMargin(8, 6, 8, 0)
setting:SetLabel(v.title)
setting:SetPanel(panel)
end
server_body:Line()
end
return server_body
end, function()
return LocalPlayer():HasPermission("manage_config")
end, 1)

View File

@@ -0,0 +1,462 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
if SAM_LOADED then return end
local sam = sam
local SQL = sam.SQL
local SUI = sam.SUI
local netstream = sam.netstream
sam.permissions.add("manage_players", nil, "superadmin")
local get_pages_count = function(count)
count = count / 35
local i2 = math.floor(count)
return count ~= i2 and i2 + 1 or count
end
if SERVER then
local check = function(ply)
return ply:HasPermission("manage_players") and ply:sam_check_cooldown("MenuViewPlayers", 0.1)
end
local limit = 35
local get_page_count = function(callback, res, page, column, order_by, sort_by, keyword)
local query = [[
SELECT
COUNT(`steamid`) AS `count`
FROM
`sam_players`]]
if keyword then
if column == "steamid" and sam.is_steamid64(keyword) then
keyword = util.SteamIDFrom64(keyword)
end
query = string.format("%s WHERE `%s` LIKE %s", query, column, SQL.Escape("%" .. keyword .. "%"))
end
SQL.Query(query, callback, true, {res, page, column, order_by, sort_by, keyword})
end
local valid_columns = {
steamid = true,
name = true,
rank = true
}
local valid_sorts = {
id = true,
name = true,
rank = true,
play_time = true,
last_join = true
}
local resolve_promise = function(data, arguments)
local res = arguments[1]
arguments[1] = data
res(arguments)
end
local get_players = function(count_data, arguments)
local res, page, column, order_by, sort_by, keyword = unpack(arguments)
local count = count_data.count
local current_page
if page < 1 then
page, current_page = 1, 1
end
local pages_count = get_pages_count(count)
if page > pages_count then
page, current_page = pages_count, pages_count
end
local query = [[
SELECT
`steamid`,
`name`,
`rank`,
`expiry_date`,
`first_join`,
`last_join`,
`play_time`
FROM
`sam_players`
]]
local args = {}
if keyword then
args[1] = column
args[2] = "%" .. keyword .. "%"
query = query .. [[
WHERE
`{1f}` LIKE {2}
]]
end
args[3] = sort_by
if order_by == "DESC" then
query = query .. [[
ORDER BY `{3f}` DESC
]]
else
query = query .. [[
ORDER BY `{3f}` ASC
]]
end
args[4] = limit
args[5] = math.abs(limit * (page - 1))
query = query .. [[
LIMIT {4} OFFSET {5}
]]
SQL.FQuery(query, args, resolve_promise, false, {res, count, current_page})
end
netstream.async.Hook("SAM.GetPlayers", function(res, ply, page, column, order_by, sort_by, keyword)
if not isnumber(page) then return end
if not valid_columns[column] then return end
if order_by ~= "ASC" and order_by ~= "DESC" then return end
if not valid_sorts[sort_by] then return end
if keyword ~= nil and not sam.isstring(keyword) then return end
get_page_count(get_players, res, page, column, order_by, sort_by, keyword)
end, check)
return
end
local GetColor = SUI.GetColor
local Line = sui.TDLib.LibClasses.Line
local COLUMN_FONT = SUI.CreateFont("Column", "Roboto", 18)
local LINE_FONT = SUI.CreateFont("Line", "Roboto", 16)
local NEXT_FONT = SUI.CreateFont("NextButton", "Roboto", 18)
local button_click = function(s)
local v = s.v
local dmenu = vgui.Create("SAM.Menu")
dmenu:SetInternal(s)
if v.name and v.name ~= "" then
dmenu:AddOption("Copy Name", function()
SetClipboardText(v.name)
end)
end
dmenu:AddOption("Copy SteamID", function()
SetClipboardText(v.steamid)
end)
dmenu:AddOption("Copy Rank", function()
SetClipboardText(v.rank)
end)
dmenu:AddOption("Copy Play Time", function()
SetClipboardText(sam.reverse_parse_length(tonumber(v.play_time) / 60))
end)
dmenu:AddSpacer()
dmenu:AddOption("Change Rank", function()
local querybox = vgui.Create("SAM.QueryBox")
querybox:SetTitle(string.format("Change rank for '%s'", v.name or v.steamid))
querybox:SetWide(360)
local ranks = querybox:Add("SAM.ComboBox")
ranks:SetTall(28)
for rank_name in SortedPairsByMemberValue(sam.ranks.get_ranks(), "immunity", true) do
if v.rank ~= rank_name then
ranks:AddChoice(rank_name, nil, true)
end
end
querybox:Done()
querybox.save:SetEnabled(true)
querybox:SetCallback(function()
RunConsoleCommand("sam", "setrankid", v.steamid, ranks:GetValue())
end)
end)
dmenu:Open()
end
sam.menu.add_tab("https://raw.githubusercontent.com/Srlion/Addons-Data/main/icons/sam/user.png", function(column_sheet)
local refresh, pages
local current_page, current_column, current_order, current_sort, keyword = nil, "steamid", "DESC", "id", nil
local players_body = column_sheet:Add("Panel")
players_body:Dock(FILL)
players_body:DockMargin(0, 1, 0, 0)
players_body:DockPadding(10, 10, 10, 10)
local toggle_loading, is_loading = sam.menu.add_loading_panel(players_body)
local title = players_body:Add("SAM.Label")
title:Dock(TOP)
title:SetFont(SAM_TAB_TITLE_FONT)
title:SetText("Players")
title:SetTextColor(GetColor("menu_tabs_title"))
title:SizeToContents()
local total = players_body:Add("SAM.Label")
total:Dock(TOP)
total:DockMargin(0, 6, 0, 0)
total:SetFont(SAM_TAB_DESC_FONT)
total:SetText("60 total players")
total:SetTextColor(GetColor("menu_tabs_title"))
total:SetPos(10, SUI.Scale(40))
total:SizeToContents()
local search_entry
do
local container = players_body:Add("SAM.Panel")
container:Dock(TOP)
container:DockMargin(0, 6, 10, 0)
container:SetTall(30)
local sort_by = container:Add("SAM.ComboBox")
sort_by:Dock(RIGHT)
sort_by:DockMargin(4, 0, 0, 0)
sort_by:SetWide(106)
sort_by:SetValue("Sort By (ID)")
sort_by:AddChoice("ID")
sort_by:AddChoice("Name")
sort_by:AddChoice("Rank")
sort_by:AddChoice("Play Time")
function sort_by:OnSelect(_, value)
value = value:lower():gsub(" ", "_")
if current_sort ~= value then
current_sort = value
refresh()
end
end
local sort_order = container:Add("SAM.ComboBox")
sort_order:Dock(RIGHT)
sort_order:SetWide(96)
sort_order:SetValue("Desc")
sort_order:AddChoice("Desc")
sort_order:AddChoice("Asc")
function sort_order:OnSelect(_, value)
value = value:upper()
if current_order ~= value then
current_order = value
refresh()
end
end
local column = container:Add("SAM.ComboBox")
column:Dock(RIGHT)
column:DockMargin(0, 0, 4, 0)
column:SetWide(140)
column:SetValue("Search (SteamID)")
column:AddChoice("SteamID")
column:AddChoice("Name")
column:AddChoice("Rank")
function column:OnSelect(_, value)
value = value:lower()
if current_column ~= value then
current_column = value
refresh()
end
end
search_entry = container:Add("SAM.TextEntry")
search_entry:Dock(LEFT)
search_entry:SetNoBar(true)
search_entry:SetPlaceholder("Search...")
search_entry:SetRadius(4)
search_entry:SetWide(220)
function search_entry:OnEnter(no_refresh)
local value = self:GetValue()
if keyword ~= value then
keyword = value ~= "" and value or nil
if not no_refresh then
refresh()
end
end
end
end
Line(players_body, nil, -5, SUI.Scale(15), -5, 0)
do
local columns = players_body:Add("Panel")
columns:Dock(TOP)
columns:DockMargin(0, 10, 0, 0)
local info = columns:Add("SAM.Label")
info:Dock(LEFT)
info:DockMargin(4, 0, 0, 0)
info:SetFont(COLUMN_FONT)
info:SetText("Player")
info:SetTextColor(GetColor("player_list_titles"))
info:SetWide(SUI.Scale(280) + SUI.Scale(34))
info:SizeToContentsY(3)
local play_time = columns:Add("SAM.Label")
play_time:Dock(LEFT)
play_time:DockMargin(-4, 0, 0, 0)
play_time:SetFont(COLUMN_FONT)
play_time:SetText("Play Time")
play_time:SetTextColor(GetColor("player_list_titles"))
play_time:SetWide(SUI.Scale(180))
play_time:SizeToContentsY(3)
local rank_expiry = columns:Add("SAM.Label")
rank_expiry:Dock(LEFT)
rank_expiry:DockMargin(-4, 0, 0, 0)
rank_expiry:SetFont(COLUMN_FONT)
rank_expiry:SetText("Rank Expiry")
rank_expiry:SetTextColor(GetColor("player_list_titles"))
rank_expiry:SetWide(SUI.Scale(280))
rank_expiry:SizeToContentsY(3)
columns:SizeToChildren(false, true)
end
local body = players_body:Add("SAM.ScrollPanel")
body:Dock(FILL)
body:DockMargin(0, 10, 0, 0)
body:SetVBarPadding(6)
local set_data = function(data)
body:GetCanvas():Clear()
body.VBar.Scroll = 0
local players, players_count, current_page_2 = unpack(data)
total:SetText(players_count .. " total players")
pages = get_pages_count(players_count)
current_page.i = pages == 0 and 0 or current_page_2 or current_page.i
current_page:SetText(current_page.i .. "/" .. pages)
body:Line()
for k, v in ipairs(players) do
local line = body:Add("SAM.PlayerLine")
line:DockMargin(0, 0, 0, 10)
local name = v.name ~= "" and v.name or nil
line:SetInfo({
steamid = v.steamid,
name = name,
rank = v.rank
})
local play_time = line:Add("SAM.Label")
play_time:Dock(LEFT)
play_time:DockMargin(4, 0, 0, 0)
play_time:SetFont(LINE_FONT)
play_time:SetText(sam.reverse_parse_length(tonumber(v.play_time) / 60))
play_time:SetTextColor(GetColor("player_list_data"))
play_time:SetContentAlignment(4)
play_time:SetWide(SUI.Scale(180))
local expiry_date = tonumber(v.expiry_date)
local rank_expiry = line:Add("SAM.Label")
rank_expiry:Dock(LEFT)
rank_expiry:DockMargin(-3, 0, 0, 0)
rank_expiry:SetFont(LINE_FONT)
rank_expiry:SetText(expiry_date == 0 and "Never" or sam.reverse_parse_length((expiry_date - os.time()) / 60))
rank_expiry:SetTextColor(GetColor("player_list_data"))
rank_expiry:SetContentAlignment(4)
rank_expiry:SizeToContents()
local but = line:Actions()
but.v = v
but:On("DoClick", button_click)
body:Line()
end
end
refresh = function()
if not is_loading() and LocalPlayer():HasPermission("manage_players") then
search_entry:OnEnter(true)
local refresh_query = netstream.async.Start("SAM.GetPlayers", toggle_loading, current_page.i, current_column, current_order, current_sort, keyword)
refresh_query:done(set_data)
end
end
local bottom_panel = players_body:Add("SAM.Panel")
bottom_panel:Dock(BOTTOM)
bottom_panel:DockMargin(0, 6, 0, 0)
bottom_panel:SetTall(30)
bottom_panel:Background(GetColor("page_switch_bg"))
local previous_page = bottom_panel:Add("SAM.Button")
previous_page:Dock(LEFT)
previous_page:SetWide(30)
previous_page:SetText("<")
previous_page:SetFont(NEXT_FONT)
previous_page:On("DoClick", function()
if current_page.i <= 1 then return end
current_page.i = current_page.i - 1
refresh()
end)
current_page = bottom_panel:Add("SAM.Label")
current_page:Dock(FILL)
current_page:SetContentAlignment(5)
current_page:SetFont(SAM_TAB_DESC_FONT)
current_page:SetText("loading...")
current_page.i = 1
local next_page = bottom_panel:Add("SAM.Button")
next_page:Dock(RIGHT)
next_page:SetWide(30)
next_page:SetText(">")
next_page:SetFont(NEXT_FONT)
next_page:On("DoClick", function()
if current_page.i == pages then return end
current_page.i = current_page.i + 1
refresh()
end)
function bottom_panel:Think()
next_page:SetEnabled(current_page.i ~= pages)
previous_page:SetEnabled(current_page.i > 1)
end
do
local refresh_2 = function()
timer.Simple(1, refresh)
end
for k, v in ipairs({"SAM.AuthedPlayer", "SAM.ChangedPlayerRank", "SAM.ChangedSteamIDRank"}) do
hook.Add(v, "SAM.MenuPlayers", refresh_2)
end
end
refresh()
return players_body
end, function()
return LocalPlayer():HasPermission("manage_players")
end, 2)

637
lua/sam/menu/tabs/ranks.lua Normal file
View File

@@ -0,0 +1,637 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
if SAM_LOADED then return end
if SERVER then return end
local sam = sam
local SUI = sam.SUI
local GetColor = SUI.GetColor
local Line = sui.TDLib.LibClasses.Line
local AnimatedSetVisible = sui.TDLib.LibClasses.AnimatedSetVisible
local RANK_NAME = SUI.CreateFont("RankName", "Roboto Bold", 18)
local RANK_INFO = SUI.CreateFont("RankInfo", "Roboto Medium", 12)
local CREATE_RANK = SUI.CreateFont("CreateRank", "Roboto Bold", 16, 200)
local RANK_TITLE = SUI.CreateFont("RankTitle", "Roboto Bold", 20)
local rank_menu = function(rank, data)
local valid = sui.valid_options()
local imm, banlim
if rank then
imm, banlim = data.immunity, data.ban_limit
end
local edit_rank = vgui.Create("SAM.QueryBox")
edit_rank:SetWide(470)
edit_rank:SetTitle(rank and string.format("Edit Rank '%s'", rank) or "Create Rank")
local new_name = rank
if not sam.ranks.is_default_rank(rank) then
local name = edit_rank:Add("SAM.LabelPanel")
name:SetLabel("Rank Name")
local entry = name:Add("SAM.TextEntry")
entry:SetSize(210, 28)
entry:SetNoBar(false)
entry:SetPlaceholder("")
entry:SetValue(rank or "")
entry:SetCheck(function(_name)
new_name = _name
if _name == rank then return end
if _name == "" or sam.ranks.is_rank(_name) then
return false
end
end)
valid.Add(entry)
end
local new_immunity = imm
do
local immunity = edit_rank:Add("SAM.LabelPanel")
immunity:SetLabel("Immunity (2~99)")
immunity:DockMargin(0, 5, 0, 0)
local entry = immunity:Add("SAM.TextEntry")
entry:SetSize(210, 28)
entry:SetNumeric(true)
entry:DisallowFloats(true)
entry:DisallowNegative(true)
entry:SetPlaceholder("")
entry:SetValue(imm or "2")
entry:SetCheck(function(_immunity)
new_immunity = _immunity
if _immunity == "" then
return false
end
_immunity = tonumber(_immunity)
new_immunity = _immunity
if _immunity < 2 or _immunity > 99 then
return false
end
end)
valid.Add(entry)
end
local new_banlimit = banlim
do
local banlimit = edit_rank:Add("SAM.LabelPanel")
banlimit:SetLabel("Ban Limit (1y 1mo 1w 1d 1h 1m)")
banlimit:DockMargin(0, 5, 0, 0)
local entry = banlimit:Add("SAM.TextEntry")
entry:SetSize(210, 28)
entry:SetNoBar(false)
entry:SetPlaceholder("")
entry:SetValue(banlim and sam.reverse_parse_length(banlim) or "2w")
entry:SetCheck(function(_banlimit)
new_banlimit = sam.parse_length(_banlimit)
if not new_banlimit and _banlimit ~= banlim then
return false
end
end)
valid.Add(entry)
end
local inherit = rank and sam.ranks.get_rank(rank).inherit or "user"
local new_inherit = inherit
do
local inherits_from = edit_rank:Add("SAM.LabelPanel")
inherits_from:SetLabel("Inherits From")
inherits_from:DockMargin(0, 5, 0, 0)
local entry = inherits_from:Add("SAM.ComboBox")
entry:SetSize(210, 28)
entry:SetValue(inherit)
for name in SortedPairsByMemberValue(sam.ranks.get_ranks(), "immunity", true) do
if name ~= rank and not sam.ranks.inherits_from(name, rank) then
entry:AddChoice(name)
end
end
function entry:OnSelect(_, value)
new_inherit = value
end
end
edit_rank:Done()
edit_rank.save:SetEnabled(true)
edit_rank.save:SetText("SAVE")
if rank then
edit_rank:SetCallback(function()
local to_run = {}
if new_immunity ~= imm then
table.insert(to_run, {"changerankimmunity", rank, new_immunity})
end
if new_banlimit ~= banlim then
table.insert(to_run, {"changerankbanlimit", rank, new_banlimit})
end
if new_inherit ~= inherit then
table.insert(to_run, {"changeinherit", rank, new_inherit})
end
if new_name ~= rank then
table.insert(to_run, {"renamerank", rank, new_name})
end
sam.command.run_commands(to_run)
end)
else
edit_rank:SetCallback(function()
RunConsoleCommand("sam", "addrank", new_name, new_inherit, new_immunity, new_banlimit)
end)
end
function edit_rank.save:Think()
self:SetEnabled(valid.IsValid())
end
end
sam.menu.add_tab("https://raw.githubusercontent.com/Srlion/Addons-Data/main/icons/sam/military_rank.png", function(column_sheet)
local current_rank
local parent = column_sheet:Add("Panel")
parent:Dock(FILL)
parent:DockMargin(0, 1, 0, 0)
local title = parent:Add("SAM.Label")
title:Dock(TOP)
title:DockMargin(10, 10, 0, 0)
title:SetFont(SAM_TAB_TITLE_FONT)
title:SetText("Ranks")
title:SetTextColor(GetColor("menu_tabs_title"))
title:SizeToContents()
local total = parent:Add("SAM.Label")
total:Dock(TOP)
total:DockMargin(10, 6, 0, 0)
total:SetFont(SAM_TAB_DESC_FONT)
total:SetText(table.Count(sam.ranks.get_ranks()) .. " total ranks")
total:SetTextColor(GetColor("menu_tabs_title"))
total:SizeToContents()
local search_entry
do
local container = parent:Add("SAM.Panel")
container:Dock(TOP)
container:DockMargin(10, 6, 10, SUI.Scale(15))
container:SetTall(30)
search_entry = container:Add("SAM.TextEntry")
search_entry:Dock(LEFT)
search_entry:SetNoBar(true)
search_entry:SetPlaceholder("Search...")
search_entry:SetRadius(4)
search_entry:SetWide(220)
end
local create_rank = parent:Add("SAM.Button")
create_rank:SetFont(CREATE_RANK)
create_rank:SetText("Create Rank")
create_rank:Dock(BOTTOM)
create_rank:DockMargin(10, 0, 10, 10)
create_rank:On("DoClick", function()
rank_menu()
end)
local right_body = parent:Add("Panel")
right_body:Dock(RIGHT)
right_body:DockMargin(0, 5, 10, 10)
right_body:SetWide(0)
right_body:SetZPos(-1)
local rank_title = right_body:Add("SAM.Label")
rank_title:Dock(TOP)
rank_title:DockMargin(0, 0, 0, 5)
rank_title:SetFont(RANK_TITLE)
rank_title:SetTextColor(GetColor("menu_tabs_title"))
local permissions_body = right_body:Add("SAM.CollapseCategory")
permissions_body:Dock(FILL)
permissions_body:GetCanvas():DockPadding(0, 0, 5, 0)
local function refresh_access()
if not IsValid(current_rank) then return end
for k, v in ipairs(permissions_body.items) do
AnimatedSetVisible(v.img, sam.ranks.has_permission(current_rank.name, v.name))
end
end
for k, v in ipairs({"SAM.ChangedInheritRank", "SAM.RankPermissionGiven", "SAM.RankPermissionTaken"}) do
hook.Add(v, "SAM.Menu.RefreshPermissions ", refresh_access)
end
local function sortFunc(a, b)
if (a.category == b.category) then return a.name < b.name end
return a.category < b.category
end
local function refresh_permissions()
permissions_body:GetCanvas():Clear()
table.Empty(permissions_body.items)
table.Empty(permissions_body.categories)
local item_click = function(s)
local rank = current_rank.name
if not sam.ranks.has_permission(rank, s.name) then
RunConsoleCommand("sam", "givepermission", rank, s.name)
else
RunConsoleCommand("sam", "takepermission", rank, s.name)
end
end
local permissions = sam.permissions.get()
table.sort(permissions, sortFunc)
for k, v in ipairs(permissions) do
local item = permissions_body:add_item(v.name, v.category)
item:SetContentAlignment(4)
item:SetTextInset(6, 0)
item:SizeToContentsY(SUI.Scale(10))
item:SetZPos(k)
item.name = v.name
item.DoClick = item_click
local img = item:Add("SAM.Image")
img:Dock(RIGHT)
img:DockMargin(4, 4, 4, 4)
img:InvalidateParent(true)
img:SetWide(img:GetTall())
img:SetImageColor(Color(52, 161, 224))
img:SetImage("https://raw.githubusercontent.com/Srlion/Addons-Data/main/icons/sam/check_mark.png")
item.img = img
end
end
local limits_body
do
local permissions_search = right_body:Add("SAM.TextEntry")
permissions_search:Dock(TOP)
permissions_search:DockMargin(0, 0, 5, 10)
permissions_search:SetNoBar(true)
permissions_search:SetPlaceholder("Search...")
permissions_search:SetRadius(4)
permissions_search:SetTall(30)
function permissions_search:OnValueChange(text)
if limits_body and limits_body:IsVisible() then
local children = limits_body:GetCanvas():GetChildren()
for k, v in ipairs(children) do
v:AnimatedSetVisible(v.title:find(text, nil, true) ~= nil)
end
limits_body:InvalidateLayout(true)
else
permissions_body:Search(text)
end
end
Line(right_body):SetZPos(2)
end
local function load_limits()
if sam.limit_types then
if limits_body then return end
else
if limits_body then
limits_body:SetVisible(false)
permissions_body:AnimatedSetVisible(true)
limits_body:Remove()
limits_body = nil
end
return
end
limits_body = right_body:Add("SAM.ScrollPanel")
limits_body:Dock(FILL)
limits_body:GetCanvas():DockPadding(0, 0, 5, 0)
limits_body:SetVisible(false)
local item_enter = function(s)
if not IsValid(current_rank) then return end
local rank = current_rank.name
local limit = math.Clamp(s:GetValue(), -1, 1000)
if limit ~= sam.ranks.get_limit(rank, s.limit_type) then
RunConsoleCommand("sam", "changeranklimit", rank, s.limit_type, limit)
else
s:SetText(tostring(sam.ranks.get_limit(rank, s.limit_type)))
end
end
local not_empty = function(s)
return s and s ~= ""
end
local limit_values = {}
for k, v in ipairs(sam.limit_types) do
local immunity = limits_body:Add("SAM.LabelPanel")
immunity:SetLabel(v)
immunity:DockMargin(5, 0, 0, 5)
local entry = immunity:Add("SAM.TextEntry")
entry:SetSize(60, 26)
entry:SetNumeric(true)
entry:DisallowFloats(true)
entry:SetPlaceholder("")
entry:SetCheck(not_empty)
entry.limit_type = v
entry.OnEnter = item_enter
table.insert(limit_values, entry)
end
function limits_body:Refresh()
if not IsValid(current_rank) then return end
local rank = current_rank.name
for k, v in ipairs(limit_values) do
v:SetValue(tostring(sam.ranks.get_limit(rank, v.limit_type)))
end
end
local right_current_rank = right_body:Add("SAM.Button")
right_current_rank:Dock(BOTTOM)
right_current_rank:DockMargin(0, 5, 0, 0)
right_current_rank:SetFont(CREATE_RANK)
right_current_rank:SetText("Switch to Limits")
right_current_rank:On("DoClick", function()
limits_body:AnimatedToggleVisible()
permissions_body:AnimatedToggleVisible()
if permissions_body:AnimatedIsVisible() then
right_current_rank:SetText("Switch to Limits")
else
right_current_rank:SetText("Switch to Permissions")
end
end)
limits_body:On("OnRemove", function()
right_current_rank:Remove()
end)
limits_body:Refresh()
end
local function refresh_all()
timer.Create("SAM.Menu.Ranks.Refresh", 1, 1, function()
load_limits()
refresh_permissions()
refresh_access()
end)
end
sam.config.hook({"Restrictions.Limits"}, refresh_all)
for k, v in ipairs({"SAM.AddedPermission", "SAM.PermissionModified", "SAM.RemovedPermission"}) do
hook.Add(v, "SAM.Menu.RefreshPermissions", refresh_all)
end
local body = parent:Add("SAM.ScrollPanel")
body:Dock(FILL)
body:DockMargin(10, 0, 5, 10)
body:SetVBarPadding(6)
body:Line():SetZPos(-101)
local select_rank = function(s)
if not IsValid(s) then
current_rank = nil
right_body:SizeTo(0, -1, 0.3)
return
end
if IsValid(current_rank) then
current_rank.Selected = false
if current_rank == s then
current_rank = nil
right_body:SizeTo(0, -1, 0.3)
return
end
end
s.Selected = true
current_rank = s
refresh_access()
if limits_body then
limits_body:Refresh()
end
right_body:SizeTo(SUI.Scale(300), -1, 0.3)
rank_title:SetText(s.name)
rank_title:SizeToContents()
end
local ranks = {}
function search_entry:OnValueChange()
local value = self:GetValue()
for k, v in pairs(ranks) do
local show = k:find(value, nil, true)
show = show ~= nil
v.line:AnimatedSetVisible(show)
v:GetParent():AnimatedSetVisible(show)
end
end
local add_rank = function(rank_name, data)
if rank_name == "superadmin" then return end
if not IsValid(body) then return end
local line = body:Add("SAM.Panel")
line:Dock(TOP)
line:DockMargin(0, 0, 0, 10)
line:SetTall(34)
line:SetZPos(-data.immunity)
line:InvalidateLayout(true)
local container = line:Add("SAM.Button")
container:Dock(FILL)
container:DockMargin(0, 0, 5, 0)
container:DockPadding(5, 5, 0, 5)
container:SetText("")
container:SetContained(false)
container.name = rank_name
ranks[rank_name] = container
container:On("DoClick", select_rank)
function container:DoRightClick()
rank_name = container.name
if rank_name == "user" then return end
local dmenu = vgui.Create("SAM.Menu")
dmenu:SetSize(w, h)
dmenu:SetInternal(container)
dmenu:AddOption("Edit Rank", function()
rank_menu(rank_name, sam.ranks.get_rank(rank_name))
end)
if not sam.ranks.is_default_rank(rank_name) then
dmenu:AddSpacer()
dmenu:AddOption("Remove Rank", function()
local remove_rank = vgui.Create("SAM.QueryBox")
remove_rank:SetWide(350)
local check = remove_rank:Add("SAM.Label")
check:SetText("Are you sure that you want to remove '" .. rank_name .. "'?")
check:SetFont("SAMLine")
check:SetWrap(true)
check:SetAutoStretchVertical(true)
remove_rank:Done()
remove_rank.save:SetEnabled(true)
remove_rank.save:SetText("REMOVE")
remove_rank.save:SetContained(false)
remove_rank.save:SetColors(GetColor("query_box_cancel"), GetColor("query_box_cancel_text"))
remove_rank.cancel:SetContained(true)
remove_rank.cancel:SetColors()
remove_rank:SetCallback(function()
RunConsoleCommand("sam", "removerank", rank_name)
end)
end)
end
dmenu:Open()
dmenu:SetPos(input.GetCursorPos())
end
do
local name = container:Add("SAM.Label")
name:Dock(TOP)
name:DockMargin(0, 0, 0, 2)
name:SetTextColor(GetColor("player_list_names"))
name:SetFont(RANK_NAME)
name:SetText(rank_name)
name:SizeToContents()
local immunity = container:Add("SAM.Label")
immunity:Dock(TOP)
immunity:SetTextColor(GetColor("player_list_steamid"))
immunity:SetFont(RANK_INFO)
immunity:SetText("Immunity: " .. data.immunity)
immunity:SizeToContents()
local banlimit = container:Add("SAM.Label")
banlimit:Dock(TOP)
banlimit:SetTextColor(GetColor("player_list_steamid"))
banlimit:SetFont(RANK_INFO)
banlimit:SetText("Ban limit: " .. sam.reverse_parse_length(sam.parse_length(data.ban_limit)))
banlimit:SizeToContents()
local inherit = container:Add("SAM.Label")
inherit:Dock(TOP)
inherit:SetTextColor(GetColor("player_list_steamid"))
inherit:SetFont(RANK_INFO)
inherit:SetText("Inherits from: " .. (sam.isstring(data.inherit) and data.inherit or "none"))
inherit:SizeToContents()
end
container:InvalidateLayout(true)
container:SizeToChildren(false, true)
line:SizeToChildren(false, true)
local _line = body:Line()
_line:SetZPos(-data.immunity)
container.line = _line
container.data = data
end
for rank_name, v in pairs(sam.ranks.get_ranks()) do
add_rank(rank_name, v)
end
hook.Add("SAM.AddedRank", "SAM.RefreshRanksList", function(name, rank)
add_rank(name, rank)
end)
hook.Add("SAM.RemovedRank", "SAM.RefreshRanksList", function(name)
local line = ranks[name]
if not IsValid(line) then return end
line.line:Remove()
line:GetParent():Remove()
ranks[name] = nil
if line == current_rank then
select_rank()
end
end)
-- This is just better than caching panels for stuff that ain't gonna be called a lot
hook.Add("SAM.RankNameChanged", "SAM.RefreshRanksList", function(name, new_name)
local line = ranks[name]
if not IsValid(line) then return end
-- if current_rank == name then
-- rank_name:SetText(new_name)
-- end
line:GetChildren()[1]:SetText(new_name)
ranks[new_name], ranks[name] = line, nil
line.name = new_name
end)
hook.Add("SAM.RankImmunityChanged", "SAM.RefreshRanksList", function(name, immunity)
local line = ranks[name]
if not IsValid(line) then return end
line:GetChildren()[2]:SetText("Immunity: " .. immunity)
line:GetParent():SetZPos(-immunity)
-- SetZPos is kinda weird to deal with
line.line:SetZPos(-immunity + 1)
line.line:SetZPos(-immunity)
end)
hook.Add("SAM.RankBanLimitChanged", "SAM.RefreshRanksList", function(name, new_limit)
local line = ranks[name]
if IsValid(line) then
line:GetChildren()[3]:SetText("Ban limit: " .. sam.reverse_parse_length(new_limit))
end
end)
hook.Add("SAM.ChangedInheritRank", "SAM.RefreshRanksList", function(name, new_inherit)
local line = ranks[name]
if IsValid(line) then
line:GetChildren()[4]:SetText("Inherits from: " .. new_inherit)
end
end)
return parent
end, function()
return LocalPlayer():HasPermission("manage_ranks")
end, 3)