BindableFunction can't get string result?

Hi, I am working on my game which features a dialog system to locally chat with NPCs. When an NPC presents a question, it invokes a BindableFunction (named DialogChoice) which the client receives. This opens up the choice box. There are multiple answer choices for the player, which DialogChoice returns to the NPC as a string. However, while the player can read the string value and send it successfully, the NPC is unable to receive it, and the result of DialogChoice prints “nil.”

Here is a snippet of the dialog code for the NPC, found within a Script set to Client RunContext inside a ProximityPrompt inside their character:

Prompt.Triggered:Connect(function(plr)
	if plr ~= Player then return end
	Prompt.Enabled = false
	Title.PlayerToHideFrom = Player
	local Initial = {
		"Ahoy! I'm William of the Single Sea, the greatest pirate ever! I see you are...",
		Player.DisplayName.."! That's right! I heard about you from the locals around here.",
	}
	for i,v in pairs(Initial) do
		TextEvent:Fire(v)
		Voice.SoundId = "rbxassetid://"..NeutralVoices[math.random(#NeutralVoices)]
		repeat wait() until Voice.IsLoaded
		Voice:Stop()
		Voice:Play()
		TextAnimator.typeWrite(Title.invisible, v, 0.05)
		task.wait(2)
	end
	local q1 = "Say, how does a good ol' treasure hunt sound? It should only take a few minutes."
	local response = ChoiceEvent:Invoke(q1, 5)
	print(response)
end)

As shown above, the NPC reads the first few initial lines to the player. Then, it presents the first question by invoking the ChoiceEvent.

Here is a snippet of the LocalScript inside a ScreenGui in the Client’s PlayerGui:

local function Choice(questionText, timerAmount)
	assert(questionText, "Invalid string.")
	assert(timerAmount, "Invalid number.")
	
	local tInfo = TweenInfo.new(timerAmount, Enum.EasingStyle.Linear)
	local TimerTween = ts:Create(Timer, tInfo, {Size = UDim2.new(0,0,0.1,0)})
	local oldpos = ChoiceBox.Position
	ChoiceBox.Position = oldpos+UDim2.new(0,0,0.5,0)
	ChoiceBox:TweenPosition(oldpos, Enum.EasingDirection.Out, Enum.EasingStyle.Sine, 1)
	ChoiceBox.Visible = true
	Timer.Size = UDim2.new(1,0,0.1,0)
	Dialog.Enabled = true
	Choices.Visible = false
	Timer.Visible = false
	Header.Visible = false
	task.wait(1)
	TextAnimator.typeWrite(Question, questionText, 0.05)
	Choices.Visible = true
	Timer.Visible = true
	Header.Visible = true
	
	local active = true
	for i,v in pairs(Choices:GetChildren()) do
		if v:IsA("TextButton") then
			local connection
			connection = v.MouseButton1Click:Connect(function()
				if not active then return end
				active = false
				TimerTween:Cancel()
				connection:Disconnect()
				game.SoundService.ui_Click:Stop()
				game.SoundService.ui_Click:Play()
				ChoiceBox.Visible = false
				return v.Name
			end)
		end
	end
	TimerTween:Play()
	TimerTween.Completed:Wait()
	if active then
		active = false
		for i,v in pairs(Choices:GetChildren()) do
			if v:IsA("TextButton") then
				game.SoundService.ui_Click:Stop()
				game.SoundService.ui_Click:Play()
				ChoiceBox.Visible = false
				return v.Name
			end
		end
	end
end

TextEvent.Event:Connect(function(promptText)
	if not promptText then
		DialogText.Text = ""
		DialogText.Visible = false
	end
	DialogText.Visible = true
	TextAnimator.typeWrite(DialogText, promptText, 0.05)
end)

ChoiceEvent.OnInvoke = Choice

The Choice function is the core of the script, as it handles the majority of the work. The TextEvent function handles the initial prompts by the NPC. One thing I would like to mention is that the “v” value in the for loops represents the TextButtons, which represent the answer choices that the player can select.

At the moment, the player is able to read and print v.Name all the way up until the function returns the value. On the other hand, the NPC receives the result but cannot read the value, thus printing “nil.”

1 Like

it seems like the Choice function on the client-side is not actually returning any value to the server-side script

Both scripts are client-side. One of them is a LocalScript instance while the other is a Script instance that has the Client RunContext. I could try to make both of them a script with Client RunContext, though.

Edit:
Same result.

1 Like

Hey ZurichBT! It looks like there’s an issue with passing the player’s response back to the NPC. Instead of directly returning a value, the NPC script expects a callback function. To fix this, you can modify the code in two places. In the NPC script, pass a callback function to the ChoiceEvent:InvokeServer instead of assigning the return value. In the client script, modify the Choice function to invoke the server-side function with the chosen answer using the provided callback. Hope that helps!

1 Like

Apologies, but I do not understand what you mean by “passing a callback function.” Are you saying that I should create a separate BindableFunction after DialogChoice (or ChoiceEvent) that triggers after ChoiceEvent gets a result? Or to make a separate BindableFunction once Choice starts? Sorry, but I do not see what you are trying to get at.

Edit:
I would also like to clarify for everyone that the entire system is client-sided. There is no server-side script, the NPC script is simply a Script that has the Client RunContext as it is running inside an NPC character model in Workspace.

Your callback isn’t returning anything so it makes sense that you’re getting no result. The return statements in the TextButton clicks are returning back to an RBXScriptConnection object and return values on functions called by an RBXScriptConnection are ignored.

This issue has more to do with the flow of logic. You have the right idea with making this asynchronous but your buttons also have to be. Specifically: the TextButtons, on click, need to signal to a waiting thread about the choice that was made and then the thread can resume running.

1 Like

Sorry for the confusion. Let me explain it simply.

In your NPC script, instead of assigning the return value of ChoiceEvent:Invoke to a variable, you need to provide a special function called a “callback function” as an argument.

For example, you can create a function like this:

local function HandleChoiceResponse(choice)
    print(choice)  -- This will print the chosen answer
end

Then, when invoking ChoiceEvent, you pass this function as an argument:

ChoiceEvent:InvokeServer(q1, 5, HandleChoiceResponse)

On the client side, in the Choice function, you don’t need to create a separate function. Instead, modify the code to call the provided callback function with the chosen answer:

callback(v.Name)  -- Invoke the callback function with the chosen answer

By doing this, the NPC script will be able to receive the player’s response correctly and process it.

I hope this explanation helps! Let me know if you have further questions.

1 Like

Thank you. I followed the steps you detailed and it works as intended.

I have not tested this yet and there is not much reason to do so since the system now works for me, but if anyone else looks at this post in the future for further support, try the solution that colbert2677 proposes. Thank you, everyone, for chipping in your responses.

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