Can't implement callback functions into dialogue system

Heya, so I made a dialogue system using a tutorial and here’s the thing - I can’t figure out how to integrate callback functions.

MODULE // CODE
local Info = {} local head = script.Parent.Head Info.LeaveMessage = "..'Cya 'round, I guess." Info.LeaveButtonText = "I must go." local Callbacks = {	killSomeone = function()	print("Starting assassination quest.")	end,	birthdayWish = function()	print("Happy Birthday event triggered!")	end,	luckyOne = function()	print("Who is to die?")	end } Info.Dialog = {	[1] = {	Text = "'Sup, need someone popped?",	Choices = {	[1] = {	Text = "Err.. popped?",	Next = 2	},	[2] = {	Text = "Happy birthday.",	Next = 3,	Follow = false,	Callback = Callbacks.birthdayWish	},	},	};	[2] = {	Text = "Popped, killed, terminated, whatever you wanna call it. Whaddya need?",	Choices = {	[1] = {	Text = "Kill someone.",	Next = 4 ,	Follow = false,	},	},	};	[3] = {	Text = "..These words will have you end up in a grave some unfortunate day.",	Choices = {},	};	[4] = {	Text = "Great! Now, who's the lucky fella?",	Choices = {},	}; } Info.CameraSpeed = 2 Info.CameraCFrame = CFrame.new(Vector3.new(-104.361, 75.317, 133.111), head.Position) return Info 

The above code is what stores the information (it’s located inside of the NPCs)

CLIENTSIDE HANDLER // CODE
repeat wait() until game:IsLoaded() ------------------------- -- VARIABLES / SERVICES -------------------------- local repStorage = game:GetService("ReplicatedStorage") local players = game:GetService("Players") local tweenService = game:GetService("TweenService") ----------------------- -- VARIABLES / GUI ----------------------- local gui = script.Parent local main = gui.Main local background = main.BG local choiceFrame = main.ChoicesFrame local nameFrame = main.NameFrame local dialogFrame = main.DialogFrame local nameLabel = nameFrame.Label local dialogLabel = dialogFrame.Label local choiceTemplate = script.ChoiceTemplate -------------------------- -- VARIABLES / CONSTANTS -------------------------- local event = repStorage.Events:FindFirstChild("TalkEvent") local camera = workspace.CurrentCamera local plr = players.LocalPlayer local char = plr.Character or plr.CharacterAdded:Wait() local currentNPC = nil local storedChoices = {} -------- -- CODE -------- char:WaitForChild('Humanoid').Died:Connect(function()	task.wait(0.5)	if currentNPC then	currentNPC.HumanoidRootPart.ProximityPrompt.Enabled = true	end	camera.CameraType = Enum.CameraType.Custom	camera.CameraSubject = game.Players.LocalPlayer.Character	main:TweenPosition(UDim2.new(0,0,1,0))	task.wait(1)	main.Visible = false	nameLabel.Text = ""	dialogLabel.Text = ""	storedChoices = {}	currentNPC = nil end) -- // TYPEWRITER EFFECT local function typeWrite(text, label, TypeDelay)	label.Text = ""	for _, letter in text:split("") do	label.Text = label.Text..letter	task.wait(TypeDelay)	end end -- // END DIALOG local function endDialog(info)	for _, button in pairs(choiceFrame:GetChildren()) do	if button:IsA("TextButton") then	button:Destroy()	end	end	typeWrite(info.LeaveMessage, dialogLabel, 0.05)	task.wait(0.5)	currentNPC.HumanoidRootPart.TalkPrompt.Enabled = true	camera.CameraType = Enum.CameraType.Custom	camera.CameraSubject = char	main:TweenPosition(UDim2.new(0, 0,1.221, 0))	task.wait(1)	main.Visible = false	nameLabel.Text = ""	dialogLabel.Text = ""	storedChoices = {}	currentNPC = nil end -- // MOVE TO NEXT DIALOG local function nextDialog(info, lastChoice)	for _, button in pairs(choiceFrame:GetChildren()) do	if button:IsA("TextButton") then	button:Destroy()	end	end	local dialog	if lastChoice then	if lastChoice.Next then	dialog = info.Dialog[lastChoice.Next]	if lastChoice.Callback then	lastChoice.Callback()	end	else	end	else	dialog = info.Dialog[1]	end	if not dialog then return end	typeWrite(dialog.Text, dialogLabel, 0.05)	if dialog.Choices and (#dialog.Choices > 0 or #storedChoices > 0) then	for i, choice in pairs(storedChoices) do	local clone = choiceTemplate:Clone()	clone.Text = choice.Text	clone.Name = choice.Text	clone.Parent = choiceFrame	clone.MouseButton1Click:Connect(function()	storedChoices[i] = nil	nextDialog(info, choice)	end)	end	local leaveButton = choiceTemplate:Clone()	leaveButton.Text = info.LeaveButtonText	leaveButton.Name = "Leave"	leaveButton.UIStroke.Color = Color3.fromRGB(136, 0, 0)	leaveButton.BackgroundColor3 = Color3.fromRGB(83, 0, 0)	leaveButton.TextColor3 = Color3.fromRGB(255, 0, 0)	leaveButton.Parent = choiceFrame	leaveButton.MouseButton1Click:Connect(function()	endDialog(info)	end)	for i, choice in pairs(dialog.Choices) do	local clone = choiceTemplate:Clone()	clone.Text = choice.Text	clone.Name = choice.Text	clone.Parent = choiceFrame	if choice.Follow == true then	storedChoices[i] = choice	end	clone.MouseButton1Click:Connect(function()	storedChoices[i] = nil	if choice.Callback then	print("Calling callback for choice:", choice.Text) -- Debugging: Confirm callback exists	choice.Callback() -- Call the callback	else	print("No callback found for choice:", choice.Text) -- Debugging: If no callback, print this	print(choice.Next)	print(choice.Follow)	end	nextDialog(info, choice)	end)	if dialog.DialogCallback then	dialog.DialogCallback()	end	end	else	task.wait(1)	if dialog.Next then	nextDialog(info,dialog)	else	endDialog(info)	end	end end -- // MAIN CODE event.OnClientEvent:Connect(function(npc, info, useCameraParams)	if currentNPC == nil and npc then	currentNPC = npc	nameLabel.Text = npc.Name	-- // Reset dialogue	dialogLabel.Text = ""	storedChoices = {}	-- // Reset choices	for _, button in pairs(choiceFrame:GetChildren()) do	if button:IsA("TextButton") then	button:Destroy()	end	end	end	npc.HumanoidRootPart.TalkPrompt.Enabled = false	main.Visible = true	if useCameraParams == true then	camera.CameraType = Enum.CameraType.Scriptable	local Tinfo = TweenInfo.new(info.CameraSpeed, Enum.EasingStyle.Cubic, Enum.EasingDirection.In, 0, false)	local goal = {}	goal.CFrame = info.CameraCFrame	tweenService:Create(camera, Tinfo, goal):Play()	end	main.Position = UDim2.new(0, 0,1.221, 0)	main:TweenPosition(UDim2.new(0, 0,0.561, 0), Enum.EasingDirection.In, Enum.EasingStyle.Sine, 1, true)	task.wait(1)	nextDialog(info, nil) end) 

That is the clientside code. I can’t seem to figure out how to implement the callbacks, as it keeps printing No callback found for choice. It’s been getting on my nerves quite a bit.

You can’t pass a function from server to client as a callback in this way. The ‘callback’ being sent via the remote event in the info table is a reference to a function which is on the server, so the client can’t actually see it let alone run it.

To get around this you could either:

If you want the callback to run on client.
Put the callbacks in a module in replicated storage, have the client require this module then use a string name (instead of the function reference) to find the correct call back and call it.

If you want it to run on server.
Fire a remote event (or remote function if you want a return value) to the server with the selection reference to find and run the right callback.

2 Likes
[2] = {	Text = "Ooh! A client. What is it that you need?",	Choices = {	[1] = {	Text = "Kill someone.",	Next = 4 ,	Follow = true,	Callback = function()	print("Peak")	end	},	},	}; 

It won’t detect the callback though.

callback.OnServerInvoke = function(plr, info, choice)	print("huh")	print(choice.Text)	if choice.Callback then	choice.Callback()	end end 

Server ^

Client:

clone.MouseButton1Click:Connect(function()	storedChoices[i] = nil	callbackEv:InvokeServer(info, choice)	nextDialog(info, choice) end) 

You can’t pass functions through the client-server boundary

Then how am I meant to do this?

Why are you creating a remote function just to call the function? It seems redundant you can just call the function on the client

It does not even detect the callback function despite it being there.

As @Wigglyaa said, define the callback in a module script in a location accessible by both the client and server (ReplicatedStorage perhaps), and then pass the name of the callback to the client so that it can call the function in the module with that name.

If the callback function HAS to run on the server (for example, if its setting a leaderstat), create a remoteEvent which will activate said function when called, and pass in the name of the remoteEvent as the callback function instead. Then, using this name, find the remoteEvent and fire it when you want to call the callback function.

1 Like

So… require the callback module in the client handler script or something along the lines?

How do I link said callback function to the choices then??

yes

I believe module[name](arguments) should work

1 Like

Holy crap, I got it to work.
I added an additional value in the dialogue module called ‘Id’ (Names contain invalid characters) then checked it like this:

if callbacks[choice.Id] then callbacks[choice.Id]() end 

Thank you!

(i would make both replies as answers but i cant do that so i’ll just like them)

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.