GMod Lua- How to use 'self' outside a function (in a STool)?

Okay, so I’m trying to make a fairly basic STool, except when I try to use the variable ‘self’ to sample a ClientNumber (and make it a bool) to determine an ‘if’ sequence, a very painful error keeps popping up (self is a nil value).

How can I use the var ‘self’ WITHOUT using a function containing TOOL at the start? This is the annoyance, just add it into any STool script (outside a function) and the error will pop up.


local ANYTHING = tobool( self:GetClientNumber( "SOMETHING_WITH_A_NUMBER" ) )

(Replace the value ‘SOMETHING_WITH_A_NUMBER’ with an actual command if you want)

It’s meant to set a var ‘ANYTHING’, checking whether the ClientNumber of ‘SOMETHING_WITH_A_NUMBER’ is true or false, but it cannot check the variable ‘self’ because it is undefined.

How can I define it?

i never made an sTool but i think it’s the same as a swep.
try doing
[lua]
TOOL.something = nil
[/lua]

and then when you want to use it again you could probably do:
[lua]
self.something = tobool( self:GetClientNumber( “SOMETHING_WITH_A_NUMBER” ) )
[/lua]

again, i haven’t made a sTool before so i could be horribly, horribly wrong.

So, the way the gmod_tool works is rather irritating and somewhat confusing. I’ve posted a similar thread a few weeks ago (http://forum.facepunch.com/showthread.php?t=1451137).

Essentially there is only one tool that is created on a player when they are given it, and it is removed when the player dies, drops it, or is stripped of it.

That single tool creates multiple tool objects (ToolObj) which are the actual STools like the Balloon tool or the tool you are creating (the functions prefixed with TOOL:).

From looking at the gmod_tool SWEP and ToolObj code (found in garrysmod\garrysmod\gamemodes\sandbox\entities\weapons\gmod_tool), it looks like all ToolObjs (STools) are created and stored on the gmod_tool SWEP when it is initialized. Basically when you use ‘self’ for your STool, it refers to the corresponding ToolObj, shown in stool.lua:


local toolmodes = file.Find( SWEP.Folder.."/stools/*.lua", "LUA" )

for key, val in pairs( toolmodes ) do

    local char1,char2,toolmode = string.find( val, "([%w_]*).lua" )

    TOOL = ToolObj:Create() -- creates a new ToolObj that all STools use (as TOOL)
    TOOL.Mode = toolmode
    
    AddCSLuaFile( "stools/"..val ) -- these two calls then fill the TOOL table with the STool functions and values from STools like balloon, or your own STool
    include( "stools/"..val )
    
    TOOL:CreateConVars()

    SWEP.Tool[ toolmode ] = TOOL -- the ToolObj is then store within the gmod_tool SWEP
        
    TOOL = nil -- clears the TOOL table for the next STool to be loaded, since this is a loop through all STools
    
end

Basically using ‘self’ in your STool doesn’t refer to anything until the ToolObj is created, which occurs when the gmod_tool SWEP is initialized. Once that happens, the gmod_tool SWEP goes through and initializes all of the ToolObjs, shown in shared.lua:



function SWEP:InitializeTools()

    local temp = {}
    
    for k,v in pairs( self.Tool ) do
    
        temp[k] = table.Copy(v)
        temp[k].SWEP = self
        temp[k].Owner = self.Owner
        temp[k].Weapon = self.Weapon
        temp[k]:Init()
        
    end
    
    self.Tool = temp
    
end

So when the gmod_tool is given to a player, the ToolObjs are created and ‘self’ becomes valid (when used within function TOOL:Something()).

That’s a long ass confusing explanation to get to the point that whenever you try to use ‘self’ outside of a TOOL function, it doesn’t refer to anything. You’d basically have to do something horrid like the following:



local tool -- we'll be storing 'self' inside here when the ToolObj becomes valid

function TOOL:Init() -- called from the gmod_tool SWEP, shown in the code snippet above
    tool = self -- now our local variable refers to self
end

timer.Create( "BadExample", 1, 0, function()
    print( tool, IsValid( tool ) ) -- this should print 'nil false' until TOOL:Init() is called and 'self' is assigned to tool
end )


Basically this waits until the ToolObj has been created, which then assigns self to a variable that you can use outside of any TOOL functions.

The problem with doing that, however, is that as soon as the player dies or loses the gmod_tool SWEP, all of the ToolObjs are removed and trying to use ‘self’ (‘tool’ in the example above) will return NULL. If the player is given the gmod_tool SWEP again, it will recreate the ToolObjs, except they’ll all refer to different objects in memory now.

So if you have a hook or something that relies on the tool object like the example shown above (stored in ‘tool’), they will break as soon as the player dies.

Alright, thanks for your help! Wait, it doesn’t work. Here’s some simplified code, cause I have no idea why it won’t work (save it as test.lua in ‘garrysmod\lua\weapons\gmod_tool\stools’):



--This is some extrememly simplified code of what I'd like to do (no TOOL:RightClick or TOOL:LeftClick).
TOOL.Category 		= "Construction"
TOOL.Name 			= "#Test"
TOOL.Command		= nil
TOOL.ConfigName		= nil

if CLIENT then
language.Add("tool.test.name", "PLEASE FIX ME")
language.Add("tool.test.desc", "This tool does nothing and gives a very annoying error.")
language.Add("tool.test.checkbox", "Change what the box below does")
language.Add("tool.test.box", "Do something") --No, I can't use a second box in this case :(
end

TOOL.ClientConVar[ "checkbox" ] = "0"


--This is the bit that I was told to add (although in this case, it doesn't work for some reason)
local tool

function TOOL:Init()
    tool = self
end

concommand.Add( "test_box", function()
		local editable = tobool( self:GetClientNumber( "checkbox" ) ) --self should be 'tool' actually
		if editable == false then
		
print("BOX IS NOT CHECKED")

	else
	
	print("BOX IS CHECKED")
	
	end
end)

local ConVarsDefault = TOOL:BuildConVarList()

function TOOL.BuildCPanel( CPanel )

	CPanel:AddControl( "Header", { Text = "#tool.test.name", Description	= "#tool.test.desc" } )
	CPanel:AddControl( "CheckBox", { Label = "#tool.test.checkbox", Command = "test_checkbox" } )
	CPanel:AddControl( "Button", { Label = "#tool.test.box", Command = "test_box" } )

end


Wait, just realised I needed to change



local editable = tobool( self:GetClientNumber( "checkbox" ) )


to



local editable = tobool( tool:GetClientNumber( "checkbox" ) )


Just tell me if I’m actually doing it right though, cause I have a feeling it’s a bit buggy

If you only want the:



local editable = tobool( self:GetClientNumber( "checkbox" ) )


You can get it anywhere with this



local yourvalue = GetConVarNumber("toolUniqueID_checkbox")


Tool use concommand to save value

MPan1 didn’t state whether he wants the value clientside or serverside.

If you run GetConVarNumber serverside, you will retrieve the server’s convar value (which most likely doesn’t exist because we’re geting a client convar).

It ultimately depends on whether he is trying to retrieve the value clientside or not. If clientside, GetConVar* will work. But the moment he wants the value serverside, the issue of needing TOOL’s self will appear again.

Although my example was bad to lead to the issue it causes, yes, it should be ‘tool’ since that’s the variable that self was assigned to.

But referring to the issue I stated in my first post, the moment the player dies or loses the gmod_tool SWEP, the ToolObj that tool refers to will become NULL, and trying to do NULL:GetClientNumber() will cause an error.
The example is bad because it only works while the player has the tool swep, hence why the way the tool swep is written makes it nearly impossible to use things like hooks that rely on the ToolObj being valid.

You’re basically faced with the same predicament that I am in, and I don’t know of any way to solve the issue without rewriting the way the gmod_tool SWEP and ToolObjs are handled.