dobrograd-13-06-2022/garrysmod/addons/admin-sg/lua/modules/sh_utilities.lua
Jonny_Bro (Nikita) e4d5311906 first commit
2023-11-16 15:01:19 +05:00

1269 lines
34 KiB
Lua

--[[
© 2017 Thriving Ventures Limited do not share, re-distribute or modify
without permission of its author (gustaf@thrivingventures.com).
]]
--- ## Shared
--- Extension of util library to provide convenience functions.
-- @module util
color_white = Color(255, 255, 255, 255)
color_black = Color(0, 0, 0, 255)
color_red = Color(255, 0, 0, 255)
color_green = Color(0, 255, 0, 255)
color_green_darker = Color(0, 200, 0, 255)
color_blue = Color(0, 0, 255, 255)
color_yellow = Color(255, 255, 0, 255)
color_purple_light = Color(106, 90, 205, 255)
color_orange = Color(255, 165, 0, 255)
color_brown = Color(150, 75, 0, 255)
local temptbl = {}
local function MergeSortByName( tbl, iLow, iHigh, bReverse )
if ( iLow < iHigh ) then
local iMiddle = math.floor( iLow + (iHigh - iLow) / 2 )
MergeSortByName( tbl, iLow, iMiddle, bReverse )
MergeSortByName( tbl, iMiddle + 1, iHigh, bReverse )
for i = iLow, iHigh do
temptbl[i] = tbl[i]
end
local i = iLow
local j = iMiddle + 1
local k = iLow
while ( i <= iMiddle and j <= iHigh ) do
if ( temptbl[i]:Nick() <= temptbl[j]:Nick() ) then
if ( bReverse ) then
tbl[k] = temptbl[j]
j = j + 1
else
tbl[k] = temptbl[i]
i = i + 1
end
else
if ( bReverse ) then
tbl[k] = temptbl[i]
i = i + 1
else
tbl[k] = temptbl[j]
j = j + 1
end
end
k = k + 1
end
while ( i <= iMiddle ) do
tbl[k] = temptbl[i]
k = k + 1
i = i + 1
end
end
end
--- Gets a sorted list of players connected to the server.
-- @treturn table Player list.
function player.GetSortedPlayers(bReverse --[[= false]])
local tbl = player.GetAll()
MergeSortByName(tbl, 1, #tbl, bReverse)
return tbl;
end;
-- Taken from the gmod table library. Necessary to restore the original
-- functionality broken due to the following commit:
-- https://github.com/garrynewman/garrysmod/commit/5e9d4fa05ea9cc69c56e5c0857d5247dcabdc192
local function fnPairsSorted(tab, index)
if (index == nil) then
index = 1;
else
for k, v in pairs(tab.__SortedIndex) do
if (v == index) then
index = k + 1;
break;
end;
end;
end;
local key = tab.__SortedIndex[index];
if (!key) then
tab.__SortedIndex = nil;
return;
end;
index = index + 1;
return key, tab[key];
end
-- See fnPairsSorted
function util.SortedPairsByMemberValue(tab, valueName, descending)
descending = descending or false;
local sortedIndex = {};
local sortedTable = table.ClearKeys(tab, true);
table.SortByMember(sortedTable, valueName, !descending);
for k, v in ipairs(sortedTable) do
table.insert(sortedIndex, v.__key);
end;
tab.__SortedIndex = sortedIndex;
return fnPairsSorted, tab, nil;
end;
--- Returns whether or not the player can be compared **EXACTLY** with a given identifier.
-- This is more strict, and will match exact names.
-- @player player The player to extract the information from.
-- @string identifier The string to compare with.
-- @treturn bool Whether the player matches the given identifier.
function util.PlayerMatchesIdentifier(player, identifier)
local result = hook.Call("serverguard.PlayerMatchesIdentifier", nil, player, identifier);
return (IsValid(player) and identifier and result or string.lower(identifier) == string.lower(player:Name()) or
string.lower(identifier) == string.lower(player:Nick()) or
player:SteamID() == identifier or
player:UniqueID() == identifier or
player:SteamID64() == identifier or
(player:IPAddress():gsub(":%d+", "")) == identifier);
end;
--- Returns whether or not the player can be compared with a given identifier.
-- This will be more lenient with finding substrings in a player's name, for example.
-- @player player The player to extract the information from.
-- @string identifier The string to compare with.
-- @treturn bool Whether the player contains the given identifier.
function util.PlayerContainsIdentifier(player, identifier)
local result = hook.Call("serverguard.PlayerContainsIdentifier", nil, player, identifier);
return (IsValid(player) and identifier and result or (string.find(utf8.lower(player:Name()), utf8.lower(identifier), 0, true) or
string.find(utf8.lower(player:Nick()), utf8.lower(identifier), 0, true) or
player:SteamID() == identifier or
player:UniqueID() == identifier or
player:SteamID64() == identifier or
(player:IPAddress():gsub(":%d+", "")) == identifier));
end;
--- Returns whether or not the target's immunity level fulfills the required immunity comparison with the player.
-- @number requiredImmunity The required immunity comparison.
-- @player player The player to be compared to.
-- @player target The player to be compared with.
-- @treturn bool Whether the immunity requirement has been fulfilled.
function util.PlayerMatchesImmunity(requiredImmunity, pPlayer, target, bNoTargetSelf)
local result = hook.Call("serverguard.PlayerMatchesImmunity", nil, requiredImmunity, pPlayer, target, bNoTargetSelf);
return result or
(requiredImmunity == SERVERGUARD.IMMUNITY.EQUAL and serverguard.player:GetImmunity(target) == serverguard.player:GetImmunity(pPlayer)) or
(requiredImmunity == SERVERGUARD.IMMUNITY.LESS and serverguard.player:GetImmunity(target) < serverguard.player:GetImmunity(pPlayer)) or
(requiredImmunity == SERVERGUARD.IMMUNITY.LESSOREQUAL and serverguard.player:GetImmunity(target) <= serverguard.player:GetImmunity(pPlayer)) or
(requiredImmunity == SERVERGUARD.IMMUNITY.ANY) or
(!bNoTargetSelf and pPlayer == target);
end;
local identifierTags = {
-- Immunity is automatically checked beforehand so we don't have to do it here.
["^"] = {
filter = function(callingPlayer, targetPlayer)
return callingPlayer == targetPlayer
end,
negated = function(callingPlayer, targetPlayer)
return callingPlayer != targetPlayer
end
},
["\\*"] = {
filter = function(callingPlayer, targetPlayer)
return true
end,
negated = function(callingPlayer, targetPlayer)
return false
end
},
["#*"] = {
filter = function(callingPlayer, targetPlayer, argument)
return serverguard.player:GetRank(targetPlayer) == argument
end,
negated = function(callingPlayer, targetPlayer, argument)
return serverguard.player:GetRank(targetPlayer) != argument
end
},
["@"] = {
filter = function(callingPlayer, targetPlayer, argument)
return callingPlayer:GetEyeTraceNoCursor().Entity == targetPlayer
end,
negated = function(callingPlayer, targetPlayer, argument)
return callingPlayer:GetEyeTraceNoCursor().Entity != targetPlayer
end
}
};
--- Attempts to find all connected players matching the given query, and runs a function with that player as an argument.
-- Valid identifiers include name, Steam ID, 64-bit Steam ID, IP address, a comma-separated list, or a special tag.
-- Tags include ^ for self, * for all, and #name for all players with the given rank. You can use ! to negate the action.
-- @string identifier String to try and match players with.
-- @player callingPlayer Player who initiated the query.
-- @number requiredImmunity The required immunity level between the calling player and any targets for the function to run.
-- @func func Function to run with all applicable targets.
-- @treturn table List of applicable players.
function util.ExecuteOnPlayers(identifier, callingPlayer, requiredImmunity, func)
if (!identifier or !callingPlayer or !requiredImmunity or !func) then
return;
end;
local tAllPlayers = player.GetAll()
local players = {};
for k, v in ipairs(tAllPlayers) do
if (util.PlayerMatchesImmunity(requiredImmunity, callingPlayer, v)) then
if (util.PlayerMatchesIdentifier(v, identifier)) then
-- If we match exactly, we only execute on the one player.
table.insert(players, v);
break;
elseif (util.PlayerContainsIdentifier(v, identifier)) then
if (#players == 0) then
table.insert(players, v);
else
serverguard.Notify(callingPlayer, SGPF("player_found_multiple", identifier));
return {};
end;
end;
end;
end;
-- Matching special tags.
if (#players == 0) then
local negate = (string.sub(identifier, 1, 1) == "!");
for k, v in pairs(identifierTags) do
local tag = k;
local text = identifier;
local argument = "";
local func = "filter";
if (negate) then
text = string.sub(identifier, 2, string.len(identifier));
func = "negated";
end;
local wildcardPosition = string.find(tag, "*", 0, true);
if (wildcardPosition) then
if (string.sub(tag, wildcardPosition - 1, wildcardPosition - 1) == "\\") then
tag = string.sub(tag, wildcardPosition, wildcardPosition);
else
tag = string.gsub(tag, "*", "");
argument = string.gsub(text, tag, "");
tag = tag .. argument;
end;
end;
for k, pPlayer in pairs(tAllPlayers) do
if (tag == text and util.PlayerMatchesImmunity(requiredImmunity, callingPlayer, pPlayer) and v[func](callingPlayer, pPlayer, argument)) then
table.insert(players, pPlayer);
end;
end;
end;
-- Matching name-separated list.
if (#players == 0) then
if (string.find(identifier, ",", 0, true)) then
local identifiers = string.Explode(",", identifier);
for k, v in pairs(identifiers) do
local found = false;
for k2, v2 in ipairs(tAllPlayers) do
if (util.PlayerMatchesImmunity(requiredImmunity, callingPlayer, v2) and util.PlayerContainsIdentifier(v2, v)) then
if (!found) then
found = true;
table.insert(players, v2);
else
serverguard.Notify(callingPlayer, SGPF("player_found_multiple", v));
return {};
end;
end;
end;
end;
end;
-- Give up if we haven't found anyone.
if (#players == 0) then
serverguard.Notify(callingPlayer, SGPF("player_cant_find_suitable"));
return {};
end;
end;
end;
local result = {};
for k, v in pairs(players) do
local status = func(v);
if (status) then
table.insert(result, v);
end;
end;
-- Oops, everyone was filtered out.
if (#result == 0) then
serverguard.Notify(callingPlayer, SGPF("player_cant_find_suitable"));
return {};
end;
return result;
end;
function util.GetListenServerHost()
for k, v in ipairs(player.GetAll()) do
if (v:IsListenServerHost()) then
return v
end
end
return NULL
end
local PLAYER = FindMetaTable("Player")
function isplayer(v)
return getmetatable(v) == PLAYER
end
--- Obtains a list of elements suitable for `serverguard.Notify` for the given player list.
-- It is assumed that there is at least one target.
-- @table targets Player targets to output.
-- @bool bOwnership Whether or not to add an ownership suffix (such as "'s").
-- @number targetColor A SERVERGUARD.NOTIFY color to use.
-- @treturn table Notify components.
-- @see serverguard.Notify
function util.GetNotifyListForTargets(targets, bOwnership, targetColor)
targetColor = targetColor or SERVERGUARD.NOTIFY.RED;
if (#targets < 1) then
return {};
end;
if (#targets == #player.GetAll() and #targets != 1) then
if (bOwnership) then
return {targetColor, "everyone's"};
end;
return {targetColor, "everyone"};
end
local result = {};
table.insert(result, targetColor);
table.insert(result, targets[1]:Name());
for i = 2, #targets - 1 do
table.insert(result, SERVERGUARD.NOTIFY.WHITE);
table.insert(result, ", ");
table.insert(result, targetColor);
table.insert(result, targets[i]:Name());
end;
if (#targets > 1) then
table.insert(result, SERVERGUARD.NOTIFY.WHITE);
table.insert(result, " and ");
table.insert(result, targetColor);
table.insert(result, targets[#targets]:Name());
end;
if (bOwnership) then
table.insert(result, string.Ownership(result[#result], true));
end;
hook.Call("serverguard.GetNotifyListForTargets", nil, targets, bOwnership, targetColor, result);
return result;
end;
--- Attempts to find a connected player by a given identifier.
-- Valid identifiers include name, Steam ID, 64-bit Steam ID, and IP address.
-- @string identifier Identifier to search with.
-- @player user Player to provide notifies to.
-- @bool bNoMessage Whether or not to provide feedback to the player.
function util.FindPlayer(identifier, user, bNoMessage)
if (!identifier) then
return;
end;
local output = {};
for k, v in ipairs(player.GetAll()) do
local playerNick = string.lower(v:Nick());
local playerName = string.lower(v:Name());
if (v:SteamID() == identifier or v:UniqueID() == identifier
or v:SteamID64() == identifier or (v:IPAddress():gsub(":%d+", "")) == identifier
or playerNick == string.lower(identifier) or playerName == string.lower(identifier)) then
return v;
end;
if (string.find(playerNick, string.lower(identifier), 0, true)
or string.find(playerName, string.lower(identifier), 0, true)) then
table.insert(output, v);
end;
end;
if (#output == 1) then
return output[1];
elseif (#output > 1) then
if (!bNoMessage) then
if (IsValid(user)) then
if (serverguard and serverguard.Notify) then
serverguard.Notify(user, SERVERGUARD.NOTIFY.RED, "Found more than one player with that identifier.");
else
user:ChatPrint("Found more than one player with that identifier.");
end;
else
if (SERVER) then
Msg("Found more than one player with that identifier.\n");
end;
end;
end;
else
if (!bNoMessage) then
if (IsValid(user)) then
if (serverguard and serverguard.Notify) then
serverguard.Notify(user, SERVERGUARD.NOTIFY.RED, "Can't find any player with that identifier.");
else
user:ChatPrint("Can't find any player with that identifier.");
end;
else
if (SERVER) then
Msg("Can't find any player with that identifier.\n");
end;
end;
end;
end;
end;
--- Alternative tonumber function that always returns a number. Returns 0 if unable to convert to number.
-- @param value Any variable to attempt to a number.
-- @treturn number Converted number.
function util.ToNumber(value)
if (type(value) == "string") then
if (tonumber(value) == nil) then
return 0;
end;
return tonumber(value);
end;
if (type(value) == "boolean") then
if (value) then
return 1;
end;
return 0;
end;
if (type(value) == "number") then
return value;
end;
if (value == nil) then
return 0;
end;
return 0;
end;
--- Formats a number to include commas.
-- @number number Number to format.
-- @treturn string Formatted number.
function util.FormatNumber(number)
if (number >= 1e14) then
return tostring(number)
end
number = tostring(number)
local dp = string.find(number, "%.") or #number +1
for i = dp -4, 1, -3 do
number = string.sub(number, 1, i) .. "," .. string.sub(number, i +1)
end
return number
end
--- Capitalizes the first letter of a string.
-- @string text String to capitalize.
-- @treturn string Capitalized string.
function string.Capitalize(text)
return string.upper(string.sub(text, 1, 1)) .. string.sub(text, 2)
end
--- Returns the ownership suffix of a string.
-- @string text String to find ownership for.
-- @bool bSuffixOnly Whether or not to return only the suffix.
-- @treturn string String with ownership.
function string.Ownership(text, bSuffixOnly)
local suffix = "'s";
if (text[string.len(text)] == "'" or text[string.len(text)] == "'s") then
suffix = "";
elseif (text[string.len(text)] == "s") then
suffix = "'"
end;
if (bSuffixOnly) then
return suffix;
end;
return text .. suffix;
end;
--- Returns a Steam ID from a string.
-- @string text String to check for a Steam ID.
-- @treturn text The matched Steam ID.
function string.SteamID(text)
return string.match(text, "STEAM_%d:%d:%d+");
end;
--- Explodes a string by enclosed tabs.
-- @string text String to explode.
-- @string seperator String to separate tags by.
-- @string open String to use as the beginning of a tag.
-- @string close String to use as the end of a tag.
-- @bool bRemoveTag Whether or not to remove the tag characters from the result.
-- @treturn table Exploded string.
-- @usage util.ExplodeByTags("!this 'is a test'", " ", "'", "'", true);
function util.ExplodeByTags(text, seperator, open, close, bRemoveTag)
local results = {};
local current = "";
local tag = nil;
text = string.gsub(text, "%s+", " ");
for i = 1, #text do
local character = string.sub(text, i, i);
if (!tag) then
if (character == open) then
if (!bRemoveTag) then
current = current..character;
end;
tag = true;
elseif (character == seperator) then
results[#results + 1] = current; current = "";
else
current = current..character;
end;
else
if (character == close) then
if (!bRemoveTag) then
current = current..character;
end;
tag = nil;
else
current = current..character;
end;
end;
end;
if (current != "") then
results[#results + 1] = current;
end;
return results;
end;
--- Checks to see whether or not a variable is the console.
-- @param object Any variable.
-- @treturn bool Whether or not the variable is the console.
function util.IsConsole(object)
if (type(object) == "Entity" and !IsValid(object) and (object.EntIndex and object:EntIndex() == 0)) then
return true;
end;
return false;
end;
--- Converts an ISO 8601 duration timestamp used by YouTube to seconds.
-- Note: this is not entirely accurate to the ISO standard - it's only tested for use with YouTube's duration format.
-- @string iso The ISO duration.
-- @treturn number The ISO duration in seconds. Returns -1 for an invalid ISO duration.
function util.IsoDurationToSeconds(iso)
local duration = 0;
local number = "";
if (string.sub(iso, 1, 1) != "P") then
return -1;
end;
for i = 1, string.len(iso), 1 do
local character = string.sub(iso, i, i);
if (character == "P" or character == "T") then
continue;
end;
if (tonumber(character)) then
number = number .. character;
end;
if (!tonumber(number)) then
return -1;
end;
if (character == "D") then
duration = duration + tonumber(number) * 86400;
number = "";
end;
if (character == "H") then
duration = duration + tonumber(number) * 3600;
number = "";
end;
if (character == "M") then
duration = duration + tonumber(number) * 60;
number = "";
end;
if (character == "S") then
duration = duration + tonumber(number);
number = "";
end;
end;
return duration;
end;
local durationUnits = {
["m"] = {1, "minute"},
["h"] = {60, "hour"},
["d"] = {1440, "day"},
["w"] = {10080, "week"},
["n"] = {43200, "month"},
["y"] = {525600, "year"}
};
local orderedDurationUnits = {
{525600, "year"},
{43200, "month"},
{10080, "week"},
{1440, "day"},
{60, "hour"},
{1, "minute"},
};
--- Converts a duration (e.g 1d12h) to minutes. Clamps all numbers between 0 and 99.
-- @string input The input duration. This can be a number, but it will return that number.
-- @treturn number The duration in minutes.
-- @treturn text The duration in text form (1 year, 3 days, etc).
-- @treturn bool Whether or not any input values have been clamped.
function util.ParseDuration(input)
if (tonumber(input)) then
local output = {};
local number = tonumber(input);
if (number <= 0) then
return 0, "Indefinitely", false;
end;
for k, v in ipairs(orderedDurationUnits) do
if (number >= v[1]) then
local count = math.floor(number / v[1]);
if (count > 1) then
output[#output + 1] = tostring(count).." "..v[2].."s";
else
output[#output + 1] = tostring(count).." "..v[2];
end;
number = number - (v[1] * count);
end;
end;
return tonumber(input), table.concat(output, ", "), false;
end;
local bClamped = false;
local duration = 0;
local text = "";
local number = "";
for i = 1, string.len(input), 1 do
local character = string.sub(input, i, i);
if (tonumber(character)) then
number = number .. character;
continue;
end
if (!tonumber(number)) then
number = "";
continue;
end;
if (!durationUnits[character]) then
number = "";
continue;
end;
if (tonumber(number) < 0 || tonumber(number) > 99) then
number = tostring(math.Clamp(tonumber(number), 0, 99));
bClamped = true;
end;
duration = duration + (tonumber(number) * durationUnits[character][1]);
if (tonumber(number) > 0) then
if (string.len(text) > 0) then
text = text .. ", ";
end;
text = text .. number .. " " .. durationUnits[character][2];
if (tonumber(number) > 1) then
text = text .. "s";
end;
end;
number = "";
end;
if (text == "") then
text = "Indefinitely";
end;
return duration, text, bClamped;
end;
if (SERVER) then
--- (SERVER) Prints text to the chatbox of all connected players.
-- @string text Text to print.
function util.PrintAll(text)
for k, v in ipairs(player.GetAll()) do
v:ChatPrint(text)
end
end
--- (SERVER) Prints coloured text to the chatbox of all connected players.
-- @param ... Color and text to use.
-- @usage util.PrintAllColor(Color(255, 255, 255), "Hello! ", Color(200, 30, 30), "This is a test.");
function util.PrintAllColor(...)
serverguard.netstream.Start(nil, "sgPrintAllColor", {...});
end;
--- (SERVER) Prints text to the console of connected admins.
-- @string text Text to print.
function util.PrintConsoleAdmins(text)
for k, pPlayer in ipairs(player.GetAll()) do
if (pPlayer:IsAdmin()) then
serverguard.netstream.Start(pPlayer, "sgPrintConsole", text);
end;
end;
end;
elseif (CLIENT) then
serverguard.bLogEnabled = CreateClientConVar("serverguard_log", "1", true, false);
serverguard.netstream.Hook("sgPrintAllColor", function(data)
chat.AddText(unpack(data));
end);
serverguard.netstream.Hook("sgPrintConsole", function(data)
if (serverguard.bLogEnabled:GetBool()) then
Msg(data);
end;
end);
-- Thanks capsadmin <3.
--- (CLIENT) Draws a textured line.
-- @number x1 Starting X coordinate.
-- @number y1 Starting Y coordinate.
-- @number x2 Ending X coordinate.
-- @number y2 Ending Y coordinate.
-- @number w With of the line.
function surface.DrawLineEx(x1, y1, x2, y2, w)
local dx, dy = x1 -x2, y1 -y2
local rotation = math.deg(math.atan2(dx, dy))
local distance = math.Distance(x1, y1, x2, y2)
x1 = x1 -dx *0.5
y1 = y1 -dy *0.5
surface.DrawTexturedRectRotated(x1, y1, w, distance, rotation)
end
--- Makes a panel change the cursor to a hand when hovered.
-- @panel panel Panel to install hover to.
function util.InstallHandHover(panel)
panel:SetMouseInputEnabled(true)
function panel:OnCursorEntered()
self:SetCursor("hand")
end
function panel:OnCursorExited()
self:SetCursor("arrow")
end
end
--- (CLIENT) Draws a box shadow.
-- @number x X coordinate.
-- @number y Y coordinate.
-- @number w Width of box.
-- @number h Height of box.
-- @number passes The amount of passes when drawing the shadow.
-- @number depth The depth of the shadow.
function util.PaintShadow(x, y, w, h, passes, depth)
passes = passes or 4
depth = depth or 0.2
for i = 1, passes do
local color = Color(0, 0, 0, (255 /i) *depth)
-- Top shadow.
draw.SimpleRect(x, y +(-1 +i), w, 1, color)
-- Left shadow.
draw.SimpleRect(x +(-1 +i), y, 1, h, color)
-- Bottom shadow.
draw.SimpleRect(x, y +(h -i), w, 1, color)
-- Right shadow.
draw.SimpleRect(x +(w -i), y, 1, h, color)
end
end
--- (CLIENT) Darkens a colour by a given amount. Does not include alpha.
-- @color color Colour to dim.
-- @number amount How much to dim.
-- @treturn color Dimmed colour.
function util.DimColor(color, amount)
return Color(
math.Clamp(color.r - amount, 0, 255),
math.Clamp(color.g - amount, 0, 255),
math.Clamp(color.b - amount, 0, 255),
color.a
);
end;
--- (CLIENT) Limits how dark a colour can be.
-- @color color Colour to limit.
-- @number r Minimum red value.
-- @number g Minimum green value.
-- @number b Minimum blue value.
-- @number a Minimum alpha value.
-- @treturn color Limited colour.
function util.ColorLimit(color, r, g, b, a)
return Color(
math.min(color.r, r),
math.min(color.g, g),
math.min(color.b, b),
math.min(color.a, a)
)
end
--- (CLIENT) Gets the string version of a colour.
-- @color color Colour to get string from.
-- @bool bExcludeAlpha Whether or not to include the alpha in the string.
-- @treturn string Colour string.
function util.ColorString(color, bExcludeAlpha)
return color.r .. "," .. color.g .. "," .. color.b .. (!bExcludeAlpha and "," .. color.a or "")
end
--- (CLIENT) Draws a coloured rectangle.
-- @number x X coordinate.
-- @number y Y coordinate.
-- @number w Width of rectangle.
-- @number h Height of rectangle.
-- @color col Colour of rectangle.
function draw.SimpleRect(x, y, w, h, col)
surface.SetDrawColor(col)
surface.DrawRect(x, y, w, h)
end
--- (CLIENT) Draws a coloured rectangle outline.
-- @number x X coordinate.
-- @number y Y coordinate.
-- @number w Width of rectangle.
-- @number h Height of rectangle.
-- @color col Colour of rectangle.
function draw.SimpleOutlined(x, y, w, h, col)
surface.SetDrawColor(col)
surface.DrawOutlinedRect(x, y, w, h)
end
--- (CLIENT) Draws two coloured rectangle outlines.
-- @number x X coordinate.
-- @number y Y coordinate.
-- @number w Width of rectangle.
-- @number h Height of rectangle.
-- @color col Colour of rectangle.
function draw.DoubleOutlined(x, y, w, h, col)
surface.SetDrawColor(col)
surface.DrawOutlinedRect(x, y, w, h)
surface.DrawOutlinedRect(x +1, y +1, w -2, h -2)
end
--- (CLIENT) Draws a coloured rectangle with a material.
-- @number x X coordinate.
-- @number y Y coordinate.
-- @number w Width of rectangle.
-- @number h Height of rectangle.
-- @color color Colour of rectangle.
-- @material material Material to draw with.
function draw.Material(x, y, w, h, color, material)
surface.SetDrawColor(color)
surface.SetMaterial(material)
surface.DrawTexturedRect(x, y, w, h)
end
--- (CLIENT) Draws a coloured rotated rectangle with a material.
-- @number x X coordinate.
-- @number y Y coordinate.
-- @number w Width of rectangle.
-- @number h Height of rectangle.
-- @color color Colour of rectangle.
-- @material material Material to draw with.
-- @number rotated Amount to rotate rectangle by.
function draw.MaterialRotated(x, y, w, h, color, material, rotated)
surface.SetDrawColor(color)
surface.SetMaterial(material)
surface.DrawTexturedRectRotated(x, y, w, h, rotated)
end
--- (CLIENT) Draws a coloured rectangle with a texture.
-- @number x X coordinate.
-- @number y Y coordinate.
-- @number w Width of rectangle.
-- @number h Height of rectangle.
-- @color color Colour of rectangle.
-- @texture texture Texture to draw with.
function draw.Texture(x, y, w, h, color, texture)
surface.SetDrawColor(color)
surface.SetTexture(texture)
surface.DrawTexturedRect(x, y, w, h)
end
--- (CLIENT) Draws a coloured rotated rectangle with a texture.
-- @number x X coordinate.
-- @number y Y coordinate.
-- @number w Width of rectangle.
-- @number h Height of rectangle.
-- @color color Colour of rectangle.
-- @texture texture Texture to draw with.
-- @number rotated Amount to rotate rectangle by.
function draw.TextureRotated(x, y, w, h, color, texture, rotated)
surface.SetDrawColor(color)
surface.SetTexture(texture)
surface.DrawTexturedRectRotated(x, y, w, h, rotated)
end
--- (CLIENT) Draws text with an outline.
-- @string text Text to draw.
-- @string font Font to use.
-- @number x X coordinate.
-- @number y Y coordinate.
-- @color col Colour of text.
-- @color colOutline Colour of outline.
-- @number xAlign Horizontal alignment.
-- @number yAlign Vertical alignment.
-- @number outline Outline width.
function draw.SimpleTextOutline(text, font, x, y, col, colOutline, xAlign, yAlign, outline)
draw.SimpleText(text, font, x +(outline or 1), y +(outline or 1), colOutline, xAlign, yAlign)
draw.SimpleText(text, font, x, y, col, xAlign, yAlign)
end
--- (CLIENT) Gets the size of text with a given font.
-- @string font Font to use.
-- @string text Text to use.
-- @treturn number Width of text.
-- @treturn number Height of text.
function util.GetTextSize(font, text)
surface.SetFont(font)
local w, h = surface.GetTextSize(text)
return w, h
end
--- (CLIENT) Creates a parented label.
-- @panel parent Panel to parent to.
-- @string title Text for the label.
-- @color col Colour of text.
-- @string font Font to use.
-- @number x X coordinate.
-- @number y Y coordinate.
-- @treturn panel Created label.
function util.simpleLabel(parent, title, col, font, x, y)
local DLabel = nil
if (parent != nil) then
DLabel = vgui.Create("DLabel", parent)
else
DLabel = vgui.Create("DLabel")
end
if (x and y) then
DLabel:SetPos(x, y)
end
if (col) then
DLabel:SetColor(col)
end
DLabel:SetText(title)
DLabel:SetFont(font)
DLabel:SizeToContents()
return DLabel
end;
--- (CLIENT) Creates a parented button.
-- @panel parent Panel to parent to.
-- @number x X coordinate.
-- @number y Y coordinate.
-- @number w Width of button.
-- @number h Height of button.
-- @string text Text for the button.
-- @bool bDisabled Whether or not the button is disabled.
-- @func func Callback to use when button is clicked.
-- @treturn panel Created button.
function util.simpleButton(parent, x, y, w, h, text, bDisabled, func)
local DButton = nil
if (parent != nil) then
DButton = vgui.Create("DButton", parent)
else
DButton = vgui.Create("DButton")
end
DButton:SetText(text)
if (w and h) then
DButton:SetSize(w, h)
end
if (x and y) then
DButton:SetPos(x, y)
end
DButton:SetDisabled(bDisabled)
DButton.DoClick = func
return DButton
end
--- (CLIENT) Creates a visual request for an input string.
-- @string title Title of request.
-- @param ... Options to use.
-- @usage util.CreateStringRequest("Please enter a string!", function(text) print(text) end, "Okay", function(text) end, "Cancel");
function util.CreateStringRequest(title, ...)
local arguments = {...}
if (#arguments == 0) then
return;
end;
local buttons = {};
local dialog = vgui.Create("tiger.panel");
dialog:SetTitle(title, false);
dialog:SetSize(350, 150);
dialog:Center();
dialog:MakePopup();
dialog:DockPadding(24, 24, 24, 48);
local textEntry = dialog:Add("DTextEntry");
textEntry:SetTall(20);
textEntry:SetSkin("serverguard");
textEntry:Dock(TOP);
for k, v in ipairs(arguments) do
if (isfunction(v)) then
local arg = arguments[k + 1]
if (!arg or not isstring(arg)) then
continue;
end;
local button = dialog:Add("tiger.button");
button:SetText(arg);
button:SizeToContents();
function button:DoClick()
v(textEntry:GetValue());
dialog:Remove();
end;
table.insert(buttons, button);
end;
end;
function dialog:PerformLayout(width, height)
local button = buttons[1]
if (button) then
button:SetPos(width - (button:GetWide() + 24), height - (button:GetTall() + 14));
for i = 2, #buttons do
buttons[i]:SetPos(0, height - (buttons[i]:GetTall() + 14));
buttons[i]:MoveLeftOf(button, 14);
end;
end;
end;
end;
--- (CLIENT) Creates a dialog. Prefix option strings with "&" to make the button text bold.
-- @string title Title of dialog.
-- @string text Text for dialog.
-- @param ... Options to use.
-- @usage util.CreateDialog("Test", "This is simply a test dialog.", function() print("Clicked okay!") end, "&Okay", function() end, "Cancel");
function util.CreateDialog(title, text, ...)
local arguments = {...}
local dialog = vgui.Create("tiger.dialog")
dialog:SetTitle(title)
dialog:SetText(text)
for k, v in ipairs(arguments) do
if (isfunction(v)) then
local arg = arguments[k + 1]
if (!arg or not isstring(arg)) then
continue;
end;
local buttonText = arguments[k + 1]
dialog:AddButton(buttonText, function()
v()
dialog:FadeOut(0.5, function()
dialog:Remove()
end)
end)
end
end
dialog:SizeToContents()
dialog:Center()
dialog:MakePopup()
dialog:FadeIn()
return dialog
end
--- (CLIENT) Creates multiple derma controls.
-- @param object[opt] parent The parent control.
-- @param ... The controls to create.
-- @usage local label, button = util.CreateControls(parent, "DLabel", "tiger.button");
-- @usage local panel, label, button = util.CreateControls("tiger.panel", "DLabel", "tiger.button");
function util.CreateControls(parent, ...)
local controls = {};
local bCreatedParent = false;
if (isstring(parent)) then
parent = vgui.Create(parent);
bCreatedParent = true;
end;
for k, v in ipairs({...}) do
table.insert(controls, vgui.Create(v, parent));
end;
if (bCreatedParent) then
return parent, unpack(controls);
else
return unpack(controls);
end;
end;
-- Author: Wizard of Ass
-- http://www.facepunch.com/threads/1089200?p=32614030&viewfull=1#post32614030
local DrawText = surface.DrawText
local SetTextPos = surface.SetTextPos
local PopModelMatrix = cam.PopModelMatrix
local PushModelMatrix = cam.PushModelMatrix
local matrix = Matrix()
local matrixAngle = Angle(0, 0, 0)
local matrixScale = Vector(0, 0, 0)
local matrixTranslation = Vector(0, 0, 0)
--- (CLIENT) Draws rotated text.
-- @string text Text to draw.
-- @number x X coordinate.
-- @number y Y coordinate.
-- @number xScale Horizontal scale of text.
-- @number yScale Vertical scale of text.
-- @number angle Angle to rotate text by.
function draw.TextRotated(text, x, y, xScale, yScale, angle)
matrixAngle.y = angle
matrix:SetAngle(matrixAngle)
matrixTranslation.x = x
matrixTranslation.y = y
matrix:SetTranslation(matrixTranslation)
matrixScale.x = xScale
matrixScale.y = yScale
matrix:Scale(matrixScale)
SetTextPos(0, 0)
PushModelMatrix(matrix)
DrawText(text)
PopModelMatrix()
end
--- (CLIENT) Draws a circle.
-- @number x X coordinate.
-- @number y Y coordinate.
-- @number radius Radius of circle.
-- @number segments Number of line segments to use.
function draw.Circle(x, y, radius, segments)
local points = {};
table.insert(points, {
x = x,
y = y,
u = 0.5,
v = 0.5
});
for i = 0, segments do
local angle = math.rad((i / segments) * -360);
table.insert(points, {
x = x + math.sin(angle) * radius,
y = y + math.cos(angle) * radius,
u = math.sin(angle) / 2 + 0.5,
v = math.cos(angle) / 2 + 0.5
});
end;
local angle = math.rad(0);
table.insert(points, {
x = x + math.sin(angle) * radius,
y = y + math.cos(angle) * radius,
u = math.sin(angle) / 2 + 0.5,
v = math.cos(angle) / 2 + 0.5
});
surface.DrawPoly(points);
end;
end;