I was looking through code for btools so I could check how they used handles to resize things. Then at line 61-63 I got confused by how they could call a function without it existing
Code:
Core = require(Tool.Core);
SnapTracking = require(Tool.Core.Snapping);
-- Services
local ContextActionService = game:GetService 'ContextActionService'
local Workspace = game:GetService 'Workspace'
-- Libraries
local Libraries = Tool:WaitForChild 'Libraries'
local Signal = require(Libraries:WaitForChild 'Signal')
local Make = require(Libraries:WaitForChild 'Make')
local ListenForManualWindowTrigger = require(Tool.Core:WaitForChild('ListenForManualWindowTrigger'))
-- Import relevant references
Selection = Core.Selection;
Support = Core.Support;
Security = Core.Security;
Support.ImportServices();
-- Initialize the tool
local ResizeTool = {
Name = 'Resize Tool';
Color = BrickColor.new 'Cyan';
-- Default options
Increment = 1;
Directions = 'Normal';
}
ResizeTool.ManualText = [[<font face="GothamBlack" size="16">Resize Tool đź› </font>
Allows you to resize parts.<font size="12"><br /></font>
<font size="12" color="rgb(150, 150, 150)"><b>Directions</b></font>
Lets you choose in which directions to resize the part.<font size="6"><br /></font>
<b>TIP: </b>Click on a part to focus the handles on it.<font size="6"><br /></font>
<b>TIP: </b>Hit <b>Enter</b> to switch between directions quickly.<font size="12"><br /></font>
<font size="12" color="rgb(150, 150, 150)"><b>Increment</b></font>
Lets you choose how many studs to resize by.<font size="6"><br /></font>
<b>TIP: </b>Hit the – key to quickly type increments.<font size="6"><br /></font>
<b>TIP: </b>Use your number pad to resize exactly by the current increment. Holding <b>Shift</b> reverses the increment.<font size="4"><br /></font>
<font color="rgb(150, 150, 150)">•</font> 8 & 2 — up & down
<font color="rgb(150, 150, 150)">•</font> 1 & 9 — back & forth
<font color="rgb(150, 150, 150)">•</font> 4 & 6 — left & right<font size="12"><br /></font>
<font size="12" color="rgb(150, 150, 150)"><b>Snapping</b></font>
Hold the <b><i>R</i></b> key, and <b>click and drag the snap point</b> of a part (in the direction you want to resize) towards the snap point of another part, to resize up to that point.
]]
-- Container for temporary connections (disconnected automatically)
local Connections = {};
function ResizeTool.Equip()
-- Enables the tool's equipped functionality
-- Start up our interface
ShowUI();
ShowHandles();
BindShortcutKeys();
end;
getfenv()['ShowUI'] = function()
-- do whatever
end
which btw is impractical. But I don’t know how they would’ve done it as calling getfenv will return the environment of the script the function is from (module script).
You use setfenv() with the stack level and new environment:
local t = {}
function t.set(var)
setfenv(2, setmetatable({ --> replace the environment 2 stacks above the current one
setValue = var --> add a custom variable to the environment (global)
}, {
__index = getfenv()
}))
end
t.set(12)
print(setValue) --> 12
Edit: You don’t have to use a table either.
Without Table
local function set(var)
setfenv(2, setmetatable({ --> replace the environment 2 stacks above the current one
setValue = var --> add a custom variable to the environment (global)
}, {
__index = getfenv()
}))
end
set(12)
print(setValue) --> 12
They allow you to manipulate the stack’s environment (variables, functions, etc). When you use getfenv(), you’re retrieving the current stack and all the global variables in the stack; when you use setfenv() you’re setting (and overwriting) the globals for either the stack, or a given function.
Example with metatable
local function c_stack()
print(hello) --> prints "world!"
end
setfenv(c_stack, setmetatable({
hello = "world!"
}, {__index=getfenv()})
c_stack()
Example without metatable
local function c_stack()
print(hello) --> errors (attempt to call a nil value)
-- since the environment was overwriten to { hello = "world!" },
-- built-in globals (like print or game) no longer exist in the environment and must be
-- explicitly defined (this is why a metatable pointing to the default
-- environment was used in the first example).
end
setfenv(c_stack, {
hello = "world!"
})
c_stack()
I’d advise against using these though since they de-optimize the Luau environment in order to function, which can lead to worse performance for your code (and they’re deprecated).