Handy Scripts - A Collection of Handy Scripts

A Resource for Lazy Programmers

Hello there. I am just open sourcing this. The scripts here I created because I found myself re-writing a lot of code similar to this. So, I thought it would be useful for me to open source this. Each script comes with a description on where when and how to use it. Most of these scripts aren’t really essential but I found if you find yourself re-writing a lot of these then you can simply copy-paste and use it in game jams or to reach deadlines etc.

Types

Love autocomplete? So, do I . As such I typed all the “untyped” but documented objects. Making autocomplete do the rest of the work for you. I found the most useful is the ChatService types which make scripting ChatModules a cinch.

Type

local Types = {}
export type dictionary<I,V> = {[I]: V}

export type array<V> = {[number]: V}

export type TerrainMaterial = { 
	Size: Vector3,
	[number]: {{Enum.Material}}
}

export type TerrainOccupancy = { 
	Size: Vector3,
	[number]: {{number}}
}

export type OnlineFriend = {
	VisitorId: number,
	UserName :string,
	DisplayName :string,	
	LastOnline :string,
	IsOnline :boolean,
	LastLocation: string,	
	PlaceId: number,
	GameId:	string,
	LocationType: number	
}

export type HttpRequestOptions = {
	Url: string	,
	Method: string?,
	Headers: dictionary<string,string>?,
	Body: string?
}

export type HttpResponse = {
	Success: boolean,
	StatusCode:	number,	
	StatusMessage: string,	
	Headers: dictionary<string,string>,
	Body: any
}

export type ChatMessageConfig = {
	Text: string,
	Color: Color3?,	
	Font: Enum.Font?,	
	TextSize: number?,
}

export type NotificationConfig = {
	Title: string,
	Text: string,
	Icon: string?,
	Duration: number?,
	Callback: BindableFunction?,
	Button1: string?,
	Button2: string?
}

export type AgentParameters = {
	AgentRadius: number?,
	AgentHeight: number?,
	AgentCanJump: boolean?,
	WaypointSpacing: number?,
	Costs: dictionary<string,number>?
}

export type BoundActionInfo = {
	stackOrder:number,
	priorityLevel:number,
	createTouchButton:boolean,
	inputTypes:{Enum.UserInputType | Enum.KeyCode},
	description:string?,
	title:string?,
	image:string?,
}

export type ExtraData = {
	ChatColor: Color3,
	NameColor: string,
	Font: Enum.Font,
	TextSize: number,
	Tags: array<string>, 
}

export type CustomRBXScriptSignal<Arguments> = {
	Wait: (CustomRBXScriptSignal<Arguments>) -> (),
	Connect: (CustomRBXScriptSignal<Arguments>,(Arguments: any) -> ()) -> (RBXScriptConnection),
	ConnectParallel: (CustomRBXScriptSignal<Arguments>,(Arguments: any) -> ()) -> (RBXScriptConnection)
}

export type ChatService = {
	AddChannel: (ChatService: ChatService, channelName: string) -> (ChatChannel),
	RemoveChannel: (ChatService: ChatService, channelName: string) -> (),
	GetChannel: (ChatService: ChatService, channelName: string) -> (ChatChannel),
	AddSpeaker: (ChatService: ChatService, speakerName: string) -> (ChatSpeaker),
	RemoveSpeaker: (ChatService: ChatService, speakerName: string) -> (),
	GetSpeaker: (ChatService: ChatService, speakerName: string) -> (ChatSpeaker),
	GetChannelList: (ChatService: ChatService) -> (array<string>),
	GetAutoJoinChannelList: (ChatService: ChatService) -> (array<string>),
	RegisterFilterMessageFunction: (ChatService: ChatService, functionId: string, func: (speaker: string, messageObject: ChatMessage, channelName: string) -> ()) -> (),
	UnregisterFilterMessageFunction: (ChatService: ChatService, functionId: string) -> (),
	RegisterProcessCommandsFunction: (ChatService: ChatService, functionId: string, func: (speakerName: string, message: string, channelName: string) -> ()) -> (),
	UnregisterProcessCommandsFunction: (ChatService: ChatService, functionId: string) -> (),
	ChannelAdded: RBXScriptSignal,
	ChannelRemoved: RBXScriptSignal,
	SpeakerAdded: RBXScriptSignal,
	SpeakerRemoved: RBXScriptSignal,
}

export type ChatMessage = {
	ID: number,
	FromSpeaker: string,
	OriginalChannel: string,
	IsFiltered: boolean,
	MessageLength: number,
	Message: string,
	MessageType: string,--"Message", "System", "MeCommand", "Welcome", "SetCore", "Whisper
	Time: number,
	ExtraData: ExtraData
}

export type ChatChannel = {
	Name: string,
	WelcomeMessage: string,
	Joinable: boolean,
	Leavable: boolean,
	AutoJoin: boolean,
	Private: boolean,
	KickSpeaker:  (speakerName: string, reason: string?) -> (),
	MuteSpeaker: (speakerName: string, reason: string?, duration: number?) -> (),
	UnmuteSpeaker: (speakerName: string) -> (),
	IsSpeakerMuted: (speakerName: string) -> (boolean),
	GetSpeakerList: () -> (array<string>),
	SendSystemMessage: (message: string),
	RegisterFilterMessageFunction: (functionId: string, func: (speaker: string, messageObject: ChatMessage, channelName: string) -> ()) ->(),
	UnregisterProcessCommandsFunction: (functionId: string) -> (),
	MessagePosted: RBXScriptSignal,
	SpeakerJoined: RBXScriptSignal,
	SpeakerLeft: RBXScriptSignal,
	SpeakerMuted: RBXScriptSignal,
	SpeakerUnmuted: RBXScriptSignal,
}

export type ChatSpeaker = {
	Name: string,
	JoinChannel: (ChatSpeaker, channelName: string) -> (),
	LeaveChannel: (ChatSpeaker, channelName: string) -> (),
	GetChannelList: (ChatSpeaker) -> ({channelName: string}),
	IsInChannel: (ChatSpeaker, channelName: string) -> (boolean),
	SayMessage: (ChatSpeaker, message: string,channelName: string,extraData: ExtraData?) -> (ChatMessage),
	SendMessage: (ChatSpeaker, mesage: string,channelName: string,fromSpeaker: string) -> (),	
	SendSystemMessage: (ChatSpeaker, messsage: string,channelName: string) -> (),	
	GetPlayer: (ChatSpeaker) -> (Player?),	
	SetExtraData: (ChatSpeaker, key: string,data: ExtraData) -> (),	
	GetExtraData: (ChatSpeaker) -> (),
	SetMainChannel: (ChatSpeaker, channelName: string) -> (nil),
	SaidMessage: RBXScriptSignal,
	ReceivedMessage: RBXScriptSignal,
	ReceivedSystemMessage: RBXScriptSignal,
	ChannelJoined: RBXScriptSignal,
	ChannelLeft: RBXScriptSignal,
	Muted: RBXScriptSignal,
	Unmuted: RBXScriptSignal,
	ExtraDataUpdated: RBXScriptSignal,
	MainChannelSet: RBXScriptSignal,
}

return Types

Example of use in action

Scripting Templates

This is probably helpful for people who already know what they want to do. I have only created a couple of templates. Most of the template are Classes. I am out of ideas for new templates. If you have any leave your idea down below.

ServerToolScript
--!strict
-- Services
local Players = game:GetService("Players")

-- References
local Tool: Tool = script.Parent

-- Variable
local ACTIVATED_Connection: RBXScriptConnection = nil

-- Events
Tool.Equipped:Connect(function()-- Connect events after equip
	local Character = Tool.Parent
	local Player: Player? = Players:GetPlayerFromCharacter(Character)
	if Player then
		ACTIVATED_Connection = Tool.Activated:Connect(function()
			
			Tool.Deactivated:Wait()
			
		end)
		
		Tool.Unequipped:Wait() -- Disconnect events after unequip
		ACTIVATED_Connection:Disconnect()
	end
end)
ClientToolScript
--!strict
-- Services
local UserInputService = game:GetService("UserInputService")
local ContextActionService = game:GetService("ContextActionService")
local Players = game:GetService("Players")

-- References
local Tool: Tool = script.Parent
local Player = Players.LocalPlayer

-- Variable
local LMB_Connection: RBXScriptConnection = nil
local RMB_Connection: RBXScriptConnection = nil

-- Events
Tool.Equipped:Connect(function(mouse: Mouse)-- Connect events after equip
	LMB_Connection = mouse.Button1Down:Connect(function()
		local Origin: CFrame = mouse.Origin
		
		mouse.Button1Up:Wait()
		
	end)
	RMB_Connection = mouse.Button2Down:Connect(function()
		
		mouse.Button2Up:Wait()
		
	end)
	Tool.Unequipped:Wait() -- Disconnect events after unequip
	LMB_Connection:Disconnect()
	RMB_Connection:Disconnect()
end)
Object-Oriented-Programming Classes

Here is a list of different OOP methods I found on the devforum whilst making this imply copy and paste the templatess

Class
local Class = {}

Class._index = Class

function Class.new()
	local self = setmetatable(
		{
		
		},
		Class
	)
	return self
end

Class.Do = function(self)
	
end

return Class

ClosureClass
local ClosureClass = {}

function ClosureClass.new()
	local self = {
		
	}
	
	local function Do()
		
	end
	
	return {
		Do = Do
	}
end

return ClosureClass
ProxyClass
local ClassMain = {}
local ClassProxy = {}
local ClassMeta = {}

local ChangedEvent = Instance.new("BindableEvent")
ClassProxy.Changed = ChangedEvent.Event 

ClassMeta.__index = ClassProxy
ClassMeta.__newindex = function(t, i, v)
	if t[i] ~= v then
		ClassProxy[i] = v
		ChangedEvent:Fire(i, v) 
	end
end

function ClassMain.new(self)
	self = setmetatable(ClassMain,ClassMeta)
	
	return self
end


return ClassProxy
NewConstructorClass
local Class = {}
Class.__index = Class

local function new()
	local self = {
		
	}
	
	return setmetatable(self, Class)
end

setmetatable(Class, {__call = new})

function Class:Do()
	
end

return Class
ChatModule
-- Type needed
local Types = require(script.Parent.Types)
type ChatService = Types.ChatService
local function Run(ChatService: ChatService)
	
end
return Run

Module

Other misc modules. Planning to add a simple data handler module.

MaidModule
local Maid = {}

function Maid.new()
	local self = {
		_tasks = {},
	}
	setmetatable(self, Maid)

	return self
end

function Maid:__index(key)
	return Maid[key] or self._tasks[key]
end

function Maid:__newindex(key, newTask)
	if Maid[key] then
		error(string.format("Cannot use %q as a Maid key", tostring(key)))
	end
	local tasks = self._tasks
	local oldTask = tasks[key]
	tasks[key] = newTask

	if oldTask then
		Maid.cleanupTask(oldTask)
	end
end

function Maid:give(task)
	local tasks = self._tasks
	tasks[#tasks+1] = task
end

function Maid.cleanupTask(task)
	local taskTy = typeof(task)
	if taskTy == 'function' then
		task()
	elseif taskTy == 'RBXScriptConnection' then
		task:Disconnect()
	elseif taskTy == 'Instance' then
		task:Destroy()
	elseif task.Destroy then
		task:Destroy()
	elseif task.destroy then
		task:destroy()
	elseif task.disconnect then
		task:disconnect()
	else
		error("Unable to cleanup unknown task")
	end
end

function Maid:clean()
	local tasks = self._tasks

	for key,task in pairs(tasks) do
		if typeof(task) == 'RBXScriptConnection' then
			tasks[key] = nil
			task:Disconnect()
		end
	end

	local index, task = next(tasks)
	while task ~= nil do
		tasks[index] = nil
		Maid.cleanupTask(task)
		index, task = next(tasks)
	end
end

Maid.destroy = Maid.clean
Maid.Destroy = Maid.clean

return Maid
Stack
Stack = {}
Stack.__index = Stack
function Stack.new() return setmetatable({}, Stack) end
 
-- put a new object onto a stack
function Stack:push(input)
	self[#self+1] = input
end
-- take an object off a stack
function Stack:pop()
	assert(#self > 0, "Stack underflow")
	local output = self[#self]
	self[#self] = nil
	return output
end
Ragdoll
RagdollModule
local CollectionService = game:GetService("CollectionService")
local Players = game:GetService("Players")

local RigTypes = require(script.RigTypes)

local RAGDOLLED_TAG = "__Ragdoll_Active"

local function ragdoll(model, humanoid)
	assert(humanoid:IsDescendantOf(model))
	if CollectionService:HasTag(model, RAGDOLLED_TAG) then
		return
	end
	CollectionService:AddTag(model, RAGDOLLED_TAG)

	-- Turn into loose body:
	humanoid:ChangeState(Enum.HumanoidStateType.Physics)

	-- Instantiate BallSocketConstraints:
	local attachments = RigTypes.getAttachments(model, humanoid.RigType)
	for name, objects in pairs(attachments) do
		local parent = model:FindFirstChild(name)
		if parent then
			local constraint = Instance.new("BallSocketConstraint")
			constraint.Name = "RagdollBallSocketConstraint"
			constraint.Attachment0 = objects.attachment0
			constraint.Attachment1 = objects.attachment1
			constraint.LimitsEnabled = true
			constraint.UpperAngle = objects.limits.UpperAngle
			constraint.TwistLimitsEnabled = true
			constraint.TwistLowerAngle = objects.limits.TwistLowerAngle
			constraint.TwistUpperAngle = objects.limits.TwistUpperAngle
			constraint.Parent = parent
		end
	end

	-- Instantiate NoCollisionConstraints:
	local parts = RigTypes.getNoCollisions(model, humanoid.RigType)
	for _, objects in pairs(parts) do
		local constraint = Instance.new("NoCollisionConstraint")
		constraint.Name = "RagdollNoCollisionConstraint"
		constraint.Part0 = objects[1]
		constraint.Part1 = objects[2]
		constraint.Parent = objects[1]
	end

	-- Destroy all regular joints:
	for _, motor in pairs(model:GetDescendants()) do
		if motor:IsA("Motor6D") then
			motor:Destroy()
		end
	end
end

return ragdoll

RigTypes
local RigTypes = {}

local HEAD_LIMITS = {
	UpperAngle = 60;
	TwistLowerAngle = -60;
	TwistUpperAngle = 60;
}

local LOWER_TORSO_LIMITS = {
	UpperAngle = 20;
	TwistLowerAngle = -30;
	TwistUpperAngle = 60;
}

local HAND_FOOT_LIMITS = {
	UpperAngle = 10;
	TwistLowerAngle = -10;
	TwistUpperAngle = 10;
}

local ELBOW_LIMITS = {
	UpperAngle = 30;
	TwistLowerAngle = 0;
	TwistUpperAngle = 120;
}

local KNEE_LIMITS = {
	UpperAngle = 30;
	TwistLowerAngle = -120;
	TwistUpperAngle = 0;
}

local SHOULDER_LIMITS = {
	UpperAngle = 60;
	TwistLowerAngle = -60;
	TwistUpperAngle = 175;
}

local HIP_LIMITS = {
	UpperAngle = 40;
	TwistLowerAngle = -5;
	TwistUpperAngle = 150;
}

local R6_HEAD_LIMITS = {
	UpperAngle = 30;
	TwistLowerAngle = -60;
	TwistUpperAngle = 60;
}

local R6_SHOULDER_LIMITS = {
	UpperAngle = 90;
	TwistLowerAngle = -30;
	TwistUpperAngle = 175;
}

local R6_HIP_LIMITS = {
	UpperAngle = 60;
	TwistLowerAngle = -5;
	TwistUpperAngle = 120;
}

local function createJointData(attach0, attach1, limits)
	assert(attach0)
	assert(attach1)
	assert(limits)
	assert(limits.UpperAngle >= 0)
	assert(limits.TwistLowerAngle <= limits.TwistUpperAngle)

	return {
		attachment0 = attach0,
		attachment1 = attach1,
		limits = limits
	}
end

local function find(model)
	return function(first, second, limits)
		local part0 = model:FindFirstChild(first[1])
		local part1 = model:FindFirstChild(second[1])
		if part0 and part1 then
			local attach0 = part0:FindFirstChild(first[2])
			local attach1 = part1:FindFirstChild(second[2])
			if attach0 and attach1 and attach0:IsA("Attachment") and attach1:IsA("Attachment") then
				return createJointData(attach0, attach1, limits)
			end
		end
	end
end

function RigTypes.getNoCollisions(model, rigType)
	if rigType == Enum.HumanoidRigType.R6 then
		return RigTypes.getR6NoCollisions(model)
	elseif rigType == Enum.HumanoidRigType.R15 then
		return RigTypes.getR15NoCollisions(model)
	else
		return {}
	end
end

-- Get list of attachments to make ballsocketconstraints between:
function RigTypes.getAttachments(model, rigType)
	if rigType == Enum.HumanoidRigType.R6 then
		return RigTypes.getR6Attachments(model)
	elseif rigType == Enum.HumanoidRigType.R15 then
		return RigTypes.getR15Attachments(model)
	else
		return {}
	end
end

function RigTypes.getR6Attachments(model)
	local rightLegAttachment = Instance.new("Attachment")
	rightLegAttachment.Name = "RagdollRightLegAttachment"
	rightLegAttachment.Position = Vector3.new(0, 1, 0)
	rightLegAttachment.Parent = model:FindFirstChild("Right Leg")

	local leftLegAttachment = Instance.new("Attachment")
	leftLegAttachment.Name = "RagdollLeftLegAttachment"
	leftLegAttachment.Position = Vector3.new(0, 1, 0)
	leftLegAttachment.Parent = model:FindFirstChild("Left Leg")

	local torsoLeftAttachment = Instance.new("Attachment")
	torsoLeftAttachment.Name = "RagdollTorsoLeftAttachment"
	torsoLeftAttachment.Position = Vector3.new(-0.5, -1, 0)
	torsoLeftAttachment.Parent = model:FindFirstChild("Torso")

	local torsoRightAttachment = Instance.new("Attachment")
	torsoRightAttachment.Name = "RagdollTorsoRightAttachment"
	torsoRightAttachment.Position = Vector3.new(0.5, -1, 0)
	torsoRightAttachment.Parent = model:FindFirstChild("Torso")

	local headAttachment = Instance.new("Attachment")
	headAttachment.Name = "RagdollHeadAttachment"
	headAttachment.Position = Vector3.new(0, -0.5, 0)
	headAttachment.Parent = model:FindFirstChild("Head")

	local leftArmAttachment = Instance.new("Attachment")
	leftArmAttachment.Name = "RagdollLeftArmAttachment"
	leftArmAttachment.Position = Vector3.new(0.5, 1, 0)
	leftArmAttachment.Parent = model:FindFirstChild("Left Arm")

	local ragdollRightArmAttachment = Instance.new("Attachment")
	ragdollRightArmAttachment.Name = "RagdollRightArmAttachment"
	ragdollRightArmAttachment.Position = Vector3.new(-0.5, 1, 0)
	ragdollRightArmAttachment.Parent = model:FindFirstChild("Right Arm")

	local query = find(model)

	return {
		Head = query(
			{"Torso", "NeckAttachment"},
			{"Head", "RagdollHeadAttachment"},
			R6_HEAD_LIMITS),
		["Left Arm"] = query(
			{"Torso", "LeftCollarAttachment"},
			{"Left Arm", "RagdollLeftArmAttachment"},
			R6_SHOULDER_LIMITS),
		["Right Arm"] = query(
			{"Torso", "RightCollarAttachment"},
			{"Right Arm", "RagdollRightArmAttachment"},
			R6_SHOULDER_LIMITS),
		["Left Leg"] = createJointData(torsoLeftAttachment, leftLegAttachment, R6_HIP_LIMITS),
		["Right Leg"] = createJointData(torsoRightAttachment, rightLegAttachment, R6_HIP_LIMITS),
	}
end

function RigTypes.getR15Attachments(model)
	local query = find(model)

	return {
		Head = query(
			{"UpperTorso", "NeckRigAttachment"},
			{"Head", "NeckRigAttachment"},
			HEAD_LIMITS),

		LowerTorso = query(
			{"UpperTorso", "WaistRigAttachment"},
			{"LowerTorso", "WaistRigAttachment"},
			LOWER_TORSO_LIMITS),

		LeftUpperArm = query(
			{"UpperTorso", "LeftShoulderRigAttachment"},
			{"LeftUpperArm", "LeftShoulderRigAttachment"},
			SHOULDER_LIMITS),
		LeftLowerArm = query(
			{"LeftUpperArm", "LeftElbowRigAttachment"},
			{"LeftLowerArm", "LeftElbowRigAttachment"},
			ELBOW_LIMITS),
		LeftHand = query(
			{"LeftLowerArm", "LeftWristRigAttachment"},
			{"LeftHand", "LeftWristRigAttachment"},
			HAND_FOOT_LIMITS),

		RightUpperArm = query(
			{"UpperTorso", "RightShoulderRigAttachment"},
			{"RightUpperArm", "RightShoulderRigAttachment"},
			SHOULDER_LIMITS),
		RightLowerArm = query(
			{"RightUpperArm", "RightElbowRigAttachment"},
			{"RightLowerArm", "RightElbowRigAttachment"},
			ELBOW_LIMITS),
		RightHand = query(
			{"RightLowerArm", "RightWristRigAttachment"},
			{"RightHand", "RightWristRigAttachment"},
			HAND_FOOT_LIMITS),

		LeftUpperLeg = query(
			{"LowerTorso", "LeftHipRigAttachment"},
			{"LeftUpperLeg", "LeftHipRigAttachment"},
			HIP_LIMITS),
		LeftLowerLeg = query(
			{"LeftUpperLeg", "LeftKneeRigAttachment"},
			{"LeftLowerLeg", "LeftKneeRigAttachment"},
			KNEE_LIMITS),
		LeftFoot = query(
			{"LeftLowerLeg", "LeftAnkleRigAttachment"},
			{"LeftFoot", "LeftAnkleRigAttachment"},
			HAND_FOOT_LIMITS),

		RightUpperLeg = query(
			{"LowerTorso", "RightHipRigAttachment"},
			{"RightUpperLeg", "RightHipRigAttachment"},
			HIP_LIMITS),
		RightLowerLeg = query(
			{"RightUpperLeg", "RightKneeRigAttachment"},
			{"RightLowerLeg", "RightKneeRigAttachment"},
			KNEE_LIMITS),
		RightFoot = query(
			{"RightLowerLeg", "RightAnkleRigAttachment"},
			{"RightFoot", "RightAnkleRigAttachment"},
			HAND_FOOT_LIMITS),
	}
end

function RigTypes.getR6NoCollisions(model)
	local list = {}

	local function addPair(pair)
		local part0 = model:FindFirstChild(pair[1])
		local part1 = model:FindFirstChild(pair[2])

		if part0 and part1 then
			table.insert(list, {part0, part1})
		end
	end

	addPair({"Head", "Torso"})
	addPair({"Left Arm", "Torso"})
	addPair({"Right Arm", "Torso"})
	addPair({"Left Leg", "Torso"})
	addPair({"Right Leg", "Torso"})

	addPair({"Left Leg", "Right Leg"})

	return list
end


function RigTypes.getR15NoCollisions(model)
	local list = {}

	local function addPair(pair)
		local part0 = model:FindFirstChild(pair[1])
		local part1 = model:FindFirstChild(pair[2])

		if part0 and part1 then
			table.insert(list, {part0, part1})
		end
	end

	addPair({"Head", "UpperTorso"})
	addPair({"UpperTorso", "LowerTorso"})

	addPair({"UpperTorso", "LeftUpperArm"})
	addPair({"LowerTorso", "LeftUpperArm"})
	addPair({"LeftUpperArm", "LeftLowerArm"})
	addPair({"LeftLowerArm", "LeftHand"})
	addPair({"LeftUpperArm", "LeftHand"})

	addPair({"UpperTorso", "RightUpperArm"})
	addPair({"LowerTorso", "RightUpperArm"})
	addPair({"RightUpperArm", "RightLowerArm"})
	addPair({"RightLowerArm", "RightHand"})
	addPair({"RightUpperArm", "RightHand"})

	addPair({"LeftUpperLeg", "RightUpperLeg"})

	addPair({"UpperTorso", "RightUpperLeg"})
	addPair({"LowerTorso", "RightUpperLeg"})
	addPair({"RightUpperLeg", "RightLowerLeg"})
	addPair({"RightLowerLeg", "RightFoot"})
	addPair({"RightUpperLeg", "RightFoot"})

	addPair({"UpperTorso", "LeftUpperLeg"})
	addPair({"LowerTorso", "LeftUpperLeg"})
	addPair({"LeftUpperLeg", "LeftLowerLeg"})
	addPair({"LeftLowerLeg", "LeftFoot"})
	addPair({"LeftUpperLeg", "LeftFoot"})

	-- Support weird R15 rigs
	addPair({"UpperTorso", "LeftLowerLeg"})
	addPair({"UpperTorso", "RightLowerLeg"})
	addPair({"LowerTorso", "LeftLowerLeg"})
	addPair({"LowerTorso", "RightLowerLeg"})

	addPair({"UpperTorso", "LeftLowerArm"})
	addPair({"UpperTorso", "RightLowerArm"})

	local upperTorso = model:FindFirstChild("UpperTorso")
	if upperTorso and upperTorso.Size.x <= 1.5 then
		addPair({"Head", "LeftUpperArm"})
		addPair({"Head", "RightUpperArm"})
	end

	return list
end

return RigTypes
Dataset
local Set = {}
Set.__index = Set

-- Function to construct a set from an optional list of items
function Set.new(items)
	local newSet = {}
	for key, value in ipairs(items or {}) do
		newSet[value] = true
	end
	return setmetatable(newSet, Set)
end

-- Function to add an item to a set
function Set:add(item)
	self[item] = true
end

-- Function to remove an item from a set
function Set:remove(item)
	self[item] = nil
end

-- Function to check if a set contains an item
function Set:contains(item)
	return self[item] == true
end

-- Function to output set as a comma-delimited list for debugging
function Set:output()
	local elems = {}
	for key, value in pairs(self) do
		table.insert(elems, tostring(key))
	end
	print(table.concat(elems, ", "))
end

function Set.getIntersection(set1, set2)
	local result = Set.new()
	for key, value in pairs(set1) do
		if set2:contains(key) then
			result:add(key)
		end
	end
	return result
end

function Set:__add(otherSet)
	local result = Set.new()
	for entry in pairs(self) do
		result[entry] = true
	end
	for entry in pairs(otherSet) do
		result[entry] = true
	end
	return result
end

function Set:__sub(otherSet)
	local result = Set.new()
	for entry in pairs(self) do
		result[entry] = true
	end
	for entry in pairs(otherSet) do
		result[entry] = nil
	end
	return result
end

return Set
Smooth Camera
-- Services
local ContextActionService = game:GetService("ContextActionService")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local GameSettings = UserSettings().GameSettings
local RenderSettings = game:GetService("RenderSettings")

-- References
local Players = game:GetService("Players")
local Camera = workspace.CurrentCamera
local Player = Players.LocalPlayer

-- Objects
local SmoothCamera = {
	CameraSubject = nil;
	CameraRotation = {
		X = 0,
		Y = 45
	};
	CameraMaxZoomDistance = 50;
	CameraMinZoomDistance = 0.1;
	CameraDistance = 25;
	LockToCharacter = true,
	SmoothSpeed = 3
}


-- Constants

local sin, cos, rad = math.sin, math.cos, math.rad
local clamp, abs = math.clamp, math.abs
local Vector2new =  Vector2.new
local Vector3new = Vector3.new

local min, max = math.min, math.max

local RandomShake = Random.new()

-- Variables
local SubjectPosition = Vector3new()
local PointerEvent: RBXScriptConnection = nil
local CacheCFrame: CFrame = nil
local Fullscreen: boolean = GameSettings:InFullScreen()
local QualityLevel: number = GameSettings.SavedQualityLevel.Value

local CameraRotationMatrix = CFrame.new()
local qW = 30 -- Quaternion rotation speed
local S = 1

local ShakeType = nil

-- Functions
local function UpdateCamera(DeltaTime)
	local MouseDelta = UserInputService:GetMouseDelta() * -GameSettings.MouseSensitivity
	SmoothCamera.CameraRotation.X += MouseDelta.X
	SmoothCamera.CameraRotation.Y += MouseDelta.Y
	
	-- UpValue S prevents camera from moving past clamp
	--[[if abs(SmoothCamera.CameraRotation.Y) > 1000 then
		S = 0
		SmoothCamera.CameraRotation.Y = clamp(SmoothCamera.CameraRotation.Y,-1000,1000)
	else 
		S = 1
	end]]
	-- Rotate Camera on Y Axis
	CameraRotationMatrix *= (CFrame.new(0,0,0,rad(MouseDelta.Y * S),0,0,qW))
	-- Rotate Camera on Z Axis
	--CameraRotationMatrix *= (CFrame.Angles(0,rad(MouseDelta.X / 16),0))
	CameraRotationMatrix = (CFrame.Angles(0,rad(MouseDelta.X / 16),0) * CameraRotationMatrix)

	if SmoothCamera.LockToCharacter then
		CacheCFrame = SmoothCamera.CameraSubject.CFrame:ToWorldSpace(CameraRotationMatrix)
	else
		CacheCFrame = CameraRotationMatrix + SmoothCamera.CameraSubject.Position
	end
	-- Lerp Camera Target
	if SmoothCamera.CameraSubject.Position == SmoothCamera.CameraSubject.Position then
		SubjectPosition = SubjectPosition:Lerp(
			SmoothCamera.CameraSubject.Position,
			min(0.5,DeltaTime * SmoothCamera.SmoothSpeed*3)--30
		)
	end
	-- Lerp Camera Position and UpVector
	Camera.CFrame =  CFrame.lookAt( 
		Camera.CFrame.Position:Lerp(
			CacheCFrame.Position+(CacheCFrame.LookVector * -SmoothCamera.CameraDistance),
			min(0.5,DeltaTime * SmoothCamera.SmoothSpeed*2)--20
		),
		SubjectPosition,
		Camera.CFrame.UpVector:Lerp(
			CacheCFrame.UpVector,
			min(0.5,DeltaTime * SmoothCamera.SmoothSpeed)--10
		)
	)
end

function ShakeCamera(DeltaTime)
	Camera.CFrame += Vector3.new(RandomShake:NextNumber(-2,2),RandomShake:NextNumber(-2,2),0)
end

function SmoothCamera:Init(LockToCharacter: boolean?)
	SmoothCamera.LockToCharacter = LockToCharacter or false
	RunService:BindToRenderStep("SmoothCameraUpdate",Enum.RenderPriority.Camera.Value,UpdateCamera)
	PointerEvent = UserInputService.PointerAction:Connect(function(Wheel,Pan,Pinch,GameProcessedEvent)
		SmoothCamera.CameraDistance = clamp(
			SmoothCamera.CameraDistance - (Wheel + Pinch) * GameSettings.MouseSensitivity * 5,
			SmoothCamera.CameraMinZoomDistance,
			SmoothCamera.CameraMaxZoomDistance
		) 
	end)
end

function SmoothCamera:Term()
	RunService:UnbindFromRenderStep("SmoothCameraUpdate")
	PointerEvent:Disconnect()
end

function SmoothCamera:Shake(Duration: number)--,Type: number?)
	--[[ Types: -- Not implemented yet
	* UpDown
	* LeftRight
	* All
	* Rotate
	* InOut
	]]
	RunService:BindToRenderStep("Shake",Enum.RenderPriority.Camera.Value + 1,ShakeCamera)
	task.delay(Duration,RunService.UnbindFromRenderStep,RunService,"Shake")
end

-- Events

-- Init



return SmoothCamera

Method

This module is a collection of methods and functions that I found useful to me whilst scripting. If you have any thing more to add to this please leve it in the bottom or dm me on discord.

Method

local newVector3 = Vector3.new
local floor = math.floor

local Methods = {
	import = function(self, Table: {any})
		local Environment = getfenv(0)
		for Key, Value in ipairs(Table) do
			Environment[tostring(Key)] = Value
		end
		return
	end,
	roundToNearest = function(self, number: number, roundTo: number)
		local x = number + (roundTo / 2)
		return x - (number % roundTo)
	end,
	nestedToFlat3 = function(self, position: Vector3,size: Vector3)
		return (position.X) + ((position.Y - 1) * size.X) + ((position.Z - 1) * size.X * size.Y)
	end,
	flatToNested3 = function(self, i: number, size: Vector3)
		return newVector3(
			i % size.X,
			floor((i / size.X) % size.Y + 1),
			floor((i / size.Z + size.Z)/size.Y )
		) 
	end,
	lerp = function(self, x: number, y: number, alpha: number)
		return alpha * (y - x) + x
	end,
	factorial = function(self, n)
		if n <= 1 then -- base case
			return 1
		else
			return n * self.factorial(n-1) -- recursive step
		end
	end,
	createNestedArray = function(self, dimensions: number, defaultValue: any?,...:number)
		local sizes = {...}
		local array = {}
		if dimensions < 2 then
			error("Nested arrays require 2 or more dimensions.")
		end
		for x = 1, sizes[1] do
			if dimensions == 2 then
				array[x] = {}
			else
				table.remove(sizes,1)
				array[x] = self.createNestedArray(dimensions - 1,table.unpack(sizes))
			end
		end	
		
		return array
	end
	
}

return Methods

server link: TuVzJ9xuzJ

35 Likes

To help, NewConstructorClass code block has a method with PascalCase. That’s inconsistent with the rest of the code blocks.

4 Likes

It should be noted that modifying the Luau function environment table will disable Luau optimizations

5 Likes

What’s that for? The shorthand {string} can be used in place of {[number]:string}.

1 Like

It’s possible he wrote this a while ago back when this wasn’t available, this is slightly recent.

1 Like

I see. Most of the code is actually sourced from random places I found on the developer hub and forum. So, there probably are some inconsistencies.

Also, @Expertcoderz yes. You are right. However I typed the array<> like that because the documentation on the DevHub does. Since I don’t really want to go through the code and change every array to {V}. I could use replace but I guess I am just too lazy

2 Likes

Hey there!
This post seems like it could be useful for a lot of newer developers, but generally it just seems way too inconsistent and complicated. A lot of the people who can fully understand the concepts used in these would already have enough experience to make their own versions of scripts like this. Also, to be quite honest, there really isn’t any functionality for the modules that you have supplied, and the modules that could be useful seem very confusing to customize and modify. It’s quite confusing to look at, even though I’ve personally used OOP on ROBLOX for most of my projects.

Modules/Utilities
For example, the “ChatModule” doesn’t have any functionality to it, so it really doesn’t have a usage. It’s not really clear what it would be used for either since its not documented.

There are, for sure, some very useful modules and scripts provided here, but they don’t really have any documentation or explanation. This is prominent in the utility modules provided at the end of the post, such as “Maid” and “Ragdoll”. There is no explanation for the two modules/scripts, so only the people who have actually used them before will be able to understand what they are used for. Also, if I’m not wrong, haven’t those modules been posted already? I remember seeing a Maid module somewhere, and if so, you should really credit the original creator because it seems as though you are taking credit for this clearly incredibly useful module.

Server-Sided Tool Script
The server-sided tool management script just seems overly complicated. The main script connection for the tool being equipped doesn’t need to yield for the unequipped event to be called, as you can simply connect another callback function to the “Tool.Unequipped” event to do the exact same logic.

Class Module
Overall, a lot of the examples just seem to be incredibly complicated for seemingly no reason, and aren’t easily usable and understandable by newer developers. An example of this can be seen in the “Class” module, which uses two different formats to define functions for really no reason.

-- Format #1
function Class.new()
	local self = setmetatable(
		{
		
		},
		Class
	)
	return self
end

-- Format #2
Class.Do = function(self)

end

It makes it very confusing to read, and it doesn’t provide the most efficient and understandable example for newer developers who don’t necessarily fully understand OOP. The function has an argument named “self” (which people who have used OOP with lua before can immediately recognize), but it doesn’t provide an explanation for what this argument is and what it can be used for. Also, the “setmetatable” function being called is using multiple lines for really no reason, and can be easily simplified to the following:

local self = setmetatable({}, Class)

return self

Conclusion
Overall, this post does seem helpful in many ways, but I just personally feel as though the inconsistencies and lack of explanations could do more harm than good for newer developers. Please do correct me if you see any issues in my post, as I may not have completely understood the topic or parts of it. :sweat_smile:

Regardless, have a wonderful day! :wave:

3 Likes

the module is so over-complicated that it’s not even worth crediting. Maid (non-roblox-developer) edition:

local function CleanupTrash(Trash)
   local Type = typeof(Trash)
   if Type == "RBXScriptConnection" do
      task:Disconnect()
   elseif Type == "Instance" do
      task:Destroy()
   end
end
return function(Table)
   for _, Trash in pairs(Table) do
      Cleanup(Trash)
   end
end

The whole idea of making it a class seems pointless.

Actually the maid module is really useful in terms of object-oriented programming. When you create a new class, you can define a maid in the “constructor” (definition) of your class and then use that to store all of the RBXScriptConnections. Also, it makes it very intuitive to use and you wouldn’t have to keep retyping the same code over and over every time you have to use it, since maids are primarily used to prevent memory leaks. Overall, it has a very commonly applicable concept and creating it as a class just makes it easier to understand and actually put into usage.

Also, as a side-note, even if it’s over-complicated, it doesn’t mean that it doesn’t deserve to be credited. That seems to be created by someone else, who does not get any form of credit for a module that really is useful and handles a common issue quite efficiently.

Have a great day! :wave:

2 Likes

Maid and Ragdoll modules are both undocumented so I will put some documentation.

They are also both not possible to credit as the author is not known.

The scripts I put here are simply if you don’t want to write it out yourself and/or if you can’t actually script the things. It’s really just a large collection of scripts that do random but common tasks.

The template scripts are not supposed to have functionality. The idea is that you don’t have to remember the exact for making a Chatmodule or a Class. You can simply copy and paste these scripts.

Yielding the thread allows us to think procedurally which is a lot easier to program in contrast to event based. You are welcome to add those events but it’s much better, easier and readable imo to have the thread yielded.

For example
Object.Start:Connect(function(arg)
    -- Code to do something
    Object.End:Wait()
    -- Code to stop doing something
end)

I prefer instead of:

local Arguments = {}
Object.Start:Connect(function(...)
    -- Code to do something
    Arguments = {...}
end)
Object.End:Connect(function() -- Sometimes arguments are not always passed in the "secondary" event and as such, needs to be stored in a higher scope
    -- Code to stop doing something
    Arguments = {}
end)

Okay then. I’ll see to it that everything here gets documented in the coming days so that people understand what these scripts are for and how they can be used.

Thanks for your feedback :slightly_smiling_face:

I like the ragdoll script as it is hard to find some working right now.
Thanks for this.

Update ! :nerd_face:

Added SmoothCamera Module as requested by @IamACoolGuy_1230

If you need full rotation simply comment the lines 57 to 63

I have also added a line 67 and 68 the option of switching between local rotation and global rotation in the horizontal axis. Note that global rotation is opposite when you are upside down. Also note that roblox’s default walk does not work as intended when camera is upside down. However it guaruantees that the camera will not end up tilted when set level with character.

I bumped this topic because I don’t think this smooth camera module is worth a topic.