User:JulienDethurens/Scripts/Heading code

From Legacy Roblox Wiki
Jump to navigationJump to search

This is code I often put at the top of my code (that's why this page is called "Heading code", btw).

It contains many useful functions, many that I designed for making libraries easier to create.

Variables

The code creates a variable that makes accessing the Terrain object easier, that variable is called "Terrain".

It also creates many variables for some services that I commonly use:

  • Players
  • StarterPack
  • StarterGui
  • Lighting
  • Debris
  • Teams
  • BadgeService
  • InsertService
local Players = Game:GetService('Players')
local StarterPack = Game:GetService('StarterPack')
local StarterGui = Game:GetService('StarterGui')
local Lighting = Game:GetService('Lighting')
local Debris = Game:GetService('Debris')
local Teams = Game:GetService('Teams')
local BadgeService = Game:GetService('BadgeService')
local InsertService = Game:GetService('InsertService')
local Terrain = Workspace.Terrain

local verify_arg -- Since other functions defined before need to use it, this needs to be defined here.

is_a(value, data_type)

Parameters
Name Type Description
value The value you want to check the type of.
data_type string The type you want to check for.

This function returns true if value is of type data_type. Otherwise, it returns false.

It supports the following types:

  • all the Lua types (recognized by the Lua type function)
  • all the Instance types (recognized by the IsA method
  • all the Enum types
  • Enum (the enum object itself, accessed through the Enum variable
  • Color3
  • BrickColor
  • Vector2
  • Vector3
  • CFrame/CoordinateFrame (both names are recognized by the function)
  • UDim
  • UDim2
  • RBXScriptSignal (the real name of events)
  • Axes
  • Faces
  • Ray

Just as a note, you can't use a "fake" value to make it return true with a value that is similar to a real value of the correct data type. If it says that the value is of the correct data type, then the value is of the correct data type.

local function is_a(value, data_type)
	-- Supported types: Lua types, Instance types, Enum types, Enum, Color3, BrickColor, Vector2, Vector3, CFrame/CoordinateFrame, UDim, UDim2, RBXScriptSignal, Axes, Faces, Ray
	-- Will return false if the type is not supported, even though the value might be of that type.
	-- This function can not be fooled by a fake value. If it says the value is a ClickDetector, then it IS a ClickDetector.
	-- This function uses a variety of ugly hacks that were found by JulienDethurens.
	-- I wish ROBLOX just provided an official way to do this, I wouldn't have to use lots of unrealiable ways to get the info I need... :/

	-- Here is a nice collection of bad practices, ugly hacks, and other things you should never use, but that you don't have the choice of using, because of ROBLOX's lack of an official way to distinguish data types:
	data_type = verify_arg(data_type, 'string', "data_type")
	if type(value) == data_type then return true end -- Lua types
	if pcall(function() assert(Game.IsA(value, data_type)) end) then return true end -- Instance types
	if pcall(function() assert(Enum[data_type]) end) then -- Enum types
		for _, enum in next, Enum[data_type]:GetEnumItems() do
			if value == enum then
				return true
			end
		end
	elseif pcall(Enum.Material.GetEnumItems, value) then
		for _, enum in next, value:GetEnumItems() do
			if value == enum then
				return true
			end
		end
	end
	if data_type == 'Color3' and pcall(function() Instance.new('Color3Value').Value = value end) then return true -- Color3
	elseif data_type == 'BrickColor' and pcall(function() Instance.new('BrickColorValue').Value = value end) then return true -- BrickColor
	elseif data_type == 'Vector2' and pcall(function() return Vector2.new() + value end) then return true -- Vector2
	elseif data_type == 'Vector3' and pcall(function() Instance.new('Vector3Value').Value = value end) then return true -- Vector3
	elseif (data_type == 'CFrame' or data_type == 'CoordinateFrame') and pcall(function() Instance.new('CFrameValue').Value = value end) then return true -- CFrame
	elseif data_type == 'UDim' and pcall(function() return UDim.new() + value end) then return true -- UDim
	elseif data_type == 'UDim2' and pcall(function() Instance.new('Frame').Position = value end) then return true -- UDim2
	elseif data_type == 'Ray' and pcall(function() Ray.new(Vector3.new(), Vector3.new()).Distance(value, Vector3.new()) end) then return true -- Ray
	elseif data_type == 'Axes' and pcall(function() Instance.new('ArcHandles').Axes = value end) then return true -- Axes
	elseif data_type == 'Faces' and pcall(function() Instance.new('Handles').Faces = value end) then return true -- Faces
	elseif data_type == 'Enum' and pcall(Enum.Material.GetEnumItems, value) then return true -- Enum
	elseif data_type == 'RBXScriptSignal' then
		local _, connection = pcall(function() return Game.AllowedGearTypeChanged.connect(value) end)
		if _ and connection then
			connection:disconnect()
			return true
		end
	end
	return false
end

cpp_is_a(value, data_type)

Parameters
Name Type Description
value The value you want to check the type of.
data_type string The type you want to check if the value is of.

This function is like the is_a function, but is used with the C++ types used by ROBLOX methods and properties. It should be used to know if a certain value would be accepted by a method or a property.

It supports the following types:

  • int
  • double
  • bool
  • string
  • float
local function cpp_is_a(value, data_type)
	-- Same as is_a, but for methods and properties. Only supports basic types.
	-- Supports: int, double, bool, string, float
	-- Note: this function should be used to know if it is safe to send an argument to a method or a property, as it will also return true for values that will be automatically coerced by ROBLOX.
	data_type = verify_arg(data_type, 'string', "data_type")
	if data_type == 'int' then
		if pcall(function() Instance.new('IntValue').Value = value end) then
			return true
		end
	elseif data_type == 'double' then
		if pcall(function() Instance.new('NumberValue').Value = value end) then
			return true
		end
	elseif data_type == 'bool' then
		if pcall(function() Instance.new('BoolValue').Value = value end) then
			return true
		end
	elseif data_type == 'string' then
		if pcall(function() Instance.new('StringValue').Value = value end) then
			return true
		end
	elseif data_type == 'float' then
		if pcall(function() Instance.new('ClickDetector').MaxActivationDistance = value end) then
			return true
		end
	end
	return false
end

get_type(value)

Parameters
Name Type Description
value The value you want to get the type of.

This function returns the most specific type it can get for a certain value. It supports all the types supported by the is_a function.

local function get_type(value)
	-- Returns the most specific type it can return. Supports the same types as the is_a function, except the enum types.
	if is_a(value, 'Instance') then return value.ClassName
	elseif is_a(value, 'Enum') then return 'Enum'
	elseif is_a(value, 'Color3') then return 'Color3'
	elseif is_a(value, 'BrickColor') then return 'BrickColor'
	elseif is_a(value, 'Vector2') then return 'Vector2'
	elseif is_a(value, 'Vector3') then return 'Vector3'
	elseif is_a(value, 'CFrame') then return 'CFrame'
	elseif is_a(value, 'UDim') then return 'UDim'
	elseif is_a(value, 'UDim2') then return 'UDim2'
	elseif is_a(value, 'Ray') then return 'Ray'
	elseif is_a(value, 'Axes') then return 'Axes'
	elseif is_a(value, 'Faces') then return 'Faces'
	elseif is_a(value, 'RBXScriptSignal') then return 'RBXScriptSignal'
	else return type(value)
	end
end

verify_arg(value, data_type, arg_name, optional)

Parameters
Name Type Description
value The value you want the verify the type of.
data_type string The data type the value should be of.
arg_name string The name of the argument.
optional boolean Whether the argument can be nil or not.

This function will check if value is of type data_type. If not, it will print an error message relating to the type.

Use it in functions that receive arguments to check if arguments are of the correct type. Using this function can make debugging a lot easier (trust me, that's from experience).

function verify_arg(value, data_type, arg_name, optional)
	-- Makes the function that called the calling function error, with an error message relating to a wrong type. Supports all the types supported by the is_a and the cpp_is_a functions.
	-- Also supports coercion for the number and string types.
	-- Returns the value, as it might be automatically converted if a coercion has been done.
	if type(data_type) ~= 'string' then error("bad 'data_type' argument (string expected, got " .. get_type(data_type) .. ")", 2) end
	if type(arg_name) ~= 'string' then error("bad 'arg_name' argument (string expected, got " .. get_type(arg_name) .. ")") end

	if optional and value == nil then
		return value
	elseif type(value) == data_type then
		return value
	elseif is_a(value, data_type) or cpp_is_a(value, data_type) then
		return value
	elseif data_type == 'number' and tonumber(value) then
		return tonumber(value)
	elseif data_type == 'string' and type(value) == 'number' then
		return tostring(value)
	else
		error("bad '" .. arg_name .. "'" .. (optional and " optional" or "") .. " argument (" .. data_type .. " expected, got " .. get_type(value) .. ")", 3)
	end
end

modify(instance, t)

Parameters
Name Type Description
instance Instance The instance you want to edit.
t table The changes you want to make to instance.

This function allows you to edit the properties and the hierarchy of a certain object. Because I am too lazy to explain exactly how it works, and because you already have the code anyways, I will only give you an example:

local part = modify(Instance.new('Part'), {
	Name = "Wall";
	Position = Vector3.new(10, 50, 10);
	with (Instance.new('Fire'), {
		Heat = 10;
		Size = 50;
	});
})

That code creates a part, sets its name to "Wall", sets its position to a certain Vector3, creates a Fire object in it, sets some of the properties of that Fire object, and stores the part in the part variable.

local function modify(instance, t)
	instance = verify_arg(instance, 'Instance', "instance")
	t = verify_arg(t, 'table', "t")
	for key, value in next, t do
		if type(key) == 'number' then
			value.Parent = instance 
		else
			instance[key] = value
		end
	end
	return instance
end

call_on_descendants(instance, func)

Parameters
Name Type Description
instance Instance The object you want to call a function on all the descendants of.
func function The function you want to call on all the descendants of the object.

This function calls func on all the descendants of instance, including instance itself.

local function call_on_descendants(instance, func)
	-- Calls 'func' on 'instance' and all its descendants, with the instance or descendant as argument.
	instance = verify_arg(instance, 'Instance', "instance")
	func = verify_arg(func, 'function', "func")
	func(instance)
	for _, child in next, instance:GetChildren() do
		call_on_descendants(child, func)
	end
end

get_nearest_ancestor(instance, class_name)

Parameters
Name Type Description
instance Instance The object of which you want to find an ancestor.
class_name string The class name of the ancestor you want to find.

This function returns the nearest ancestor of instance of type class_name in ascending order.

local function get_nearest_ancestor(instance, class_name)
	-- Returns the nearest ancestor of a certain instance which is of a certain type.
	instance = verify_arg(instance, 'Instance', "instance")
	class_name = verify_arg(class_name, 'string', "class_name")
	local ancestor = instance
	repeat
		ancestor = ancestor.Parent
		if ancestor == nil then
			return nil
		end
	until ancestor:IsA(class_name)
	return ancestor
end

get_character(descendant)

Parameters
Name Type Description
descendant Instance The descendant of the character you want to get.

This function returns the character a descendant is a part of.

local function get_character(descendant)
	-- Returns a character from one of its descendants.
	descendant = verify_arg(descendant, 'Instance', "descendant")
	local character = descendant
	repeat
		if character.Parent then
			character = character.Parent
		else
			return nil
		end
	until Players:GetPlayerFromCharacter(character)
	return character
end

show_message(text, lifetime)

Parameters
Name Type Description
text string The text you want to show in the message.
lifetime number The time, in seconds, during which the message must be displayed.

This function shows a message and removes it after a certain time.

local function show_message(text, lifetime)
	-- Shows a message for a certain time, which is set to be 3 seconds by default.
	text = verify_arg(text, 'string', "text")
	lifetime = verify_arg(lifetime, 'number', "lifetime")
	local message = Instance.new('Message')
	message.Text = text
	Debris:AddItem(message, lifetime or 3)
end

show_hint(text, lifetime)

Parameters
Name Type Description
text string The text you want to show in the hint.
lifetime number The time, in seconds, during which the hint must be displayed.

This function shows a hint and removes it after a certain time.

local function show_hint(text, lifetime)
	-- Shows a hint for a certain time, which is set to be 3 seconds by default.
	text = verify_arg(text, 'string', "text")
	lifetime = verify_arg(lifetime, 'number', "lifetime")
	local hint = Instance.new('Hint')
	hint.Text = text
	Debris:AddItem(hint, lifetime or 3)
end

Entire code

Here is the whole code:

local Players = Game:GetService('Players')
local StarterPack = Game:GetService('StarterPack')
local StarterGui = Game:GetService('StarterGui')
local Lighting = Game:GetService('Lighting')
local Debris = Game:GetService('Debris')
local Teams = Game:GetService('Teams')
local BadgeService = Game:GetService('BadgeService')
local InsertService = Game:GetService('InsertService')
local Terrain = Workspace.Terrain

local verify_arg -- Since other functions defined before need to use it, this needs to be defined here.

local function is_a(value, data_type)
	-- Supported types: Lua types, Instance types, Enum types, Enum, Color3, BrickColor, Vector2, Vector3, CFrame/CoordinateFrame, UDim, UDim2, RBXScriptSignal, Axes, Faces, Ray
	-- Will return false if the type is not supported, even though the value might be of that type.
	-- This function can not be fooled by a fake value. If it says the value is a ClickDetector, then it IS a ClickDetector.
	-- This function uses a variety of ugly hacks that were found by JulienDethurens.
	-- I wish ROBLOX just provided an official way to do this, I wouldn't have to use lots of unrealiable ways to get the info I need... :/

	-- Here is a nice collection of bad practices, ugly hacks, and other things you should never use, but that you don't have the choice of using, because of ROBLOX's lack of an official way to distinguish data types:
	data_type = verify_arg(data_type, 'string', "data_type")
	if type(value) == data_type then return true end -- Lua types
	if pcall(function() assert(Game.IsA(value, data_type)) end) then return true end -- Instance types
	if pcall(function() assert(Enum[data_type]) end) then -- Enum types
		for _, enum in next, Enum[data_type]:GetEnumItems() do
			if value == enum then
				return true
			end
		end
	elseif pcall(Enum.Material.GetEnumItems, value) then
		for _, enum in next, value:GetEnumItems() do
			if value == enum then
				return true
			end
		end
	end
	if data_type == 'Color3' and pcall(function() Instance.new('Color3Value').Value = value end) then return true -- Color3
	elseif data_type == 'BrickColor' and pcall(function() Instance.new('BrickColorValue').Value = value end) then return true -- BrickColor
	elseif data_type == 'Vector2' and pcall(function() return Vector2.new() + value end) then return true -- Vector2
	elseif data_type == 'Vector3' and pcall(function() Instance.new('Vector3Value').Value = value end) then return true -- Vector3
	elseif (data_type == 'CFrame' or data_type == 'CoordinateFrame') and pcall(function() Instance.new('CFrameValue').Value = value end) then return true -- CFrame
	elseif data_type == 'UDim' and pcall(function() return UDim.new() + value end) then return true -- UDim
	elseif data_type == 'UDim2' and pcall(function() Instance.new('Frame').Position = value end) then return true -- UDim2
	elseif data_type == 'Ray' and pcall(function() Ray.new(Vector3.new(), Vector3.new()).Distance(value, Vector3.new()) end) then return true -- Ray
	elseif data_type == 'Axes' and pcall(function() Instance.new('ArcHandles').Axes = value end) then return true -- Axes
	elseif data_type == 'Faces' and pcall(function() Instance.new('Handles').Faces = value end) then return true -- Faces
	elseif data_type == 'Enum' and pcall(Enum.Material.GetEnumItems, value) then return true -- Enum
	elseif data_type == 'RBXScriptSignal' then
		local _, connection = pcall(function() return Game.AllowedGearTypeChanged.connect(value) end)
		if _ and connection then
			connection:disconnect()
			return true
		end
	end
	return false
end

local function cpp_is_a(value, data_type)
	-- Same as is_a, but for methods and properties. Only supports basic types.
	-- Supports: int, double, bool, string, float
	-- Note: this function should be used to know if it is safe to send an argument to a method or a property, as it will also return true for values that will be automatically coerced by ROBLOX.
	data_type = verify_arg(data_type, 'string', "data_type")
	if data_type == 'int' then
		if pcall(function() Instance.new('IntValue').Value = value end) then
			return true
		end
	elseif data_type == 'double' then
		if pcall(function() Instance.new('NumberValue').Value = value end) then
			return true
		end
	elseif data_type == 'bool' then
		if pcall(function() Instance.new('BoolValue').Value = value end) then
			return true
		end
	elseif data_type == 'string' then
		if pcall(function() Instance.new('StringValue').Value = value end) then
			return true
		end
	elseif data_type == 'float' then
		if pcall(function() Instance.new('ClickDetector').MaxActivationDistance = value end) then
			return true
		end
	end
	return false
end

local function get_type(value)
	-- Returns the most specific type it can return. Supports the same types as the is_a function, except the enum types.
	if is_a(value, 'Instance') then return value.ClassName
	elseif is_a(value, 'Enum') then return 'Enum'
	elseif is_a(value, 'Color3') then return 'Color3'
	elseif is_a(value, 'BrickColor') then return 'BrickColor'
	elseif is_a(value, 'Vector2') then return 'Vector2'
	elseif is_a(value, 'Vector3') then return 'Vector3'
	elseif is_a(value, 'CFrame') then return 'CFrame'
	elseif is_a(value, 'UDim') then return 'UDim'
	elseif is_a(value, 'UDim2') then return 'UDim2'
	elseif is_a(value, 'Ray') then return 'Ray'
	elseif is_a(value, 'Axes') then return 'Axes'
	elseif is_a(value, 'Faces') then return 'Faces'
	elseif is_a(value, 'RBXScriptSignal') then return 'RBXScriptSignal'
	else return type(value)
	end
end

function verify_arg(value, data_type, arg_name, optional)
	-- Makes the function that called the calling function error, with an error message relating to a wrong type. Supports all the types supported by the is_a and the cpp_is_a functions.
	-- Also supports coercion for the number and string types.
	-- Returns the value, as it might be automatically converted if a coercion has been done.
	if type(data_type) ~= 'string' then error("bad 'data_type' argument (string expected, got " .. get_type(data_type) .. ")", 2) end
	if type(arg_name) ~= 'string' then error("bad 'arg_name' argument (string expected, got " .. get_type(arg_name) .. ")") end

	if optional and value == nil then
		return value
	elseif type(value) == data_type then
		return value
	elseif is_a(value, data_type) or cpp_is_a(value, data_type) then
		return value
	elseif data_type == 'number' and tonumber(value) then
		return tonumber(value)
	elseif data_type == 'string' and type(value) == 'number' then
		return tostring(value)
	else
		error("bad '" .. arg_name .. "'" .. (optional and " optional" or "") .. " argument (" .. data_type .. " expected, got " .. get_type(value) .. ")", 3)
	end
end

local function modify(instance, t)
	instance = verify_arg(instance, 'Instance', "instance")
	t = verify_arg(t, 'table', "t")
	for key, value in next, t do
		if type(key) == 'number' then
			value.Parent = instance 
		else
			instance[key] = value
		end
	end
	return instance
end

local function call_on_descendants(instance, func)
	-- Calls 'func' on 'instance' and all its descendants, with the instance or descendant as argument.
	instance = verify_arg(instance, 'Instance', "instance")
	func = verify_arg(func, 'function', "func")
	func(instance)
	for _, child in next, instance:GetChildren() do
		call_on_descendants(child, func)
	end
end

local function get_nearest_ancestor(instance, class_name)
	-- Returns the nearest ancestor of a certain instance which is of a certain type.
	instance = verify_arg(instance, 'Instance', "instance")
	class_name = verify_arg(class_name, 'string', "class_name")
	local ancestor = instance
	repeat
		ancestor = ancestor.Parent
		if ancestor == nil then
			return nil
		end
	until ancestor:IsA(class_name)
	return ancestor
end

local function get_character(descendant)
	-- Returns a character from one of its descendants.
	descendant = verify_arg(descendant, 'Instance', "descendant")
	local character = descendant
	repeat
		if character.Parent then
			character = character.Parent
		else
			return nil
		end
	until Players:GetPlayerFromCharacter(character)
	return character
end

local function show_message(text, lifetime)
	-- Shows a message for a certain time, which is set to be 3 seconds by default.
	text = verify_arg(text, 'string', "text")
	lifetime = verify_arg(lifetime, 'number', "lifetime")
	local message = Instance.new('Message')
	message.Text = text
	Debris:AddItem(message, lifetime or 3)
end

local function show_hint(text, lifetime)
	-- Shows a hint for a certain time, which is set to be 3 seconds by default.
	text = verify_arg(text, 'string', "text")
	lifetime = verify_arg(lifetime, 'number', "lifetime")
	local hint = Instance.new('Hint')
	hint.Text = text
	Debris:AddItem(hint, lifetime or 3)
end