Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- This module holds any type of chatting functions
- CATEGORY_NAME = "Chat"
- ------------------------------ Psay ------------------------------
- function ulx.psay( calling_ply, target_ply, message )
- if calling_ply:GetNWBool( "ulx_muted", false ) then
- ULib.tsayError( calling_ply, "You are muted, and therefore cannot speak! Use asay for admin chat if urgent.", true )
- return
- end
- ulx.fancyLog( { target_ply, calling_ply }, "#P to #P: " .. message, calling_ply, target_ply )
- end
- local psay = ulx.command( CATEGORY_NAME, "ulx psay", ulx.psay, "!p", true )
- psay:addParam{ type=ULib.cmds.PlayerArg, target="!^", ULib.cmds.ignoreCanTarget }
- psay:addParam{ type=ULib.cmds.StringArg, hint="message", ULib.cmds.takeRestOfLine }
- psay:defaultAccess( ULib.ACCESS_ALL )
- psay:help( "Send a private message to target." )
- ------------------------------ Asay ------------------------------
- local seeasayAccess = "ulx seeasay"
- if SERVER then ULib.ucl.registerAccess( seeasayAccess, ULib.ACCESS_OPERATOR, "Ability to see 'ulx asay'", "Other" ) end -- Give operators access to see asays echoes by default
- function ulx.asay( calling_ply, message )
- local format
- local me = "/me "
- if message:sub( 1, me:len() ) == me then
- format = "(ADMINS) *** #P #s"
- message = message:sub( me:len() + 1 )
- else
- format = "#P to admins: #s"
- end
- local players = player.GetAll()
- for i=#players, 1, -1 do
- local v = players[ i ]
- if not ULib.ucl.query( v, seeasayAccess ) and v ~= calling_ply then -- Calling player always gets to see the echo
- table.remove( players, i )
- end
- end
- ulx.fancyLog( players, format, calling_ply, message )
- end
- local asay = ulx.command( CATEGORY_NAME, "ulx asay", ulx.asay, "@", true, true )
- asay:addParam{ type=ULib.cmds.StringArg, hint="message", ULib.cmds.takeRestOfLine }
- asay:defaultAccess( ULib.ACCESS_ALL )
- asay:help( "Send a message to currently connected admins." )
- ------------------------------ Tsay ------------------------------
- function ulx.tsay( calling_ply, message )
- ULib.tsay( _, message )
- if ULib.toBool( GetConVarNumber( "ulx_logChat" ) ) then
- ulx.logString( string.format( "(tsay from %s) %s", calling_ply:IsValid() and calling_ply:Nick() or "Console", message ) )
- end
- end
- local tsay = ulx.command( CATEGORY_NAME, "ulx tsay", ulx.tsay, "@@", true, true )
- tsay:addParam{ type=ULib.cmds.StringArg, hint="message", ULib.cmds.takeRestOfLine }
- tsay:defaultAccess( ULib.ACCESS_ADMIN )
- tsay:help( "Send a message to everyone in the chat box." )
- ------------------------------ Csay ------------------------------
- function ulx.csay( calling_ply, message )
- ULib.csay( _, message )
- if ULib.toBool( GetConVarNumber( "ulx_logChat" ) ) then
- ulx.logString( string.format( "(csay from %s) %s", calling_ply:IsValid() and calling_ply:Nick() or "Console", message ) )
- end
- end
- local csay = ulx.command( CATEGORY_NAME, "ulx csay", ulx.csay, "@@@", true, true )
- csay:addParam{ type=ULib.cmds.StringArg, hint="message", ULib.cmds.takeRestOfLine }
- csay:defaultAccess( ULib.ACCESS_ADMIN )
- csay:help( "Send a message to everyone in the middle of their screen." )
- ------------------------------ Thetime ------------------------------
- local waittime = 60
- local lasttimeusage = -waittime
- function ulx.thetime( calling_ply )
- if lasttimeusage + waittime > CurTime() then
- ULib.tsayError( calling_ply, "I just told you what time it is! Please wait " .. waittime .. " seconds before using this command again", true )
- return
- end
- lasttimeusage = CurTime()
- ulx.fancyLog( "The time is now #s.", os.date( "%I:%M %p") )
- end
- local thetime = ulx.command( CATEGORY_NAME, "ulx thetime", ulx.thetime, "!thetime" )
- thetime:defaultAccess( ULib.ACCESS_ALL )
- thetime:help( "Shows you the server time." )
- ------------------------------ Adverts ------------------------------
- ulx.adverts = ulx.adverts or {}
- local adverts = ulx.adverts -- For XGUI, too lazy to change all refs
- local function doAdvert( group, id )
- if adverts[ group ][ id ] == nil then
- if adverts[ group ].removed_last then
- adverts[ group ].removed_last = nil
- id = 1
- else
- id = #adverts[ group ]
- end
- end
- local info = adverts[ group ][ id ]
- local message = string.gsub( info.message, "%%curmap%%", game.GetMap() )
- message = string.gsub( message, "%%host%%", GetConVarString( "hostname" ) )
- message = string.gsub( message, "%%ulx_version%%", ULib.pluginVersionStr( "ULX" ) )
- if not info.len then -- tsay
- local lines = ULib.explode( "\\n", message )
- for i, line in ipairs( lines ) do
- local trimmed = line:Trim()
- if trimmed:len() > 0 then
- ULib.tsayColor( _, true, info.color, trimmed ) -- Delaying runs one message every frame (to ensure correct order)
- end
- end
- else
- ULib.csay( _, message, info.color, info.len )
- end
- ULib.queueFunctionCall( function()
- local nextid = math.fmod( id, #adverts[ group ] ) + 1
- timer.Remove( "ULXAdvert" .. type( group ) .. group )
- timer.Create( "ULXAdvert" .. type( group ) .. group, adverts[ group ][ nextid ].rpt, 1, function() doAdvert( group, nextid ) end )
- end )
- end
- -- Whether or not it's a csay is determined by whether there's a value specified in "len"
- function ulx.addAdvert( message, rpt, group, color, len )
- local t
- if group then
- t = adverts[ tostring( group ) ]
- if not t then
- t = {}
- adverts[ tostring( group ) ] = t
- end
- else
- group = table.insert( adverts, {} )
- t = adverts[ group ]
- end
- local id = table.insert( t, { message=message, rpt=rpt, color=color, len=len } )
- if not timer.Exists( "ULXAdvert" .. type( group ) .. group ) then
- timer.Create( "ULXAdvert" .. type( group ) .. group, rpt, 1, function() doAdvert( group, id ) end )
- end
- end
- ------------------------------ Gimp ------------------------------
- ulx.gimpSays = ulx.gimpSays or {} -- Holds gimp says
- local gimpSays = ulx.gimpSays -- For XGUI, too lazy to change all refs
- local ID_GIMP = 1
- local ID_MUTE = 2
- function ulx.addGimpSay( say )
- table.insert( gimpSays, say )
- end
- function ulx.clearGimpSays()
- table.Empty( gimpSays )
- end
- function ulx.gimp( calling_ply, target_plys, should_ungimp )
- for i=1, #target_plys do
- local v = target_plys[ i ]
- if should_ungimp then
- v.gimp = nil
- else
- v.gimp = ID_GIMP
- end
- v:SetNWBool("ulx_gimped", not should_ungimp)
- end
- if not should_ungimp then
- ulx.fancyLogAdmin( calling_ply, "#A gimped #T", target_plys )
- else
- ulx.fancyLogAdmin( calling_ply, "#A ungimped #T", target_plys )
- end
- end
- local gimp = ulx.command( CATEGORY_NAME, "ulx gimp", ulx.gimp, "!gimp" )
- gimp:addParam{ type=ULib.cmds.PlayersArg }
- gimp:addParam{ type=ULib.cmds.BoolArg, invisible=true }
- gimp:defaultAccess( ULib.ACCESS_ADMIN )
- gimp:help( "Gimps target(s) so they are unable to chat normally." )
- gimp:setOpposite( "ulx ungimp", {_, _, true}, "!ungimp" )
- ------------------------------ Mute ------------------------------
- function ulx.mute( calling_ply, target_plys, should_unmute )
- for i=1, #target_plys do
- local v = target_plys[ i ]
- if should_unmute then
- v.gimp = nil
- else
- v.gimp = ID_MUTE
- end
- v:SetNWBool("ulx_muted", not should_unmute)
- end
- if not should_unmute then
- ulx.fancyLogAdmin( calling_ply, "#A muted #T", target_plys )
- else
- ulx.fancyLogAdmin( calling_ply, "#A unmuted #T", target_plys )
- end
- end
- local mute = ulx.command( CATEGORY_NAME, "ulx mute", ulx.mute, "!mute" )
- mute:addParam{ type=ULib.cmds.PlayersArg }
- mute:addParam{ type=ULib.cmds.BoolArg, invisible=true }
- mute:defaultAccess( ULib.ACCESS_ADMIN )
- mute:help( "Mutes target(s) so they are unable to chat." )
- mute:setOpposite( "ulx unmute", {_, _, true}, "!unmute" )
- if SERVER then
- local function gimpCheck( ply, strText )
- if ply.gimp == ID_MUTE then return "" end
- if ply.gimp == ID_GIMP then
- if #gimpSays < 1 then return nil end
- return gimpSays[ math.random( #gimpSays ) ]
- end
- end
- hook.Add( "PlayerSay", "ULXGimpCheck", gimpCheck, HOOK_LOW )
- end
- ------------------------------ Gag ------------------------------
- function ulx.gag( calling_ply, target_plys, should_ungag )
- local players = player.GetAll()
- for i=1, #target_plys do
- local v = target_plys[ i ]
- v.ulx_gagged = not should_ungag
- v:SetNWBool("ulx_gagged", v.ulx_gagged)
- end
- if not should_ungag then
- ulx.fancyLogAdmin( calling_ply, "#A gagged #T", target_plys )
- else
- ulx.fancyLogAdmin( calling_ply, "#A ungagged #T", target_plys )
- end
- end
- local gag = ulx.command( CATEGORY_NAME, "ulx gag", ulx.gag, "!gag" )
- gag:addParam{ type=ULib.cmds.PlayersArg }
- gag:addParam{ type=ULib.cmds.BoolArg, invisible=true }
- gag:defaultAccess( ULib.ACCESS_ADMIN )
- gag:help( "Gag target(s), disables microphone." )
- gag:setOpposite( "ulx ungag", {_, _, true}, "!ungag" )
- local function gagHook( listener, talker )
- if talker.ulx_gagged then
- return false
- end
- end
- hook.Add( "PlayerCanHearPlayersVoice", "ULXGag", gagHook )
- -- Anti-spam stuff
- if SERVER then
- local chattime_cvar = ulx.convar( "chattime", "1.5", "<time> - Players can only chat every x seconds (anti-spam). 0 to disable.", ULib.ACCESS_ADMIN )
- local function playerSay( ply )
- if not ply.lastChatTime then ply.lastChatTime = 0 end
- local chattime = chattime_cvar:GetFloat()
- if chattime <= 0 then return end
- if ply.lastChatTime + chattime > CurTime() then
- return ""
- else
- ply.lastChatTime = CurTime()
- return
- end
- end
- hook.Add( "PlayerSay", "ulxPlayerSay", playerSay, HOOK_LOW )
- local function meCheck( ply, strText, bTeam )
- local meChatEnabled = GetConVarNumber( "ulx_meChatEnabled" )
- if ply.gimp or meChatEnabled == 0 or (meChatEnabled ~= 2 and GAMEMODE.Name ~= "Sandbox") then return end -- Don't mess
- if strText:sub( 1, 4 ) == "/me " then
- strText = string.format( "*** %s %s", ply:Nick(), strText:sub( 5 ) )
- if not bTeam then
- ULib.tsay( _, strText )
- else
- strText = "(TEAM) " .. strText
- local teamid = ply:Team()
- local players = team.GetPlayers( teamid )
- for _, ply2 in ipairs( players ) do
- ULib.tsay( ply2, strText )
- end
- end
- if game.IsDedicated() then
- Msg( strText .. "\n" ) -- Log to console
- end
- if ULib.toBool( GetConVarNumber( "ulx_logChat" ) ) then
- ulx.logString( strText )
- end
- return ""
- end
- end
- hook.Add( "PlayerSay", "ULXMeCheck", meCheck, HOOK_LOW ) -- Extremely low priority
- end
- local function showWelcome( ply )
- local message = GetConVarString( "ulx_welcomemessage" )
- if not message or message == "" then return end
- message = string.gsub( message, "%%curmap%%", game.GetMap() )
- message = string.gsub( message, "%%host%%", GetConVarString( "hostname" ) )
- message = string.gsub( message, "%%ulx_version%%", ULib.pluginVersionStr( "ULX" ) )
- ply:ChatPrint( message ) -- We're not using tsay because ULib might not be loaded yet. (client side)
- end
- hook.Add( "PlayerInitialSpawn", "ULXWelcome", showWelcome )
- if SERVER then
- ulx.convar( "meChatEnabled", "1", "Allow players to use '/me' in chat. 0 = Disabled, 1 = Sandbox only (Default), 2 = Enabled", ULib.ACCESS_ADMIN )
- ulx.convar( "welcomemessage", "", "<msg> - This is shown to players on join.", ULib.ACCESS_ADMIN )
- end
- local CATEGORY_NAME = "Fun"
- ------------------------------ Slap ------------------------------
- function ulx.slap( calling_ply, target_plys, dmg )
- local affected_plys = {}
- for i=1, #target_plys do
- local v = target_plys[ i ]
- if v:IsFrozen() then
- ULib.tsayError( calling_ply, v:Nick() .. " is frozen!", true )
- else
- ULib.slap( v, dmg )
- table.insert( affected_plys, v )
- end
- end
- ulx.fancyLogAdmin( calling_ply, "#A slapped #T with #i damage", affected_plys, dmg )
- end
- local slap = ulx.command( CATEGORY_NAME, "ulx slap", ulx.slap, "!slap" )
- slap:addParam{ type=ULib.cmds.PlayersArg }
- slap:addParam{ type=ULib.cmds.NumArg, min=0, default=0, hint="damage", ULib.cmds.optional, ULib.cmds.round }
- slap:defaultAccess( ULib.ACCESS_ADMIN )
- slap:help( "Slaps target(s) with given damage." )
- ------------------------------ Whip ------------------------------
- function ulx.whip( calling_ply, target_plys, times, dmg )
- local affected_plys = {}
- for i=1, #target_plys do
- local v = target_plys[ i ]
- if v.whipped then
- ULib.tsayError( calling_ply, v:Nick() .. " is already being whipped by " .. v.whippedby, true )
- elseif v:IsFrozen() then
- ULib.tsayError( calling_ply, v:Nick() .. " is frozen!", true )
- else
- local dtime = 0
- v.whipped = true
- v.whippedby = calling_ply:IsValid() and calling_ply:Nick() or "(Console)"
- v.whipcount = 0
- v.whipamt = times
- timer.Create( "ulxWhip" .. v:EntIndex(), 0.5, 0, function() -- Repeat forever, we have an unhooker inside.
- if not v:IsValid() then timer.Remove( "ulxWhip" .. v:EntIndex() ) return end -- Gotta make sure they're still there since this is a timer.
- if v.whipcount == v.whipamt or not v:Alive() then
- v.whipped = nil
- v.whippedby = nil
- v.whipcount = nil
- v.whipamt = nil
- timer.Remove( "ulxWhip" .. v:EntIndex() )
- else
- ULib.slap( v, dmg )
- v.whipcount = v.whipcount + 1
- end
- end )
- table.insert( affected_plys, v )
- end
- end
- ulx.fancyLogAdmin( calling_ply, "#A whipped #T #i times with #i damage", affected_plys, times, dmg )
- end
- local whip = ulx.command( CATEGORY_NAME, "ulx whip", ulx.whip, "!whip" )
- whip:addParam{ type=ULib.cmds.PlayersArg }
- whip:addParam{ type=ULib.cmds.NumArg, min=2, max=100, default=10, hint="times", ULib.cmds.optional, ULib.cmds.round }
- whip:addParam{ type=ULib.cmds.NumArg, min=0, default=0, hint="damage", ULib.cmds.optional, ULib.cmds.round }
- whip:defaultAccess( ULib.ACCESS_ADMIN )
- whip:help( "Slaps target(s) x times with given damage each time." )
- ------------------------------ Slay ------------------------------
- function ulx.slay( calling_ply, target_plys )
- local affected_plys = {}
- for i=1, #target_plys do
- local v = target_plys[ i ]
- if ulx.getExclusive( v, calling_ply ) then
- ULib.tsayError( calling_ply, ulx.getExclusive( v, calling_ply ), true )
- elseif not v:Alive() then
- ULib.tsayError( calling_ply, v:Nick() .. " is already dead!", true )
- elseif v:IsFrozen() then
- ULib.tsayError( calling_ply, v:Nick() .. " is frozen!", true )
- else
- v:Kill()
- table.insert( affected_plys, v )
- end
- end
- ulx.fancyLogAdmin( calling_ply, "#A slayed #T", affected_plys )
- end
- local slay = ulx.command( CATEGORY_NAME, "ulx slay", ulx.slay, "!slay" )
- slay:addParam{ type=ULib.cmds.PlayersArg }
- slay:defaultAccess( ULib.ACCESS_ADMIN )
- slay:help( "Slays target(s)." )
- ------------------------------ Sslay ------------------------------
- function ulx.sslay( calling_ply, target_plys )
- local affected_plys = {}
- for i=1, #target_plys do
- local v = target_plys[ i ]
- if ulx.getExclusive( v, calling_ply ) then
- ULib.tsayError( calling_ply, ulx.getExclusive( v, calling_ply ), true )
- elseif not v:Alive() then
- ULib.tsayError( calling_ply, v:Nick() .. " is already dead!", true )
- elseif v:IsFrozen() then
- ULib.tsayError( calling_ply, v:Nick() .. " is frozen!", true )
- else
- if v:InVehicle() then
- v:ExitVehicle()
- end
- v:KillSilent()
- table.insert( affected_plys, v )
- end
- end
- ulx.fancyLogAdmin( calling_ply, "#A silently slayed #T", affected_plys )
- end
- local sslay = ulx.command( CATEGORY_NAME, "ulx sslay", ulx.sslay, "!sslay" )
- sslay:addParam{ type=ULib.cmds.PlayersArg }
- sslay:defaultAccess( ULib.ACCESS_ADMIN )
- sslay:help( "Silently slays target(s)." )
- ------------------------------ Ignite ------------------------------
- function ulx.ignite( calling_ply, target_plys, seconds, should_extinguish )
- local affected_plys = {}
- for i=1, #target_plys do
- local v = target_plys[ i ]
- if not should_extinguish then
- v:Ignite( seconds )
- v.ulx_ignited_until = CurTime() + seconds
- table.insert( affected_plys, v )
- elseif v:IsOnFire() then
- v:Extinguish()
- v.ulx_ignited_until = nil
- table.insert( affected_plys, v )
- end
- end
- if not should_extinguish then
- ulx.fancyLogAdmin( calling_ply, "#A ignited #T for #i seconds", affected_plys, seconds )
- else
- ulx.fancyLogAdmin( calling_ply, "#A extinguished #T", affected_plys )
- end
- end
- local ignite = ulx.command( CATEGORY_NAME, "ulx ignite", ulx.ignite, "!ignite" )
- ignite:addParam{ type=ULib.cmds.PlayersArg }
- ignite:addParam{ type=ULib.cmds.NumArg, min=1, max=300, default=300, hint="seconds", ULib.cmds.optional, ULib.cmds.round }
- ignite:addParam{ type=ULib.cmds.BoolArg, invisible=true }
- ignite:defaultAccess( ULib.ACCESS_ADMIN )
- ignite:help( "Ignites target(s)." )
- ignite:setOpposite( "ulx unignite", {_, _, _, true}, "!unignite" )
- local function checkFireDeath( ply )
- if ply.ulx_ignited_until and ply.ulx_ignited_until >= CurTime() and ply:IsOnFire() then
- ply:Extinguish()
- ply.ulx_ignited_until = nil
- end
- end
- hook.Add( "PlayerDeath", "ULXCheckFireDeath", checkFireDeath, HOOK_MONITOR_HIGH )
- ------------------------------ Unigniteall ------------------------------
- function ulx.unigniteall( calling_ply )
- local flame_ents = ents.FindByClass( 'entityflame' )
- for _,v in ipairs( flame_ents ) do
- if v:IsValid() then
- v:Remove()
- end
- end
- local plys = player.GetAll()
- for _, v in ipairs( plys ) do
- if v:IsOnFire() then
- v:Extinguish()
- v.ulx_ignited_until = nil
- end
- end
- ulx.fancyLogAdmin( calling_ply, "#A extinguished everything" )
- end
- local unigniteall = ulx.command( CATEGORY_NAME, "ulx unigniteall", ulx.unigniteall, "!unigniteall" )
- unigniteall:defaultAccess( ULib.ACCESS_ADMIN )
- unigniteall:help( "Extinguishes all players and all entities." )
- ------------------------------ Playsound ------------------------------
- function ulx.playsound( calling_ply, sound )
- if not ULib.fileExists( "sound/" .. sound ) then
- ULib.tsayError( calling_ply, "That sound doesn't exist on the server!", true )
- return
- end
- umsg.Start( "ulib_sound" )
- umsg.String( Sound( sound ) )
- umsg.End()
- ulx.fancyLogAdmin( calling_ply, "#A played sound #s", sound )
- end
- local playsound = ulx.command( CATEGORY_NAME, "ulx playsound", ulx.playsound )
- playsound:addParam{ type=ULib.cmds.StringArg, hint="sound", autocomplete_fn=ulx.soundComplete }
- playsound:defaultAccess( ULib.ACCESS_ADMIN )
- playsound:help( "Plays a sound (relative to sound dir)." )
- ------------------------------ Freeze ------------------------------
- function ulx.freeze( calling_ply, target_plys, should_unfreeze )
- local affected_plys = {}
- for i=1, #target_plys do
- if not should_unfreeze and ulx.getExclusive( target_plys[ i ], calling_ply ) then
- ULib.tsayError( calling_ply, ulx.getExclusive( target_plys[ i ], calling_ply ), true )
- else
- local v = target_plys[ i ]
- if v:InVehicle() then
- v:ExitVehicle()
- end
- if not should_unfreeze then
- v:Lock()
- v.frozen = true
- ulx.setExclusive( v, "frozen" )
- else
- v:UnLock()
- v.frozen = nil
- ulx.clearExclusive( v )
- end
- v:DisallowSpawning( not should_unfreeze )
- ulx.setNoDie( v, not should_unfreeze )
- table.insert( affected_plys, v )
- if v.whipped then
- v.whipcount = v.whipamt -- Will make it remove
- end
- end
- end
- if not should_unfreeze then
- ulx.fancyLogAdmin( calling_ply, "#A froze #T", affected_plys )
- else
- ulx.fancyLogAdmin( calling_ply, "#A unfroze #T", affected_plys )
- end
- end
- local freeze = ulx.command( CATEGORY_NAME, "ulx freeze", ulx.freeze, "!freeze" )
- freeze:addParam{ type=ULib.cmds.PlayersArg }
- freeze:addParam{ type=ULib.cmds.BoolArg, invisible=true }
- freeze:defaultAccess( ULib.ACCESS_ADMIN )
- freeze:help( "Freezes target(s)." )
- freeze:setOpposite( "ulx unfreeze", {_, _, true}, "!unfreeze" )
- ------------------------------ God ------------------------------
- function ulx.god( calling_ply, target_plys, should_revoke )
- if not target_plys[ 1 ]:IsValid() then
- if not should_revoke then
- Msg( "You are the console, you are already god.\n" )
- else
- Msg( "Your position of god is irrevocable; if you don't like it, leave the matrix.\n" )
- end
- return
- end
- local affected_plys = {}
- for i=1, #target_plys do
- local v = target_plys[ i ]
- if ulx.getExclusive( v, calling_ply ) then
- ULib.tsayError( calling_ply, ulx.getExclusive( v, calling_ply ), true )
- else
- if not should_revoke then
- v:GodEnable()
- v.ULXHasGod = true
- else
- v:GodDisable()
- v.ULXHasGod = nil
- end
- table.insert( affected_plys, v )
- end
- end
- if not should_revoke then
- ulx.fancyLogAdmin( calling_ply, "#A granted god mode upon #T", affected_plys )
- else
- ulx.fancyLogAdmin( calling_ply, "#A revoked god mode from #T", affected_plys )
- end
- end
- local god = ulx.command( CATEGORY_NAME, "ulx god", ulx.god, "!god" )
- god:addParam{ type=ULib.cmds.PlayersArg, ULib.cmds.optional }
- god:addParam{ type=ULib.cmds.BoolArg, invisible=true }
- god:defaultAccess( ULib.ACCESS_ADMIN )
- god:help( "Grants god mode to target(s)." )
- god:setOpposite( "ulx ungod", {_, _, true}, "!ungod" )
- ------------------------------ Hp ------------------------------
- function ulx.hp( calling_ply, target_plys, amount )
- for i=1, #target_plys do
- target_plys[ i ]:SetHealth( amount )
- end
- ulx.fancyLogAdmin( calling_ply, "#A set the hp for #T to #i", target_plys, amount )
- end
- local hp = ulx.command( CATEGORY_NAME, "ulx hp", ulx.hp, "!hp" )
- hp:addParam{ type=ULib.cmds.PlayersArg }
- hp:addParam{ type=ULib.cmds.NumArg, min=1, max=2^32/2-1, hint="hp", ULib.cmds.round }
- hp:defaultAccess( ULib.ACCESS_ADMIN )
- hp:help( "Sets the hp for target(s)." )
- ------------------------------ Armor ------------------------------
- function ulx.armor( calling_ply, target_plys, amount )
- for i=1, #target_plys do
- target_plys[ i ]:SetArmor( amount )
- end
- ulx.fancyLogAdmin( calling_ply, "#A set the armor for #T to #i", target_plys, amount )
- end
- local armor = ulx.command( CATEGORY_NAME, "ulx armor", ulx.armor, "!armor" )
- armor:addParam{ type=ULib.cmds.PlayersArg }
- armor:addParam{ type=ULib.cmds.NumArg, min=0, max=255, hint="armor", ULib.cmds.round }
- armor:defaultAccess( ULib.ACCESS_ADMIN )
- armor:help( "Sets the armor for target(s)." )
- ------------------------------ Cloak ------------------------------
- function ulx.cloak( calling_ply, target_plys, amount, should_uncloak )
- if not target_plys[ 1 ]:IsValid() then
- Msg( "You are always invisible.\n" )
- return
- end
- amount = 255 - amount
- for i=1, #target_plys do
- ULib.invisible( target_plys[ i ], not should_uncloak, amount )
- end
- if not should_uncloak then
- ulx.fancyLogAdmin( calling_ply, "#A cloaked #T by amount #i", target_plys, amount )
- else
- ulx.fancyLogAdmin( calling_ply, "#A uncloaked #T", target_plys )
- end
- end
- local cloak = ulx.command( CATEGORY_NAME, "ulx cloak", ulx.cloak, "!cloak" )
- cloak:addParam{ type=ULib.cmds.PlayersArg, ULib.cmds.optional }
- cloak:addParam{ type=ULib.cmds.NumArg, min=0, max=255, default=255, hint="amount", ULib.cmds.round, ULib.cmds.optional }
- cloak:addParam{ type=ULib.cmds.BoolArg, invisible=true }
- cloak:defaultAccess( ULib.ACCESS_ADMIN )
- cloak:help( "Cloaks target(s)." )
- cloak:setOpposite( "ulx uncloak", {_, _, _, true}, "!uncloak" )
- ------------------------------ Blind ------------------------------
- function ulx.blind( calling_ply, target_plys, amount, should_unblind )
- for i=1, #target_plys do
- local v = target_plys[ i ]
- umsg.Start( "ulx_blind", v )
- umsg.Bool( not should_unblind )
- umsg.Short( amount )
- umsg.End()
- if should_unblind then
- if v.HadCamera then
- v:Give( "gmod_camera" )
- end
- v.HadCamera = nil
- else
- if v.HadCamera == nil then -- In case blind is run twice
- v.HadCamera = v:HasWeapon( "gmod_camera" )
- end
- v:StripWeapon( "gmod_camera" )
- end
- end
- if not should_unblind then
- ulx.fancyLogAdmin( calling_ply, "#A blinded #T by amount #i", target_plys, amount )
- else
- ulx.fancyLogAdmin( calling_ply, "#A unblinded #T", target_plys )
- end
- end
- local blind = ulx.command( CATEGORY_NAME, "ulx blind", ulx.blind, "!blind" )
- blind:addParam{ type=ULib.cmds.PlayersArg }
- blind:addParam{ type=ULib.cmds.NumArg, min=0, max=255, default=255, hint="amount", ULib.cmds.round, ULib.cmds.optional }
- blind:addParam{ type=ULib.cmds.BoolArg, invisible=true }
- blind:defaultAccess( ULib.ACCESS_ADMIN )
- blind:help( "Blinds target(s)." )
- blind:setOpposite( "ulx unblind", {_, _, _, true}, "!unblind" )
- ------------------------------ Jail ------------------------------
- local doJail
- local jailableArea
- function ulx.jail( calling_ply, target_plys, seconds, should_unjail )
- local affected_plys = {}
- for i=1, #target_plys do
- local v = target_plys[ i ]
- if not should_unjail then
- if ulx.getExclusive( v, calling_ply ) then
- ULib.tsayError( calling_ply, ulx.getExclusive( v, calling_ply ), true )
- elseif not jailableArea( v:GetPos() ) then
- ULib.tsayError( calling_ply, v:Nick() .. " is not in an area where a jail can be placed!", true )
- else
- doJail( v, seconds )
- table.insert( affected_plys, v )
- end
- elseif v.jail then
- v.jail.unjail()
- v.jail = nil
- table.insert( affected_plys, v )
- end
- end
- if not should_unjail then
- local str = "#A jailed #T"
- if seconds > 0 then
- str = str .. " for #i seconds"
- end
- ulx.fancyLogAdmin( calling_ply, str, affected_plys, seconds )
- else
- ulx.fancyLogAdmin( calling_ply, "#A unjailed #T", affected_plys )
- end
- end
- local jail = ulx.command( CATEGORY_NAME, "ulx jail", ulx.jail, "!jail" )
- jail:addParam{ type=ULib.cmds.PlayersArg }
- jail:addParam{ type=ULib.cmds.NumArg, min=0, default=0, hint="seconds, 0 is forever", ULib.cmds.round, ULib.cmds.optional }
- jail:addParam{ type=ULib.cmds.BoolArg, invisible=true }
- jail:defaultAccess( ULib.ACCESS_ADMIN )
- jail:help( "Jails target(s)." )
- jail:setOpposite( "ulx unjail", {_, _, _, true}, "!unjail" )
- ------------------------------ Jail TP ------------------------------
- function ulx.jailtp( calling_ply, target_ply, seconds )
- local t = {}
- t.start = calling_ply:GetPos() + Vector( 0, 0, 32 ) -- Move them up a bit so they can travel across the ground
- t.endpos = calling_ply:GetPos() + calling_ply:EyeAngles():Forward() * 16384
- t.filter = target_ply
- if target_ply ~= calling_ply then
- t.filter = { target_ply, calling_ply }
- end
- local tr = util.TraceEntity( t, target_ply )
- local pos = tr.HitPos
- if ulx.getExclusive( target_ply, calling_ply ) then
- ULib.tsayError( calling_ply, ulx.getExclusive( target_ply, calling_ply ), true )
- return
- elseif not target_ply:Alive() then
- ULib.tsayError( calling_ply, target_ply:Nick() .. " is dead!", true )
- return
- elseif not jailableArea( pos ) then
- ULib.tsayError( calling_ply, "That is not an area where a jail can be placed!", true )
- return
- else
- target_ply.ulx_prevpos = target_ply:GetPos()
- target_ply.ulx_prevang = target_ply:EyeAngles()
- if target_ply:InVehicle() then
- target_ply:ExitVehicle()
- end
- target_ply:SetPos( pos )
- target_ply:SetLocalVelocity( Vector( 0, 0, 0 ) ) -- Stop!
- doJail( target_ply, seconds )
- end
- local str = "#A teleported and jailed #T"
- if seconds > 0 then
- str = str .. " for #i seconds"
- end
- ulx.fancyLogAdmin( calling_ply, str, target_ply, seconds )
- end
- local jailtp = ulx.command( CATEGORY_NAME, "ulx jailtp", ulx.jailtp, "!jailtp" )
- jailtp:addParam{ type=ULib.cmds.PlayerArg }
- jailtp:addParam{ type=ULib.cmds.NumArg, min=0, default=0, hint="seconds, 0 is forever", ULib.cmds.round, ULib.cmds.optional }
- jailtp:defaultAccess( ULib.ACCESS_ADMIN )
- jailtp:help( "Teleports, then jails target(s)." )
- local function jailCheck()
- local remove_timer = true
- local players = player.GetAll()
- for i=1, #players do
- local ply = players[ i ]
- if ply.jail then
- remove_timer = false
- end
- if ply.jail and (ply.jail.pos-ply:GetPos()):LengthSqr() >= 6500 then
- ply:SetPos( ply.jail.pos )
- if ply.jail.jail_until then
- doJail( ply, ply.jail.jail_until - CurTime() )
- else
- doJail( ply, 0 )
- end
- end
- end
- if remove_timer then
- timer.Remove( "ULXJail" )
- end
- end
- jailableArea = function( pos )
- entList = ents.FindInBox( pos - Vector( 35, 35, 5 ), pos + Vector( 35, 35, 110 ) )
- for i=1, #entList do
- if entList[ i ]:GetClass() == "trigger_remove" then
- return false
- end
- end
- return true
- end
- local mdl1 = Model( "models/props_building_details/Storefront_Template001a_Bars.mdl" )
- local jail = {
- { pos = Vector( 0, 0, -5 ), ang = Angle( 90, 0, 0 ), mdl=mdl1 },
- { pos = Vector( 0, 0, 97 ), ang = Angle( 90, 0, 0 ), mdl=mdl1 },
- { pos = Vector( 21, 31, 46 ), ang = Angle( 0, 90, 0 ), mdl=mdl1 },
- { pos = Vector( 21, -31, 46 ), ang = Angle( 0, 90, 0 ), mdl=mdl1 },
- { pos = Vector( -21, 31, 46 ), ang = Angle( 0, 90, 0 ), mdl=mdl1 },
- { pos = Vector( -21, -31, 46), ang = Angle( 0, 90, 0 ), mdl=mdl1 },
- { pos = Vector( -52, 0, 46 ), ang = Angle( 0, 0, 0 ), mdl=mdl1 },
- { pos = Vector( 52, 0, 46 ), ang = Angle( 0, 0, 0 ), mdl=mdl1 },
- }
- doJail = function( v, seconds )
- if v.jail then -- They're already jailed
- v.jail.unjail()
- end
- if v:InVehicle() then
- local vehicle = v:GetParent()
- v:ExitVehicle()
- vehicle:Remove()
- end
- -- Force other players to let go of this player
- if v.physgunned_by then
- for ply, v in pairs( v.physgunned_by ) do
- if ply:IsValid() and ply:GetActiveWeapon():IsValid() and ply:GetActiveWeapon():GetClass() == "weapon_physgun" then
- ply:ConCommand( "-attack" )
- end
- end
- end
- if v:GetMoveType() == MOVETYPE_NOCLIP then -- Take them out of noclip
- v:SetMoveType( MOVETYPE_WALK )
- end
- local pos = v:GetPos()
- local walls = {}
- for _, info in ipairs( jail ) do
- local ent = ents.Create( "prop_physics" )
- ent:SetModel( info.mdl )
- ent:SetPos( pos + info.pos )
- ent:SetAngles( info.ang )
- ent:Spawn()
- ent:GetPhysicsObject():EnableMotion( false )
- ent:SetMoveType( MOVETYPE_NONE )
- ent.jailWall = true
- table.insert( walls, ent )
- end
- local key = {}
- local function unjail()
- if not v:IsValid() or not v.jail or v.jail.key ~= key then -- Nope
- return
- end
- for _, ent in ipairs( walls ) do
- if ent:IsValid() then
- ent:DisallowDeleting( false )
- ent:Remove()
- end
- end
- if not v:IsValid() then return end -- Make sure they're still connected
- v:DisallowNoclip( false )
- v:DisallowMoving( false )
- v:DisallowSpawning( false )
- v:DisallowVehicles( false )
- ulx.clearExclusive( v )
- ulx.setNoDie( v, false )
- v.jail = nil
- end
- if seconds > 0 then
- timer.Simple( seconds, unjail )
- end
- local function newWall( old, new )
- table.insert( walls, new )
- end
- for _, ent in ipairs( walls ) do
- ent:DisallowDeleting( true, newWall )
- ent:DisallowMoving( true )
- end
- v:DisallowNoclip( true )
- v:DisallowMoving( true )
- v:DisallowSpawning( true )
- v:DisallowVehicles( true )
- v.jail = { pos=pos, unjail=unjail, key=key }
- if seconds > 0 then
- v.jail.jail_until = CurTime() + seconds
- end
- ulx.setExclusive( v, "in jail" )
- ulx.setNoDie( v, true )
- timer.Create( "ULXJail", 1, 0, jailCheck )
- end
- local function jailDisconnectedCheck( ply )
- if ply.jail then
- ply.jail.unjail()
- end
- end
- hook.Add( "PlayerDisconnected", "ULXJailDisconnectedCheck", jailDisconnectedCheck, HOOK_MONITOR_HIGH )
- local function playerPickup( ply, ent )
- if CLIENT then return end
- if ent:IsPlayer() then
- ent.physgunned_by = ent.physgunned_by or {}
- ent.physgunned_by[ ply ] = true
- end
- end
- hook.Add( "PhysgunPickup", "ulxPlayerPickupJailCheck", playerPickup, HOOK_MONITOR_HIGH )
- local function playerDrop( ply, ent )
- if CLIENT then return end
- if ent:IsPlayer() and ent.physgunned_by then
- ent.physgunned_by[ ply ] = nil
- end
- end
- hook.Add( "PhysgunDrop", "ulxPlayerDropJailCheck", playerDrop )
- ------------------------------ Ragdoll ------------------------------
- local function ragdollPlayer( v )
- if v:InVehicle() then
- local vehicle = v:GetParent()
- v:ExitVehicle()
- end
- ULib.getSpawnInfo( v ) -- Collect information so we can respawn them in the same state.
- local ragdoll = ents.Create( "prop_ragdoll" )
- ragdoll.ragdolledPly = v
- ragdoll:SetPos( v:GetPos() )
- local velocity = v:GetVelocity()
- ragdoll:SetAngles( v:GetAngles() )
- ragdoll:SetModel( v:GetModel() )
- ragdoll:Spawn()
- ragdoll:Activate()
- v:SetParent( ragdoll ) -- So their player ent will match up (position-wise) with where their ragdoll is.
- -- Set velocity for each piece of the ragdoll
- local j = 1
- while true do -- Break inside
- local phys_obj = ragdoll:GetPhysicsObjectNum( j )
- if phys_obj then
- phys_obj:SetVelocity( velocity )
- j = j + 1
- else
- break
- end
- end
- v:Spectate( OBS_MODE_CHASE )
- v:SpectateEntity( ragdoll )
- v:StripWeapons() -- Otherwise they can still use the weapons.
- ragdoll:DisallowDeleting( true, function( old, new )
- if v:IsValid() then v.ragdoll = new end
- end )
- v:DisallowSpawning( true )
- v.ragdoll = ragdoll
- ulx.setExclusive( v, "ragdolled" )
- end
- local function unragdollPlayer( v )
- v:DisallowSpawning( false )
- v:SetParent()
- v:UnSpectate() -- Need this for DarkRP for some reason, works fine without it in sbox
- local ragdoll = v.ragdoll
- v.ragdoll = nil -- Gotta do this before spawn or our hook catches it
- if not ragdoll:IsValid() then -- Something must have removed it, just spawn
- ULib.spawn( v, true )
- else
- local pos = ragdoll:GetPos()
- pos.z = pos.z + 10 -- So they don't end up in the ground
- ULib.spawn( v, true )
- v:SetPos( pos )
- v:SetVelocity( ragdoll:GetVelocity() )
- local yaw = ragdoll:GetAngles().yaw
- v:SetAngles( Angle( 0, yaw, 0 ) )
- ragdoll:DisallowDeleting( false )
- ragdoll:Remove()
- end
- ulx.clearExclusive( v )
- end
- function ulx.ragdoll( calling_ply, target_plys, should_unragdoll )
- local affected_plys = {}
- for i=1, #target_plys do
- local v = target_plys[ i ]
- if not should_unragdoll then
- if ulx.getExclusive( v, calling_ply ) then
- ULib.tsayError( calling_ply, ulx.getExclusive( v, calling_ply ), true )
- elseif not v:Alive() then
- ULib.tsayError( calling_ply, v:Nick() .. " is dead and cannot be ragdolled!", true )
- else
- ragdollPlayer( v )
- table.insert( affected_plys, v )
- end
- elseif v.ragdoll then -- Only if they're ragdolled...
- unragdollPlayer( v )
- table.insert( affected_plys, v )
- end
- end
- if not should_unragdoll then
- ulx.fancyLogAdmin( calling_ply, "#A ragdolled #T", affected_plys )
- else
- ulx.fancyLogAdmin( calling_ply, "#A unragdolled #T", affected_plys )
- end
- end
- local ragdoll = ulx.command( CATEGORY_NAME, "ulx ragdoll", ulx.ragdoll, "!ragdoll" )
- ragdoll:addParam{ type=ULib.cmds.PlayersArg }
- ragdoll:addParam{ type=ULib.cmds.BoolArg, invisible=true }
- ragdoll:defaultAccess( ULib.ACCESS_ADMIN )
- ragdoll:help( "ragdolls target(s)." )
- ragdoll:setOpposite( "ulx unragdoll", {_, _, true}, "!unragdoll" )
- local function ragdollSpawnCheck( ply )
- if ply.ragdoll then
- timer.Simple( 0.01, function() -- Doesn't like us using it instantly
- if not ply:IsValid() then return end -- Make sure they're still here
- ply:Spectate( OBS_MODE_CHASE )
- ply:SpectateEntity( ply.ragdoll )
- ply:StripWeapons() -- Otherwise they can still use the weapons.
- end )
- end
- end
- hook.Add( "PlayerSpawn", "ULXRagdollSpawnCheck", ragdollSpawnCheck )
- local function ragdollDisconnectedCheck( ply )
- if ply.ragdoll then
- ply.ragdoll:DisallowDeleting( false )
- ply.ragdoll:Remove()
- end
- end
- hook.Add( "PlayerDisconnected", "ULXRagdollDisconnectedCheck", ragdollDisconnectedCheck, HOOK_MONITOR_HIGH )
- local function removeRagdollOnCleanup()
- local players = player.GetAll()
- for i=1, #players do
- local ply = players[i]
- if ply.ragdoll then
- ply.ragdollAfterCleanup = true
- unragdollPlayer( ply )
- end
- end
- end
- hook.Add("PreCleanupMap","ULXRagdollBeforeCleanup", removeRagdollOnCleanup )
- local function createRagdollAfterCleanup()
- local players = player.GetAll()
- for i=1, #players do
- local ply = players[i]
- if ply.ragdollAfterCleanup then
- ply.ragdollAfterCleanup = nil
- timer.Simple( 0.1, function() -- Doesn't like us re-creating the ragdoll immediately
- ragdollPlayer( ply )
- end)
- end
- end
- end
- hook.Add("PostCleanupMap","ULXRagdollAfterCleanup", createRagdollAfterCleanup )
- ------------------------------ Maul ------------------------------
- local zombieDeath -- We need these registered up here because functions reference each other.
- local checkMaulDeath
- local function newZombie( pos, ang, ply, b )
- local ent = ents.Create( "npc_fastzombie" )
- ent:SetPos( pos )
- ent:SetAngles( ang )
- ent:Spawn()
- ent:Activate()
- ent:AddRelationship("player D_NU 98") -- Don't attack other players
- ent:AddEntityRelationship( ply, D_HT, 99 ) -- Hate target
- ent:DisallowDeleting( true, _, true )
- ent:DisallowMoving( true )
- if not b then
- ent:CallOnRemove( "NoDie", zombieDeath, ply )
- end
- return ent
- end
- -- Utility function
- zombieDeath = function( ent, ply )
- if ply.maul_npcs then -- Recreate!
- local pos = ent:GetPos()
- local ang = ent:GetAngles()
- ULib.queueFunctionCall( function() -- Create it next frame because 1. Old NPC won't be in way and 2. We won't overflow the server while shutting down with someone being mauled
- if not ply:IsValid() then return end -- Player left
- local ent2 = newZombie( pos, ang, ply )
- table.insert( ply.maul_npcs, ent2 ) -- Don't worry about removing the old one, doesn't matter.
- -- Make sure we didn't make a headcrab!
- local ents = ents.FindByClass( "npc_headcrab_fast" )
- for _, ent in ipairs( ents ) do
- dist = ent:GetPos():Distance( pos )
- if dist < 128 then -- Assume it's from the zombies
- ent:Remove()
- end
- end
- end )
- end
- end
- -- Another utility for maul
- local function maulMoreDamage()
- local players = player.GetAll()
- for _, ply in ipairs( players ) do
- if ply.maul_npcs and ply:Alive() then
- if CurTime() > ply.maulStart + 10 then
- local damage = math.ceil( ply.maulStartHP / 10 ) -- Damage per second
- damage = damage * FrameTime() -- Damage this frame
- damage = math.ceil( damage )
- local newhp = ply:Health() - damage
- if newhp < 1 then newhp = 1 end
- ply:SetHealth( newhp ) -- We don't use takedamage because the player slides across the ground.
- if CurTime() > ply.maulStart + 20 then
- ply:Kill() -- Worst case senario.
- checkMaulDeath( ply ) -- Just in case the death hook is broken
- end
- end
- ply.maul_lasthp = ply:Health()
- end
- end
- end
- function ulx.maul( calling_ply, target_plys )
- local affected_plys = {}
- for i=1, #target_plys do
- local v = target_plys[ i ]
- if ulx.getExclusive( v, calling_ply ) then
- ULib.tsayError( calling_ply, ulx.getExclusive( v, calling_ply ), true )
- elseif not v:Alive() then
- ULib.tsayError( calling_ply, v:Nick() .. " is dead!", true )
- else
- local pos = {}
- local testent = newZombie( Vector( 0, 0, 0 ), Angle( 0, 0, 0 ), v, true ) -- Test ent for traces
- local yawForward = v:EyeAngles().yaw
- local directions = { -- Directions to try
- math.NormalizeAngle( yawForward - 180 ), -- Behind first
- math.NormalizeAngle( yawForward + 90 ), -- Right
- math.NormalizeAngle( yawForward - 90 ), -- Left
- yawForward,
- }
- local t = {}
- t.start = v:GetPos() + Vector( 0, 0, 32 ) -- Move them up a bit so they can travel across the ground
- t.filter = { v, testent }
- for i=1, #directions do -- Check all directions
- t.endpos = v:GetPos() + Angle( 0, directions[ i ], 0 ):Forward() * 47 -- (33 is player width, this is sqrt( 33^2 * 2 ))
- local tr = util.TraceEntity( t, testent )
- if not tr.Hit then
- table.insert( pos, v:GetPos() + Angle( 0, directions[ i ], 0 ):Forward() * 47 )
- end
- end
- testent:DisallowDeleting( false )
- testent:Remove() -- Don't forget to remove our friend now!
- if #pos > 0 then
- v.maul_npcs = {}
- for _, newpos in ipairs( pos ) do
- local newang = (v:GetPos() - newpos):Angle()
- local ent = newZombie( newpos, newang, v )
- table.insert( v.maul_npcs, ent )
- end
- v:SetMoveType( MOVETYPE_WALK )
- v:DisallowNoclip( true )
- v:DisallowSpawning( true )
- v:DisallowVehicles( true )
- v:GodDisable()
- v:SetArmor( 0 ) -- Armor takes waaaay too long for them to take down
- v.maulOrigWalk = v:GetWalkSpeed()
- v.maulOrigSprint = v:GetRunSpeed()
- v:SetWalkSpeed(1)
- v:SetRunSpeed(1)
- v.maulStart = CurTime()
- v.maulStartHP = v:Health()
- hook.Add( "Think", "MaulMoreDamageThink", maulMoreDamage )
- ulx.setExclusive( v, "being mauled" )
- table.insert( affected_plys, v )
- else
- ULib.tsayError( calling_ply, "Can't find a place to put the npcs for " .. v:Nick(), true )
- end
- end
- end
- ulx.fancyLogAdmin( calling_ply, "#A mauled #T", affected_plys )
- end
- local maul = ulx.command( CATEGORY_NAME, "ulx maul", ulx.maul, "!maul" )
- maul:addParam{ type=ULib.cmds.PlayersArg }
- maul:defaultAccess( ULib.ACCESS_SUPERADMIN )
- maul:help( "Maul target(s)." )
- checkMaulDeath = function( ply, weapon, killer )
- if ply.maul_npcs then
- if killer == ply and CurTime() < ply.maulStart + 20 then -- Suicide
- ply:AddFrags( 1 ) -- Won't show on scoreboard
- local pos = ply:GetPos()
- local ang = ply:EyeAngles()
- ULib.queueFunctionCall( function()
- if not ply:IsValid() then return end -- They left
- ply:Spawn()
- ply:SetPos( pos )
- ply:SetEyeAngles( ang )
- ply:SetArmor( 0 )
- ply:SetHealth( ply.maul_lasthp )
- timer.Simple( 0.1, function()
- if not ply:IsValid() then return end -- They left
- ply:SetCollisionGroup( COLLISION_GROUP_WORLD )
- ply:SetWalkSpeed(1)
- ply:SetRunSpeed(1)
- end )
- end )
- return true -- Don't register their death on HUD
- end
- local npcs = ply.maul_npcs
- ply.maul_npcs = nil -- We have to do it this way to signal that we're done mauling
- for _, ent in ipairs( npcs ) do
- if ent:IsValid() then
- ent:DisallowDeleting( false )
- ent:Remove()
- end
- end
- ulx.clearExclusive( ply )
- ply.maulStart = nil
- ply.maul_lasthp = nil
- ply:DisallowNoclip( false )
- ply:DisallowSpawning( false )
- ply:DisallowVehicles( false )
- ply:SetWalkSpeed(ply.maulOrigWalk)
- ply:SetRunSpeed(ply.maulOrigSprint)
- ply.maulOrigWalk = nil
- ply.maulOrigSprint = nil
- ulx.clearExclusive( ply )
- -- Now let's check if there's still players being mauled
- local players = player.GetAll()
- for _, ply in ipairs( players ) do
- if ply.maul_npcs then
- return
- end
- end
- -- No more? Remove hook.
- hook.Remove( "Think", "MaulMoreDamageThink" )
- end
- end
- hook.Add( "PlayerDeath", "ULXCheckMaulDeath", checkMaulDeath, HOOK_HIGH ) -- Hook it first because we're changing speed. Want others to override us.
- local function maulDisconnectedCheck( ply )
- checkMaulDeath( ply ) -- Just run it through the death function
- end
- hook.Add( "PlayerDisconnected", "ULXMaulDisconnectedCheck", maulDisconnectedCheck, HOOK_MONITOR_HIGH )
- ------------------------------ Strip ------------------------------
- function ulx.stripweapons( calling_ply, target_plys )
- for i=1, #target_plys do
- target_plys[ i ]:StripWeapons()
- end
- ulx.fancyLogAdmin( calling_ply, "#A stripped weapons from #T", target_plys )
- end
- local strip = ulx.command( CATEGORY_NAME, "ulx strip", ulx.stripweapons, "!strip" )
- strip:addParam{ type=ULib.cmds.PlayersArg }
- strip:defaultAccess( ULib.ACCESS_ADMIN )
- strip:help( "Strip weapons from target(s)." )
- local CATEGORY_NAME = "Menus"
- if ULib.fileExists( "lua/ulx/modules/cl/motdmenu.lua" ) or ulx.motdmenu_exists then
- local function sendMotd( ply, showMotd )
- if ply.ulxHasMotd then return end -- This player already has the motd data
- if showMotd == "1" then -- Assume it's a file
- if not ULib.fileExists( GetConVarString( "ulx_motdfile" ) ) then return end -- Invalid
- local f = ULib.fileRead( GetConVarString( "ulx_motdfile" ) )
- ULib.clientRPC( ply, "ulx.rcvMotd", showMotd, f )
- elseif showMotd == "2" then
- ULib.clientRPC( ply, "ulx.rcvMotd", showMotd, ulx.motdSettings )
- else -- Assume URL
- ULib.clientRPC( ply, "ulx.rcvMotd", showMotd, GetConVarString( "ulx_motdurl" ) )
- end
- ply.ulxHasMotd = true
- end
- local function showMotd( ply )
- local showMotd = GetConVarString( "ulx_showMotd" )
- if showMotd == "0" then return end
- if not ply:IsValid() then return end -- They left, doh!
- sendMotd( ply, showMotd )
- ULib.clientRPC( ply, "ulx.showMotdMenu", ply:SteamID() ) -- Passing it because they may get it before LocalPlayer() is valid
- end
- hook.Add( "PlayerInitialSpawn", "showMotd", showMotd )
- function ulx.motdUpdated()
- for i=1, #player.GetAll() do
- player.GetAll()[i].ulxHasMotd = false
- end
- end
- local function conVarUpdated( sv_cvar, cl_cvar, ply, old_val, new_val )
- if string.lower( cl_cvar ) == "ulx_showmotd" or string.lower( cl_cvar ) == "ulx_motdfile" or string.lower( cl_cvar ) == "ulx_motdurl" then
- ulx.motdUpdated()
- end
- end
- hook.Add( "ULibReplicatedCvarChanged", "ulx.clearMotdCache", conVarUpdated )
- function ulx.motd( calling_ply )
- if not calling_ply:IsValid() then
- Msg( "You can't see the motd from the console.\n" )
- return
- end
- if GetConVarString( "ulx_showMotd" ) == "0" then
- ULib.tsay( calling_ply, "The MOTD has been disabled on this server." )
- return
- end
- if GetConVarString( "ulx_showMotd" ) == "1" and not ULib.fileExists( GetConVarString( "ulx_motdfile" ) ) then
- ULib.tsay( calling_ply, "The MOTD file could not be found." )
- return
- end
- showMotd( calling_ply )
- end
- local motdmenu = ulx.command( CATEGORY_NAME, "ulx motd", ulx.motd, "!motd" )
- motdmenu:defaultAccess( ULib.ACCESS_ALL )
- motdmenu:help( "Show the message of the day." )
- if SERVER then
- ulx.convar( "showMotd", "2", " <0/1/2/3> - MOTD mode. 0 is off.", ULib.ACCESS_ADMIN )
- ulx.convar( "motdfile", "ulx_motd.txt", "MOTD filepath from gmod root to use if ulx showMotd is 1.", ULib.ACCESS_ADMIN )
- ulx.convar( "motdurl", "ulyssesmod.net", "MOTD URL to use if ulx showMotd is 3.", ULib.ACCESS_ADMIN )
- function ulx.populateMotdData()
- if ulx.motdSettings == nil or ulx.motdSettings.info == nil then return end
- ulx.motdSettings.admins = {}
- local getAddonInfo = false
- -- Gather addon/admin information to display
- for i=1, #ulx.motdSettings.info do
- local sectionInfo = ulx.motdSettings.info[i]
- if sectionInfo.type == "mods" and not ulx.motdSettings.addons then
- getAddonInfo = true
- elseif sectionInfo.type == "admins" then
- for a=1, #sectionInfo.contents do
- ulx.motdSettings.admins[sectionInfo.contents[a]] = true
- end
- end
- end
- if getAddonInfo then
- ulx.motdSettings.addons = {}
- local addons = engine.GetAddons()
- for i=1, #addons do
- local addon = addons[i]
- if addon.mounted then
- table.insert( ulx.motdSettings.addons, { title=addon.title, workshop_id=addon.file:gsub("%D", "") } )
- end
- end
- local _, possibleaddons = file.Find( "addons/*", "GAME" )
- for _, addon in ipairs( possibleaddons ) do
- if ULib.fileExists( "addons/" .. addon .. "/addon.txt" ) then
- local t = ULib.parseKeyValues( ULib.stripComments( ULib.fileRead( "addons/" .. addon .. "/addon.txt" ), "//" ) )
- if t and t.AddonInfo then
- local name = t.AddonInfo.name or addon
- table.insert( ulx.motdSettings.addons, { title=name, author=t.AddonInfo.author_name } )
- end
- end
- end
- table.sort( ulx.motdSettings.addons, function(a,b) return string.lower(a.title) < string.lower(b.title) end )
- end
- for group, _ in pairs( ulx.motdSettings.admins ) do
- ulx.motdSettings.admins[group] = {}
- for steamID, data in pairs( ULib.ucl.users ) do
- if data.group == group and data.name then
- table.insert( ulx.motdSettings.admins[group], data.name )
- end
- end
- end
- end
- hook.Add( ULib.HOOK_UCLCHANGED, "ulx.updateMotd.adminsChanged", ulx.populateMotdData )
- end
- end
- -- This module holds any type of remote execution functions (IE, 'dangerous')
- local CATEGORY_NAME = "Rcon"
- function ulx.rcon( calling_ply, command )
- ULib.consoleCommand( command .. "\n" )
- ulx.fancyLogAdmin( calling_ply, true, "#A ran rcon command: #s", command )
- end
- local rcon = ulx.command( CATEGORY_NAME, "ulx rcon", ulx.rcon, "!rcon", true, false, true )
- rcon:addParam{ type=ULib.cmds.StringArg, hint="command", ULib.cmds.takeRestOfLine }
- rcon:defaultAccess( ULib.ACCESS_SUPERADMIN )
- rcon:help( "Execute command on server console." )
- function ulx.luaRun( calling_ply, command )
- local return_results = false
- if command:sub( 1, 1 ) == "=" then
- command = "tmp_var" .. command
- return_results = true
- end
- RunString( command )
- if return_results then
- if type( tmp_var ) == "table" then
- ULib.console( calling_ply, "Result:" )
- local lines = ULib.explode( "\n", ulx.dumpTable( tmp_var ) )
- local chunk_size = 50
- for i=1, #lines, chunk_size do -- Break it up so we don't overflow the client
- ULib.queueFunctionCall( function()
- for j=i, math.min( i+chunk_size-1, #lines ) do
- ULib.console( calling_ply, lines[ j ]:gsub( "%%", "<p>" ) )
- end
- end )
- end
- else
- ULib.console( calling_ply, "Result: " .. tostring( tmp_var ):gsub( "%%", "<p>" ) )
- end
- end
- ulx.fancyLogAdmin( calling_ply, true, "#A ran lua: #s", command )
- end
- local luarun = ulx.command( CATEGORY_NAME, "ulx luarun", ulx.luaRun, nil, false, false, true )
- luarun:addParam{ type=ULib.cmds.StringArg, hint="command", ULib.cmds.takeRestOfLine }
- luarun:defaultAccess( ULib.ACCESS_SUPERADMIN )
- luarun:help( "Executes lua in server console. (Use '=' for output)" )
- function ulx.exec( calling_ply, config )
- if string.sub( config, -4 ) ~= ".cfg" then config = config .. ".cfg" end
- if not ULib.fileExists( "cfg/" .. config ) then
- ULib.tsayError( calling_ply, "That config does not exist!", true )
- return
- end
- ULib.execFile( "cfg/" .. config )
- ulx.fancyLogAdmin( calling_ply, "#A executed file #s", config )
- end
- local exec = ulx.command( CATEGORY_NAME, "ulx exec", ulx.exec, nil, false, false, true )
- exec:addParam{ type=ULib.cmds.StringArg, hint="file" }
- exec:defaultAccess( ULib.ACCESS_SUPERADMIN )
- exec:help( "Execute a file from the cfg directory on the server." )
- function ulx.cexec( calling_ply, target_plys, command )
- for _, v in ipairs( target_plys ) do
- v:ConCommand( command )
- end
- ulx.fancyLogAdmin( calling_ply, "#A ran #s on #T", command, target_plys )
- end
- local cexec = ulx.command( CATEGORY_NAME, "ulx cexec", ulx.cexec, "!cexec", false, false, true )
- cexec:addParam{ type=ULib.cmds.PlayersArg }
- cexec:addParam{ type=ULib.cmds.StringArg, hint="command", ULib.cmds.takeRestOfLine }
- cexec:defaultAccess( ULib.ACCESS_SUPERADMIN )
- cexec:help( "Run a command on console of target(s)." )
- function ulx.ent( calling_ply, classname, params )
- if not calling_ply:IsValid() then
- Msg( "Can't create entities from dedicated server console.\n" )
- return
- end
- classname = classname:lower()
- newEnt = ents.Create( classname )
- -- Make sure it's a valid ent
- if not newEnt or not newEnt:IsValid() then
- ULib.tsayError( calling_ply, "Unknown entity type (" .. classname .. "), aborting.", true )
- return
- end
- local trace = calling_ply:GetEyeTrace()
- local vector = trace.HitPos
- vector.z = vector.z + 20
- newEnt:SetPos( vector ) -- Note that the position can be overridden by the user's flags
- params:gsub( "([^|:\"]+)\"?:\"?([^|]+)", function( key, value )
- key = key:Trim()
- value = value:Trim()
- newEnt:SetKeyValue( key, value )
- end )
- newEnt:Spawn()
- newEnt:Activate()
- undo.Create( "ulx_ent" )
- undo.AddEntity( newEnt )
- undo.SetPlayer( calling_ply )
- undo.Finish()
- if not params or params == "" then
- ulx.fancyLogAdmin( calling_ply, "#A created ent #s", classname )
- else
- ulx.fancyLogAdmin( calling_ply, "#A created ent #s with params #s", classname, params )
- end
- end
- local ent = ulx.command( CATEGORY_NAME, "ulx ent", ulx.ent, nil, false, false, true )
- ent:addParam{ type=ULib.cmds.StringArg, hint="classname" }
- ent:addParam{ type=ULib.cmds.StringArg, hint="<flag> : <value> |", ULib.cmds.takeRestOfLine, ULib.cmds.optional }
- ent:defaultAccess( ULib.ACCESS_SUPERADMIN )
- ent:help( "Spawn an ent, separate flag and value with ':', flag:value pairs with '|'." )
- CATEGORY_NAME = "Teleport"
- local function spiralGrid(rings)
- local grid = {}
- local col, row
- for ring=1, rings do -- For each ring...
- row = ring
- for col=1-ring, ring do -- Walk right across top row
- table.insert( grid, {col, row} )
- end
- col = ring
- for row=ring-1, -ring, -1 do -- Walk down right-most column
- table.insert( grid, {col, row} )
- end
- row = -ring
- for col=ring-1, -ring, -1 do -- Walk left across bottom row
- table.insert( grid, {col, row} )
- end
- col = -ring
- for row=1-ring, ring do -- Walk up left-most column
- table.insert( grid, {col, row} )
- end
- end
- return grid
- end
- local tpGrid = spiralGrid( 24 )
- -- Utility function for bring, goto, and send
- local function playerSend( from, to, force )
- if not to:IsInWorld() and not force then return false end -- No way we can do this one
- local yawForward = to:EyeAngles().yaw
- local directions = { -- Directions to try
- math.NormalizeAngle( yawForward - 180 ), -- Behind first
- math.NormalizeAngle( yawForward + 90 ), -- Right
- math.NormalizeAngle( yawForward - 90 ), -- Left
- yawForward,
- }
- local t = {}
- t.start = to:GetPos() + Vector( 0, 0, 32 ) -- Move them up a bit so they can travel across the ground
- t.filter = { to, from }
- local i = 1
- t.endpos = to:GetPos() + Angle( 0, directions[ i ], 0 ):Forward() * 47 -- (33 is player width, this is sqrt( 33^2 * 2 ))
- local tr = util.TraceEntity( t, from )
- while tr.Hit do -- While it's hitting something, check other angles
- i = i + 1
- if i > #directions then -- No place found
- if force then
- from.ulx_prevpos = from:GetPos()
- from.ulx_prevang = from:EyeAngles()
- return to:GetPos() + Angle( 0, directions[ 1 ], 0 ):Forward() * 47
- else
- return false
- end
- end
- t.endpos = to:GetPos() + Angle( 0, directions[ i ], 0 ):Forward() * 47
- tr = util.TraceEntity( t, from )
- end
- from.ulx_prevpos = from:GetPos()
- from.ulx_prevang = from:EyeAngles()
- return tr.HitPos
- end
- -- Based on code donated by Timmy (https://github.com/Toxsa)
- function ulx.bring( calling_ply, target_plys )
- local cell_size = 50 -- Constance spacing value
- if not calling_ply:IsValid() then
- Msg( "If you brought someone to you, they would instantly be destroyed by the awesomeness that is console.\n" )
- return
- end
- if ulx.getExclusive( calling_ply, calling_ply ) then
- ULib.tsayError( calling_ply, ulx.getExclusive( calling_ply, calling_ply ), true )
- return
- end
- if not calling_ply:Alive() then
- ULib.tsayError( calling_ply, "You are dead!", true )
- return
- end
- if calling_ply:InVehicle() then
- ULib.tsayError( calling_ply, "Please leave the vehicle first!", true )
- return
- end
- local t = {
- start = calling_ply:GetPos(),
- filter = { calling_ply },
- endpos = calling_ply:GetPos(),
- }
- local tr = util.TraceEntity( t, calling_ply )
- if tr.Hit then
- ULib.tsayError( calling_ply, "Can't teleport when you're inside the world!", true )
- return
- end
- local teleportable_plys = {}
- for i=1, #target_plys do
- local v = target_plys[ i ]
- if ulx.getExclusive( v, calling_ply ) then
- ULib.tsayError( calling_ply, ulx.getExclusive( v, calling_ply ), true )
- elseif not v:Alive() then
- ULib.tsayError( calling_ply, v:Nick() .. " is dead!", true )
- else
- table.insert( teleportable_plys, v )
- end
- end
- local players_involved = table.Copy( teleportable_plys )
- table.insert( players_involved, calling_ply )
- local affected_plys = {}
- for i=1, #tpGrid do
- local c = tpGrid[i][1]
- local r = tpGrid[i][2]
- local target = table.remove( teleportable_plys )
- if not target then break end
- local yawForward = calling_ply:EyeAngles().yaw
- local offset = Vector( r * cell_size, c * cell_size, 0 )
- offset:Rotate( Angle( 0, yawForward, 0 ) )
- local t = {}
- t.start = calling_ply:GetPos() + Vector( 0, 0, 32 ) -- Move them up a bit so they can travel across the ground
- t.filter = players_involved
- t.endpos = t.start + offset
- local tr = util.TraceEntity( t, target )
- if tr.Hit then
- table.insert( teleportable_plys, target )
- else
- if target:InVehicle() then target:ExitVehicle() end
- target.ulx_prevpos = target:GetPos()
- target.ulx_prevang = target:EyeAngles()
- target:SetPos( t.endpos )
- target:SetEyeAngles( (calling_ply:GetPos() - t.endpos):Angle() )
- target:SetLocalVelocity( Vector( 0, 0, 0 ) )
- table.insert( affected_plys, target )
- end
- end
- if #teleportable_plys > 0 then
- ULib.tsayError( calling_ply, "Not enough free space to bring everyone!", true )
- end
- if #affected_plys > 0 then
- ulx.fancyLogAdmin( calling_ply, "#A brought #T", affected_plys )
- end
- end
- local bring = ulx.command( CATEGORY_NAME, "ulx bring", ulx.bring, "!bring" )
- bring:addParam{ type=ULib.cmds.PlayersArg, target="!^" }
- bring:defaultAccess( ULib.ACCESS_ADMIN )
- bring:help( "Brings target(s) to you." )
- function ulx.goto( calling_ply, target_ply )
- if not calling_ply:IsValid() then
- Msg( "You may not step down into the mortal world from console.\n" )
- return
- end
- if ulx.getExclusive( calling_ply, calling_ply ) then
- ULib.tsayError( calling_ply, ulx.getExclusive( calling_ply, calling_ply ), true )
- return
- end
- if not target_ply:Alive() then
- ULib.tsayError( calling_ply, target_ply:Nick() .. " is dead!", true )
- return
- end
- if not calling_ply:Alive() then
- ULib.tsayError( calling_ply, "You are dead!", true )
- return
- end
- if target_ply:InVehicle() and calling_ply:GetMoveType() ~= MOVETYPE_NOCLIP then
- ULib.tsayError( calling_ply, "Target is in a vehicle! Noclip and use this command to force a goto.", true )
- return
- end
- local newpos = playerSend( calling_ply, target_ply, calling_ply:GetMoveType() == MOVETYPE_NOCLIP )
- if not newpos then
- ULib.tsayError( calling_ply, "Can't find a place to put you! Noclip and use this command to force a goto.", true )
- return
- end
- if calling_ply:InVehicle() then
- calling_ply:ExitVehicle()
- end
- local newang = (target_ply:GetPos() - newpos):Angle()
- calling_ply:SetPos( newpos )
- calling_ply:SetEyeAngles( newang )
- calling_ply:SetLocalVelocity( Vector( 0, 0, 0 ) ) -- Stop!
- ulx.fancyLogAdmin( calling_ply, "#A teleported to #T", target_ply )
- end
- local goto = ulx.command( CATEGORY_NAME, "ulx goto", ulx.goto, "!goto" )
- goto:addParam{ type=ULib.cmds.PlayerArg, target="!^", ULib.cmds.ignoreCanTarget }
- goto:defaultAccess( ULib.ACCESS_ADMIN )
- goto:help( "Goto target." )
- function ulx.send( calling_ply, target_from, target_to )
- if target_from == target_to then
- ULib.tsayError( calling_ply, "You listed the same target twice! Please use two different targets.", true )
- return
- end
- if ulx.getExclusive( target_from, calling_ply ) then
- ULib.tsayError( calling_ply, ulx.getExclusive( target_from, calling_ply ), true )
- return
- end
- if ulx.getExclusive( target_to, calling_ply ) then
- ULib.tsayError( calling_ply, ulx.getExclusive( target_to, calling_ply ), true )
- return
- end
- local nick = target_from:Nick() -- Going to use this for our error (if we have one)
- if not target_from:Alive() or not target_to:Alive() then
- if not target_to:Alive() then
- nick = target_to:Nick()
- end
- ULib.tsayError( calling_ply, nick .. " is dead!", true )
- return
- end
- if target_to:InVehicle() and target_from:GetMoveType() ~= MOVETYPE_NOCLIP then
- ULib.tsayError( calling_ply, "Target is in a vehicle!", true )
- return
- end
- local newpos = playerSend( target_from, target_to, target_from:GetMoveType() == MOVETYPE_NOCLIP )
- if not newpos then
- ULib.tsayError( calling_ply, "Can't find a place to put them!", true )
- return
- end
- if target_from:InVehicle() then
- target_from:ExitVehicle()
- end
- local newang = (target_from:GetPos() - newpos):Angle()
- target_from:SetPos( newpos )
- target_from:SetEyeAngles( newang )
- target_from:SetLocalVelocity( Vector( 0, 0, 0 ) ) -- Stop!
- ulx.fancyLogAdmin( calling_ply, "#A transported #T to #T", target_from, target_to )
- end
- local send = ulx.command( CATEGORY_NAME, "ulx send", ulx.send, "!send" )
- send:addParam{ type=ULib.cmds.PlayerArg, target="!^" }
- send:addParam{ type=ULib.cmds.PlayerArg, target="!^" }
- send:defaultAccess( ULib.ACCESS_ADMIN )
- send:help( "Goto target." )
- function ulx.teleport( calling_ply, target_ply )
- if not calling_ply:IsValid() then
- Msg( "You are the console, you can't teleport or teleport others since you can't see the world!\n" )
- return
- end
- if ulx.getExclusive( target_ply, calling_ply ) then
- ULib.tsayError( calling_ply, ulx.getExclusive( target_ply, calling_ply ), true )
- return
- end
- if not target_ply:Alive() then
- ULib.tsayError( calling_ply, target_ply:Nick() .. " is dead!", true )
- return
- end
- local t = {}
- t.start = calling_ply:GetPos() + Vector( 0, 0, 32 ) -- Move them up a bit so they can travel across the ground
- t.endpos = calling_ply:GetPos() + calling_ply:EyeAngles():Forward() * 16384
- t.filter = target_ply
- if target_ply ~= calling_ply then
- t.filter = { target_ply, calling_ply }
- end
- local tr = util.TraceEntity( t, target_ply )
- local pos = tr.HitPos
- if target_ply == calling_ply and pos:Distance( target_ply:GetPos() ) < 64 then -- Laughable distance
- return
- end
- target_ply.ulx_prevpos = target_ply:GetPos()
- target_ply.ulx_prevang = target_ply:EyeAngles()
- if target_ply:InVehicle() then
- target_ply:ExitVehicle()
- end
- target_ply:SetPos( pos )
- target_ply:SetLocalVelocity( Vector( 0, 0, 0 ) ) -- Stop!
- if target_ply ~= calling_ply then
- ulx.fancyLogAdmin( calling_ply, "#A teleported #T", target_ply ) -- We don't want to log otherwise
- end
- end
- local teleport = ulx.command( CATEGORY_NAME, "ulx teleport", ulx.teleport, {"!tp", "!teleport"} )
- teleport:addParam{ type=ULib.cmds.PlayerArg, ULib.cmds.optional }
- teleport:defaultAccess( ULib.ACCESS_ADMIN )
- teleport:help( "Teleports target." )
- function ulx.retrn( calling_ply, target_ply )
- if not target_ply:IsValid() then
- Msg( "Return where? The console may never return to the mortal realm.\n" )
- return
- end
- if not target_ply.ulx_prevpos then
- ULib.tsayError( calling_ply, target_ply:Nick() .. " does not have any previous locations to send them to.", true )
- return
- end
- if ulx.getExclusive( target_ply, calling_ply ) then
- ULib.tsayError( calling_ply, ulx.getExclusive( target_ply, calling_ply ), true )
- return
- end
- if not target_ply:Alive() then
- ULib.tsayError( calling_ply, target_ply:Nick() .. " is dead!", true )
- return
- end
- if target_ply:InVehicle() then
- target_ply:ExitVehicle()
- end
- target_ply:SetPos( target_ply.ulx_prevpos )
- target_ply:SetEyeAngles( target_ply.ulx_prevang )
- target_ply.ulx_prevpos = nil
- target_ply.ulx_prevang = nil
- target_ply:SetLocalVelocity( Vector( 0, 0, 0 ) ) -- Stop!
- ulx.fancyLogAdmin( calling_ply, "#A returned #T to their original position", target_ply )
- end
- local retrn = ulx.command( CATEGORY_NAME, "ulx return", ulx.retrn, "!return" )
- retrn:addParam{ type=ULib.cmds.PlayerArg, ULib.cmds.optional }
- retrn:defaultAccess( ULib.ACCESS_ADMIN )
- retrn:help( "Returns target to last position before a teleport." )
- local CATEGORY_NAME = "User Management"
- local function checkForValidId( calling_ply, id )
- if id == "BOT" or id == "NULL" then -- Bot check
- return true
- elseif id:find( "%." ) then -- Assume IP and check
- if not ULib.isValidIP( id ) then
- ULib.tsayError( calling_ply, "Invalid IP.", true )
- return false
- end
- elseif id:find( ":" ) then
- if not ULib.isValidSteamID( id ) then -- Assume steamid and check
- ULib.tsayError( calling_ply, "Invalid steamid.", true )
- return false
- end
- elseif not tonumber( id ) then -- Assume uniqueid and check
- ULib.tsayError( calling_ply, "Invalid Unique ID", true )
- return false
- end
- return true
- end
- ulx.group_names = {}
- ulx.group_names_no_user = {}
- local function updateNames()
- table.Empty( ulx.group_names ) -- Don't reassign so we don't lose our refs
- table.Empty( ulx.group_names_no_user )
- for group_name, _ in pairs( ULib.ucl.groups ) do
- table.insert( ulx.group_names, group_name )
- if group_name ~= ULib.ACCESS_ALL then
- table.insert( ulx.group_names_no_user, group_name )
- end
- end
- end
- hook.Add( ULib.HOOK_UCLCHANGED, "ULXGroupNamesUpdate", updateNames )
- updateNames() -- Init
- function ulx.usermanagementhelp( calling_ply )
- if calling_ply:IsValid() then
- ULib.clientRPC( calling_ply, "ulx.showUserHelp" )
- else
- ulx.showUserHelp()
- end
- end
- local usermanagementhelp = ulx.command( CATEGORY_NAME, "ulx usermanagementhelp", ulx.usermanagementhelp )
- usermanagementhelp:defaultAccess( ULib.ACCESS_ALL )
- usermanagementhelp:help( "See the user management help." )
- function ulx.adduser( calling_ply, target_ply, group_name )
- local userInfo = ULib.ucl.authed[ target_ply:UniqueID() ]
- local id = ULib.ucl.getUserRegisteredID( target_ply )
- if not id then id = target_ply:SteamID() end
- ULib.ucl.addUser( id, userInfo.allow, userInfo.deny, group_name )
- ulx.fancyLogAdmin( calling_ply, "#A added #T to group #s", target_ply, group_name )
- end
- local adduser = ulx.command( CATEGORY_NAME, "ulx adduser", ulx.adduser, nil, false, false, true )
- adduser:addParam{ type=ULib.cmds.PlayerArg }
- adduser:addParam{ type=ULib.cmds.StringArg, completes=ulx.group_names_no_user, hint="group", error="invalid group \"%s\" specified", ULib.cmds.restrictToCompletes }
- adduser:defaultAccess( ULib.ACCESS_SUPERADMIN )
- adduser:help( "Add a user to specified group." )
- function ulx.adduserid( calling_ply, id, group_name )
- id = id:upper() -- Steam id needs to be upper
- -- Check for valid and properly formatted ID
- if not checkForValidId( calling_ply, id ) then return false end
- -- Now add the fool!
- ULib.ucl.addUser( id, allows, denies, group_name )
- if ULib.ucl.users[ id ] and ULib.ucl.users[ id ].name then
- ulx.fancyLogAdmin( calling_ply, "#A added #s to group #s", ULib.ucl.users[ id ].name, group_name )
- else
- ulx.fancyLogAdmin( calling_ply, "#A added userid #s to group #s", id, group_name )
- end
- end
- local adduserid = ulx.command( CATEGORY_NAME, "ulx adduserid", ulx.adduserid, nil, false, false, true )
- adduserid:addParam{ type=ULib.cmds.StringArg, hint="SteamID, IP, or UniqueID" }
- adduserid:addParam{ type=ULib.cmds.StringArg, completes=ulx.group_names_no_user, hint="group", error="invalid group \"%s\" specified", ULib.cmds.restrictToCompletes }
- adduserid:defaultAccess( ULib.ACCESS_SUPERADMIN )
- adduserid:help( "Add a user by ID to specified group." )
- function ulx.removeuser( calling_ply, target_ply )
- ULib.ucl.removeUser( target_ply:UniqueID() )
- ulx.fancyLogAdmin( calling_ply, "#A removed all access rights from #T", target_ply )
- end
- local removeuser = ulx.command( CATEGORY_NAME, "ulx removeuser", ulx.removeuser, nil, false, false, true )
- removeuser:addParam{ type=ULib.cmds.PlayerArg }
- removeuser:defaultAccess( ULib.ACCESS_SUPERADMIN )
- removeuser:help( "Permanently removes a user's access." )
- function ulx.removeuserid( calling_ply, id )
- id = id:upper() -- Steam id needs to be upper
- -- Check for valid and properly formatted ID
- if not checkForValidId( calling_ply, id ) then return false end
- if not ULib.ucl.authed[ id ] and not ULib.ucl.users[ id ] then
- ULib.tsayError( calling_ply, "No player with id \"" .. id .. "\" exists in the ULib user list", true )
- return false
- end
- local name = (ULib.ucl.authed[ id ] and ULib.ucl.authed[ id ].name) or (ULib.ucl.users[ id ] and ULib.ucl.users[ id ].name)
- ULib.ucl.removeUser( id )
- if name then
- ulx.fancyLogAdmin( calling_ply, "#A removed all access rights from #s", name )
- else
- ulx.fancyLogAdmin( calling_ply, "#A removed all access rights from #s", id )
- end
- end
- local removeuserid = ulx.command( CATEGORY_NAME, "ulx removeuserid", ulx.removeuserid, nil, false, false, true )
- removeuserid:addParam{ type=ULib.cmds.StringArg, hint="SteamID, IP, or UniqueID" }
- removeuserid:defaultAccess( ULib.ACCESS_SUPERADMIN )
- removeuserid:help( "Permanently removes a user's access by ID." )
- function ulx.userallow( calling_ply, target_ply, access_string, access_tag )
- if access_tag then access_tag = access_tag end
- local accessTable
- if access_tag and access_tag ~= "" then
- accessTable = { [access_string]=access_tag }
- else
- accessTable = { access_string }
- end
- local id = ULib.ucl.getUserRegisteredID( target_ply )
- if not id then id = target_ply:SteamID() end
- local success = ULib.ucl.userAllow( id, accessTable )
- if not success then
- ULib.tsayError( calling_ply, string.format( "User \"%s\" already has access to \"%s\"", target_ply:Nick(), access_string ), true )
- else
- if not access_tag or access_tag == "" then
- ulx.fancyLogAdmin( calling_ply, "#A granted access #q to #T", access_string, target_ply )
- else
- ulx.fancyLogAdmin( calling_ply, "#A granted access #q with tag #q to #T", access_string, access_tag, target_ply )
- end
- end
- end
- local userallow = ulx.command( CATEGORY_NAME, "ulx userallow", ulx.userallow, nil, false, false, true )
- userallow:addParam{ type=ULib.cmds.PlayerArg }
- userallow:addParam{ type=ULib.cmds.StringArg, hint="command" } -- TODO, add completes for this
- userallow:addParam{ type=ULib.cmds.StringArg, hint="access tag", ULib.cmds.optional }
- userallow:defaultAccess( ULib.ACCESS_SUPERADMIN )
- userallow:help( "Add to a user's access." )
- function ulx.userallowid( calling_ply, id, access_string, access_tag )
- if access_tag then access_tag = access_tag end
- id = id:upper() -- Steam id needs to be upper
- -- Check for valid and properly formatted ID
- if not checkForValidId( calling_ply, id ) then return false end
- if not ULib.ucl.authed[ id ] and not ULib.ucl.users[ id ] then
- ULib.tsayError( calling_ply, "No player with id \"" .. id .. "\" exists in the ULib user list", true )
- return false
- end
- local accessTable
- if access_tag and access_tag ~= "" then
- accessTable = { [access_string]=access_tag }
- else
- accessTable = { access_string }
- end
- local success = ULib.ucl.userAllow( id, accessTable )
- local name = (ULib.ucl.authed[ id ] and ULib.ucl.authed[ id ].name) or (ULib.ucl.users[ id ] and ULib.ucl.users[ id ].name) or id
- if not success then
- ULib.tsayError( calling_ply, string.format( "User \"%s\" already has access to \"%s\"", name, access_string ), true )
- else
- if not access_tag or access_tag == "" then
- ulx.fancyLogAdmin( calling_ply, "#A granted access #q to #s", access_string, name )
- else
- ulx.fancyLogAdmin( calling_ply, "#A granted access #q with tag #q to #s", access_string, access_tag, name )
- end
- end
- end
- local userallowid = ulx.command( CATEGORY_NAME, "ulx userallowid", ulx.userallowid, nil, false, false, true )
- userallowid:addParam{ type=ULib.cmds.StringArg, hint="SteamID, IP, or UniqueID" }
- userallowid:addParam{ type=ULib.cmds.StringArg, hint="command" } -- TODO, add completes for this
- userallowid:addParam{ type=ULib.cmds.StringArg, hint="access tag", ULib.cmds.optional }
- userallowid:defaultAccess( ULib.ACCESS_SUPERADMIN )
- userallowid:help( "Add to a user's access." )
- function ulx.userdeny( calling_ply, target_ply, access_string, should_use_neutral )
- local success = ULib.ucl.userAllow( target_ply:UniqueID(), access_string, should_use_neutral, true )
- if should_use_neutral then
- success = success or ULib.ucl.userAllow( target_ply:UniqueID(), access_string, should_use_neutral, false ) -- Remove from both lists
- end
- if should_use_neutral then
- if success then
- ulx.fancyLogAdmin( calling_ply, "#A made access #q neutral to #T", access_string, target_ply )
- else
- ULib.tsayError( calling_ply, string.format( "User \"%s\" isn't denied or allowed access to \"%s\"", target_ply:Nick(), access_string ), true )
- end
- else
- if not success then
- ULib.tsayError( calling_ply, string.format( "User \"%s\" is already denied access to \"%s\"", target_ply:Nick(), access_string ), true )
- else
- ulx.fancyLogAdmin( calling_ply, "#A denied access #q to #T", access_string, target_ply )
- end
- end
- end
- local userdeny = ulx.command( CATEGORY_NAME, "ulx userdeny", ulx.userdeny, nil, false, false, true )
- userdeny:addParam{ type=ULib.cmds.PlayerArg }
- userdeny:addParam{ type=ULib.cmds.StringArg, hint="command" } -- TODO, add completes for this
- userdeny:addParam{ type=ULib.cmds.BoolArg, hint="remove explicit allow or deny instead of outright denying", ULib.cmds.optional }
- userdeny:defaultAccess( ULib.ACCESS_SUPERADMIN )
- userdeny:help( "Remove from a user's access." )
- function ulx.userdenyid( calling_ply, id, access_string, should_use_neutral )
- id = id:upper() -- Steam id needs to be upper
- -- Check for valid and properly formatted ID
- if not checkForValidId( calling_ply, id ) then return false end
- if not ULib.ucl.authed[ id ] and not ULib.ucl.users[ id ] then
- ULib.tsayError( calling_ply, "No player with id \"" .. id .. "\" exists in the ULib user list", true )
- return false
- end
- local success = ULib.ucl.userAllow( id, access_string, should_use_neutral, true )
- if should_use_neutral then
- success = success or ULib.ucl.userAllow( id, access_string, should_use_neutral, false ) -- Remove from both lists
- end
- local name = (ULib.ucl.authed[ id ] and ULib.ucl.authed[ id ].name) or (ULib.ucl.users[ id ] and ULib.ucl.users[ id ].name) or id
- if should_use_neutral then
- if success then
- ulx.fancyLogAdmin( calling_ply, "#A made access #q neutral to #s", access_string, name )
- else
- ULib.tsayError( calling_ply, string.format( "User \"%s\" isn't denied or allowed access to \"%s\"", name, access_string ), true )
- end
- else
- if not success then
- ULib.tsayError( calling_ply, string.format( "User \"%s\" is already denied access to \"%s\"", name, access_string ), true )
- else
- ulx.fancyLogAdmin( calling_ply, "#A denied access #q to #s", access_string, name )
- end
- end
- end
- local userdenyid = ulx.command( CATEGORY_NAME, "ulx userdenyid", ulx.userdenyid, nil, false, false, true )
- userdenyid:addParam{ type=ULib.cmds.StringArg, hint="SteamID, IP, or UniqueID" }
- userdenyid:addParam{ type=ULib.cmds.StringArg, hint="command" } -- TODO, add completes for this
- userdenyid:addParam{ type=ULib.cmds.BoolArg, hint="remove explicit allow or deny instead of outright denying", ULib.cmds.optional }
- userdenyid:defaultAccess( ULib.ACCESS_SUPERADMIN )
- userdenyid:help( "Remove from a user's access." )
- function ulx.addgroup( calling_ply, group_name, inherit_from )
- if ULib.ucl.groups[ group_name ] ~= nil then
- ULib.tsayError( calling_ply, "This group already exists!", true )
- return
- end
- if not ULib.ucl.groups[ inherit_from ] then
- ULib.tsayError( calling_ply, "The group you specified for inheritence doesn't exist!", true )
- return
- end
- ULib.ucl.addGroup( group_name, _, inherit_from )
- ulx.fancyLogAdmin( calling_ply, "#A created group #s which inherits rights from group #s", group_name, inherit_from )
- end
- local addgroup = ulx.command( CATEGORY_NAME, "ulx addgroup", ulx.addgroup, nil, false, false, true )
- addgroup:addParam{ type=ULib.cmds.StringArg, hint="group" }
- addgroup:addParam{ type=ULib.cmds.StringArg, completes=ulx.group_names, hint="inherits from", error="invalid group \"%s\" specified", ULib.cmds.restrictToCompletes, default="user", ULib.cmds.optional }
- addgroup:defaultAccess( ULib.ACCESS_SUPERADMIN )
- addgroup:help( "Create a new group with optional inheritance." )
- function ulx.renamegroup( calling_ply, current_group, new_group )
- if ULib.ucl.groups[ new_group ] then
- ULib.tsayError( calling_ply, "The target group already exists!", true )
- return
- end
- ULib.ucl.renameGroup( current_group, new_group )
- ulx.fancyLogAdmin( calling_ply, "#A renamed group #s to #s", current_group, new_group )
- end
- local renamegroup = ulx.command( CATEGORY_NAME, "ulx renamegroup", ulx.renamegroup, nil, false, false, true )
- renamegroup:addParam{ type=ULib.cmds.StringArg, completes=ulx.group_names_no_user, hint="current group", error="invalid group \"%s\" specified", ULib.cmds.restrictToCompletes }
- renamegroup:addParam{ type=ULib.cmds.StringArg, hint="new group" }
- renamegroup:defaultAccess( ULib.ACCESS_SUPERADMIN )
- renamegroup:help( "Renames a group." )
- function ulx.setGroupCanTarget( calling_ply, group, can_target )
- if can_target and can_target ~= "" and can_target ~= "*" then
- ULib.ucl.setGroupCanTarget( group, can_target )
- ulx.fancyLogAdmin( calling_ply, "#A changed group #s to only be able to target #s", group, can_target )
- else
- ULib.ucl.setGroupCanTarget( group, nil )
- ulx.fancyLogAdmin( calling_ply, "#A changed group #s to be able to target anyone", group )
- end
- end
- local setgroupcantarget = ulx.command( CATEGORY_NAME, "ulx setgroupcantarget", ulx.setGroupCanTarget, nil, false, false, true )
- setgroupcantarget:addParam{ type=ULib.cmds.StringArg, completes=ulx.group_names, hint="group", error="invalid group \"%s\" specified", ULib.cmds.restrictToCompletes }
- setgroupcantarget:addParam{ type=ULib.cmds.StringArg, hint="target string", ULib.cmds.optional }
- setgroupcantarget:defaultAccess( ULib.ACCESS_SUPERADMIN )
- setgroupcantarget:help( "Sets what a group is allowed to target" )
- function ulx.removegroup( calling_ply, group_name )
- ULib.ucl.removeGroup( group_name )
- ulx.fancyLogAdmin( calling_ply, "#A removed group #s", group_name )
- end
- local removegroup = ulx.command( CATEGORY_NAME, "ulx removegroup", ulx.removegroup, nil, false, false, true )
- removegroup:addParam{ type=ULib.cmds.StringArg, completes=ulx.group_names_no_user, hint="group", error="invalid group \"%s\" specified", ULib.cmds.restrictToCompletes }
- removegroup:defaultAccess( ULib.ACCESS_SUPERADMIN )
- removegroup:help( "Removes a group. USE WITH CAUTION." )
- function ulx.groupallow( calling_ply, group_name, access_string, access_tag )
- access_tag = access_tag
- local accessTable
- if access_tag and access_tag ~= "" then
- accessTable = { [access_string]=access_tag }
- else
- accessTable = { access_string }
- end
- local success = ULib.ucl.groupAllow( group_name, accessTable )
- if not success then
- ULib.tsayError( calling_ply, string.format( "Group \"%s\" already has access to \"%s\"", group_name, access_string ), true )
- else
- if not access_tag or access_tag == "" then
- ulx.fancyLogAdmin( calling_ply, "#A granted access #q to group #s", access_string, group_name )
- else
- ulx.fancyLogAdmin( calling_ply, "#A granted access #q with tag #q to group #s", access_string, access_tag, group_name )
- end
- end
- end
- local groupallow = ulx.command( CATEGORY_NAME, "ulx groupallow", ulx.groupallow, nil, false, false, true )
- groupallow:addParam{ type=ULib.cmds.StringArg, completes=ulx.group_names, hint="group", error="invalid group \"%s\" specified", ULib.cmds.restrictToCompletes }
- groupallow:addParam{ type=ULib.cmds.StringArg, hint="command" } -- TODO, add completes for this
- groupallow:addParam{ type=ULib.cmds.StringArg, hint="access tag", ULib.cmds.optional }
- groupallow:defaultAccess( ULib.ACCESS_SUPERADMIN )
- groupallow:help( "Add to a group's access." )
- function ulx.groupdeny( calling_ply, group_name, access_string )
- local accessTable
- if access_tag and access_tag ~= "" then
- accessTable = { [access_string]=access_tag }
- else
- accessTable = { access_string }
- end
- local success = ULib.ucl.groupAllow( group_name, access_string, true )
- if success then
- ulx.fancyLogAdmin( calling_ply, "#A revoked access #q to group #s", access_string, group_name )
- else
- ULib.tsayError( calling_ply, string.format( "Group \"%s\" doesn't have access to \"%s\"", group_name, access_string ), true )
- end
- end
- local groupdeny = ulx.command( CATEGORY_NAME, "ulx groupdeny", ulx.groupdeny, nil, false, false, true )
- groupdeny:addParam{ type=ULib.cmds.StringArg, completes=ulx.group_names, hint="group", error="invalid group \"%s\" specified", ULib.cmds.restrictToCompletes }
- groupdeny:addParam{ type=ULib.cmds.StringArg, hint="command" } -- TODO, add completes for this
- groupdeny:defaultAccess( ULib.ACCESS_SUPERADMIN )
- groupdeny:help( "Remove from a group's access." )
- local help = [[
- General User Management Concepts:
- User access is driven by ULib's Ulysses Control List (UCL). This list contains users and groups
- which in turn contains lists of allowed and denied accesses. The allow and deny lists contain
- access strings like "ulx slap" or "ulx phygunplayer" to show what a user and/or group does and does
- not have access to. If a user has "ulx slap" in their user allow list or in the allow list of one
- of the groups they belong to, they have access to slap. If a user has "ulx slap" in their user deny
- list they are DENIED the command, even if they have the command in one of their group's allow
- lists. In this way, deny takes precedence over allow.
- ULib supports immunity by being able to specify what various users and groups are allowed to
- target. This is often used to make it so lower admins cannot target higher admins. EG, by default
- admins can't target superadmins, but superadmins can still target admins.
- More Advanced Concepts:
- Groups have inheritance. You can specify what group they inherit from in the addgroup command. If a
- user is in a group that has inheritance, UCL will check all groups connected in the inheritance
- chain. Note that groups do not support deny lists for the sake of simplicity. If you feel that a
- group needs to be denied something, you should split your groups up instead.
- The "user" group applies to everyone who does not otherwise belong in a group. You can use
- groupallow on this group just like any other, just remember that everyone is being allowed access.
- ULib supports an advanced, highly configurable permission system by using "access tags". Access
- tags specify what a user is allowed to pass as arguments to a command. For example, you can make it
- so that admins are only allowed to slay users with "killme" somewhere in their name, or you can
- give everyone access to the "ulx teleport" command, but only allow them to teleport themselves.
- Examples of using access tags are given below in the userallow and groupallow commands. The format
- for access tags is as follows. Each argument that is passed to the command can be limited by the
- access tag. Each argument being limited must be listed in the same order as in the command,
- separated by spaces. If you don't want to limit an argument, use a star ("*"). EG, to limit "ulx
- slap" damage to 0 through 10, but still allow it to be used on anyone, use the tag "* 0:10".
- User Management Commands:
- ulx adduser <user> <group> - Add the specified CONNECTED player to the specified group.
- The group MUST exist for this command to succeed. Use operator, admin, superadmin, or see ulx
- addgroup. You can only specify one group. See above for explanation on immunity.
- Ex 1. ulx adduser "Someguy" superadmin -- This will add the connected "Someguy" as a superadmin
- Ex 2. ulx adduser "Dood" monkey -- This will add the connected "Dood" to the group monkey
- on the condition that the group exists
- ulx removeuser <user> - Remove the specified connected player from the permanent access list.
- Ex 1. ulx removeuser "Foo bar" -- This removes the user "Foo bar"
- ulx userallow <user> <access> [<access tag>] - Puts the access on the USER'S ALLOW list, with
- optional access tag (see above)
- See above for explanation of allow list vs. deny list, as well as how access strings/tags work.
- Ex 1. ulx userallow "Pi" "ulx slap" -- This grants the user access to "ulx slap"
- Ex 2. ulx userallow "Pi" "ulx slap" "!%admin 0" -- This grants the user access to "ulx slap"
- -- but they can only slap users lower than an admin, and they can only slap for 0 damage
- ulx userdeny <user> <access> [<revoke>] - Removes a player's access. If revoke is true, this simply
- removes the access string from the user's allow/deny lists instead of adding it to the user's
- deny list. See above for an explanation on the deny list.
- ulx addgroup <group> [<inherits from>] - Creates a group, optionally inheriting from the specified
- group. See above for explanation on inheritance.
- ulx removegroup <group> - Removes a group PERMANENTLY. Also removes the group from all connected
- users and all users who connect in the future. If a user has no group besides this, they will
- become guests. Please be VERY careful with this command!
- ulx renamegroup <current group> <new group> - Renames a group
- ulx setgroupcantarget <group> [<target string>] - Limits what users a group can target. Pass no
- argument to clear the restriction.
- Ex 1. ulx setgroupcantarget user !%admin - Guests cannot target admins or above
- Ex 2. ulx setgroupcantarget admin !^ - Admins cannot target themselves
- ulx groupallow <group> <access> [<access tag>] - Puts the access on the group's allow list. See
- above on how access strings/tags work.
- ulx groupdeny <group> <access> - Removes the group's access.
- ]]
- function ulx.showUserHelp()
- local lines = ULib.explode( "\n", help )
- for _, line in ipairs( lines ) do
- Msg( line .. "\n" )
- end
- end
- local CATEGORY_NAME = "Utility"
- ------------------------------ Who ------------------------------
- function ulx.who( calling_ply, steamid )
- if not steamid or steamid == "" then
- ULib.console( calling_ply, "ID Name Group" )
- local players = player.GetAll()
- for _, player in ipairs( players ) do
- local id = tostring( player:UserID() )
- local nick = utf8.force( player:Nick() )
- local text = string.format( "%i%s %s%s ", id, string.rep( " ", 2 - id:len() ), nick, string.rep( " ", 31 - utf8.len( nick ) ) )
- text = text .. player:GetUserGroup()
- ULib.console( calling_ply, text )
- end
- else
- data = ULib.ucl.getUserInfoFromID( steamid )
- if not data then
- ULib.console( calling_ply, "No information for provided id exists" )
- else
- ULib.console( calling_ply, " ID: " .. steamid )
- ULib.console( calling_ply, " Name: " .. data.name )
- ULib.console( calling_ply, "Group: " .. data.group )
- end
- end
- end
- local who = ulx.command( CATEGORY_NAME, "ulx who", ulx.who )
- who:addParam{ type=ULib.cmds.StringArg, hint="steamid", ULib.cmds.optional }
- who:defaultAccess( ULib.ACCESS_ALL )
- who:help( "See information about currently online users." )
- ------------------------------ Version ------------------------------
- function ulx.versionCmd( calling_ply )
- ULib.tsay( calling_ply, "ULib " .. ULib.pluginVersionStr("ULib"), true )
- ULib.tsay( calling_ply, "ULX " .. ULib.pluginVersionStr("ULX"), true )
- end
- local version = ulx.command( CATEGORY_NAME, "ulx version", ulx.versionCmd, "!version" )
- version:defaultAccess( ULib.ACCESS_ALL )
- version:help( "See version information." )
- ------------------------------ Map ------------------------------
- function ulx.map( calling_ply, map, gamemode )
- if not gamemode or gamemode == "" then
- ulx.fancyLogAdmin( calling_ply, "#A changed the map to #s", map )
- else
- ulx.fancyLogAdmin( calling_ply, "#A changed the map to #s with gamemode #s", map, gamemode )
- end
- if gamemode and gamemode ~= "" then
- game.ConsoleCommand( "gamemode " .. gamemode .. "\n" )
- end
- game.ConsoleCommand( "changelevel " .. map .. "\n" )
- end
- local map = ulx.command( CATEGORY_NAME, "ulx map", ulx.map, "!map" )
- map:addParam{ type=ULib.cmds.StringArg, completes=ulx.maps, hint="map", error="invalid map \"%s\" specified", ULib.cmds.restrictToCompletes }
- map:addParam{ type=ULib.cmds.StringArg, completes=ulx.gamemodes, hint="gamemode", error="invalid gamemode \"%s\" specified", ULib.cmds.restrictToCompletes, ULib.cmds.optional }
- map:defaultAccess( ULib.ACCESS_ADMIN )
- map:help( "Changes map and gamemode." )
- function ulx.kick( calling_ply, target_ply, reason )
- if target_ply:IsListenServerHost() then
- ULib.tsayError( calling_ply, "This player is immune to kicking", true )
- return
- end
- if reason and reason ~= "" then
- ulx.fancyLogAdmin( calling_ply, "#A kicked #T (#s)", target_ply, reason )
- else
- reason = nil
- ulx.fancyLogAdmin( calling_ply, "#A kicked #T", target_ply )
- end
- -- Delay by 1 frame to ensure the chat hook finishes with player intact. Prevents a crash.
- ULib.queueFunctionCall( ULib.kick, target_ply, reason, calling_ply )
- end
- local kick = ulx.command( CATEGORY_NAME, "ulx kick", ulx.kick, "!kick" )
- kick:addParam{ type=ULib.cmds.PlayerArg }
- kick:addParam{ type=ULib.cmds.StringArg, hint="reason", ULib.cmds.optional, ULib.cmds.takeRestOfLine, completes=ulx.common_kick_reasons }
- kick:defaultAccess( ULib.ACCESS_ADMIN )
- kick:help( "Kicks target." )
- ------------------------------ Ban ------------------------------
- function ulx.ban( calling_ply, target_ply, minutes, reason )
- if target_ply:IsListenServerHost() or target_ply:IsBot() then
- ULib.tsayError( calling_ply, "This player is immune to banning", true )
- return
- end
- local time = "for #s"
- if minutes == 0 then time = "permanently" end
- local str = "#A banned #T " .. time
- if reason and reason ~= "" then str = str .. " (#s)" end
- ulx.fancyLogAdmin( calling_ply, str, target_ply, minutes ~= 0 and ULib.secondsToStringTime( minutes * 60 ) or reason, reason )
- -- Delay by 1 frame to ensure any chat hook finishes with player intact. Prevents a crash.
- ULib.queueFunctionCall( ULib.kickban, target_ply, minutes, reason, calling_ply )
- end
- local ban = ulx.command( CATEGORY_NAME, "ulx ban", ulx.ban, "!ban", false, false, true )
- ban:addParam{ type=ULib.cmds.PlayerArg }
- ban:addParam{ type=ULib.cmds.NumArg, hint="minutes, 0 for perma", ULib.cmds.optional, ULib.cmds.allowTimeString, min=0 }
- ban:addParam{ type=ULib.cmds.StringArg, hint="reason", ULib.cmds.optional, ULib.cmds.takeRestOfLine, completes=ulx.common_kick_reasons }
- ban:defaultAccess( ULib.ACCESS_ADMIN )
- ban:help( "Bans target." )
- ------------------------------ BanID ------------------------------
- function ulx.banid( calling_ply, steamid, minutes, reason )
- steamid = steamid:upper()
- if not ULib.isValidSteamID( steamid ) then
- ULib.tsayError( calling_ply, "Invalid steamid." )
- return
- end
- local name, target_ply
- local plys = player.GetAll()
- for i=1, #plys do
- if plys[ i ]:SteamID() == steamid then
- target_ply = plys[ i ]
- name = target_ply:Nick()
- break
- end
- end
- if target_ply and (target_ply:IsListenServerHost() or target_ply:IsBot()) then
- ULib.tsayError( calling_ply, "This player is immune to banning", true )
- return
- end
- local time = "for #s"
- if minutes == 0 then time = "permanently" end
- local str = "#A banned steamid #s "
- displayid = steamid
- if name then
- displayid = displayid .. "(" .. name .. ") "
- end
- str = str .. time
- if reason and reason ~= "" then str = str .. " (#4s)" end
- ulx.fancyLogAdmin( calling_ply, str, displayid, minutes ~= 0 and ULib.secondsToStringTime( minutes * 60 ) or reason, reason )
- -- Delay by 1 frame to ensure any chat hook finishes with player intact. Prevents a crash.
- ULib.queueFunctionCall( ULib.addBan, steamid, minutes, reason, name, calling_ply )
- end
- local banid = ulx.command( CATEGORY_NAME, "ulx banid", ulx.banid, nil, false, false, true )
- banid:addParam{ type=ULib.cmds.StringArg, hint="steamid" }
- banid:addParam{ type=ULib.cmds.NumArg, hint="minutes, 0 for perma", ULib.cmds.optional, ULib.cmds.allowTimeString, min=0 }
- banid:addParam{ type=ULib.cmds.StringArg, hint="reason", ULib.cmds.optional, ULib.cmds.takeRestOfLine, completes=ulx.common_kick_reasons }
- banid:defaultAccess( ULib.ACCESS_SUPERADMIN )
- banid:help( "Bans steamid." )
- function ulx.unban( calling_ply, steamid )
- steamid = steamid:upper()
- if not ULib.isValidSteamID( steamid ) then
- ULib.tsayError( calling_ply, "Invalid steamid." )
- return
- end
- name = ULib.bans[ steamid ] and ULib.bans[ steamid ].name
- ULib.unban( steamid, calling_ply )
- if name then
- ulx.fancyLogAdmin( calling_ply, "#A unbanned steamid #s", steamid .. " (" .. name .. ")" )
- else
- ulx.fancyLogAdmin( calling_ply, "#A unbanned steamid #s", steamid )
- end
- end
- local unban = ulx.command( CATEGORY_NAME, "ulx unban", ulx.unban, nil, false, false, true )
- unban:addParam{ type=ULib.cmds.StringArg, hint="steamid" }
- unban:defaultAccess( ULib.ACCESS_ADMIN )
- unban:help( "Unbans steamid." )
- ------------------------------ Noclip ------------------------------
- function ulx.noclip( calling_ply, target_plys )
- if not target_plys[ 1 ]:IsValid() then
- Msg( "You are god, you are not constrained by walls built by mere mortals.\n" )
- return
- end
- local affected_plys = {}
- for i=1, #target_plys do
- local v = target_plys[ i ]
- if v.NoNoclip then
- ULib.tsayError( calling_ply, v:Nick() .. " can't be noclipped right now.", true )
- else
- if v:GetMoveType() == MOVETYPE_WALK then
- v:SetMoveType( MOVETYPE_NOCLIP )
- table.insert( affected_plys, v )
- elseif v:GetMoveType() == MOVETYPE_NOCLIP then
- v:SetMoveType( MOVETYPE_WALK )
- table.insert( affected_plys, v )
- else -- Ignore if they're an observer
- ULib.tsayError( calling_ply, v:Nick() .. " can't be noclipped right now.", true )
- end
- end
- end
- end
- local noclip = ulx.command( CATEGORY_NAME, "ulx noclip", ulx.noclip, "!noclip" )
- noclip:addParam{ type=ULib.cmds.PlayersArg, ULib.cmds.optional }
- noclip:defaultAccess( ULib.ACCESS_ADMIN )
- noclip:help( "Toggles noclip on target(s)." )
- function ulx.spectate( calling_ply, target_ply )
- if not calling_ply:IsValid() then
- Msg( "You can't spectate from dedicated server console.\n" )
- return
- end
- -- Check if player is already spectating. If so, stop spectating so we can start again
- local hookTable = hook.GetTable()["KeyPress"]
- if hookTable and hookTable["ulx_unspectate_" .. calling_ply:EntIndex()] then
- -- Simulate keypress to properly exit spectate.
- hook.Call( "KeyPress", _, calling_ply, IN_FORWARD )
- end
- if ulx.getExclusive( calling_ply, calling_ply ) then
- ULib.tsayError( calling_ply, ulx.getExclusive( calling_ply, calling_ply ), true )
- return
- end
- ULib.getSpawnInfo( calling_ply )
- local pos = calling_ply:GetPos()
- local ang = calling_ply:GetAngles()
- local wasAlive = calling_ply:Alive()
- local function stopSpectate( player )
- if player ~= calling_ply then -- For the spawning, make sure it's them doing the spawning
- return
- end
- hook.Remove( "PlayerSpawn", "ulx_unspectatedspawn_" .. calling_ply:EntIndex() )
- hook.Remove( "KeyPress", "ulx_unspectate_" .. calling_ply:EntIndex() )
- hook.Remove( "PlayerDisconnected", "ulx_unspectatedisconnect_" .. calling_ply:EntIndex() )
- if player.ULXHasGod then player:GodEnable() end -- Restore if player had ulx god.
- player:UnSpectate() -- Need this for DarkRP for some reason, works fine without it in sbox
- ulx.fancyLogAdmin( calling_ply, true, "#A stopped spectating #T", target_ply )
- ulx.clearExclusive( calling_ply )
- end
- hook.Add( "PlayerSpawn", "ulx_unspectatedspawn_" .. calling_ply:EntIndex(), stopSpectate, HOOK_MONITOR_HIGH )
- local function unspectate( player, key )
- if calling_ply ~= player then return end -- Not the person we want
- if key ~= IN_FORWARD and key ~= IN_BACK and key ~= IN_MOVELEFT and key ~= IN_MOVERIGHT then return end -- Not a key we're interested in
- hook.Remove( "PlayerSpawn", "ulx_unspectatedspawn_" .. calling_ply:EntIndex() ) -- Otherwise spawn would cause infinite loop
- if wasAlive then -- We don't want to spawn them if they were already dead.
- ULib.spawn( player, true ) -- Get out of spectate.
- end
- stopSpectate( player )
- player:SetPos( pos )
- player:SetAngles( ang )
- end
- hook.Add( "KeyPress", "ulx_unspectate_" .. calling_ply:EntIndex(), unspectate, HOOK_MONITOR_LOW )
- local function disconnect( player ) -- We want to watch for spectator or target disconnect
- if player == target_ply or player == calling_ply then -- Target or spectator disconnecting
- unspectate( calling_ply, IN_FORWARD )
- end
- end
- hook.Add( "PlayerDisconnected", "ulx_unspectatedisconnect_" .. calling_ply:EntIndex(), disconnect, HOOK_MONITOR_HIGH )
- calling_ply:Spectate( OBS_MODE_IN_EYE )
- calling_ply:SpectateEntity( target_ply )
- calling_ply:StripWeapons() -- Otherwise they can use weapons while spectating
- ULib.tsay( calling_ply, "To get out of spectate, move forward.", true )
- ulx.setExclusive( calling_ply, "spectating" )
- ulx.fancyLogAdmin( calling_ply, true, "#A began spectating #T", target_ply )
- end
- local spectate = ulx.command( CATEGORY_NAME, "ulx spectate", ulx.spectate, "!spectate", true )
- spectate:addParam{ type=ULib.cmds.PlayerArg, target="!^" }
- spectate:defaultAccess( ULib.ACCESS_ADMIN )
- spectate:help( "Spectate target." )
- function ulx.addForcedDownload( path )
- if ULib.fileIsDir( path ) then
- files = ULib.filesInDir( path )
- for _, v in ipairs( files ) do
- ulx.addForcedDownload( path .. "/" .. v )
- end
- elseif ULib.fileExists( path ) then
- resource.AddFile( path )
- else
- Msg( "[ULX] ERROR: Tried to add nonexistent or empty file to forced downloads '" .. path .. "'\n" )
- end
- end
- function ulx.debuginfo( calling_ply )
- local str = string.format( "ULX version: %s\nULib version: %s\n", ULib.pluginVersionStr( "ULX" ), ULib.pluginVersionStr( "ULib" ) )
- str = str .. string.format( "Gamemode: %s\nMap: %s\n", GAMEMODE.Name, game.GetMap() )
- str = str .. "Dedicated server: " .. tostring( game.IsDedicated() ) .. "\n\n"
- local players = player.GetAll()
- str = str .. string.format( "Currently connected players:\nNick%s steamid%s uid%s id lsh\n", str.rep( " ", 27 ), str.rep( " ", 12 ), str.rep( " ", 7 ) )
- for _, ply in ipairs( players ) do
- local id = string.format( "%i", ply:EntIndex() )
- local steamid = ply:SteamID()
- local uid = tostring( ply:UniqueID() )
- local name = utf8.force( ply:Nick() )
- local plyline = name .. str.rep( " ", 32 - utf8.len( name ) ) -- Name
- plyline = plyline .. steamid .. str.rep( " ", 20 - steamid:len() ) -- Steamid
- plyline = plyline .. uid .. str.rep( " ", 11 - uid:len() ) -- Steamid
- plyline = plyline .. id .. str.rep( " ", 3 - id:len() ) -- id
- if ply:IsListenServerHost() then
- plyline = plyline .. "y "
- else
- plyline = plyline .. "n "
- end
- str = str .. plyline .. "\n"
- end
- local gmoddefault = ULib.parseKeyValues( ULib.stripComments( ULib.fileRead( "settings/users.txt", true ), "//" ) ) or {}
- str = str .. "\n\nULib.ucl.users (#=" .. table.Count( ULib.ucl.users ) .. "):\n" .. ulx.dumpTable( ULib.ucl.users, 1 ) .. "\n\n"
- str = str .. "ULib.ucl.groups (#=" .. table.Count( ULib.ucl.groups ) .. "):\n" .. ulx.dumpTable( ULib.ucl.groups, 1 ) .. "\n\n"
- str = str .. "ULib.ucl.authed (#=" .. table.Count( ULib.ucl.authed ) .. "):\n" .. ulx.dumpTable( ULib.ucl.authed, 1 ) .. "\n\n"
- str = str .. "Garrysmod default file (#=" .. table.Count( gmoddefault ) .. "):\n" .. ulx.dumpTable( gmoddefault, 1 ) .. "\n\n"
- str = str .. "Active workshop addons on this server:\n"
- local addons = engine.GetAddons()
- for i=1, #addons do
- local addon = addons[i]
- if addon.mounted then
- local name = utf8.force( addon.title )
- str = str .. string.format( "%s%s workshop ID %s\n", name, str.rep( " ", 32 - utf8.len( name ) ), addon.file:gsub( "%D", "" ) )
- end
- end
- str = str .. "\n"
- str = str .. "Active legacy addons on this server:\n"
- local _, possibleaddons = file.Find( "addons/*", "GAME" )
- for _, addon in ipairs( possibleaddons ) do
- if not ULib.findInTable( {"checkers", "chess", "common", "go", "hearts", "spades"}, addon:lower() ) then -- Not sure what these addon folders are
- local name = addon
- local author, version, date
- if ULib.fileExists( "addons/" .. addon .. "/addon.txt" ) then
- local t = ULib.parseKeyValues( ULib.stripComments( ULib.fileRead( "addons/" .. addon .. "/addon.txt" ), "//" ) )
- if t and t.AddonInfo then
- t = t.AddonInfo
- if t.name then name = t.name end
- if t.version then version = t.version end
- if tonumber( version ) then version = string.format( "%g", version ) end -- Removes innaccuracy in floating point numbers
- if t.author_name then author = t.author_name end
- if t.up_date then date = t.up_date end
- end
- end
- name = utf8.force( name )
- str = str .. name .. str.rep( " ", 32 - utf8.len( name ) )
- if author then
- str = string.format( "%s by %s%s", str, author, version and "," or "" )
- end
- if version then
- str = str .. " version " .. version
- end
- if date then
- str = string.format( "%s (%s)", str, date )
- end
- str = str .. "\n"
- end
- end
- ULib.fileWrite( "data/ulx/debugdump.txt", str )
- Msg( "Debug information written to garrysmod/data/ulx/debugdump.txt on server.\n" )
- end
- local debuginfo = ulx.command( CATEGORY_NAME, "ulx debuginfo", ulx.debuginfo )
- debuginfo:help( "Dump some debug information." )
- function ulx.resettodefaults( calling_ply, param )
- if param ~= "FORCE" then
- local str = "Are you SURE about this? It will remove ulx-created temporary bans, configs, groups, EVERYTHING!"
- local str2 = "If you're sure, type \"ulx resettodefaults FORCE\""
- if calling_ply:IsValid() then
- ULib.tsayError( calling_ply, str, true )
- ULib.tsayError( calling_ply, str2, true )
- else
- Msg( str .. "\n" )
- Msg( str2 .. "\n" )
- end
- return
- end
- ULib.fileDelete( "data/ulx/adverts.txt" )
- ULib.fileDelete( "data/ulx/banreasons.txt" )
- ULib.fileDelete( "data/ulx/config.txt" )
- ULib.fileDelete( "data/ulx/downloads.txt" )
- ULib.fileDelete( "data/ulx/gimps.txt" )
- ULib.fileDelete( "data/ulx/sbox_limits.txt" )
- ULib.fileDelete( "data/ulx/votemaps.txt" )
- ULib.fileDelete( "data/ulib/bans.txt" )
- ULib.fileDelete( "data/ulib/groups.txt" )
- ULib.fileDelete( "data/ulib/misc_registered.txt" )
- ULib.fileDelete( "data/ulib/users.txt" )
- local str = "Please change levels to finish the reset"
- if calling_ply:IsValid() then
- ULib.tsayError( calling_ply, str, true )
- else
- Msg( str .. "\n" )
- end
- ulx.fancyLogAdmin( calling_ply, "#A reset all ULX and ULib configuration" )
- end
- local resettodefaults = ulx.command( CATEGORY_NAME, "ulx resettodefaults", ulx.resettodefaults )
- resettodefaults:addParam{ type=ULib.cmds.StringArg, ULib.cmds.optional }
- resettodefaults:help( "Resets ALL ULX and ULib configuration!" )
- if SERVER then
- local ulx_kickAfterNameChanges = ulx.convar( "kickAfterNameChanges", "0", "<number> - Players can only change their name x times every ulx_kickAfterNameChangesCooldown seconds. 0 to disable.", ULib.ACCESS_ADMIN )
- local ulx_kickAfterNameChangesCooldown = ulx.convar( "kickAfterNameChangesCooldown", "60", "<time> - Players can change their name ulx_kickAfterXNameChanges times every x seconds.", ULib.ACCESS_ADMIN )
- local ulx_kickAfterNameChangesWarning = ulx.convar( "kickAfterNameChangesWarning", "1", "<1/0> - Display a warning to users to let them know how many more times they can change their name.", ULib.ACCESS_ADMIN )
- ulx.nameChangeTable = ulx.nameChangeTable or {}
- local function checkNameChangeLimit( ply, oldname, newname )
- local maxAttempts = ulx_kickAfterNameChanges:GetInt()
- local duration = ulx_kickAfterNameChangesCooldown:GetInt()
- local showWarning = ulx_kickAfterNameChangesWarning:GetInt()
- if maxAttempts ~= 0 then
- if not ulx.nameChangeTable[ply:SteamID()] then
- ulx.nameChangeTable[ply:SteamID()] = {}
- end
- for i=#ulx.nameChangeTable[ply:SteamID()], 1, -1 do
- if CurTime() - ulx.nameChangeTable[ply:SteamID()][i] > duration then
- table.remove( ulx.nameChangeTable[ply:SteamID()], i )
- end
- end
- table.insert( ulx.nameChangeTable[ply:SteamID()], CurTime() )
- local curAttempts = #ulx.nameChangeTable[ply:SteamID()]
- if curAttempts >= maxAttempts then
- ULib.kick( ply, "Changed name too many times" )
- else
- if showWarning == 1 then
- ULib.tsay( ply, "Warning: You have changed your name " .. curAttempts .. " out of " .. maxAttempts .. " time" .. ( maxAttempts ~= 1 and "s" ) .. " in the past " .. duration .. " second" .. ( duration ~= 1 and "s" ) )
- end
- end
- end
- end
- hook.Add( "ULibPlayerNameChanged", "ULXCheckNameChangeLimit", checkNameChangeLimit )
- end
- --------------------
- -- Hooks --
- --------------------
- -- This cvar also exists in DarkRP (thanks, FPtje)
- local cl_cvar_pickup = "cl_pickupplayers"
- if CLIENT then CreateClientConVar( cl_cvar_pickup, "1", true, true ) end
- local function playerPickup( ply, ent )
- local access, tag = ULib.ucl.query( ply, "ulx physgunplayer" )
- if ent:GetClass() == "player" and ULib.isSandbox() and access and not ent.NoNoclip and not ent.frozen and ply:GetInfoNum( cl_cvar_pickup, 1 ) == 1 then
- -- Extra restrictions! UCL wasn't designed to handle this sort of thing so we're putting it in by hand...
- local restrictions = {}
- ULib.cmds.PlayerArg.processRestrictions( restrictions, ply, {}, tag and ULib.splitArgs( tag )[ 1 ] )
- if restrictions.restrictedTargets == false or (restrictions.restrictedTargets and not table.HasValue( restrictions.restrictedTargets, ent )) then
- return
- end
- ent:SetMoveType( MOVETYPE_NONE ) -- So they don't bounce
- return true
- end
- end
- hook.Add( "PhysgunPickup", "ulxPlayerPickup", playerPickup, HOOK_HIGH ) -- Allow admins to move players. Call before the prop protection hook.
- if SERVER then ULib.ucl.registerAccess( "ulx physgunplayer", ULib.ACCESS_ADMIN, "Ability to physgun other players", "Other" ) end
- local function playerDrop( ply, ent )
- if ent:GetClass() == "player" then
- ent:SetMoveType( MOVETYPE_WALK )
- end
- end
- hook.Add( "PhysgunDrop", "ulxPlayerDrop", playerDrop )
- local CATEGORY_NAME = "Voting"
- ---------------
- --Public vote--
- ---------------
- if SERVER then ulx.convar( "voteEcho", "0", _, ULib.ACCESS_SUPERADMIN ) end -- Echo votes?
- -- First, our helper function to make voting so much easier!
- function ulx.doVote( title, options, callback, timeout, filter, noecho, ... )
- timeout = timeout or 20
- if ulx.voteInProgress then
- Msg( "Error! ULX tried to start a vote when another vote was in progress!\n" )
- return false
- end
- if not options[ 1 ] or not options[ 2 ] then
- Msg( "Error! ULX tried to start a vote without at least two options!\n" )
- return false
- end
- local voters = 0
- local rp = RecipientFilter()
- if not filter then
- rp:AddAllPlayers()
- voters = #player.GetAll()
- else
- for _, ply in ipairs( filter ) do
- rp:AddPlayer( ply )
- voters = voters + 1
- end
- end
- umsg.Start( "ulx_vote", rp )
- umsg.String( title )
- umsg.Short( timeout )
- ULib.umsgSend( options )
- umsg.End()
- ulx.voteInProgress = { callback=callback, options=options, title=title, results={}, voters=voters, votes=0, noecho=noecho, args={...} }
- timer.Create( "ULXVoteTimeout", timeout, 1, ulx.voteDone )
- return true
- end
- function ulx.voteCallback( ply, command, argv )
- if not ulx.voteInProgress then
- ULib.tsayError( ply, "There is not a vote in progress" )
- return
- end
- if not argv[ 1 ] or not tonumber( argv[ 1 ] ) or not ulx.voteInProgress.options[ tonumber( argv[ 1 ] ) ] then
- ULib.tsayError( ply, "Invalid or out of range vote." )
- return
- end
- if ply.ulxVoted then
- ULib.tsayError( ply, "You have already voted!" )
- return
- end
- local echo = ULib.toBool( GetConVarNumber( "ulx_voteEcho" ) )
- local id = tonumber( argv[ 1 ] )
- ulx.voteInProgress.results[ id ] = ulx.voteInProgress.results[ id ] or 0
- ulx.voteInProgress.results[ id ] = ulx.voteInProgress.results[ id ] + 1
- ulx.voteInProgress.votes = ulx.voteInProgress.votes + 1
- ply.ulxVoted = true -- Tag them as having voted
- local str = ply:Nick() .. " voted for: " .. ulx.voteInProgress.options[ id ]
- if echo and not ulx.voteInProgress.noecho then
- ULib.tsay( _, str ) -- TODO, color?
- end
- ulx.logString( str )
- if game.IsDedicated() then Msg( str .. "\n" ) end
- if ulx.voteInProgress.votes >= ulx.voteInProgress.voters then
- ulx.voteDone()
- end
- end
- if SERVER then concommand.Add( "ulx_vote", ulx.voteCallback ) end
- function ulx.voteDone( cancelled )
- local players = player.GetAll()
- for _, ply in ipairs( players ) do -- Clear voting tags
- ply.ulxVoted = nil
- end
- local vip = ulx.voteInProgress
- ulx.voteInProgress = nil
- timer.Remove( "ULXVoteTimeout" )
- if not cancelled then
- ULib.pcallError( vip.callback, vip, unpack( vip.args, 1, 10 ) ) -- Unpack is explicit in length to avoid odd LuaJIT quirk.
- end
- end
- -- End our helper functions
- local function voteDone( t )
- local results = t.results
- local winner
- local winnernum = 0
- for id, numvotes in pairs( results ) do
- if numvotes > winnernum then
- winner = id
- winnernum = numvotes
- end
- end
- local str
- if not winner then
- str = "Vote results: No option won because no one voted!"
- else
- str = "Vote results: Option '" .. t.options[ winner ] .. "' won. (" .. winnernum .. "/" .. t.voters .. ")"
- end
- ULib.tsay( _, str ) -- TODO, color?
- ulx.logString( str )
- Msg( str .. "\n" )
- end
- function ulx.vote( calling_ply, title, ... )
- if ulx.voteInProgress then
- ULib.tsayError( calling_ply, "There is already a vote in progress. Please wait for the current one to end.", true )
- return
- end
- ulx.doVote( title, { ... }, voteDone )
- ulx.fancyLogAdmin( calling_ply, "#A started a vote (#s)", title )
- end
- local vote = ulx.command( CATEGORY_NAME, "ulx vote", ulx.vote, "!vote" )
- vote:addParam{ type=ULib.cmds.StringArg, hint="title" }
- vote:addParam{ type=ULib.cmds.StringArg, hint="options", ULib.cmds.takeRestOfLine, repeat_min=2, repeat_max=10 }
- vote:defaultAccess( ULib.ACCESS_ADMIN )
- vote:help( "Starts a public vote." )
- -- Stop a vote in progress
- function ulx.stopVote( calling_ply )
- if not ulx.voteInProgress then
- ULib.tsayError( calling_ply, "There is no vote currently in progress.", true )
- return
- end
- ulx.voteDone( true )
- ulx.fancyLogAdmin( calling_ply, "#A has stopped the current vote." )
- end
- local stopvote = ulx.command( CATEGORY_NAME, "ulx stopvote", ulx.stopVote, "!stopvote" )
- stopvote:defaultAccess( ULib.ACCESS_SUPERADMIN )
- stopvote:help( "Stops a vote in progress." )
- local function voteMapDone2( t, changeTo, ply )
- local shouldChange = false
- if t.results[ 1 ] and t.results[ 1 ] > 0 then
- ulx.logServAct( ply, "#A approved the votemap" )
- shouldChange = true
- else
- ulx.logServAct( ply, "#A denied the votemap" )
- end
- if shouldChange then
- ULib.consoleCommand( "changelevel " .. changeTo .. "\n" )
- end
- end
- local function voteMapDone( t, argv, ply )
- local results = t.results
- local winner
- local winnernum = 0
- for id, numvotes in pairs( results ) do
- if numvotes > winnernum then
- winner = id
- winnernum = numvotes
- end
- end
- local ratioNeeded = GetConVarNumber( "ulx_votemap2Successratio" )
- local minVotes = GetConVarNumber( "ulx_votemap2Minvotes" )
- local str
- local changeTo
- -- Figure out the map to change to, if we're changing
- if #argv > 1 then
- changeTo = t.options[ winner ]
- else
- changeTo = argv[ 1 ]
- end
- if (#argv < 2 and winner ~= 1) or not winner or winnernum < minVotes or winnernum / t.voters < ratioNeeded then
- str = "Vote results: Vote was unsuccessful."
- elseif ply:IsValid() then
- str = "Vote results: Option '" .. t.options[ winner ] .. "' won, changemap pending approval. (" .. winnernum .. "/" .. t.voters .. ")"
- ulx.doVote( "Accept result and changemap to " .. changeTo .. "?", { "Yes", "No" }, voteMapDone2, 30000, { ply }, true, changeTo, ply )
- else -- It's the server console, let's roll with it
- str = "Vote results: Option '" .. t.options[ winner ] .. "' won. (" .. winnernum .. "/" .. t.voters .. ")"
- ULib.tsay( _, str )
- ulx.logString( str )
- ULib.consoleCommand( "changelevel " .. changeTo .. "\n" )
- return
- end
- ULib.tsay( _, str ) -- TODO, color?
- ulx.logString( str )
- if game.IsDedicated() then Msg( str .. "\n" ) end
- end
- function ulx.votemap2( calling_ply, ... )
- local argv = { ... }
- if ulx.voteInProgress then
- ULib.tsayError( calling_ply, "There is already a vote in progress. Please wait for the current one to end.", true )
- return
- end
- for i=2, #argv do
- if ULib.findInTable( argv, argv[ i ], 1, i-1 ) then
- ULib.tsayError( calling_ply, "Map " .. argv[ i ] .. " was listed twice. Please try again" )
- return
- end
- end
- if #argv > 1 then
- ulx.doVote( "Change map to..", argv, voteMapDone, _, _, _, argv, calling_ply )
- ulx.fancyLogAdmin( calling_ply, "#A started a votemap with options" .. string.rep( " #s", #argv ), ... )
- else
- ulx.doVote( "Change map to " .. argv[ 1 ] .. "?", { "Yes", "No" }, voteMapDone, _, _, _, argv, calling_ply )
- ulx.fancyLogAdmin( calling_ply, "#A started a votemap for #s", argv[ 1 ] )
- end
- end
- local votemap2 = ulx.command( CATEGORY_NAME, "ulx votemap2", ulx.votemap2, "!votemap2" )
- votemap2:addParam{ type=ULib.cmds.StringArg, completes=ulx.maps, hint="map", error="invalid map \"%s\" specified", ULib.cmds.restrictToCompletes, ULib.cmds.takeRestOfLine, repeat_min=1, repeat_max=10 }
- votemap2:defaultAccess( ULib.ACCESS_ADMIN )
- votemap2:help( "Starts a public map vote." )
- if SERVER then ulx.convar( "votemap2Successratio", "0.5", _, ULib.ACCESS_ADMIN ) end -- The ratio needed for a votemap2 to succeed
- if SERVER then ulx.convar( "votemap2Minvotes", "3", _, ULib.ACCESS_ADMIN ) end -- Minimum votes needed for votemap2
- local function voteKickDone2( t, target, time, ply, reason )
- local shouldKick = false
- if t.results[ 1 ] and t.results[ 1 ] > 0 then
- ulx.logUserAct( ply, target, "#A approved the votekick against #T (" .. (reason or "") .. ")" )
- shouldKick = true
- else
- ulx.logUserAct( ply, target, "#A denied the votekick against #T" )
- end
- if shouldKick then
- if reason and reason ~= "" then
- ULib.kick( target, "Vote kick successful. (" .. reason .. ")" )
- else
- ULib.kick( target, "Vote kick successful." )
- end
- end
- end
- local function voteKickDone( t, target, time, ply, reason )
- local results = t.results
- local winner
- local winnernum = 0
- for id, numvotes in pairs( results ) do
- if numvotes > winnernum then
- winner = id
- winnernum = numvotes
- end
- end
- local ratioNeeded = GetConVarNumber( "ulx_votekickSuccessratio" )
- local minVotes = GetConVarNumber( "ulx_votekickMinvotes" )
- local str
- if winner ~= 1 or winnernum < minVotes or winnernum / t.voters < ratioNeeded then
- str = "Vote results: User will not be kicked. (" .. (results[ 1 ] or "0") .. "/" .. t.voters .. ")"
- else
- if not target:IsValid() then
- str = "Vote results: User voted to be kicked, but has already left."
- elseif ply:IsValid() then
- str = "Vote results: User will now be kicked, pending approval. (" .. winnernum .. "/" .. t.voters .. ")"
- ulx.doVote( "Accept result and kick " .. target:Nick() .. "?", { "Yes", "No" }, voteKickDone2, 30000, { ply }, true, target, time, ply, reason )
- else -- Vote from server console, roll with it
- str = "Vote results: User will now be kicked. (" .. winnernum .. "/" .. t.voters .. ")"
- ULib.kick( target, "Vote kick successful." )
- end
- end
- ULib.tsay( _, str ) -- TODO, color?
- ulx.logString( str )
- if game.IsDedicated() then Msg( str .. "\n" ) end
- end
- function ulx.votekick( calling_ply, target_ply, reason )
- if target_ply:IsListenServerHost() then
- ULib.tsayError( calling_ply, "This player is immune to kicking", true )
- return
- end
- if ulx.voteInProgress then
- ULib.tsayError( calling_ply, "There is already a vote in progress. Please wait for the current one to end.", true )
- return
- end
- local msg = "Kick " .. target_ply:Nick() .. "?"
- if reason and reason ~= "" then
- msg = msg .. " (" .. reason .. ")"
- end
- ulx.doVote( msg, { "Yes", "No" }, voteKickDone, _, _, _, target_ply, time, calling_ply, reason )
- if reason and reason ~= "" then
- ulx.fancyLogAdmin( calling_ply, "#A started a votekick against #T (#s)", target_ply, reason )
- else
- ulx.fancyLogAdmin( calling_ply, "#A started a votekick against #T", target_ply )
- end
- end
- local votekick = ulx.command( CATEGORY_NAME, "ulx votekick", ulx.votekick, "!votekick" )
- votekick:addParam{ type=ULib.cmds.PlayerArg }
- votekick:addParam{ type=ULib.cmds.StringArg, hint="reason", ULib.cmds.optional, ULib.cmds.takeRestOfLine, completes=ulx.common_kick_reasons }
- votekick:defaultAccess( ULib.ACCESS_ADMIN )
- votekick:help( "Starts a public kick vote against target." )
- if SERVER then ulx.convar( "votekickSuccessratio", "0.6", _, ULib.ACCESS_ADMIN ) end -- The ratio needed for a votekick to succeed
- if SERVER then ulx.convar( "votekickMinvotes", "2", _, ULib.ACCESS_ADMIN ) end -- Minimum votes needed for votekick
- local function voteBanDone2( t, nick, steamid, time, ply, reason )
- local shouldBan = false
- if t.results[ 1 ] and t.results[ 1 ] > 0 then
- ulx.fancyLogAdmin( ply, "#A approved the voteban against #s (#s minutes) (#s))", nick, time, reason or "" )
- shouldBan = true
- else
- ulx.fancyLogAdmin( ply, "#A denied the voteban against #s", nick )
- end
- if shouldBan then
- ULib.addBan( steamid, time, reason, nick, ply )
- end
- end
- local function voteBanDone( t, nick, steamid, time, ply, reason )
- local results = t.results
- local winner
- local winnernum = 0
- for id, numvotes in pairs( results ) do
- if numvotes > winnernum then
- winner = id
- winnernum = numvotes
- end
- end
- local ratioNeeded = GetConVarNumber( "ulx_votebanSuccessratio" )
- local minVotes = GetConVarNumber( "ulx_votebanMinvotes" )
- local str
- if winner ~= 1 or winnernum < minVotes or winnernum / t.voters < ratioNeeded then
- str = "Vote results: User will not be banned. (" .. (results[ 1 ] or "0") .. "/" .. t.voters .. ")"
- else
- reason = ("[ULX Voteban] " .. (reason or "")):Trim()
- if ply:IsValid() then
- str = "Vote results: User will now be banned, pending approval. (" .. winnernum .. "/" .. t.voters .. ")"
- ulx.doVote( "Accept result and ban " .. nick .. "?", { "Yes", "No" }, voteBanDone2, 30000, { ply }, true, nick, steamid, time, ply, reason )
- else -- Vote from server console, roll with it
- str = "Vote results: User will now be banned. (" .. winnernum .. "/" .. t.voters .. ")"
- ULib.addBan( steamid, time, reason, nick, ply )
- end
- end
- ULib.tsay( _, str ) -- TODO, color?
- ulx.logString( str )
- Msg( str .. "\n" )
- end
- function ulx.voteban( calling_ply, target_ply, minutes, reason )
- if target_ply:IsListenServerHost() or target_ply:IsBot() then
- ULib.tsayError( calling_ply, "This player is immune to banning", true )
- return
- end
- if ulx.voteInProgress then
- ULib.tsayError( calling_ply, "There is already a vote in progress. Please wait for the current one to end.", true )
- return
- end
- local msg = "Ban " .. target_ply:Nick() .. " for " .. minutes .. " minutes?"
- if reason and reason ~= "" then
- msg = msg .. " (" .. reason .. ")"
- end
- ulx.doVote( msg, { "Yes", "No" }, voteBanDone, _, _, _, target_ply:Nick(), target_ply:SteamID(), minutes, calling_ply, reason )
- if reason and reason ~= "" then
- ulx.fancyLogAdmin( calling_ply, "#A started a voteban of #i minute(s) against #T (#s)", minutes, target_ply, reason )
- else
- ulx.fancyLogAdmin( calling_ply, "#A started a voteban of #i minute(s) against #T", minutes, target_ply )
- end
- end
- local voteban = ulx.command( CATEGORY_NAME, "ulx voteban", ulx.voteban, "!voteban" )
- voteban:addParam{ type=ULib.cmds.PlayerArg }
- voteban:addParam{ type=ULib.cmds.NumArg, min=0, default=1440, hint="minutes", ULib.cmds.allowTimeString, ULib.cmds.optional }
- voteban:addParam{ type=ULib.cmds.StringArg, hint="reason", ULib.cmds.optional, ULib.cmds.takeRestOfLine, completes=ulx.common_kick_reasons }
- voteban:defaultAccess( ULib.ACCESS_ADMIN )
- voteban:help( "Starts a public ban vote against target." )
- if SERVER then ulx.convar( "votebanSuccessratio", "0.7", _, ULib.ACCESS_ADMIN ) end -- The ratio needed for a voteban to succeed
- if SERVER then ulx.convar( "votebanMinvotes", "3", _, ULib.ACCESS_ADMIN ) end -- Minimum votes needed for voteban
- -- Our regular votemap command
- local votemap = ulx.command( CATEGORY_NAME, "ulx votemap", ulx.votemap, "!votemap" )
- votemap:addParam{ type=ULib.cmds.StringArg, completes=ulx.votemaps, hint="map", ULib.cmds.takeRestOfLine, ULib.cmds.optional }
- votemap:defaultAccess( ULib.ACCESS_ALL )
- votemap:help( "Vote for a map, no args lists available maps." )
- -- Our veto command
- local veto = ulx.command( CATEGORY_NAME, "ulx veto", ulx.votemapVeto, "!veto" )
- veto:defaultAccess( ULib.ACCESS_ADMIN )
- veto:help( "Veto a successful votemap." )
- --XLIB -- by Stickly Man!
- --A library of helper functions used by XGUI for creating derma controls with a single line of code.
- --Currently a bit disorganized and unstandardized, (just put in things as I needed them). I'm hoping to fix that sometime.
- xlib = {}
- function xlib.makecheckbox( t )
- local pnl = vgui.Create( "DCheckBoxLabel", t.parent )
- pnl:SetPos( t.x, t.y )
- pnl:SetText( t.label or "" )
- pnl:SizeToContents()
- pnl:SetValue( t.value or 0 )
- pnl:SetZPos( t.zpos or 0 )
- if t.convar then pnl:SetConVar( t.convar ) end
- if t.textcolor then
- pnl:SetTextColor( t.textcolor )
- else
- pnl:SetTextColor( SKIN.text_dark )
- end
- if not t.tooltipwidth then t.tooltipwidth = 250 end
- if t.tooltip then
- if t.tooltipwidth ~= 0 then
- t.tooltip = xlib.wordWrap( t.tooltip, t.tooltipwidth, "Default" )
- end
- pnl:SetTooltip( t.tooltip )
- end
- function pnl:SetDisabled( val )
- pnl.disabled = val
- pnl:SetMouseInputEnabled( not val )
- pnl:SetAlpha( val and 128 or 255 )
- end
- if t.disabled then pnl:SetDisabled( t.disabled ) end
- --Work around for bug where changing the parent of a disabled textbox reenables mouse input.
- local tempfunc = pnl.SetParent
- pnl.SetParent = function( self, parent )
- local ret = tempfunc( self, parent )
- self:SetDisabled( self.disabled )
- return ret
- end
- --Replicated Convar Updating
- if t.repconvar then
- xlib.checkRepCvarCreated( t.repconvar )
- pnl:SetValue( GetConVar( t.repconvar ):GetBool() )
- function pnl.ConVarUpdated( sv_cvar, cl_cvar, ply, old_val, new_val )
- if cl_cvar == t.repconvar:lower() then
- pnl:SetValue( new_val )
- end
- end
- hook.Add( "ULibReplicatedCvarChanged", "XLIB_" .. t.repconvar, pnl.ConVarUpdated )
- function pnl:OnChange( bVal )
- RunConsoleCommand( t.repconvar, tostring( bVal and 1 or 0 ) )
- end
- pnl.Think = function() end --Override think functions to remove Garry's convar check to (hopefully) speed things up
- pnl.ConVarNumberThink = function() end
- pnl.ConVarStringThink = function() end
- pnl.ConVarChanged = function() end
- end
- return pnl
- end
- function xlib.makelabel( t )
- local pnl = vgui.Create( "DLabel", t.parent )
- pnl:SetPos( t.x, t.y )
- pnl:SetText( t.label or "" )
- pnl:SetZPos( t.zpos or 0 )
- if not t.tooltipwidth then t.tooltipwidth = 250 end
- if t.tooltip then
- if t.tooltipwidth ~= 0 then
- t.tooltip = xlib.wordWrap( t.tooltip, t.tooltipwidth, "Default" )
- end
- pnl:SetTooltip( t.tooltip )
- pnl:SetMouseInputEnabled( true )
- end
- if t.font then pnl:SetFont( t.font ) end
- if t.w and t.wordwrap then
- pnl:SetText( xlib.wordWrap( t.label, t.w, t.font or "Default" ) )
- end
- pnl:SizeToContents()
- if t.w then pnl:SetWidth( t.w ) end
- if t.h then pnl:SetHeight( t.h ) end
- if t.textcolor then
- pnl:SetTextColor( t.textcolor )
- else
- pnl:SetTextColor( SKIN.text_dark )
- end
- return pnl
- end
- function xlib.makelistlayout( t )
- local pnl = vgui.Create( "DListLayout" )
- pnl.scroll = vgui.Create( "DScrollPanel", t.parent )
- pnl.scroll:SetPos( t.x, t.y )
- pnl.scroll:SetSize( t.w, t.h )
- pnl:SetSize( t.w, t.h )
- pnl.scroll:AddItem( pnl )
- pnl:SetZPos( t.zpos or 0 )
- function pnl:PerformLayout()
- self:SizeToChildren( false, true )
- self:SetWide( self.scroll:GetWide() - ( self.scroll.VBar.Enabled and 16 or 0 ) )
- end
- return pnl
- end
- function xlib.makebutton( t )
- local pnl = vgui.Create( "DButton", t.parent )
- pnl:SetSize( t.w or 20, t.h or 20 )
- pnl:SetPos( t.x, t.y )
- pnl:SetText( t.label or "" )
- pnl:SetDisabled( t.disabled )
- pnl:SetZPos( t.zpos or 0 )
- if t.icon then pnl:SetIcon( t.icon ) end
- if t.font then pnl:SetFont( t.font ) end
- if t.btype and t.btype == "close" then
- pnl.Paint = function( panel, w, h ) derma.SkinHook( "Paint", "WindowCloseButton", panel, w, h ) end
- end
- if t.centericon then --Place the image in the cetner of the button instead of the default layout.
- function pnl:PerformLayout()
- if ( IsValid( self.m_Image ) ) then
- self.m_Image:SetPos( (self:GetWide() - self.m_Image:GetWide()) * 0.5, (self:GetTall() - self.m_Image:GetTall()) * 0.5 )
- self:SetTextInset( self.m_Image:GetWide() + 16, 0 )
- end
- DLabel.PerformLayout( self )
- end
- end
- if not t.tooltipwidth then t.tooltipwidth = 250 end
- if t.tooltip then
- if t.tooltipwidth ~= 0 then
- t.tooltip = xlib.wordWrap( t.tooltip, t.tooltipwidth, "Default" )
- end
- pnl:SetTooltip( t.tooltip )
- pnl:SetMouseInputEnabled( true )
- end
- return pnl
- end
- function xlib.makeframe( t )
- local pnl = vgui.Create( "DFrame", t.parent )
- pnl:SetSize( t.w, t.h )
- if t.nopopup ~= true then pnl:MakePopup() end
- pnl:SetPos( t.x or ScrW()/2-t.w/2, t.y or ScrH()/2-t.h/2 )
- pnl:SetTitle( t.label or "" )
- if t.draggable ~= nil then pnl:SetDraggable( t.draggable ) end
- if t.showclose ~= nil then pnl:ShowCloseButton( t.showclose ) end
- if t.skin then pnl:SetSkin( t.skin ) end
- if t.visible ~= nil then pnl:SetVisible( t.visible ) end
- return pnl
- end
- function xlib.makepropertysheet( t )
- local pnl = vgui.Create( "DPropertySheet", t.parent )
- pnl:SetPos( t.x, t.y )
- pnl:SetSize( t.w, t.h )
- --Clears all of the tabs in the base.
- function pnl:Clear()
- for _, Sheet in ipairs( self.Items ) do
- Sheet.Panel:SetParent( t.offloadparent )
- Sheet.Tab:Remove()
- end
- self.m_pActiveTab = nil
- self:SetActiveTab( nil )
- self.tabScroller.Panels = {}
- self.Items = {}
- end
- return pnl
- end
- function xlib.maketextbox( t )
- local pnl = vgui.Create( "DTextEntry", t.parent )
- pnl:SetPos( t.x, t.y )
- pnl:SetWide( t.w )
- pnl:SetTall( t.h or 20 )
- pnl:SetEnterAllowed( true )
- pnl:SetZPos( t.zpos or 0 )
- if t.convar then pnl:SetConVar( t.convar ) end
- if t.text then pnl:SetText( t.text ) end
- if t.enableinput then pnl:SetEnabled( t.enableinput ) end
- if t.multiline then pnl:SetMultiline( t.multiline ) end
- pnl.selectAll = t.selectall
- if not t.tooltipwidth then t.tooltipwidth = 250 end
- if t.tooltip then
- if t.tooltipwidth ~= 0 then
- t.tooltip = xlib.wordWrap( t.tooltip, t.tooltipwidth, "Default" )
- end
- pnl:SetTooltip( t.tooltip )
- end
- function pnl:SetDisabled( val ) --Simulate enabling/disabling of a textbox
- pnl:SetEnabled( not val )
- pnl:SetMouseInputEnabled( not val )
- pnl:SetAlpha( val and 128 or 255 )
- end
- if t.disabled then pnl:SetDisabled( t.disabled ) end
- --Replicated Convar Updating
- if t.repconvar then
- xlib.checkRepCvarCreated( t.repconvar )
- pnl:SetValue( GetConVar( t.repconvar ):GetString() )
- function pnl.ConVarUpdated( sv_cvar, cl_cvar, ply, old_val, new_val )
- if cl_cvar == t.repconvar:lower() then
- pnl:SetValue( new_val )
- end
- end
- hook.Add( "ULibReplicatedCvarChanged", "XLIB_" .. t.repconvar, pnl.ConVarUpdated )
- function pnl:UpdateConvarValue()
- RunConsoleCommand( t.repconvar, self:GetValue() )
- end
- function pnl:OnEnter()
- RunConsoleCommand( t.repconvar, self:GetValue() )
- end
- pnl.Think = function() end --Override think functions to remove Garry's convar check to (hopefully) speed things up
- pnl.ConVarNumberThink = function() end
- pnl.ConVarStringThink = function() end
- pnl.ConVarChanged = function() end
- end
- return pnl
- end
- function xlib.makelistview( t )
- local pnl = vgui.Create( "DListView", t.parent )
- pnl:SetPos( t.x, t.y )
- pnl:SetSize( t.w, t.h )
- pnl:SetMultiSelect( t.multiselect )
- pnl:SetHeaderHeight( t.headerheight or 20 )
- return pnl
- end
- function xlib.makecat( t )
- local pnl = vgui.Create( "DCollapsibleCategory", t.parent )
- pnl:SetPos( t.x, t.y )
- pnl:SetSize( t.w, t.h )
- pnl:SetLabel( t.label or "" )
- pnl:SetContents( t.contents )
- t.contents:SetParent( pnl )
- t.contents:Dock( TOP )
- if t.expanded ~= nil then pnl:SetExpanded( t.expanded ) end
- if t.checkbox then
- pnl.checkBox = vgui.Create( "DCheckBox", pnl.Header )
- pnl.checkBox:SetValue( t.expanded )
- function pnl.checkBox:DoClick()
- self:Toggle()
- pnl:Toggle()
- end
- function pnl.Header:OnMousePressed( mcode )
- if ( mcode == MOUSE_LEFT ) then
- self:GetParent():Toggle()
- self:GetParent().checkBox:Toggle()
- return
- end
- return self:GetParent():OnMousePressed( mcode )
- end
- local tempfunc = pnl.PerformLayout
- pnl.PerformLayout = function( self )
- tempfunc( self )
- self.checkBox:SetPos( self:GetWide()-18, 2 )
- end
- end
- function pnl:SetOpen( bVal )
- if not self:GetExpanded() and bVal then
- pnl.Header:OnMousePressed( MOUSE_LEFT ) --Call the mouse function so it properly toggles the checkbox state (if it exists)
- elseif self:GetExpanded() and not bVal then
- pnl.Header:OnMousePressed( MOUSE_LEFT )
- end
- end
- return pnl
- end
- function xlib.makepanel( t )
- local pnl = vgui.Create( "DPanel", t.parent )
- pnl:SetPos( t.x, t.y )
- pnl:SetSize( t.w, t.h )
- pnl:SetZPos( t.zpos or 0 )
- if t.skin then pnl:SetSkin( t.skin ) end
- if t.visible ~= nil then pnl:SetVisible( t.visible ) end
- return pnl
- end
- function xlib.makeXpanel( t )
- local pnl = vgui.Create( "xlib_Panel", t.parent )
- pnl:MakePopup()
- pnl:SetPos( t.x, t.y )
- pnl:SetSize( t.w, t.h )
- if t.visible ~= nil then pnl:SetVisible( t.visible ) end
- return pnl
- end
- function xlib.makenumberwang( t )
- local pnl = vgui.Create( "DNumberWang", t.parent )
- pnl:SetPos( t.x, t.y )
- pnl:SetDecimals( t.decimal or 0 )
- pnl:SetMinMax( t.min or 0, t.max or 255 )
- pnl:SizeToContents()
- pnl:SetValue( t.value )
- pnl:SetZPos( t.zpos or 0 )
- if t.w then pnl:SetWide( t.w ) end
- if t.h then pnl:SetTall( t.h ) end
- return pnl
- end
- function xlib.makecombobox( t )
- local pnl = vgui.Create( "DComboBox", t.parent )
- t.w = t.w or 100
- t.h = t.h or 20
- pnl:SetPos( t.x, t.y )
- pnl:SetSize( t.w, t.h )
- pnl:SetZPos( t.zpos or 0 )
- --Create a textbox to use in place of the button
- if ( t.enableinput == true ) then
- pnl.TextEntry = vgui.Create( "DTextEntry", pnl )
- pnl.TextEntry.selectAll = t.selectall
- pnl.TextEntry:SetEditable( true )
- pnl.TextEntry.OnGetFocus = function( self ) --Close the menu when the textbox is clicked, IF the menu was open.
- hook.Run( "OnTextEntryGetFocus", self )
- if ( pnl.Menu ) then
- pnl.Menu:Remove()
- pnl.Menu = nil
- end
- end
- --Override GetValue/SetValue to get/set the text from the TextEntry instead of itself.
- pnl.GetValue = function( self ) return self.TextEntry:GetValue() end
- pnl.SetText = function( self, val ) self.TextEntry:SetValue( val ) end
- pnl.ChooseOption = function( self, value, index ) --Update the text of the TextEntry when an option is selected.
- if ( self.Menu ) then
- self.Menu:Remove()
- self.Menu = nil
- end
- self.TextEntry:SetText( value )
- self:OnSelect( index, value, self.Data[index] )
- end
- pnl.PerformLayout = function( self ) --Update the size of the textbox when the combobox's PerformLayout is called.
- self.DropButton:SetSize( 15, 15 )
- self.DropButton:AlignRight( 4 )
- self.DropButton:CenterVertical()
- self.TextEntry:SetSize( self:GetWide()-20, self:GetTall() )
- end
- end
- pnl:SetText( t.text or "" )
- if not t.tooltipwidth then t.tooltipwidth = 250 end
- if t.tooltip then
- if t.tooltipwidth ~= 0 then
- t.tooltip = xlib.wordWrap( t.tooltip, t.tooltipwidth, "Default" )
- end
- pnl:SetTooltip( t.tooltip )
- end
- if t.choices then
- for i, v in ipairs( t.choices ) do
- pnl:AddChoice( v )
- end
- end
- function pnl:SetDisabled( val ) --enabling/disabling of a textbox
- self:SetMouseInputEnabled( not val )
- self:SetAlpha( val and 128 or 255 )
- end
- if t.disabled then pnl:SetDisabled( t.disabled ) end
- --Garrys function with no comments, just adding support for Spacers and setting the skin.
- function pnl:OpenMenu()
- if ( #self.Choices == 0 ) then return end
- if ( IsValid( self.Menu ) ) then
- self.Menu:Remove()
- self.Menu = nil
- end
- self.Menu = DermaMenu()
- self.Menu:SetSkin( xgui.settings.skin )
- for k, v in pairs( self.Choices ) do
- if v == "--*" then --This is the string to determine where to add the spacer
- self.Menu:AddSpacer()
- else
- self.Menu:AddOption( v, function() self:ChooseOption( v, k ) end )
- end
- end
- local x, y = self:LocalToScreen( 0, self:GetTall() )
- self.Menu:SetMinimumWidth( self:GetWide() )
- self.Menu:Open( x, y, false, self )
- end
- --Replicated Convar Updating
- if t.repconvar then
- xlib.checkRepCvarCreated( t.repconvar )
- if t.isNumberConvar then --This is for convar settings stored via numbers (like ulx_rslotsMode)
- if t.numOffset == nil then t.numOffset = 1 end
- local cvar = GetConVar( t.repconvar ):GetInt()
- if tonumber( cvar ) and cvar + t.numOffset <= #pnl.Choices and cvar + t.numOffset > 0 then
- pnl:ChooseOptionID( cvar + t.numOffset )
- else
- pnl:SetText( "Invalid Convar Value" )
- end
- function pnl.ConVarUpdated( sv_cvar, cl_cvar, ply, old_val, new_val )
- if cl_cvar == t.repconvar:lower() then
- if tonumber( new_val ) and new_val + t.numOffset <= #pnl.Choices and new_val + t.numOffset > 0 then
- pnl:ChooseOptionID( new_val + t.numOffset )
- else
- pnl:SetText( "Invalid Convar Value" )
- end
- end
- end
- hook.Add( "ULibReplicatedCvarChanged", "XLIB_" .. t.repconvar, pnl.ConVarUpdated )
- function pnl:OnSelect( index )
- RunConsoleCommand( t.repconvar, tostring( index - t.numOffset ) )
- end
- else --Otherwise, use each choice as a string for the convar
- pnl:SetText( GetConVar( t.repconvar ):GetString() )
- function pnl.ConVarUpdated( sv_cvar, cl_cvar, ply, old_val, new_val )
- if cl_cvar == t.repconvar:lower() then
- if t.convarblanklabel and new_val == "" then new_val = t.convarblanklabel end
- pnl:SetText( new_val )
- end
- end
- hook.Add( "ULibReplicatedCvarChanged", "XLIB_" .. t.repconvar, pnl.ConVarUpdated )
- function pnl:OnSelect( index, value )
- if t.convarblanklabel and value == "<not specified>" then value = "" end
- RunConsoleCommand( t.repconvar, value )
- end
- end
- end
- return pnl
- end
- function xlib.maketree( t )
- local pnl = vgui.Create( "DTree", t.parent )
- pnl:SetPos( t.x, t.y )
- pnl:SetSize( t.w, t.h )
- function pnl:Clear() --Clears the DTree.
- if self.RootNode.ChildNodes then
- for _, node in ipairs( self.RootNode.ChildNodes:GetChildren() ) do
- node:Remove()
- end
- self.m_pSelectedItem = nil
- self:InvalidateLayout()
- end
- end
- return pnl
- end
- function xlib.makecolorpicker( t )
- local pnl = vgui.Create( "xlibColorPanel", t.parent )
- pnl:SetPos( t.x, t.y )
- if t.noalphamodetwo then pnl:NoAlphaModeTwo() end --Provide an alternate layout with no alpha bar.
- if t.addalpha then
- pnl:AddAlphaBar()
- if t.alphamodetwo then pnl:AlphaModeTwo() end
- end
- if t.color then pnl:SetColor( t.color ) end
- if t.repconvar then
- xlib.checkRepCvarCreated( t.repconvar )
- local col = GetConVar( t.repconvar ):GetString()
- if col == "0" then col = "0 0 0" end
- col = string.Split( col, " " )
- pnl:SetColor( Color( col[1], col[2], col[3] ) )
- function pnl.ConVarUpdated( sv_cvar, cl_cvar, ply, old_val, new_val )
- if cl_cvar == t.repconvar:lower() then
- local col = string.Split( new_val, " " )
- pnl:SetColor( Color( col[1], col[2], col[3] ) )
- end
- end
- hook.Add( "ULibReplicatedCvarChanged", "XLIB_" .. t.repconvar, pnl.ConVarUpdated )
- function pnl:OnChange( color )
- RunConsoleCommand( t.repconvar, color.r .. " " .. color.g .. " " .. color.b )
- end
- end
- return pnl
- end
- --Thanks to Megiddo for this code! :D
- function xlib.wordWrap( text, width, font )
- surface.SetFont( font )
- if not surface.GetTextSize( "" ) then
- surface.SetFont( "default" ) --Set font to default if specified font does not return a size properly.
- end
- text = text:Trim()
- local output = ""
- local pos_start, pos_end = 1, 1
- while true do
- local begin, stop = text:find( "%s+", pos_end + 1 )
- if (surface.GetTextSize( text:sub( pos_start, begin or -1 ):Trim() ) > width and pos_end - pos_start > 0) then -- If it's not going to fit, split into a newline
- output = output .. text:sub( pos_start, pos_end ):Trim() .. "\n"
- pos_start = pos_end + 1
- pos_end = pos_end + 1
- else
- pos_end = stop
- end
- if not stop then -- We've hit our last word
- output = output .. text:sub( pos_start ):Trim()
- break
- end
- end
- return output
- end
- function xlib.makeprogressbar( t )
- local pnl = vgui.Create( "DProgress", t.parent )
- pnl.Label = xlib.makelabel{ x=5, y=3, w=(t.w or 100), textcolor=SKIN.text_dark, parent=pnl }
- pnl:SetPos( t.x, t.y )
- pnl:SetSize( t.w or 100, t.h or 20 )
- pnl:SetFraction( t.value or 0 )
- if t.skin then pnl:SetSkin( t.skin ) end
- if t.visible ~= nil then pnl:SetVisible( t.visible ) end
- return pnl
- end
- function xlib.checkRepCvarCreated( cvar )
- if GetConVar( cvar ) == nil then
- CreateClientConVar( cvar:lower(), 0, false, false ) --Replicated cvar hasn't been created via ULib. Create a temporary one to prevent errors
- end
- end
- function xlib.makeslider( t )
- local pnl = vgui.Create( "DNumSlider", t.parent )
- pnl.PerformLayout = function() end -- Clears the code that automatically sets the width of the label to 41% of the entire width.
- pnl:SetPos( t.x, t.y )
- pnl:SetWide( t.w or 100 )
- pnl:SetTall( t.h or 20 )
- pnl:SetText( t.label or "" )
- pnl:SetMinMax( t.min or 0, t.max or 100 )
- pnl:SetDecimals( t.decimal or 0 )
- pnl.TextArea:SetDrawBackground( true )
- pnl.TextArea.selectAll = t.selectall
- pnl.Label:SizeToContents()
- pnl:SetZPos( t.zpos or 0 )
- if t.textcolor then
- pnl.Label:SetTextColor( t.textcolor )
- else
- pnl.Label:SetTextColor( SKIN.text_dark )
- end
- if t.fixclip then pnl.Slider.Knob:NoClipping( false ) end --Fixes clipping on the knob, an example is the sandbox limit sliders.
- if t.convar then pnl:SetConVar( t.convar ) end
- if not t.tooltipwidth then t.tooltipwidth = 250 end
- if t.tooltip then
- if t.tooltipwidth ~= 0 then
- t.tooltip = xlib.wordWrap( t.tooltip, t.tooltipwidth, "Default" )
- end
- pnl:SetTooltip( t.tooltip )
- end
- --Support for enabling/disabling slider
- pnl.SetDisabled = function( self, val )
- pnl:SetAlpha( val and 128 or 255 )
- pnl:SetEnabled( not val )
- pnl.TextArea:SetEnabled( not val )
- pnl.TextArea:SetMouseInputEnabled( not val )
- pnl.Scratch:SetMouseInputEnabled( not val )
- pnl.Slider:SetMouseInputEnabled( not val )
- end
- if t.disabled then pnl:SetDisabled( t.disabled ) end
- pnl:SizeToContents()
- --
- --The following code bits are basically copies of Garry's code with changes to prevent the slider from sending updates so often
- pnl.GetValue = function( self ) return tonumber( self.TextArea:GetValue() ) end
- function pnl.SetValue( self, val )
- if ( val == nil ) then return end
- if t.clampmin then val = math.max( tonumber( val ) or 0, self:GetMin() ) end
- if t.clampmax then val = math.min( tonumber( val ) or 0, self:GetMax() ) end
- self.Scratch:SetValue( val )
- self.ValueUpdated( val )
- self:ValueChanged( val )
- end
- function pnl.ValueChanged( self, val )
- if t.clampmin then val = math.max( tonumber( val ) or 0, self:GetMin() ) end
- if t.clampmax then val = math.min( tonumber( val ) or 0, self:GetMax() ) end
- self.Slider:SetSlideX( self.Scratch:GetFraction( val ) )
- if ( self.TextArea ~= vgui.GetKeyboardFocus() ) then
- self.TextArea:SetValue( self.Scratch:GetTextValue() )
- end
- self:OnValueChanged( val )
- end
- --Textbox
- function pnl.ValueUpdated( value )
- pnl.TextArea:SetText( string.format("%." .. ( pnl.Scratch:GetDecimals() ) .. "f", tonumber( value ) or 0) )
- end
- pnl.TextArea.OnTextChanged = function() end
- function pnl.TextArea:OnEnter()
- pnl.TextArea:SetText( string.format("%." .. ( pnl.Scratch:GetDecimals() ) .. "f", tonumber( pnl.TextArea:GetText() ) or 0) )
- if pnl.OnEnter then pnl:OnEnter() end
- end
- function pnl.TextArea:OnLoseFocus()
- pnl:SetValue( pnl.TextArea:GetText() )
- hook.Call( "OnTextEntryLoseFocus", nil, self )
- end
- --Slider
- local pnl_val
- function pnl:TranslateSliderValues( x, y )
- pnl_val = self.Scratch:GetMin() + (x * self.Scratch:GetRange()) --Store the value and update the textbox to the new value
- pnl.ValueUpdated( pnl_val )
- self.Scratch:SetFraction( x )
- return self.Scratch:GetFraction(), y
- end
- local tmpfunc = pnl.Slider.Knob.OnMouseReleased
- pnl.Slider.Knob.OnMouseReleased = function( self, mcode )
- tmpfunc( self, mcode )
- pnl.Slider:OnMouseReleased( mcode )
- end
- local tmpfunc = pnl.Slider.SetDragging
- pnl.Slider.SetDragging = function( self, bval )
- tmpfunc( self, bval )
- if ( not bval ) then pnl:SetValue( pnl.TextArea:GetText() ) end
- end
- pnl.Slider.OnMouseReleased = function( self, mcode )
- self:SetDragging( false )
- self:MouseCapture( false )
- end
- --Scratch
- function pnl.Scratch:OnCursorMoved( x, y )
- if ( not self:GetActive() ) then return end
- x = x - math.floor( self:GetWide() * 0.5 )
- y = y - math.floor( self:GetTall() * 0.5 )
- local zoom = self:GetZoom()
- local ControlScale = 100 / zoom;
- local maxzoom = 20
- if ( self:GetDecimals() ) then
- maxzoom = 10000
- end
- zoom = math.Clamp( zoom + ((y * -0.6) / ControlScale), 0.01, maxzoom );
- self:SetZoom( zoom )
- local value = self:GetFloatValue()
- value = math.Clamp( value + (x * ControlScale * 0.002), self:GetMin(), self:GetMax() );
- self:SetFloatValue( value )
- pnl_val = value --Store value for later
- pnl.ValueUpdated( pnl_val )
- self:LockCursor()
- end
- pnl.Scratch.OnMouseReleased = function( self, mousecode )
- g_Active = nil
- self:SetActive( false )
- self:MouseCapture( false )
- self:SetCursor( "sizewe" )
- pnl:SetValue( pnl.TextArea:GetText() )
- end
- --End code changes
- --
- if t.value then pnl:SetValue( t.value ) end
- --Replicated Convar Updating
- if t.repconvar then
- xlib.checkRepCvarCreated( t.repconvar )
- pnl:SetValue( GetConVar( t.repconvar ):GetFloat() )
- function pnl.ConVarUpdated( sv_cvar, cl_cvar, ply, old_val, new_val )
- if cl_cvar == t.repconvar:lower() then
- if ( IsValid( pnl ) ) then --Prevents random errors when joining.
- pnl:SetValue( new_val )
- end
- end
- end
- hook.Add( "ULibReplicatedCvarChanged", "XLIB_" .. t.repconvar, pnl.ConVarUpdated )
- function pnl:OnValueChanged( val )
- RunConsoleCommand( t.repconvar, tostring( val ) )
- end
- --Override think functions to remove Garry's convar check to (hopefully) speed things up
- pnl.ConVarNumberThink = function() end
- pnl.ConVarStringThink = function() end
- pnl.ConVarChanged = function() end
- end
- return pnl
- end
- -----------------------------------------
- --A stripped-down customized DPanel allowing for textbox input!
- -----------------------------------------
- local PANEL = {}
- AccessorFunc( PANEL, "m_bPaintBackground", "PaintBackground" )
- Derma_Hook( PANEL, "Paint", "Paint", "Panel" )
- Derma_Hook( PANEL, "ApplySchemeSettings", "Scheme", "Panel" )
- function PANEL:Init()
- self:SetPaintBackground( true )
- end
- derma.DefineControl( "xlib_Panel", "", PANEL, "EditablePanel" )
- -----------------------------------------
- --A copy of Garry's ColorCtrl used in the sandbox spawnmenu, with the following changes:
- -- -Doesn't use convars whatsoever
- -- -Is a fixed size, but you can have it with/without the alphabar, and there's two layout styles without the alpha bar.
- -- -Has two functions: OnChange and OnChangeImmediate for greater control of handling changes.
- -----------------------------------------
- local PANEL = {}
- function PANEL:Init()
- self.showAlpha=false
- self:SetSize( 130, 135 )
- self.RGBBar = vgui.Create( "DRGBPicker", self )
- self.RGBBar.OnChange = function( ctrl, color )
- if ( self.showAlpha ) then
- color.a = self.txtA:GetValue()
- end
- self:SetBaseColor( color )
- end
- self.RGBBar:SetSize( 15, 100 )
- self.RGBBar:SetPos( 5,5 )
- self.RGBBar.OnMouseReleased = function( self, mcode )
- self:MouseCapture( false )
- self:OnCursorMoved( self:CursorPos() )
- self:GetParent():OnChange( self:GetParent():GetColor() )
- end
- function self.RGBBar:SetColor( color )
- local h, s, v = ColorToHSV( color )
- self.LastY = ( 1 - h / 360 ) * self:GetTall()
- end
- self.ColorCube = vgui.Create( "DColorCube", self )
- self.ColorCube.OnUserChanged = function( ctrl ) self:ColorCubeChanged( ctrl ) end
- self.ColorCube:SetSize( 100, 100 )
- self.ColorCube:SetPos( 25,5 )
- self.ColorCube.OnMouseReleased = function( self, mcode )
- self:SetDragging( false )
- self:MouseCapture( false )
- self:GetParent():OnChange( self:GetParent():GetColor() )
- end
- self.ColorCube.Knob.OnMouseReleased = function( self, mcode )
- self:GetParent():GetParent():OnChange( self:GetParent():GetParent():GetColor() )
- return DLabel.OnMouseReleased( self, mousecode )
- end
- self.txtR = xlib.makenumberwang{ x=7, y=110, w=35, value=255, parent=self }
- self.txtR.OnValueChanged = function( self, val )
- local p = self:GetParent()
- p:SetColor( Color( val, p.txtG:GetValue(), p.txtB:GetValue(), p.showAlpha and p.txtA:GetValue() ) )
- end
- self.txtR.OnEnter = function( self )
- local val = tonumber( self:GetValue() )
- if not val then val = 0 end
- self:OnValueChanged( val )
- end
- self.txtR.OnTextChanged = function( self )
- local val = tonumber( self:GetValue() )
- if not val then val = 0 end
- if val ~= math.Clamp( val, 0, 255 ) then self:SetValue( math.Clamp( val, 0, 255 ) ) end
- self:GetParent():UpdateColorText()
- end
- self.txtR.OnLoseFocus = function( self )
- if not tonumber( self:GetValue() ) then self:SetValue( "0" ) end
- local p = self:GetParent()
- p:OnChange( p:GetColor() )
- hook.Call( "OnTextEntryLoseFocus", nil, self )
- end
- self.txtR.Up.DoClick = function( button, mcode )
- self.txtR:SetValue( tonumber( self.txtR:GetValue() ) + 1 )
- self.txtR:GetParent():OnChange( self.txtR:GetParent():GetColor() )
- end
- self.txtR.Down.DoClick = function( button, mcode )
- self.txtR:SetValue( tonumber( self.txtR:GetValue() ) - 1 )
- self.txtR:GetParent():OnChange( self.txtR:GetParent():GetColor() )
- end
- function self.txtR.OnMouseReleased( self, mousecode )
- if ( self.Dragging ) then
- self:GetParent():OnChange( self:GetParent():GetColor() )
- self:EndWang()
- return end
- end
- self.txtG = xlib.makenumberwang{ x=47, y=110, w=35, value=100, parent=self }
- self.txtG.OnValueChanged = function( self, val )
- local p = self:GetParent()
- p:SetColor( Color( p.txtR:GetValue(), val, p.txtB:GetValue(), p.showAlpha and p.txtA:GetValue() ) )
- end
- self.txtG.OnEnter = function( self )
- local val = tonumber( self:GetValue() )
- if not val then val = 0 end
- self:OnValueChanged( val )
- end
- self.txtG.OnTextChanged = function( self )
- local val = tonumber( self:GetValue() )
- if not val then val = 0 end
- if val ~= math.Clamp( val, 0, 255 ) then self:SetValue( math.Clamp( val, 0, 255 ) ) end
- self:GetParent():UpdateColorText()
- end
- self.txtG.OnLoseFocus = function( self )
- if not tonumber( self:GetValue() ) then self:SetValue( "0" ) end
- local p = self:GetParent()
- p:OnChange( p:GetColor() )
- hook.Call( "OnTextEntryLoseFocus", nil, self )
- end
- self.txtG.Up.DoClick = function( button, mcode )
- self.txtG:SetValue( tonumber( self.txtG:GetValue() ) + 1 )
- self.txtG:GetParent():OnChange( self.txtG:GetParent():GetColor() )
- end
- self.txtG.Down.DoClick = function( button, mcode )
- self.txtG:SetValue( tonumber( self.txtG:GetValue() ) - 1 )
- self.txtG:GetParent():OnChange( self.txtG:GetParent():GetColor() )
- end
- function self.txtG.OnMouseReleased( self, mousecode )
- if ( self.Dragging ) then
- self:GetParent():OnChange( self:GetParent():GetColor() )
- self:EndWang()
- return end
- end
- self.txtB = xlib.makenumberwang{ x=87, y=110, w=35, value=100, parent=self }
- self.txtB.OnValueChanged = function( self, val )
- local p = self:GetParent()
- p:SetColor( Color( p.txtR:GetValue(), p.txtG:GetValue(), val, p.showAlpha and p.txtA:GetValue() ) )
- end
- self.txtB.OnEnter = function( self )
- local val = tonumber( self:GetValue() )
- if not val then val = 0 end
- self:OnValueChanged( val )
- end
- self.txtB.OnTextChanged = function( self )
- local val = tonumber( self:GetValue() )
- if not val then val = 0 end
- if val ~= math.Clamp( val, 0, 255 ) then self:SetValue( math.Clamp( val, 0, 255 ) ) end
- self:GetParent():UpdateColorText()
- end
- self.txtB.OnLoseFocus = function( self )
- if not tonumber( self:GetValue() ) then self:SetValue( "0" ) end
- local p = self:GetParent()
- p:OnChange( p:GetColor() )
- hook.Call( "OnTextEntryLoseFocus", nil, self )
- end
- self.txtB.Up.DoClick = function( button, mcode )
- self.txtB:SetValue( tonumber( self.txtB:GetValue() ) + 1 )
- self.txtB:GetParent():OnChange( self.txtB:GetParent():GetColor() )
- end
- self.txtB.Down.DoClick = function( button, mcode )
- self.txtB:SetValue( tonumber( self.txtB:GetValue() ) - 1 )
- self.txtB:GetParent():OnChange( self.txtB:GetParent():GetColor() )
- end
- function self.txtB.OnMouseReleased( self, mousecode )
- if ( self.Dragging ) then
- self:GetParent():OnChange( self:GetParent():GetColor() )
- self:EndWang()
- return end
- end
- self:SetColor( Color( 255, 0, 0, 255 ) )
- end
- function PANEL:AddAlphaBar()
- self.showAlpha = true
- self.txtA = xlib.makenumberwang{ x=150, y=82, w=35, value=255, parent=self }
- self.txtA.OnValueChanged = function( self, val )
- local p = self:GetParent()
- p:SetColor( Color( p.txtR:GetValue(), p.txtG:GetValue(), p.txtB:GetValue(), val ) )
- end
- self.txtA.OnEnter = function( self )
- local val = tonumber( self:GetValue() )
- if not val then val = 0 end
- self:OnValueChanged( val )
- end
- self.txtA.OnTextChanged = function( self )
- local p = self:GetParent()
- local val = tonumber( self:GetValue() )
- if not val then val = 0 end
- if val ~= math.Clamp( val, 0, 255 ) then self:SetValue( math.Clamp( val, 0, 255 ) ) end
- p.AlphaBar:SetValue( 1 - ( val / 255) )
- p:OnChangeImmediate( p:GetColor() )
- end
- self.txtA.OnLoseFocus = function( self )
- if not tonumber( self:GetValue() ) then self:SetValue( "0" ) end
- local p = self:GetParent()
- p:OnChange( p:GetColor() )
- hook.Call( "OnTextEntryLoseFocus", nil, self )
- end
- self.txtA.Up.DoClick = function( button, mcode )
- self.txtA:SetValue( tonumber( self.txtA:GetValue() ) + 1 )
- self.txtA:GetParent():OnChange( self.txtA:GetParent():GetColor() )
- end
- self.txtA.Down.DoClick = function( button, mcode )
- self.txtA:SetValue( tonumber( self.txtA:GetValue() ) - 1 )
- self.txtA:GetParent():OnChange( self.txtA:GetParent():GetColor() )
- end
- function self.txtA.OnMouseReleased( self, mousecode )
- if ( self.Dragging ) then
- self:GetParent():OnChange( self:GetParent():GetColor() )
- self:EndWang()
- return end
- end
- self.AlphaBar = vgui.Create( "DAlphaBar", self )
- self.AlphaBar.OnChange = function( ctrl, alpha ) self:SetColorAlpha( alpha*255 ) end
- self.AlphaBar:SetPos( 25,5 )
- self.AlphaBar:SetSize( 15, 100 )
- self.AlphaBar:SetValue( 1 )
- self.AlphaBar.OnMouseReleased = function( self, mcode )
- self:MouseCapture( false )
- self:OnCursorMoved( self:CursorPos() )
- self:GetParent():OnChange( self:GetParent():GetColor() )
- end
- self.ColorCube:SetPos( 45,5 )
- self:SetSize( 190, 110 )
- self.txtR:SetPos( 150, 7 )
- self.txtG:SetPos( 150, 32 )
- self.txtB:SetPos( 150, 57 )
- end
- function PANEL:AlphaModeTwo()
- self:SetSize( 156, 135 )
- self.AlphaBar:SetPos( 28,5 )
- self.ColorCube:SetPos( 51,5 )
- self.txtR:SetPos( 5, 110 )
- self.txtG:SetPos( 42, 110 )
- self.txtB:SetPos( 79, 110 )
- self.txtA:SetPos( 116, 110 )
- end
- function PANEL:NoAlphaModeTwo()
- self:SetSize( 170, 110 )
- self.txtR:SetPos( 130, 7 )
- self.txtG:SetPos( 130, 32 )
- self.txtB:SetPos( 130, 57 )
- end
- function PANEL:UpdateColorText()
- self.RGBBar:SetColor( Color( self.txtR:GetValue(), self.txtG:GetValue(), self.txtB:GetValue(), self.showAlpha and self.txtA:GetValue() ) )
- self.ColorCube:SetColor( Color( self.txtR:GetValue(), self.txtG:GetValue(), self.txtB:GetValue(), self.showAlpha and self.txtA:GetValue() ) )
- if ( self.showAlpha ) then self.AlphaBar:SetBarColor( Color( self.txtR:GetValue(), self.txtG:GetValue(), self.txtB:GetValue(), 255 ) ) end
- self:OnChangeImmediate( self:GetColor() )
- end
- function PANEL:SetColor( color )
- self.RGBBar:SetColor( color )
- self.ColorCube:SetColor( color )
- if tonumber( self.txtR:GetValue() ) ~= color.r then self.txtR:SetText( color.r or 255 ) end
- if tonumber( self.txtG:GetValue() ) ~= color.g then self.txtG:SetText( color.g or 0 ) end
- if tonumber( self.txtB:GetValue() ) ~= color.b then self.txtB:SetText( color.b or 0 ) end
- if ( self.showAlpha ) then
- self.txtA:SetText( color.a or 0 )
- self.AlphaBar:SetBarColor( Color( color.r, color.g, color.b ) )
- self.AlphaBar:SetValue( ( ( color.a or 0 ) / 255) )
- end
- self:OnChangeImmediate( color )
- end
- function PANEL:SetBaseColor( color )
- self.ColorCube:SetBaseRGB( color )
- self.txtR:SetText(self.ColorCube.m_OutRGB.r)
- self.txtG:SetText(self.ColorCube.m_OutRGB.g)
- self.txtB:SetText(self.ColorCube.m_OutRGB.b)
- if ( self.showAlpha ) then
- self.AlphaBar:SetBarColor( Color( self:GetColor().r, self:GetColor().g, self:GetColor().b ) )
- end
- self:OnChangeImmediate( self:GetColor() )
- end
- function PANEL:SetColorAlpha( alpha )
- if ( self.showAlpha ) then
- alpha = alpha or 0
- self.txtA:SetValue(alpha)
- end
- end
- function PANEL:ColorCubeChanged( cube )
- self.txtR:SetText(cube.m_OutRGB.r)
- self.txtG:SetText(cube.m_OutRGB.g)
- self.txtB:SetText(cube.m_OutRGB.b)
- if ( self.showAlpha ) then
- self.AlphaBar:SetBarColor( Color( self:GetColor().r, self:GetColor().g, self:GetColor().b ) )
- end
- self:OnChangeImmediate( self:GetColor() )
- end
- function PANEL:GetColor()
- local color = Color( self.txtR:GetValue(), self.txtG:GetValue(), self.txtB:GetValue() )
- if ( self.showAlpha ) then
- color.a = self.txtA:GetValue()
- else
- color.a = 255
- end
- return color
- end
- function PANEL:PerformLayout()
- self:SetColor( Color( self.txtR:GetValue(), self.txtG:GetValue(), self.txtB:GetValue(), self.showAlpha and self.txtA:GetValue() ) )
- end
- function PANEL:OnChangeImmediate( color )
- --For override
- end
- function PANEL:OnChange( color )
- --For override
- end
- vgui.Register( "xlibColorPanel", PANEL, "DPanel" )
- -- Create font for Ban Message preview to match the font used in the actual banned/disconnect dialog.
- surface.CreateFont ("DefaultLarge", {
- font = "Tahoma",
- size = 16,
- weight = 0,
- })
- -------------------------
- --Custom Animation System
- -------------------------
- --This is a heavily edited version of Garry's derma animation stuff with the following differences:
- --Allows for animation chains (one animation to begin right after the other)
- --Can call functions anywhere during the animation cycle.
- --Reliably calls a start/end function for each animation so the animations always shows/ends properly.
- --Animations can be completely disabled by setting 0 for the animation time.
- local xlibAnimation = {}
- xlibAnimation.__index = xlibAnimation
- function xlib.anim( runFunc, startFunc, endFunc )
- local anim = {}
- anim.runFunc = runFunc
- anim.startFunc = startFunc
- anim.endFunc = endFunc
- setmetatable( anim, xlibAnimation )
- return anim
- end
- xlib.animTypes = {}
- xlib.registerAnimType = function( name, runFunc, startFunc, endFunc )
- xlib.animTypes[name] = xlib.anim( runFunc, startFunc, endFunc )
- end
- function xlibAnimation:Start( Length, Data )
- self.startFunc( Data )
- if ( Length == 0 ) then
- self.endFunc( Data )
- xlib.animQueue_call()
- else
- self.Length = Length
- self.StartTime = SysTime()
- self.EndTime = SysTime() + Length
- self.Data = Data
- table.insert( xlib.activeAnims, self )
- end
- end
- function xlibAnimation:Stop()
- self.runFunc( 1, self.Data )
- self.endFunc( self.Data )
- for i, v in ipairs( xlib.activeAnims ) do
- if v == self then table.remove( xlib.activeAnims, i ) break end
- end
- xlib.animQueue_call()
- end
- function xlibAnimation:Run()
- local CurTime = SysTime()
- local delta = (CurTime - self.StartTime) / self.Length
- if ( CurTime > self.EndTime ) then
- self:Stop()
- else
- self.runFunc( delta, self.Data )
- end
- end
- --Animation Ticker
- xlib.activeAnims = {}
- xlib.animRun = function()
- for _, v in ipairs( xlib.activeAnims ) do
- v.Run( v )
- end
- end
- hook.Add( "XLIBDoAnimation", "xlib_runAnims", xlib.animRun )
- -------------------------
- --Animation chain manager
- -------------------------
- xlib.animQueue = {}
- xlib.animBackupQueue = {}
- --This will allow us to make animations run faster when linked together
- --Makes sure the entire animation length = animationTime (~0.2 sec by default)
- xlib.animStep = 0
- --Call this to begin the animation chain
- xlib.animQueue_start = function()
- if xlib.animRunning then --If a new animation is starting while one is running, then we should instantly stop the old one.
- xlib.animQueue_forceStop()
- return --The old animation should be finished now, and the new one should be starting
- end
- xlib.curAnimStep = xlib.animStep
- xlib.animStep = 0
- xlib.animQueue_call()
- end
- xlib.animQueue_forceStop = function()
- --This will trigger the currently chained animations to run at 0 seconds.
- xlib.curAnimStep = -1
- if type( xlib.animRunning ) == "table" then xlib.animRunning:Stop() end
- end
- xlib.animQueue_call = function()
- if #xlib.animQueue > 0 then
- local func = xlib.animQueue[1]
- table.remove( xlib.animQueue, 1 )
- func()
- else
- xlib.animRunning = nil
- --Check for queues in the backup that haven't been started.
- if #xlib.animBackupQueue > 0 then
- xlib.animQueue = table.Copy( xlib.animBackupQueue )
- xlib.animBackupQueue = {}
- xlib.animQueue_start()
- end
- end
- end
- xlib.addToAnimQueue = function( obj, ... )
- local arg = { ... }
- --If there is an animation running, then we need to store the new animation stuff somewhere else temporarily.
- --Also, if ignoreRunning is true, then we'll add the anim to the regular queue regardless of running status.
- local outTable = xlib.animRunning and xlib.animBackupQueue or xlib.animQueue
- if type( obj ) == "function" then
- table.insert( outTable, function() xlib.animRunning = true obj( unpack( arg ) ) xlib.animQueue_call() end )
- elseif type( obj ) == "string" and xlib.animTypes[obj] then
- --arg[1] should be data table, arg[2] should be length
- length = arg[2] or xgui.settings.animTime or 1
- xlib.animStep = xlib.animStep + 1
- table.insert( outTable, function() xlib.animRunning = xlib.animTypes[obj] xlib.animRunning:Start( ( xlib.curAnimStep ~= -1 and ( length/xlib.curAnimStep ) or 0 ), arg[1] ) end )
- else
- Msg( "Error: XLIB recieved an invalid animation call! TYPE:" .. type( obj ) .. " VALUE:" .. tostring( obj ) .. "\n" )
- end
- end
- -------------------------
- --Default Animation Types
- -------------------------
- --Slide animation
- local function slideAnim_run( delta, data )
- --data.panel, data.startx, data.starty, data.endx, data.endy, data.setvisible
- data.panel:SetPos( data.startx+((data.endx-data.startx)*delta), data.starty+((data.endy-data.starty)*delta) )
- end
- local function slideAnim_start( data )
- data.panel:SetPos( data.startx, data.starty )
- if data.setvisible == true then
- ULib.queueFunctionCall( data.panel.SetVisible, data.panel, true )
- end
- end
- local function slideAnim_end( data )
- data.panel:SetPos( data.endx, data.endy )
- if data.setvisible == false then
- data.panel:SetVisible( false )
- end
- end
- xlib.registerAnimType( "pnlSlide", slideAnim_run, slideAnim_start, slideAnim_end )
- --Fade animation
- local function fadeAnim_run( delta, data )
- if data.panelOut then data.panelOut:SetAlpha( 255-(delta*255) ) data.panelOut:SetVisible( true ) end
- if data.panelIn then data.panelIn:SetAlpha( 255 * delta ) data.panelIn:SetVisible( true ) end
- end
- local function fadeAnim_start( data )
- if data.panelOut then data.panelOut:SetAlpha( 255 ) data.panelOut:SetVisible( true ) end
- if data.panelIn then data.panelIn:SetAlpha( 0 ) data.panelIn:SetVisible( true ) end
- end
- local function fadeAnim_end( data )
- if data.panelOut then data.panelOut:SetVisible( false ) end
- if data.panelIn then data.panelIn:SetAlpha( 255 ) end
- end
- xlib.registerAnimType( "pnlFade", fadeAnim_run, fadeAnim_start, fadeAnim_end )
- --xgui_helpers -- by Stickly Man!
- --A set of generic functions to help with various XGUI-related things.
- function xgui.load_helpers()
- --These handle keyboard focus for textboxes within XGUI.
- local function getKeyboardFocus( pnl )
- if pnl:HasParent( xgui.base ) then
- xgui.anchor:SetKeyboardInputEnabled( true )
- end
- if pnl.selectAll then
- pnl:SelectAllText()
- end
- end
- hook.Add( "OnTextEntryGetFocus", "XGUI_GetKeyboardFocus", getKeyboardFocus )
- local function loseKeyboardFocus( pnl )
- if pnl:HasParent( xgui.base ) then
- xgui.anchor:SetKeyboardInputEnabled( false )
- end
- end
- hook.Add( "OnTextEntryLoseFocus", "XGUI_LoseKeyboardFocus", loseKeyboardFocus )
- ---------------------------------
- --Code for creating the XGUI base
- ---------------------------------
- function xgui.makeXGUIbase()
- xgui.anchor = xlib.makeXpanel{ w=600, h=420, x=ScrW()/2-300, y=ScrH()/2-270 }
- xgui.anchor:SetVisible( false )
- xgui.anchor:SetKeyboardInputEnabled( false )
- xgui.anchor.Paint = function( self, w, h ) hook.Call( "XLIBDoAnimation" ) end
- xgui.anchor:SetAlpha( 0 )
- xgui.base = xlib.makepropertysheet{ x=0, y=0, w=600, h=400, parent=xgui.anchor, offloadparent=xgui.null }
- xgui.base.animOpen = function() --First 4 are fade animations, last (or invalid choice) is the default fade animation.
- xgui.settings.animIntype = tonumber( xgui.settings.animIntype )
- if xgui.settings.animIntype == 2 then
- xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(255) end )
- xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=xgui.x, starty=-490, endx=xgui.x, endy=xgui.y, setvisible=true } )
- elseif xgui.settings.animIntype == 3 then
- xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(255) end )
- xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=-610, starty=xgui.y, endx=xgui.x, endy=xgui.y, setvisible=true } )
- elseif xgui.settings.animIntype == 4 then
- xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(255) end )
- xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=xgui.x, starty=ScrH(), endx=xgui.x, endy=xgui.y, setvisible=true } )
- elseif xgui.settings.animIntype == 5 then
- xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(255) end )
- xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=ScrW(), starty=xgui.y, endx=xgui.x, endy=xgui.y, setvisible=true } )
- else
- xlib.addToAnimQueue( function() xgui.anchor:SetPos( xgui.x, xgui.y ) end )
- xlib.addToAnimQueue( "pnlFade", { panelIn=xgui.anchor } )
- end
- xlib.animQueue_start()
- end
- xgui.base.animClose = function()
- xgui.settings.animOuttype = tonumber( xgui.settings.animOuttype )
- if xgui.settings.animOuttype == 2 then
- xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=xgui.x, starty=xgui.y, endx=xgui.x, endy=-490, setvisible=false } )
- xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(0) end )
- elseif xgui.settings.animOuttype == 3 then
- xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=xgui.x, starty=xgui.y, endx=-610, endy=xgui.y, setvisible=false } )
- xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(0) end )
- elseif xgui.settings.animOuttype == 4 then
- xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=xgui.x, starty=xgui.y, endx=xgui.x, endy=ScrH(), setvisible=false } )
- xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(0) end )
- elseif xgui.settings.animOuttype == 5 then
- xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=xgui.x, starty=xgui.y, endx=ScrW(), endy=xgui.y, setvisible=false } )
- xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(0) end )
- else
- xlib.addToAnimQueue( function() xgui.anchor:SetPos( xgui.x, xgui.y ) end )
- xlib.addToAnimQueue( "pnlFade", { panelOut=xgui.anchor } )
- end
- xlib.animQueue_start()
- end
- function xgui.SetPos( pos, xoff, yoff, ignoreanim ) --Sets the position of XGUI based on "pos", and checks to make sure that with whatever offset and pos combination, XGUI does not go off the screen.
- pos = tonumber( pos )
- xoff = tonumber( xoff )
- yoff = tonumber( yoff )
- if not xoff then xoff = 0 end
- if not yoff then yoff = 0 end
- if not pos then pos = 5 end
- if pos == 1 or pos == 4 or pos == 7 then --Left side of the screen
- if xoff < -10 then
- xoff = -10
- elseif xoff > ScrW()-610 then
- xoff = ScrW()-610
- end
- xgui.x = 10+xoff
- elseif pos == 3 or pos == 6 or pos == 9 then --Right side of the screen
- if xoff < -ScrW()+610 then
- xoff = -ScrW()+610
- elseif xoff > 10 then
- xoff = 10
- end
- xgui.x = ScrW()-610+xoff
- else --Center
- if xoff < -ScrW()/2+300 then
- xoff = -ScrW()/2+300
- elseif xoff > ScrW()/2-300 then
- xoff = ScrW()/2-300
- end
- xgui.x = ScrW()/2-300+xoff
- end
- if pos == 1 or pos == 2 or pos == 3 then --Bottom of the screen
- if yoff < -ScrH()+430 then
- yoff = -ScrH()+430
- elseif yoff > 30 then
- yoff = 30
- end
- xgui.y = ScrH()-430+yoff
- elseif pos == 7 or pos == 8 or pos == 9 then --Top of the screen
- if yoff < -10 then
- yoff = -10
- elseif yoff > ScrH()-410 then
- yoff = ScrH()-410
- end
- xgui.y = yoff+10
- else --Center
- if yoff < -ScrH()/2+210 then
- yoff = -ScrH()/2+210
- elseif yoff > ScrH()/2-190 then
- yoff = ScrH()/2-190
- end
- xgui.y = ScrH()/2-210+yoff
- end
- if ignoreanim then
- xgui.anchor:SetPos( xgui.x, xgui.y )
- else
- local curx, cury = xgui.anchor:GetPos()
- xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=curx, starty=cury, endx=xgui.x, endy=xgui.y } )
- xlib.animQueue_start()
- end
- end
- xgui.SetPos( xgui.settings.xguipos.pos, xgui.settings.xguipos.xoff, xgui.settings.xguipos.yoff )
- function xgui.base:SetActiveTab( active, ignoreAnim )
- if ( self.m_pActiveTab == active ) then return end
- if ( self.m_pActiveTab ) then
- if not ignoreAnim then
- xlib.addToAnimQueue( "pnlFade", { panelOut=self.m_pActiveTab:GetPanel(), panelIn=active:GetPanel() } )
- else
- --Run this when module permissions have changed.
- xlib.addToAnimQueue( "pnlFade", { panelOut=nil, panelIn=active:GetPanel() }, 0 )
- end
- xlib.animQueue_start()
- end
- self.m_pActiveTab = active
- self:InvalidateLayout()
- end
- --Progress bar
- xgui.chunkbox = xlib.makeprogressbar{ x=420, w=180, h=20, visible=false, skin=xgui.settings.skin, parent=xgui.anchor }
- function xgui.chunkbox:Progress( datatype )
- self.value = self.value + 1
- self:SetFraction( self.value / self.max )
- self.Label:SetText( "Getting data: " .. datatype .. " - " .. string.format("%.2f", (self.value / self.max) * 100) .. "%" )
- if self.value == self.max then
- xgui.dataInitialized = true
- xgui.expectingdata = nil
- self.Label:SetText( "Waiting for clientside processing..." )
- xgui.queueFunctionCall( xgui.chunkbox.SetVisible, "chunkbox", xgui.chunkbox, false )
- RunConsoleCommand( "_xgui", "dataComplete" )
- end
- end
- end
- ------------------------
- --XGUI QueueFunctionCall
- ------------------------
- --This is essentially a straight copy of Megiddo's queueFunctionCall; Since XGUI tends to use it quite a lot, I decided to seperate it to prevent delays in ULib's stuff
- --I also now get to add a method of flushing the queue based on a tag in the event that new data needs to be updated.
- local stack = {}
- local function onThink()
- local num = #stack
- if num > 0 then
- for i=1,3 do --Run 3 lines per frame
- if stack[1] ~= nil then
- local b, e = pcall( stack[ 1 ].fn, unpack( stack[ 1 ], 1, stack[ 1 ].n ) )
- if not b then
- ErrorNoHalt( "XGUI queue error: " .. tostring( e ) .. "\n" )
- end
- end
- table.remove( stack, 1 ) -- Remove the first inserted item. This is FIFO
- end
- else
- hook.Remove( "Think", "XGUIQueueThink" )
- end
- end
- function xgui.queueFunctionCall( fn, tag, ... )
- if type( fn ) ~= "function" then
- error( "queueFunctionCall received a bad function", 2 )
- return
- end
- table.insert( stack, { fn=fn, tag=tag, n=select( "#", ... ), ... } )
- hook.Add( "Think", "XGUIQueueThink", onThink, HOOK_MONITOR_HIGH )
- end
- function xgui.flushQueue( tag )
- local removeIndecies = {}
- for i, fncall in ipairs( stack ) do
- if fncall.tag == tag then
- table.insert( removeIndecies, i )
- end
- end
- for i=#removeIndecies,1,-1 do --Remove the queue functions backwards to prevent desynchronization of pairs
- table.remove( stack, removeIndecies[i] )
- end
- end
- -------------------
- --ULIB XGUI helpers
- -------------------
- --Helper function to parse access tag for a particular argument
- function ulx.getTagArgNum( tag, argnum )
- return tag and ULib.splitArgs( tag, "<", ">" )[argnum]
- end
- --Load control interpretations for ULib argument types
- function ULib.cmds.BaseArg.x_getcontrol( arg, argnum, parent )
- return xlib.makelabel{ label="Not Supported", parent=parent }
- end
- function ULib.cmds.NumArg.x_getcontrol( arg, argnum, parent )
- local access, tag = LocalPlayer():query( arg.cmd )
- local restrictions = {}
- ULib.cmds.NumArg.processRestrictions( restrictions, arg, ulx.getTagArgNum( tag, argnum ) )
- if table.HasValue( arg, ULib.cmds.allowTimeString ) then
- local min = restrictions.min or 0
- local max = restrictions.max or 10 * 60 * 24 * 365 --default slider max 10 years
- local outPanel = xlib.makepanel{ h=40, parent=parent }
- xlib.makelabel{ x=5, y=3, label="Ban Length:", parent=outPanel }
- outPanel.interval = xlib.makecombobox{ x=90, w=75, parent=outPanel }
- outPanel.val = xlib.makeslider{ w=165, y=20, label="<--->", min=min, max=max, value=min, decimal=0, parent=outPanel }
- local divisor = {}
- local sensiblemax = {}
- if min == 0 then outPanel.interval:AddChoice( "Permanent" ) table.insert( divisor, 1 ) table.insert( sensiblemax, 0 ) end
- if max >= 1 and min <= 60*24 then outPanel.interval:AddChoice( "Minutes" ) table.insert( divisor, 1 ) table.insert( sensiblemax, 60*24 ) end
- if max >= 60 and min <= 60*24*7 then outPanel.interval:AddChoice( "Hours" ) table.insert( divisor, 60 ) table.insert( sensiblemax, 24*7 ) end
- if max >= ( 60*24 ) and min <= 60*24*120 then outPanel.interval:AddChoice( "Days" ) table.insert( divisor, 60*24 ) table.insert( sensiblemax, 120 ) end
- if max >= ( 60*24*7 ) and min <= 60*24*7*52 then outPanel.interval:AddChoice( "Weeks" ) table.insert( divisor, 60*24*7 ) table.insert( sensiblemax, 52 ) end
- if max >= ( 60*24*365 ) then outPanel.interval:AddChoice( "Years" ) table.insert( divisor, 60*24*365 ) table.insert( sensiblemax, 10 ) end
- outPanel.interval.OnSelect = function( self, index, value, data )
- outPanel.val:SetDisabled( value == "Permanent" )
- outPanel.val.maxvalue = math.min( max / divisor[index], sensiblemax[index] )
- outPanel.val.minvalue = math.max( min / divisor[index], 0 )
- outPanel.val:SetMax( outPanel.val.maxvalue )
- outPanel.val:SetMin( outPanel.val.minvalue )
- outPanel.val:SetValue( math.Clamp( tonumber( outPanel.val:GetValue() ), outPanel.val.minvalue, outPanel.val.maxvalue ) )
- end
- function outPanel.val:ValueChanged( val )
- val = math.Clamp( tonumber( val ) or 0, self.minvalue or 0, self.maxvalue or 0 )
- self.Slider:SetSlideX( self.Scratch:GetFraction( val ) )
- if ( self.TextArea ~= vgui.GetKeyboardFocus() ) then
- self.TextArea:SetValue( self.Scratch:GetTextValue() )
- end
- self:OnValueChanged( val )
- end
- if #outPanel.interval.Choices ~= 0 then
- outPanel.interval:ChooseOptionID( 1 )
- end
- outPanel.GetValue = function( self )
- local val, char = self:GetRawValue()
- return val .. char
- end
- outPanel.GetRawValue = function( self )
- local char = string.lower( self.interval:GetValue():sub(1,1) )
- if char == "m" or char == "p" or tonumber( self.val:GetValue() ) == 0 then char = "" end
- return self.val:GetValue(), char
- end
- outPanel.GetMinutes = function( self )
- local btime, char = self:GetRawValue()
- if char == "h" then btime = btime * 60
- elseif char == "d" then btime = btime * 1440
- elseif char == "w" then btime = btime * 10080
- elseif char == "y" then btime = btime * 525600 end
- return btime
- end
- outPanel.TextArea = outPanel.val.TextArea
- return outPanel
- else
- local defvalue = arg.min
- if table.HasValue( arg, ULib.cmds.optional ) then defvalue = arg.default end
- if not defvalue then defvalue = 0 end --No default was set for this command, so we'll use 0.
- local maxvalue = restrictions.max
- local minvalue = restrictions.min or 0
- if maxvalue == nil then
- if defvalue > 100 then
- maxvalue = defvalue
- else
- maxvalue = 100
- end
- end
- local decimal = 0
- if not table.HasValue( arg, ULib.cmds.round ) then
- local minMaxDelta = maxvalue - minvalue
- if minMaxDelta < 5 then
- decimal = 2
- elseif minMaxDelta <= 10 then
- decimal = 1
- end
- end
- local outPanel = xlib.makepanel{ h=35, parent=parent }
- xlib.makelabel{ label=arg.hint or "NumArg", parent=outPanel }
- outPanel.val = xlib.makeslider{ y=15, w=165, min=minvalue, max=maxvalue, value=defvalue, decimal=decimal, label="<--->", parent=outPanel }
- outPanel.GetValue = function( self ) return outPanel.val.GetValue( outPanel.val ) end
- outPanel.TextArea = outPanel.val.TextArea
- return outPanel
- end
- end
- function ULib.cmds.NumArg.getTime( arg )
- if arg == nil or arg == "" then return nil, nil end
- if arg == 0 or tonumber( arg ) == 0 then
- return "Permanent", 0
- end
- local charPriority = { "y", "w", "d", "h" }
- local charMap = { "Years", "Weeks", "Days", "Hours" }
- local divisor = { 60 * 24 * 365, 60 * 24 * 7, 60 * 24, 60 }
- for i, v in ipairs( charPriority ) do
- if arg:find( v, 1, true ) then
- if not charMap[ i ] or not divisor [ i ] or not ULib.stringTimeToMinutes( arg ) then return nil, nil end
- local val = ULib.stringTimeToMinutes( arg ) / divisor[ i ]
- if val == 0 then return "Permanent", 0 end
- return charMap[ i ], val
- end
- end
- return "Minutes", ULib.stringTimeToMinutes( arg )
- end
- function ULib.cmds.StringArg.x_getcontrol( arg, argnum, parent )
- local access, tag = LocalPlayer():query( arg.cmd )
- local restrictions = {}
- ULib.cmds.StringArg.processRestrictions( restrictions, arg, ulx.getTagArgNum( tag, argnum ) )
- local is_restricted_to_completes = table.HasValue( arg, ULib.cmds.restrictToCompletes ) -- Program-level restriction (IE, ulx map)
- or restrictions.playerLevelRestriction -- The player's tag specifies only certain strings
- if is_restricted_to_completes then
- return xlib.makecombobox{ text=arg.hint or "StringArg", choices=restrictions.restrictedCompletes, parent=parent }
- elseif restrictions.restrictedCompletes and table.Count( restrictions.restrictedCompletes ) > 0 then
- -- This is where there needs to be both a drop down AND an input box
- local outPanel = xlib.makecombobox{ text=arg.hint, choices=restrictions.restrictedCompletes, enableinput=true, selectall=true, parent=parent }
- outPanel.OnEnter = function( self )
- self:GetParent():OnEnter()
- end
- return outPanel
- else
- return xlib.maketextbox{ text=arg.hint or "StringArg", selectall=true, parent=parent }
- end
- end
- function ULib.cmds.PlayerArg.x_getcontrol( arg, argnum, parent )
- local access, tag = LocalPlayer():query( arg.cmd )
- local restrictions = {}
- ULib.cmds.PlayerArg.processRestrictions( restrictions, LocalPlayer(), arg, ulx.getTagArgNum( tag, argnum ) )
- local outPanel = xlib.makecombobox{ text=arg.hint, parent=parent }
- local targets = restrictions.restrictedTargets
- if targets == false then -- No one allowed
- targets = {}
- elseif targets == nil then -- Everyone allowed
- targets = player.GetAll()
- end
- for _, ply in ipairs( targets ) do
- outPanel:AddChoice( ply:Nick() )
- end
- return outPanel
- end
- function ULib.cmds.CallingPlayerArg.x_getcontrol( arg, argnum, parent )
- return xlib.makelabel{ label=arg.hint or "CallingPlayer", parent=parent }
- end
- function ULib.cmds.BoolArg.x_getcontrol( arg, argnum, parent )
- local access, tag = LocalPlayer():query( arg.cmd )
- local restrictions = {}
- ULib.cmds.BoolArg.processRestrictions( restrictions, arg, ulx.getTagArgNum( tag, argnum ) )
- local outPanel = xlib.makecheckbox{ label=arg.hint or "BoolArg", value=restrictions.restrictedTo, parent=parent }
- if restrictions.restrictedTo ~= nil then outPanel:SetDisabled( true ) end
- outPanel.GetValue = function( self )
- return self:GetChecked() and 1 or 0
- end
- return outPanel
- end
- end
- --XGUI: A GUI for ULX -- by Stickly Man!
- xgui = xgui or {}
- --Make a spot for modules to store data and hooks
- xgui.data = xgui.data or {}
- xgui.hook = xgui.hook or { onProcessModules={}, onOpen={}, onClose={} }
- --Call this function in your client-side module code to ensure the data types have been instantiated on the client.
- function xgui.prepareDataType( dtype, location )
- if not xgui.data[dtype] then
- xgui.data[dtype] = location or {}
- xgui.hook[dtype] = { clear={}, process={}, done={}, add={}, update={}, remove={}, data={} }
- end
- end
- --Set up various hooks modules can "hook" into.
- function xgui.hookEvent( dtype, event, func, name )
- if not xgui.hook[dtype] or ( event and not xgui.hook[dtype][event] ) then
- Msg( "XGUI: Attempted to add to invalid type or event to a hook! (" .. dtype .. ", " .. ( event or "nil" ) .. ")\n" )
- else
- if not name then name = "FixMe" .. math.floor(math.random()*10000) end -- Backwards compatibility for older XGUI modules
- if not event then
- xgui.hook[dtype][name] = func
- else
- xgui.hook[dtype][event][name] = func
- end
- end
- end
- --Set up tables and functions for creating and storing modules
- xgui.modules = xgui.modules or {}
- xgui.modules.tab = xgui.modules.tab or {}
- function xgui.addModule( name, panel, icon, access, tooltip )
- local refreshModules = false
- for i = #xgui.modules.tab, 1, -1 do
- if xgui.modules.tab[i].name == name then
- xgui.modules.tab[i].panel:Remove()
- xgui.modules.tab[i].tabpanel:Remove()
- xgui.modules.tab[i].xbutton:Remove()
- table.remove(xgui.modules.tab, i)
- refreshModules = true
- end
- end
- table.insert( xgui.modules.tab, { name=name, panel=panel, icon=icon, access=access, tooltip=tooltip } )
- if refreshModules then xgui.processModules() end
- end
- xgui.modules.setting = xgui.modules.setting or {}
- function xgui.addSettingModule( name, panel, icon, access, tooltip )
- local refreshModules = false
- for i = #xgui.modules.setting, 1, -1 do
- if xgui.modules.setting[i].name == name then
- xgui.modules.setting[i].panel:Remove()
- xgui.modules.setting[i].tabpanel:Remove()
- table.remove(xgui.modules.setting, i)
- refreshModules = true
- end
- end
- table.insert( xgui.modules.setting, { name=name, panel=panel, icon=icon, access=access, tooltip=tooltip } )
- if refreshModules then xgui.processModules() end
- end
- xgui.modules.submodule = xgui.modules.submodule or {}
- function xgui.addSubModule( name, panel, access, mtype )
- local refreshModules = false
- for i = #xgui.modules.submodule, 1, -1 do
- if xgui.modules.submodule[i].name == name then
- xgui.modules.submodule[i].panel:Remove()
- table.remove(xgui.modules.submodule, i)
- refreshModules = true
- end
- end
- table.insert( xgui.modules.submodule, { name=name, panel=panel, access=access, mtype=mtype } )
- if refreshModules then xgui.processModules() end
- end
- --Set up a spot to store entries for autocomplete.
- xgui.tabcompletes = xgui.tabcompletes or {}
- xgui.ulxmenucompletes = xgui.ulxmenucompletes or {}
- --Set up XGUI clientside settings, load settings from file if it exists
- xgui.settings = xgui.settings or {}
- if ULib.fileExists( "data/ulx/xgui_settings.txt" ) then
- local input = ULib.fileRead( "data/ulx/xgui_settings.txt" )
- input = input:match( "^.-\n(.*)$" )
- xgui.settings = ULib.parseKeyValues( input )
- end
- --Set default settings if they didn't get loaded
- if not xgui.settings.moduleOrder then xgui.settings.moduleOrder = { "Cmds", "Groups", "Maps", "Settings", "Bans" } end
- if not xgui.settings.settingOrder then xgui.settings.settingOrder = { "Sandbox", "Server", "Client" } end
- if not xgui.settings.animTime then xgui.settings.animTime = 0.22 else xgui.settings.animTime = tonumber( xgui.settings.animTime ) end
- if not xgui.settings.infoColor then
- --Default color
- xgui.settings.infoColor = Color( 100, 255, 255, 128 )
- else
- --Ensure that the color contains numbers, not strings
- xgui.settings.infoColor = Color(xgui.settings.infoColor.r, xgui.settings.infoColor.g, xgui.settings.infoColor.b, xgui.settings.infoColor.a)
- end
- if not xgui.settings.showLoadMsgs then xgui.settings.showLoadMsgs = true else xgui.settings.showLoadMsgs = ULib.toBool( xgui.settings.showLoadMsgs ) end
- if not xgui.settings.skin then xgui.settings.skin = "Default" end
- if not xgui.settings.xguipos then xgui.settings.xguipos = { pos=5, xoff=0, yoff=0 } end
- if not xgui.settings.animIntype then xgui.settings.animIntype = 1 end
- if not xgui.settings.animOuttype then xgui.settings.animOuttype = 1 end
- function xgui.init( ply )
- xgui.load_helpers()
- --Initiate the base window (see xgui_helpers.lua for code)
- xgui.makeXGUIbase{}
- --Create the bottom infobar
- xgui.infobar = xlib.makepanel{ x=10, y=399, w=580, h=20, parent=xgui.anchor }
- xgui.infobar:NoClipping( true )
- xgui.infobar.Paint = function( self, w, h )
- draw.RoundedBoxEx( 4, 0, 1, 580, 20, xgui.settings.infoColor, false, false, true, true )
- end
- local infoLabel = string.format( "\nULX Admin Mod :: XGUI - Team Ulysses | ULX %s | ULib %s", ULib.pluginVersionStr("ULX"), ULib.pluginVersionStr("ULib") )
- xlib.makelabel{ x=5, y=-10, label=infoLabel, parent=xgui.infobar }:NoClipping( true )
- xgui.thetime = xlib.makelabel{ x=515, y=-10, label="", parent=xgui.infobar }
- xgui.thetime:NoClipping( true )
- xgui.thetime.check = function()
- xgui.thetime:SetText( os.date( "\n%I:%M:%S %p" ) )
- xgui.thetime:SizeToContents()
- timer.Simple( 1, xgui.thetime.check )
- end
- xgui.thetime.check()
- --Create an offscreen place to parent modules that the player can't access
- xgui.null = xlib.makepanel{ x=-10, y=-10, w=0, h=0 }
- xgui.null:SetVisible( false )
- --Load modules
- local sm = xgui.settings.showLoadMsgs
- if sm then
- Msg( "\n///////////////////////////////////////\n" )
- Msg( "// ULX GUI -- Made by Stickly Man! //\n" )
- Msg( "///////////////////////////////////////\n" )
- Msg( "// Loading GUI Modules... //\n" )
- end
- for _, file in ipairs( file.Find( "ulx/xgui/*.lua", "LUA" ) ) do
- include( "ulx/xgui/" .. file )
- if sm then Msg( "// " .. file .. string.rep( " ", 32 - file:len() ) .. "//\n" ) end
- end
- if sm then Msg( "// Loading Setting Modules... //\n" ) end
- for _, file in ipairs( file.Find( "ulx/xgui/settings/*.lua", "LUA" ) ) do
- include( "ulx/xgui/settings/" .. file )
- if sm then Msg( "// " .. file .. string.rep( " ", 32 - file:len() ) .. "//\n" ) end
- end
- if sm then Msg( "// Loading Gamemode Module(s)... //\n" ) end
- if ULib.isSandbox() and GAMEMODE.FolderName ~= "sandbox" then -- If the gamemode sandbox-derived (but not sandbox, that will get added later), then add the sandbox Module
- include( "ulx/xgui/gamemodes/sandbox.lua" )
- if sm then Msg( "// sandbox.lua //\n" ) end
- end
- for _, file in ipairs( file.Find( "ulx/xgui/gamemodes/*.lua", "LUA" ) ) do
- if string.lower( file ) == string.lower( GAMEMODE.FolderName .. ".lua" ) then
- include( "ulx/xgui/gamemodes/" .. file )
- if sm then Msg( "// " .. file .. string.rep( " ", 32 - file:len() ) .. "//\n" ) end
- break
- end
- if sm then Msg( "// No module found! //\n" ) end
- end
- if sm then Msg( "// Modules Loaded! //\n" ) end
- if sm then Msg( "///////////////////////////////////////\n\n" ) end
- --Find any existing modules that aren't listed in the requested order.
- local function checkModulesOrder( moduleTable, sortTable )
- for _, m in ipairs( moduleTable ) do
- local notlisted = true
- for _, existing in ipairs( sortTable ) do
- if m.name == existing then
- notlisted = false
- break
- end
- end
- if notlisted then
- table.insert( sortTable, m.name )
- end
- end
- end
- checkModulesOrder( xgui.modules.tab, xgui.settings.moduleOrder )
- checkModulesOrder( xgui.modules.setting, xgui.settings.settingOrder )
- --Check if the server has XGUI installed
- RunConsoleCommand( "_xgui", "getInstalled" )
- xgui.initialized = true
- xgui.processModules()
- end
- hook.Add( ULib.HOOK_LOCALPLAYERREADY, "InitXGUI", xgui.init, HOOK_MONITOR_LOW )
- function xgui.saveClientSettings()
- if not ULib.fileIsDir( "data/ulx" ) then
- ULib.fileCreateDir( "data/ulx" )
- end
- local output = "// This file stores clientside settings for XGUI.\n"
- output = output .. ULib.makeKeyValues( xgui.settings )
- ULib.fileWrite( "data/ulx/xgui_settings.txt", output )
- end
- function xgui.checkModuleExists( modulename, moduletable )
- for k, v in ipairs( moduletable ) do
- if v.name == modulename then
- return k
- end
- end
- return false
- end
- function xgui.processModules()
- local activetab = nil
- if xgui.base:GetActiveTab() then
- activetab = xgui.base:GetActiveTab():GetValue()
- end
- local activesettingstab = nil
- if xgui.settings_tabs:GetActiveTab() then
- activesettingstab = xgui.settings_tabs:GetActiveTab():GetValue()
- end
- xgui.base:Clear() --We need to remove any existing tabs in the GUI
- xgui.tabcompletes = {}
- xgui.ulxmenucompletes = {}
- for _, modname in ipairs( xgui.settings.moduleOrder ) do
- local module = xgui.checkModuleExists( modname, xgui.modules.tab )
- if module then
- module = xgui.modules.tab[module]
- if module.xbutton == nil then
- module.xbutton = xlib.makebutton{ x=555, y=-5, w=32, h=24, btype="close", parent=module.panel }
- module.xbutton.DoClick = function()
- xgui.hide()
- end
- end
- if LocalPlayer():query( module.access ) then
- xgui.base:AddSheet( module.name, module.panel, module.icon, false, false, module.tooltip )
- module.tabpanel = xgui.base.Items[#xgui.base.Items].Tab
- table.insert( xgui.tabcompletes, "xgui show " .. modname )
- table.insert( xgui.ulxmenucompletes, "ulx menu " .. modname )
- else
- module.tabpanel = nil
- module.panel:SetParent( xgui.null )
- end
- end
- end
- xgui.settings_tabs:Clear() --Clear out settings tabs for reprocessing
- for _, modname in ipairs( xgui.settings.settingOrder ) do
- local module = xgui.checkModuleExists( modname, xgui.modules.setting )
- if module then
- module = xgui.modules.setting[module]
- if LocalPlayer():query( module.access ) then
- xgui.settings_tabs:AddSheet( module.name, module.panel, module.icon, false, false, module.tooltip )
- module.tabpanel = xgui.settings_tabs.Items[#xgui.settings_tabs.Items].Tab
- table.insert( xgui.tabcompletes, "xgui show " .. modname )
- table.insert( xgui.ulxmenucompletes, "ulx menu " .. modname )
- else
- module.tabpanel = nil
- module.panel:SetParent( xgui.null )
- end
- end
- end
- --Call any functions that requested to be called when permissions change
- xgui.callUpdate( "onProcessModules" )
- table.sort( xgui.tabcompletes )
- table.sort( xgui.ulxmenucompletes )
- local hasFound = false
- if activetab then
- for _, v in pairs( xgui.base.Items ) do
- if v.Tab:GetValue() == activetab then
- xgui.base:SetActiveTab( v.Tab, true )
- hasFound = true
- break
- end
- end
- if not hasFound then
- xgui.base.m_pActiveTab = "none"
- xgui.base:SetActiveTab( xgui.base.Items[1].Tab, true )
- end
- end
- hasFound = false
- if activesettingstab then
- for _, v in pairs( xgui.settings_tabs.Items ) do
- if v.Tab:GetValue() == activesettingstab then
- xgui.settings_tabs:SetActiveTab( v.Tab, true )
- hasFound = true
- break
- end
- end
- if not hasFound then
- xgui.settings_tabs.m_pActiveTab = "none"
- xgui.settings_tabs:SetActiveTab( xgui.settings_tabs.Items[1].Tab, true )
- end
- end
- end
- function xgui.checkNotInstalled( tabname )
- if xgui.notInstalledWarning then return end
- gui.EnableScreenClicker( true )
- RestoreCursorPosition()
- xgui.notInstalledWarning = xlib.makeframe{ label="XGUI Warning!", w=375, h=110, nopopup=true, showclose=false, skin=xgui.settings.skin }
- xlib.makelabel{ x=10, y=30, wordwrap=true, w=365, label="XGUI has not initialized properly with the server. This could be caused by a heavy server load after a mapchange, a major error during XGUI server startup, or XGUI not being installed.", parent=xgui.notInstalledWarning }
- xlib.makebutton{ x=37, y=83, w=80, label="Offline Mode", parent=xgui.notInstalledWarning }.DoClick = function()
- xgui.notInstalledWarning:Remove()
- xgui.notInstalledWarning = nil
- offlineWarning = xlib.makeframe{ label="XGUI Warning!", w=375, h=110, nopopup=true, showclose=false, skin=xgui.settings.skin }
- xlib.makelabel{ x=10, y=30, wordwrap=true, w=365, label="XGUI will run locally in offline mode. Some features will not work, and information will be missing. You can attempt to reconnect to the server using the 'Refresh Server Data' button in the XGUI client menu.", parent=offlineWarning }
- xlib.makebutton{ x=77, y=83, w=80, label="OK", parent=offlineWarning }.DoClick = function()
- offlineWarning:Remove()
- xgui.offlineMode = true
- xgui.show( tabname )
- end
- xlib.makebutton{ x=217, y=83, w=80, label="Cancel", parent=offlineWarning }.DoClick = function()
- offlineWarning:Remove()
- RememberCursorPosition()
- gui.EnableScreenClicker( false )
- end
- end
- xlib.makebutton{ x=257, y=83, w=80, label="Close", parent=xgui.notInstalledWarning }.DoClick = function()
- xgui.notInstalledWarning:Remove()
- xgui.notInstalledWarning = nil
- RememberCursorPosition()
- gui.EnableScreenClicker( false )
- end
- xlib.makebutton{ x=147, y=83, w=80, label="Try Again", parent=xgui.notInstalledWarning }.DoClick = function()
- xgui.notInstalledWarning:Remove()
- xgui.notInstalledWarning = nil
- RememberCursorPosition()
- gui.EnableScreenClicker( false )
- local reattempt = xlib.makeframe{ label="XGUI: Attempting reconnection...", w=200, h=20, nopopup=true, showclose=false, skin=xgui.settings.skin }
- timer.Simple( 1, function()
- RunConsoleCommand( "_xgui", "getInstalled" )
- reattempt:Remove()
- timer.Simple( 0.5, function() xgui.show( tabname ) end )
- end )
- end
- end
- function xgui.show( tabname )
- if not xgui.anchor then return end
- if not xgui.initialized then return end
- --Check if XGUI is not installed, display the warning if hasn't been shown yet.
- if not xgui.isInstalled and not xgui.offlineMode then
- xgui.checkNotInstalled( tabname )
- return
- end
- if not game.SinglePlayer() and not ULib.ucl.authed[LocalPlayer():UniqueID()] then
- local unauthedWarning = xlib.makeframe{ label="XGUI Error!", w=250, h=90, showclose=true, skin=xgui.settings.skin }
- xlib.makelabel{ label="Your ULX player has not been Authed!", x=10, y=30, parent=unauthedWarning }
- xlib.makelabel{ label="Please wait a couple seconds and try again.", x=10, y=45, parent=unauthedWarning }
- xlib.makebutton{ x=50, y=63, w=60, label="Try Again", parent=unauthedWarning }.DoClick = function()
- unauthedWarning:Remove()
- xgui.show( tabname )
- end
- xlib.makebutton{ x=140, y=63, w=60, label="Close", parent=unauthedWarning }.DoClick = function()
- unauthedWarning:Remove()
- end
- return
- end
- if xgui.base.refreshSkin then
- xgui.base:SetSkin( xgui.settings.skin )
- xgui.base.refreshSkin = nil
- end
- --In case the string name had spaces, it sent the whole argument table. Convert it to a string here!
- if type( tabname ) == "table" then
- tabname = table.concat( tabname, " " )
- end
- --Sets the active tab to tabname if it was specified
- if tabname and tabname ~= "" then
- local found, settingsTab
- for _, v in ipairs( xgui.modules.tab ) do
- if string.lower( v.name ) == "settings" then settingsTab = v.tabpanel end
- if string.lower( v.name ) == string.lower( tabname ) and v.panel:GetParent() ~= xgui.null then
- xgui.base:SetActiveTab( v.tabpanel )
- if xgui.anchor:IsVisible() then return end
- found = true
- break
- end
- end
- if not found then
- for _, v in ipairs( xgui.modules.setting ) do
- if string.lower( v.name ) == string.lower( tabname ) and v.panel:GetParent() ~= xgui.null then
- xgui.base:SetActiveTab( settingsTab )
- xgui.settings_tabs:SetActiveTab( v.tabpanel )
- if xgui.anchor:IsVisible() then return end
- found = true
- break
- end
- end
- end
- if not found then return end --If invalid input was taken, then do nothing.
- end
- xgui.base.animOpen()
- gui.EnableScreenClicker( true )
- RestoreCursorPosition()
- xgui.anchor:SetMouseInputEnabled( true )
- --Calls the functions requesting to hook when XGUI is opened
- xgui.callUpdate( "onOpen" )
- end
- function xgui.hide()
- if not xgui.anchor then return end
- if not xgui.anchor:IsVisible() then return end
- RememberCursorPosition()
- gui.EnableScreenClicker( false )
- xgui.anchor:SetMouseInputEnabled( false )
- xgui.base.animClose()
- CloseDermaMenus()
- --Calls the functions requesting to hook when XGUI is closed
- xgui.callUpdate( "onClose" )
- end
- function xgui.toggle( tabname )
- if xgui.anchor and ( not xgui.anchor:IsVisible() or ( tabname and #tabname ~= 0 ) ) then
- xgui.show( tabname )
- else
- xgui.hide()
- end
- end
- --New XGUI Data stuff
- function xgui.expectChunks( numofchunks )
- if xgui.isInstalled then
- xgui.expectingdata = true
- xgui.chunkbox.max = numofchunks
- xgui.chunkbox.value = 0
- xgui.chunkbox:SetFraction( 0 )
- xgui.chunkbox.Label:SetText( "Getting data: Waiting for server..." )
- xgui.chunkbox:SetVisible( true )
- xgui.chunkbox:SetSkin( xgui.settings.skin )
- xgui.flushQueue( "chunkbox" ) --Remove the queue entry that would hide the chunkbox
- end
- end
- function xgui.getChunk( flag, datatype, data )
- if xgui.expectingdata then
- --print( datatype, flag ) --Debug
- if flag == -1 then
- --Ignore these chunks
- elseif flag == 0 then --Data should be purged
- if xgui.data[datatype] then
- table.Empty( xgui.data[datatype] )
- end
- xgui.flushQueue( datatype )
- xgui.callUpdate( datatype, "clear" )
- elseif flag == 1 then
- if not xgui.mergeData then --A full data table is coming in
- if not data then data = {} end --Failsafe for no table being sent
- xgui.flushQueue( datatype )
- table.Empty( xgui.data[datatype] )
- table.Merge( xgui.data[datatype], data )
- xgui.callUpdate( datatype, "clear" )
- xgui.callUpdate( datatype, "process", data )
- xgui.callUpdate( datatype, "done" )
- else --A chunk of data is coming in
- table.Merge( xgui.data[datatype], data )
- xgui.callUpdate( datatype, "process", data )
- end
- elseif flag == 2 or flag == 3 then --Add/Update a portion of data
- table.Merge( xgui.data[datatype], data )
- xgui.callUpdate( datatype, flag == 2 and "add" or "update", data )
- elseif flag == 4 then --Remove a key from the table
- xgui.removeDataEntry( xgui.data[datatype], data ) --Needs to be called recursively!
- xgui.callUpdate( datatype, "remove", data )
- elseif flag == 5 then --Begin a set of chunks (Clear the old data, then flag to merge incoming data)
- table.Empty( xgui.data[datatype] )
- xgui.mergeData = true
- xgui.flushQueue( datatype )
- xgui.callUpdate( datatype, "clear" )
- elseif flag == 6 then --End a set of chunks (Clear the merge flag)
- xgui.mergeData = nil
- xgui.callUpdate( datatype, "done" )
- elseif flag == 7 then --Pass the data directly to the module to be handled.
- xgui.callUpdate( datatype, "data", data )
- end
- xgui.chunkbox:Progress( datatype )
- end
- end
- function xgui.removeDataEntry( data, entry )
- for k, v in pairs( entry ) do
- if type( v ) == "table" then
- xgui.removeDataEntry( data[k], v )
- else
- if type(v) == "number" then
- table.remove( data, v )
- else
- data[v] = nil
- end
- end
- end
- end
- function xgui.callUpdate( dtype, event, data )
- --Run any functions that request to be called when "curtable" is updated
- if not xgui.hook[dtype] or ( event and not xgui.hook[dtype][event] ) then
- Msg( "XGUI: Attempted to call non-existent type or event to a hook! (" .. dtype .. ", " .. ( event or "nil" ) .. ")\n" )
- else
- if not event then
- for name, func in pairs( xgui.hook[dtype] ) do func( data ) end
- else
- for name, func in pairs( xgui.hook[dtype][event] ) do func( data ) end
- end
- end
- end
- --If the player's group is changed, reprocess the XGUI modules for permissions, and request for extra data if needed
- function xgui.PermissionsChanged( ply )
- if ply == LocalPlayer() and xgui.isInstalled and xgui.dataInitialized then
- xgui.processModules()
- local types = {}
- for dtype, data in pairs( xgui.data ) do
- if table.Count( data ) > 0 then table.insert( types, dtype ) end
- end
- RunConsoleCommand( "xgui", "refreshdata", unpack( types ) )
- end
- end
- hook.Add( "UCLAuthed", "XGUI_PermissionsChanged", xgui.PermissionsChanged )
- function xgui.getInstalled()
- if not xgui.isInstalled then
- if xgui.notInstalledWarning then
- xgui.notInstalledWarning:Remove()
- xgui.notInstalledWarning = nil
- end
- xgui.isInstalled = true
- xgui.offlineMode = false
- RunConsoleCommand( "xgui", "getdata" )
- end
- end
- function xgui.cmd_base( ply, func, args )
- if not args[ 1 ] then
- xgui.toggle()
- elseif xgui.isInstalled then --First check that it's installed
- RunConsoleCommand( "_xgui", unpack( args ) )
- end
- end
- function xgui.tab_completes()
- return xgui.tabcompletes
- end
- function xgui.ulxmenu_tab_completes()
- return xgui.ulxmenucompletes
- end
- ULib.cmds.addCommandClient( "xgui", xgui.cmd_base )
- ULib.cmds.addCommandClient( "xgui show", function( ply, cmd, args ) xgui.show( args ) end, xgui.tab_completes )
- ULib.cmds.addCommandClient( "xgui hide", xgui.hide )
- ULib.cmds.addCommandClient( "xgui toggle", function() xgui.toggle() end )
- --local ulxmenu = ulx.command( CATEGORY_NAME, "ulx menu", ulx.menu, "!menu" )
- ULib.cmds.addCommandClient( "ulx menu", function( ply, cmd, args ) xgui.toggle( args ) end, xgui.ulxmenu_tab_completes )
- ulx.teams = ulx.teams or {}
- function ulx.populateClTeams( teams )
- ulx.teams = teams
- for i=1, #teams do
- local team_data = teams[ i ]
- team.SetUp( team_data.index, team_data.name, team_data.color )
- end
- end
- ulx.motdmenu_exists = true
- local mode
- local url
- function ulx.showMotdMenu( steamid )
- if mode == nil then
- return -- No data provided
- end
- local window = vgui.Create( "DFrame" )
- if ScrW() > 640 then -- Make it larger if we can.
- window:SetSize( ScrW()*0.9, ScrH()*0.9 )
- else
- window:SetSize( 640, 480 )
- end
- window:Center()
- window:SetTitle( "ULX MOTD" )
- window:SetVisible( true )
- window:MakePopup()
- local html = vgui.Create( "DHTML", window )
- --html:SetAllowLua( true ) -- Too much of a security risk for us to enable. Feel free to uncomment if you know what you're doing.
- local button = vgui.Create( "DButton", window )
- button:SetText( "Close" )
- button.DoClick = function() window:Close() end
- button:SetSize( 100, 40 )
- button:SetPos( (window:GetWide() - button:GetWide()) / 2, window:GetTall() - button:GetTall() - 10 )
- html:SetSize( window:GetWide() - 20, window:GetTall() - button:GetTall() - 50 )
- html:SetPos( 10, 30 )
- if mode == "1" then -- file
- html:SetHTML( ULib.fileRead( "data/ulx_motd.txt" ) or "" )
- elseif mode == "2" then -- generator
- html:SetHTML( ulx.generateMotdHTML() or "" )
- else -- URL
- url = string.gsub( url, "%%curmap%%", game.GetMap() )
- url = string.gsub( url, "%%steamid%%", steamid )
- html:OpenURL( url )
- end
- end
- function ulx.rcvMotd( mode_, data )
- mode = mode_
- if mode == "1" then -- file
- ULib.fileWrite( "data/ulx_motd.txt", data )
- elseif mode == "2" then -- generator
- ulx.motdSettings = data
- else -- URL
- if data:find( "://", 1, true ) then
- url = data
- else
- url = "http://" .. data
- end
- end
- end
- local template_header = [[
- <html>
- <head>
- <style>
- body {
- padding: 0;
- margin: 0;
- height: 100%;
- font-family: {{style.fonts.regular.family}};
- font-size: {{style.fonts.regular.size}};
- font-weight: {{style.fonts.regular.weight}};
- color: {{style.colors.text_color}};
- background-color: {{style.colors.background_color}};
- }
- h1 {
- font-family: {{style.fonts.server_name.family}};
- font-size: {{style.fonts.server_name.size}};
- font-weight: {{style.fonts.server_name.weight}};
- }
- h2 {
- font-family: {{style.fonts.section_title.family}};
- font-size: {{style.fonts.section_title.size}};
- font-weight: {{style.fonts.section_title.weight}};
- color: {{style.colors.section_text_color}};
- }
- h3 {
- font-family: {{style.fonts.subtitle.family}};
- font-size: {{style.fonts.subtitle.size}};
- font-weight: {{style.fonts.subtitle.weight}};
- }
- p {
- padding-left: 20px;
- }
- ul, ol {
- padding-left: 40px;
- }
- .container {
- min-height: 100%;
- position: relative;
- }
- .header, .footer {
- width: 100%;
- text-align: center;
- background-color: {{style.colors.header_color}};
- color: {{style.colors.header_text_color}};
- }
- .header {
- padding: 20px 0;
- border-bottom: {{style.borders.border_thickness}} solid {{style.borders.border_color}};
- }
- .footer {
- position:absolute;
- bottom:0;
- border-top: {{style.borders.border_thickness}} solid {{style.borders.border_color}};
- height: 68px;
- }
- .page {
- width: 90%;
- margin: 0px auto;
- padding: 10px;
- text-align: left;
- padding-bottom: 68px;
- }
- .section {
- margin-bottom: 32px;
- }
- </style>
- </head>
- <body>
- <div class="container">
- <div class="header">
- <h1>%hostname%</h1>
- <h3>{{info.description}}</h3>
- </div>
- <div class="page">
- ]]
- local template_section = [[
- <div class="section">
- <h2>%title%</h2>
- %content%
- </div>
- ]]
- local template_section_p = [[
- <p>
- %items%
- </p>
- ]]
- local template_section_ol = [[
- <ol>
- %items%
- </ol>
- ]]
- local template_section_ul = [[
- <ul>
- %items%
- </ul>
- ]]
- local template_item_li = [[
- <li>%content%</li>
- ]]
- local template_item_br = [[
- %content%</br>
- ]]
- local template_item_addon = [[
- <li><b>%title%</b> by %author%</li>
- ]]
- local template_item_workshop = [[
- <li><b>%title%</b> - <a href="http://steamcommunity.com/sharedfiles/filedetails/?id=%workshop_id%">View on Workshop</a></li>
- ]]
- local template_footer = [[
- </div>
- <div class="footer">
- <h3>Powered by ULX</h3>
- </div>
- </div>
- </body>
- </html>
- ]]
- local template_error = [[
- <html>
- <head>
- </head>
- <body style="background-color: white">
- <div class="footer">
- <h3>ULX: MOTD Generator error. Could not parse settings file.</h3>
- </div>
- </body>
- </html>
- ]]
- local function escape(str)
- return (str:gsub("<", "<"):gsub(">", ">")) -- Wrapped in parenthesis so we ignore other return vals
- end
- local function renderItemTemplate(items, template)
- local output = ""
- for i=1, #items do
- output = output .. string.gsub( template, "%%content%%", escape(items[i] or ""))
- end
- return output
- end
- local function renderMods()
- local output = ""
- for a=1, #ulx.motdSettings.addons do
- local addon = ulx.motdSettings.addons[a]
- if addon.workshop_id then
- local item = string.gsub( template_item_workshop, "%%title%%", escape(addon.title) )
- output = output .. string.gsub( item, "%%workshop_id%%", escape(addon.workshop_id or "") )
- else
- local item = string.gsub( template_item_addon, "%%title%%", escape(addon.title or "") )
- output = output .. string.gsub( item, "%%author%%", escape(addon.author or "") )
- end
- end
- return output
- end
- function ulx.generateMotdHTML()
- if ulx.motdSettings == nil or ulx.motdSettings.info == nil then return template_error end
- local header = string.gsub( template_header, "%%hostname%%", escape(GetHostName() or "") )
- header = string.gsub( header, "{{(.-)}}", function(a)
- local success, value = ULib.findVar(a, ulx.motdSettings)
- return escape( value or "")
- end )
- local body = ""
- for i=1, #ulx.motdSettings.info do
- local data = ulx.motdSettings.info[i]
- local content = ""
- if data.type == "text" then
- content = string.gsub( template_section_p, "%%items%%", renderItemTemplate(data.contents, template_item_br) )
- elseif data.type == "ordered_list" then
- content = string.gsub( template_section_ol, "%%items%%", renderItemTemplate(data.contents, template_item_li) )
- elseif data.type == "list" then
- content = string.gsub( template_section_ul, "%%items%%", renderItemTemplate(data.contents, template_item_li) )
- elseif data.type == "mods" then
- content = string.gsub( template_section_ul, "%%items%%", renderMods() )
- elseif data.type == "admins" then
- local users = {}
- for g=1, #data.contents do
- local group = data.contents[g]
- if ulx.motdSettings.admins[group] then
- for u=1, #ulx.motdSettings.admins[group] do
- table.insert( users, ulx.motdSettings.admins[group][u] )
- end
- end
- end
- table.sort( users )
- content = string.gsub( template_section_ul, "%%items%%", renderItemTemplate(users, template_item_li) )
- end
- local section = string.gsub( template_section, "%%title%%", escape(data.title or "") )
- body = body .. string.gsub( section, "%%content%%", content )
- end
- return string.format( "%s%s%s", header, body, template_footer )
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement