Voicelines Module

Hello,

I just made a module that allows to play Voicelines similar to Team Fortress 2, Anomalous Activities and other games that use Voicelines.

You can create categories but don’t need to. Here is an example of how it could look like:
image
As you can see, there are Configurations and Folder and before I forget, you play anything related to the voicelines inside the Folder called “Packages”, this also where you place everything, the sort of reference.
The Cache is where the modules places the copies of the Packages folder.

How to use
The module has 1 setting as of right now, “AutomaticallyFillEmptyCache”.
AutomaticallyFillEmptyCache will, as the name suggest, automatically fill the Cache of a Package if there was no Sound to be found.
You require the module on the client side once, and if you want to other Players to hear the Voicelines/Sounds you require the module also once on the server side.
You can use Folder, or whatever you’d like to fill the Packages. Configurations represent a Voiceline, you place all Sound variations under it.
The sounds can have attributes such as “MinVolume”, “MaxVolume”, “MinSpeed” and “MaxSpeed” with numbers as values. When you use the PlayVoiceline function it will randomly pick a Volume (if Min- or MaxVolume is given) and the same counts for Speed (PlaybackSpeed).
If you add a PitchShiftSoundEffect or an AudioPitchShifter, you can also set attributes “MinOctave” and “MaxOctave”, both are numbers and work the same as the Volume and Speed attributes on the Sound.
It also doesn’t matter if the Packages are Folders or Models.
Your Humanoid needs an attribute called “VoicelinePackage” with a string as value, for the PlayVoiceline function.

API
Getting a Sound from the Cache via a Path/Table. It will return a Sound or nothing and give a warn message if.

module.GetSoundFromCache = function(voicelinePath : {}) : Sound?

Fills the Cache Folder and creates as many new Sounds/Voicelines as the amount variable. If the Packages table is nil, it will automaticly duplicate all Packages. And if the amount variable is nil it will create 1 and it will always be atleast 1. It will tho print into the output how many Cache Packages were filled out of how many it should have created.

module.FillCache = function(amount : number, packages : {string})

Playing a Voiceline/Sound from the Cache and placing it inside the Characters Head. It won’t return anything. The Characters Humanoid or Controller (Configuration) needs to have an attribute called “VoicelinePackage” (string) which will be the first index of the voiceline path to play a voiceline. The voiceline being played will be renamed to “Voiceline”. As soon as the sound stops playing, will it return into the Cache. It will randomly pick a Sound to play.

module.PlayVoiceline = function(character : Model, voicelinePath : {}, ignoreDeath : boolean, cancelOtherVoicelines : boolean, cooldown : number?)

Important Notes
I created this module for the simple purpose of playing voicelines, you can use it probably for music too if you want since it stops the previously playing “Voiceline”/Sound if you play another one.
This module is purely client sided, meaning it won’t play any sounds on the server side.
You CAN make NPC’s play voicelines and everything.
The module needs to be required on the server side ONCE for other Players to hear the voicelines from NPC’s and other Players. I’d recommend requiring in inside of the NPC Handler/Spawner and making it a global variable but it wont matter, the event will trigger multiply times tho telling the client multiply times to play the same event.
Try to keep the voiceline path table small since the module use repeat until to search for the Instances and it can affect performance if the path is too long.
“voicelineCancelAmount” can be a number or boolean, if its set to true it will stop all playing Voicelines of the character, if it’s false, it won’t stop any Voicelines and if it’s a number, it will stop as many Voicelines as the number starting with the first Voiceline played (if it hasn’t already stopped).

Usage Example

local currentVoicelinesPath = {}

function UpdateVoicelinesFrame()
	voicelinesContainer:ClearAllChildren()
	
	local newGrid = Instance.new("UIGridLayout")
	newGrid.CellPadding = UDim2.new()
	newGrid.CellSize = UDim2.new(1, -4, 0.1, 0)
	newGrid.FillDirection = Enum.FillDirection.Horizontal
	newGrid.SortOrder = Enum.SortOrder.Name
	newGrid.Parent = voicelinesContainer
	
	local currentPath = table.clone(currentVoicelinesPath)
	table.move(currentPath, 1, #currentPath, 2, currentPath)
	currentPath[1] = controller:GetAttribute("VoicelinePackage")
	
	local lastObject = ReplicatedStorage.Voicelines.Packages
	repeat
		lastObject = lastObject:FindFirstChild(currentPath[1])
		table.remove(currentPath, 1)
	until typeof(currentPath[1]) ~= "string" or typeof(lastObject) ~= "Instance"
	
	if typeof(lastObject) == "Instance" and lastObject:IsA("Folder") then
		local index = 0
		for i, object in ipairs(lastObject:GetChildren()) do
			if object:IsA("Configuration") then
				index += 1
				local newText = Instance.new("TextLabel")
				newText.Name = index
				newText.TextStrokeTransparency = 0
				newText.TextColor3 = Color3.fromRGB(255, 255, 100)
				newText.TextTransparency = 0
				newText.TextStrokeColor3 = Color3.fromRGB(0, 0, 0)
				newText.TextScaled = true
				newText.Font = Enum.Font.Sarpanch
				newText.BackgroundTransparency = 1
				newText.TextXAlignment = Enum.TextXAlignment.Left
				newText.Text = "[ " .. index .. " ] " .. object.Name
				newText.RichText = true
				newText.Parent = voicelinesContainer
			elseif object:IsA("Folder") then
				index += 1
				local newText = Instance.new("TextLabel")
				newText.Name = index
				newText.TextStrokeTransparency = 0
				newText.TextColor3 = Color3.fromRGB(255, 255, 255)
				newText.TextTransparency = 0
				newText.TextStrokeColor3 = Color3.fromRGB(0, 0, 0)
				newText.TextScaled = true
				newText.Font = Enum.Font.Sarpanch
				newText.BackgroundTransparency = 1
				newText.TextXAlignment = Enum.TextXAlignment.Left
				newText.Text = "[ " .. index .. " ] " .. object.Name
				newText.RichText = true
				newText.Parent = voicelinesContainer
			end
		end
	elseif typeof(lastObject) == "Instance" and lastObject:IsA("Configuration") then
		voicelinesFrame.Visible = false
		coroutine.wrap(voicelineModule.PlayVoiceline)(own_character, currentVoicelinesPath, false)
		table.clear(currentVoicelinesPath)
		table.clear(currentPath)
	else
		voicelinesFrame.Visible = false
		table.clear(currentVoicelinesPath)
		table.clear(currentPath)
	end
end

UserInputService.InputBegan:Connect(function(input, processed)
	if processed then
		return
	else
		if input.KeyCode == Enum.KeyCode.Q then
			if voicelinesFrame.Visible == true then
				voicelinesFrame.Visible = false
			else
				voicelinesFrame.Visible = true
				UpdateVoicelinesFrame()
			end
		end

		if input.KeyCode == Enum.KeyCode.One then
			if voicelinesFrame.Visible == true then
				local name = voicelinesContainer["1"].Text
				name = string.gsub(name, "%[ 1", "")
				name = string.gsub(name, " %] ", "")
				table.insert(currentVoicelinesPath, name)
				UpdateVoicelinesFrame()
			end
		end
		
		if input.KeyCode == Enum.KeyCode.Two then
			if voicelinesFrame.Visible == true then
				local name = voicelinesContainer["2"].Text
				name = string.gsub(name, "%[ 2", "")
				name = string.gsub(name, " %] ", "")
				table.insert(currentVoicelinesPath, name)
				UpdateVoicelinesFrame()
			end
		end
		
		if input.KeyCode == Enum.KeyCode.Three then
			if voicelinesFrame.Visible == true then
				local name = voicelinesContainer["3"].Text
				name = string.gsub(name, "%[ 3", "")
				name = string.gsub(name, " %] ", "")
				table.insert(currentVoicelinesPath, name)
				UpdateVoicelinesFrame()
			end
		end
		
		if input.KeyCode == Enum.KeyCode.Four then
			if voicelinesFrame.Visible == true then
				local name = voicelinesContainer["4"].Text
				name = string.gsub(name, "%[ 4", "")
				name = string.gsub(name, " %] ", "")
				table.insert(currentVoicelinesPath, name)
				UpdateVoicelinesFrame()
			end
		end
		
		if input.KeyCode == Enum.KeyCode.Five then
			if voicelinesFrame.Visible == true then
				local name = voicelinesContainer["5"].Text
				name = string.gsub(name, "%[ 5", "")
				name = string.gsub(name, " %] ", "")
				table.insert(currentVoicelinesPath, name)
				UpdateVoicelinesFrame()
			end
		end
		
		if input.KeyCode == Enum.KeyCode.Six then
			if voicelinesFrame.Visible == true then
				local name = voicelinesContainer["6"].Text
				name = string.gsub(name, "%[ 6", "")
				name = string.gsub(name, " %] ", "")
				table.insert(currentVoicelinesPath, name)
				UpdateVoicelinesFrame()
			end
		end

		if input.KeyCode == Enum.KeyCode.Seven then
			if voicelinesFrame.Visible == true then
				local name = voicelinesContainer["7"].Text
				name = string.gsub(name, "%[ 7", "")
				name = string.gsub(name, " %] ", "")
				table.insert(currentVoicelinesPath, name)
				UpdateVoicelinesFrame()
			end
		end

		if input.KeyCode == Enum.KeyCode.Eight then
			if voicelinesFrame.Visible == true then
				local name = voicelinesContainer["8"].Text
				name = string.gsub(name, "%[ 8", "")
				name = string.gsub(name, " %] ", "")
				table.insert(currentVoicelinesPath, name)
				UpdateVoicelinesFrame()
			end
		end

		if input.KeyCode == Enum.KeyCode.Nine then
			if voicelinesFrame.Visible == true then
				local name = voicelinesContainer["9"].Text
				name = string.gsub(name, "%[ 9", "")
				name = string.gsub(name, " %] ", "")
				table.insert(currentVoicelinesPath, name)
				UpdateVoicelinesFrame()
			end
		end

		if input.KeyCode == Enum.KeyCode.Zero then
			if voicelinesFrame.Visible == true then
				local name = voicelinesContainer["10"].Text
				name = string.gsub(name, "%[ 10", "")
				name = string.gsub(name, " %] ", "")
				table.insert(currentVoicelinesPath, name)
				UpdateVoicelinesFrame()
			end
		end
	end
end

Here’s the module link:
Voiceline Module

(This is an unfinished edit as of right now)

8 Likes

No problem, I’m going to use it myself so I thought I could make it a resource since it isn’t bound to any of my other systems.

Ok, I believe I fixed all possible bugs, the current version of the module should work properly now

Could you make the example a little bit easier / simple to understand? My brain is having trouble understanding it.

1 Like

Which part exactly? The “path”?

How to get this to work in General actually since I’m not sure what I’m doing wrong with it and I can’t understand the example. :sweat:

1 Like

What does it say in the output?
You pass a table, for example:

{“Hi”, “Greet”}

Now, the characters humanoid needs an attribute called “VoicelinePackage”, which represents the name of a folder (Package) inside of the Packages folder.
The variables inside the table are strings (the value, not the index) and that represents the name of next object, so we are currently at the “Test” Package/Folder and the next object/string in line is “Hi”, the folder named “Hi” is now the current object, next is “Greet” so the current object is “Greet”, the Configuration.
This is where the function (module.GetSoundFromCache), which the module.PlayVoiceline function uses to get a Sound stops since the path table is now empty, and IF it’s a Configuration, it randomly picks a Sound from inside the Configuration, places it inside the Head of the character, stops the currently playing voiceline if there is one and plays the Sound. Of course all Sounds that stop return back into the Cache.
You need to fill the Cache to be able to play a Sound/Voiceline.

I’ve just fixed the FillCache function, if you have any requests, features you’d like to see or bugs, please tell me.

I tried doing something like that in my Melee weapon script it passes something about “nil” not being a folder or something I need to check again.

You don’t pass the Package Name, also try importing the newest version of the module, I’ve made some improvements.

Im not sure tho what it currently could be since I can’t look at the module as of right now, maybe show me the code snippet that uses the PlayVoiceline function and everything thats related to it

Yeah I’m pretty sure I’m using the newest version.

image

-- Other things are defined before this
local VoicelineModule = require(game.ReplicatedStorage.Voicelines)
local SfxTable = {"Attack"}

-- This is in a function
VoicelineModule.PlayVoiceline(Char, SfxTable, false)

image

You can delete the other Packages, they are just examples, did you set the humanoids attributes value “VoicelinePackage” to “JamesSunderland”, also try fill the Cache as soon as you require the module and only give the amount variable

1 Like

In the video, the text appears, disappears, and shifts around way too quickly! I’m unable to read it… so it can get in the way of immersion.

I just did it like in Anomalous Activities

Well the previous video file was too big (18 seconds).

came for the module
stayed forcefully due to brain frying
could you add a more simple “READ ME” with a step by step tutorial on how to set it up?

It currently doesn’t work, I was focused on making a different module, but when I’m done with that I’m gonna fix this module

1 Like

I have updated the module and it should work now, maybe remove the last line of the module (the FillCache(5) one)

I updated the module once again and added a feature, it should work atleast, I’ll update the post later, but you can as of right now just look at the code; I put some documentation in there.
I haven’t tested the new feature properly yet, so I don’t know if it works yet.