NPC Dialogue - Getting Different Dialogue when NPC Moves to Different Position

This is my first post so I am sorry if it is unorganized :upside_down_face:
If anyone needs more information just let me know and it will be added

I am working on a NPC Dialogue System for a different type of Obby I am currently working on. I want to have an NPC to talk the player through the beginning stages so that the player will know how to play.

  • The player will interact with the NPC by pressing E, and the Dialogue will appear on ScreenGUI. I currently have multiple pages working, buttons to go from page to page, and a typing animation.

This is how it currently works so you have an idea of how it will function:

Summary

I am having an issue with figuring out how to get different dialogue after the NPC moves to a different location. I want the NPC to have a greeting when first talked to, then move to a different location future into the lobby, and then have a different set of dialogue shown to the player.

I have been trying to achieve this by having the script check the NPCs location and then load a module a script with the dialogue into the GUI when interacted with.

  • The entire script is at the bottom of this post, this is the top of the script and includes variables and the position check to load the dialogue module:
Summary
local player = game.Players.LocalPlayer
local npcs = workspace.NPCs:GetChildren()
local ebutton = game.ReplicatedStorage.EButton
local uis = game:GetService("UserInputService")
local dialogueUI = player.PlayerGui:WaitForChild("DialogueUI")
local bg = dialogueUI.Background 
local currentPage = 1
local currentNPC = ""



-- CHOOSES DIALOGUE SCRIPT DEPENDING ON NPC LOCATION
function position()
	local currentNPCRoot = ebutton.Parent
	local currentPosition = currentNPCRoot.Position
	

	if currentPosition(-15.55, 2.7, 44.57) then
		dialogue = script:WaitForChild("DialogueOne")
	end
end

However, I get an error where the script attempts to index with the NPC

  • Please reference the entire script at the bottom of the post

image

I have tried looking through the forum and other sources for how people have done an NPC Dialogue and Movement system before, but haven’t been able to find anything to help me so far. That’s why I decided to make this my first post.

The Entire Script So Far: (The location of the error above will have a comment by it and is near the bottom)

Summary
local player = game.Players.LocalPlayer
local npcs = workspace.NPCs:GetChildren()
local ebutton = game.ReplicatedStorage.EButton
local uis = game:GetService("UserInputService")
local dialogueUI = player.PlayerGui:WaitForChild("DialogueUI")
local bg = dialogueUI.Background 
local currentPage = 1
local currentNPC = ""



-- CHOOSES DIALOGUE SCRIPT DEPENDING ON NPC LOCATION
function position()
	local currentNPCRoot = ebutton.Parent
	local currentPosition = currentNPCRoot.Position
	

	if currentPosition(-15.55, 2.7, 44.57) then
		dialogue = script:WaitForChild("DialogueOne")
	end



end




-- TYPING ANIMATION
local function typing(text1)
	bg.TextLabel1.Text = ""
	
	dialogueUI.Enabled = true
	
	local page = currentPage
	local npc = currentNPC
	
	for i = 1, string.len(text1), 1 do
		
		if dialogueUI.Enabled == false or page ~= currentPage or npc ~= currentNPC then break end
		
		if string.sub(text1, i, i) ~= "" then
			bg.TextLabel1.Text = string.sub(text1, 1, i)
			wait(.05)		
		end	
	end
end


-- DIALOGUE 
uis.InputBegan:Connect(function(key, chat)
	
	if chat then return end
	if key.KeyCode == Enum.KeyCode.E then
		if ebutton.Parent ~= game.ReplicatedStorage then
			if player.PlayerGui.DialogueUI.Enabled == false or ebutton.Parent.Parent ~= bg.NPCName.Text then 	-- This is Dialogue start
				
				currentNPC = ebutton.Parent.Parent.Name															-- This sets NPC Name for the GUI Name Label
				bg.NPCName.Text = currentNPC
				currentPage = 1
				
				local vpf = dialogueUI.ViewportFrame
				local dummy = ebutton.Parent.Parent:Clone()
				
				vpf:ClearAllChildren()
				dummy:SetPrimaryPartCFrame(CFrame.new(Vector3.new(0, .05, -10), Vector3.new(0, 0, 0)))
				dummy.Parent = vpf
				dummy.Humanoid.DisplayDistanceType = "None"
				dummy.PrimaryPart:ClearAllChildren()
				
				print("Working up till typing")
			
-- THIS IS THE ERROR LOCATION	
				typing(dialogue[currentNPC]["Page1"][1], dialogue[currentNPC]["Page1"][2])
			
			end
		end	
	end	
end)


-- NEXT PAGE 
dialogueUI.Background.Next.MouseButton1Down:Connect(function()
	
	currentPage = currentPage+1
	
	if dialogue[currentNPC]["Page"..currentPage] == nil then
		dialogueUI.Enabled = false
	else
		typing(dialogue[currentNPC]["Page"..currentPage][1], dialogue[currentNPC]["Page"..currentPage][2])
	
	end
end)


--E BUTTON
while wait(.2) do
	local character = player.Character
	if character then
		for i, npc in pairs(npcs) do
			if (npc.PrimaryPart.Position-character.PrimaryPart.Position).Magnitude < 20 then
				if ebutton.Parent == game.ReplicatedStorage or (npc.PrimaryPart.Position-character.PrimaryPart.Position).Magnitude < (ebutton.Parent.Position-character.PrimaryPart.Position).Magnitude then
					ebutton.Parent = npc.PrimaryPart		
				end
			elseif ebutton.Parent == npc.PrimaryPart then
					ebutton.Parent = game.ReplicatedStorage
					dialogueUI.Enabled = false
			end
		end	
	end
end

This is the Dialogue module script I am trying to use:

Summary

	local module = {
	
	Feugazi = {Page1 = {"example text."}, Page2 = {"example text."},
		Page3 = {"example text."}}
		
	}
	return module 

3 Likes

Did you require the module? You can’t get the contents of a module script unless you require them

2 Likes

Yes, I had it required previously but I don’t think its an issue with the contents of the module script. However, I am not sure what the issue is then.

Here the script with required, and the error is still there

Summary

2 Likes

Judging from the script, it seems like the “dialogue” variable is a global variable, is the function that defines it called before anything else?

2 Likes

This is the very top of the script, so the function is the first thing after I defined the other variables. I previously had “dialogue” just defined as local dialogue with it not being defined as anything specific.
Like this:

local example = ""
local dialogue
local example2 = ""

This is the top of the script, and how things are currently defined:

Summary
local player = game.Players.LocalPlayer
local npcs = workspace.NPCs:GetChildren()
local ebutton = game.ReplicatedStorage.EButton
local uis = game:GetService("UserInputService")
local dialogueUI = player.PlayerGui:WaitForChild("DialogueUI")
local bg = dialogueUI.Background 
local currentPage = 1
local currentNPC = ""



-- CHOOSES DIALOGUE SCRIPT DEPENDING ON NPC LOCATION
function position()
	local currentNPCRoot = ebutton.Parent
	local currentPosition = currentNPCRoot.Position
	

	if currentPosition(-15.55, 2.7, 44.57) then
		dialogue = require(script:WaitForChild("DialogueOne"))
	end
end
3 Likes

ok, one solution would be to add an if statement to check if the dialogue variable has a value because nil means that the thing you’re trying to index has no value

if dialogue then
   typing(dialogue[currentNPC]["Page1"][1], dialogue[currentNPC]["Page1"][2])
end

Another would be to call the function before it gets to the typing part:

position() -- tries to give dialogue a value before the script tries to index it
typing(dialogue[currentNPC]["Page1"][1], dialogue[currentNPC]["Page1"][2])
2 Likes

This has seemed to get rid of the index nil error

if dialogue then
	typing(dialogue[currentNPC]["Page1"][1], dialogue[currentNPC]["Page1"][2]
end

However, now the GUI is not being enabling, and there are no other errors appearing.

1 Like

ok, try this:

position() -- gives dialogue a value 

if dialogue then -- if the variable is still nil, then it won't run the thread
   typing(dialogue[currentNPC]["Page1"][1], dialogue[currentNPC]["Page1"][2])
end

Edit: I recommend allowing the position function to detect more positions

it will only assign dialogue a value if it’s at this position

1 Like

Okay I called the position before the if dialogue then.
This is the error I am getting now;

Summary

image

Could this mean that I am not allowing the position function to detect enough position like you added?

  • if currentPosition(-15.55, 2.7, 44.57) then is still equaling the position of the NPCs Root Part
Summary

1 Like

Oh, i didn’t notice that lol. Change the function from

function position()
	local currentNPCRoot = ebutton.Parent
	local currentPosition = currentNPCRoot.Position
	

	if currentPosition(-15.55, 2.7, 44.57) then
		dialogue = require(script:WaitForChild("DialogueOne"))
	end



end

to

function position()
	local currentNPCRoot = ebutton.Parent
	local currentPosition = currentNPCRoot.Position
	
	if currentPosition == Vector3.new(-15.55, 2.7, 44.57) then -- checks if the position is equivalent to the Vector3 position
		dialogue = require(script:WaitForChild("DialogueOne"))
	end
end
2 Likes

Ahh okay that was a mistake haha my bad. I changed the function to

	local currentNPCRoot = ebutton.Parent
	local currentPosition = currentNPCRoot.Position
	

	if currentPosition == Vector3.new(-15.55, 2.7, 44.57) then
		dialogue = require(script:WaitForChild("DialogueOne"))
	end
end

I no longer have any errors appearing but the GUI is still not enabling.

1 Like

that’s strange. try adding this to your script, after calling the position function

print(tostring(dialogue)) -- prints the value of the variable 
2 Likes

Okay I added that:

positionDialogue()		
				print(tostring(dialogue)) -- prints the value of the variable 
				
				if dialogue then
					typing(dialogue[currentNPC]["Page1"][1], dialogue[currentNPC]["Page1"][2])
				end

It prints nil in the output

Summary

image

Ok, that’s why.
try reforming the positionDialogue function one more time

function positionDialogue()
	local currentNPCRoot = ebutton.Parent
	local currentPosition = currentNPCRoot
	
	if currentPosition.Position == Vector3.new(-15.55, 2.7, 44.57) then
		dialogue = require(script:WaitForChild("DialogueOne"))
	end
end

Okay I reformed the positionDialogue function to what you said

Summary
	local currentNPCRoot = ebutton.Parent
	local currentPosition = currentNPCRoot
	
	if currentPosition.Position == Vector3.new(-15.55, 2.7, 44.57) then
		dialogue = require(script:WaitForChild("DialogueOne"))
	end
end

It is still printing nil in output

ok, put print(currentPosition.Position) at the end of the positionDialogue function

Edit: prints are useful to see what the script sees and where something may be going wrong

1 Like

Do you mean like this?

	local currentNPCRoot = ebutton.Parent
	local currentPosition = currentNPCRoot
	

	if currentPosition.Position == Vector3.new(-15.55, 2.7, 44.57) then
		dialogue = require(script:WaitForChild("DialogueOne"))
		
		print(currentPosition.Position)
	end
end

It is not printing anything

after the end for the if statement

Like this?

function positionDialogue()
	local currentNPCRoot = ebutton.Parent
	local currentPosition = currentNPCRoot
	

	if currentPosition.Position == Vector3.new(-15.55, 2.7, 44.57) then
		dialogue = require(script:WaitForChild("DialogueOne"))
			
	end
	print(currentPosition.Position)
end

It is still not printing anything into output.

I tried moving out of both end like this

function positionDialogue()
	local currentNPCRoot = ebutton.Parent
	local currentPosition = currentNPCRoot
	

	if currentPosition.Position == Vector3.new(-15.55, 2.7, 44.57) then
		dialogue = require(script:WaitForChild("DialogueOne"))
			
	end
end
print(currentPosition.Position)

And it is now printing this

Summary

EDIT: I guess it doesn’t make sense putting outside of the function since currentPosition isn’t defined outside of it

Yea, ok try putting it before the if statement