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