Loop Skipping Increments

This is related to move previous topic: Unable to assign property Text. string expected, got nil - #12 by Master1794

Hello, developers! I have an issue where my loop is skipping by two or more increments. What should happen is the loop should run once, then repeatedly wait until a condition (bool) is true. I’ve tried debugging by printing the bool value in the loop and it always printed false.

--//variables
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local RemoteEvents = ReplicatedStorage:WaitForChild("RemoteEvents")

local Frame = script.Parent.Frame
local ChoicesFrame = script.Parent.choices.Frame

local ChatStarted = false
local canContinue = true

local currentChatIndex = 0 --//this is used for which number of messages we're at
local choiceIndex = 0 --//this is used for which index choice the player has chosen
local maxChoices = 0 --//this is used to determine how many choices the player is given (disregarding the GoodbyeTxt choice)
local oldChoiceIndex = 0 --//this is used to get the old choice index value

local GoodbyeTxt = "Goodbye!"

local choiceSelected
local choices
local responses
local currentChoice

local chatModes = {
	Friendly = Color3.fromRGB(13, 228, 68),
	Enemy = Color3.fromRGB(233, 0, 0),
	Help = Color3.fromRGB(0, 118, 255),
	None = Frame.UIStroke.Color
}

local typewriteMod = require(ReplicatedStorage:WaitForChild("typewrite"))

--//functions
local function getTableItems(array)
	local totalItems = 0 --//total items in array

	for _, element in ipairs(array) do
		if typeof(element) == "table" then
			totalItems += getTableItems(element)
		else
			totalItems += 1
		end
	end

	return totalItems
end

local function tweenChoicesFrame(open: boolean)
	if open == true then
		ChoicesFrame.Parent:TweenPosition(
			UDim2.new(
				0.125,
				0,
				0.725,
				0
			),
			Enum.EasingDirection.InOut,
			Enum.EasingStyle.Quad,
			0.5
		)
	else
		ChoicesFrame.Parent:TweenPosition(
			UDim2.new(
				0.125,
				0,
				1.25,
				0
			),
			Enum.EasingDirection.InOut,
			Enum.EasingStyle.Quad,
			0.5
		)
	end
end

local function getResponse()
	for _, response in ipairs(responses) do
		if ChatStarted == false then
			break
		end
		
		currentChatIndex += 1
		
		if typeof(responses[currentChatIndex]) == "string" then
			Frame.speech_.Text = responses[currentChatIndex]
		elseif typeof(responses[currentChatIndex]) == "table" then
			local multiResps = responses[currentChatIndex]
			
			--print(multiResps[oldChoiceIndex])
			
			--Frame.speech_.Text = multiResps[oldChoiceIndex]
			
			if choiceSelected ~= GoodbyeTxt then
				Frame.speech_.Text = multiResps[choiceSelected]
			else
				break
			end
		end

		typewriteMod.typewrite(Frame.speech_, 0.025)
		
		print("Current chat index:", currentChatIndex)

		repeat wait()

		until canContinue == true
	end
end

local function getChoice()
	for index, choiceT in ipairs(choices) do		
		if ChatStarted == false or currentChatIndex == getTableItems(responses) - 1 or currentChoice == GoodbyeTxt then
			break
		end
		
		if currentChoice == GoodbyeTxt then
			print("wow, this should loop be broken by now!")
		end
		
		currentChoice = choices[currentChatIndex]
		
		local currentIndex = 0 --//used to be added to for the table loop below
		
		maxChoices = getTableItems(currentChoice)
		
		for _, choice in ipairs(currentChoice) do
			currentIndex += 1
			
			if maxChoices == 1 then
				ChoicesFrame["3"].Visible = false
				ChoicesFrame["2"].Text = GoodbyeTxt
			elseif maxChoices == 2 then				
				ChoicesFrame["3"].Visible = true
			end
			
			ChoicesFrame[currentIndex].Text = choice
			
			--oldChoiceIndex = table.find(currentChoice, choiceSelected)
			
			--print("old choice index:", oldChoiceIndex)
		end
		
		repeat wait()

		until canContinue == true
	end
end

RemoteEvents.StartChat.OnClientEvent:Connect(function(chatTree, speakerName, chatMode)
	local Lresponses = chatTree["resps"]
	local Lchoices = chatTree["choices"]
	
	ChatStarted = true
	
	responses = Lresponses
	choices = Lchoices
	
	Frame.name_.Text = speakerName .. ":"
	Frame.speech_.Text = "..."
	
	for modeName, modeColor in pairs(chatModes) do
		if modeName == chatMode then
			Frame.UIStroke.Color = modeColor
			ChoicesFrame.Parent.UIStroke.Color = modeColor
		end
	end
	
	Frame:TweenPosition(
		UDim2.new(
			0.5,
			0,
			0.909,
			0
		),
		Enum.EasingDirection.InOut,
		Enum.EasingStyle.Quad,
		0.5
	)
	
	wait(1)
	
	while canContinue == true do
		canContinue = false
		
		local coroGetResps = coroutine.create(getResponse)
		local coroGetChoice = coroutine.create(getChoice)
		
		if choiceSelected == GoodbyeTxt then
			ChatStarted = false

			Frame.speech_.Text = GoodbyeTxt
			typewriteMod.typewrite(Frame.speech_, 0.025)
			
			ReplicatedStorage:WaitForChild("typewrite").isFinished.Value = false
			
			coroutine.close(coroGetResps)
			coroutine.close(coroGetChoice)
			
			break
		end
		
		if coroutine.status(coroGetResps) ~= "dead" or coroutine.status(coroGetChoice) ~= "dead" then
			coroutine.resume(coroGetResps)
			coroutine.resume(coroGetChoice)

			repeat wait()

			until typewriteMod.isFinished() == true

			if currentChatIndex >= getTableItems(responses) - 1 then
				ChatStarted = false

				Frame.speech_.Text = GoodbyeTxt
				typewriteMod.typewrite(Frame.speech_, 0.025)
				
				ReplicatedStorage:WaitForChild("typewrite").isFinished.Value = false
				
				coroutine.close(coroGetResps)
				coroutine.close(coroGetChoice)

				break
			end
			
			wait(1)

			if typewriteMod.isFinished() == true then
				coroutine.wrap(tweenChoicesFrame)(true)
			end
		end

		repeat wait()

		until canContinue == true
	end
	
	wait(2)
	
	RemoteEvents.ChatEnded:FireServer()
	
	Frame:TweenPosition(
		UDim2.new(
			0.5,
			0,
			1.25,
			0
		)
	)
	
	local dialogueScript = script:Clone()
	dialogueScript.Parent = script.Parent
	script:Destroy()
end)

for _, choiceBtn in ipairs(ChoicesFrame:GetChildren()) do
	if choiceBtn:IsA("TextButton") then
		local UIStroke = ChoicesFrame:WaitForChild("UIStroke")
		
		choiceBtn.MouseButton1Click:Connect(function()
			if canContinue == false then
				canContinue = true
				
				choiceSelected = table.find(currentChoice, choiceBtn.Text)
				
				if choiceSelected == nil and choiceBtn.Text == GoodbyeTxt then
					choiceSelected = GoodbyeTxt
				end
				
				coroutine.wrap(tweenChoicesFrame)(false)
			end
		end)
		
		choiceBtn.MouseEnter:Connect(function()
			UIStroke.Parent = choiceBtn
			UIStroke.Enabled = true
		end)
		
		choiceBtn.MouseLeave:Connect(function()
			UIStroke.Parent = ChoicesFrame
			UIStroke.Enabled = false
		end)
	end
end

It probably set to true in another part of your script ? :thinking:

I set it true in another part of my script on purpose. Something to add:

I’m making a dialogue system, so the loop I’m having problems with is the NPC dialogue, and the getChoice function with the loop, gets the choices given to the player. The final loop of the script is when the player selects a choice. That’s where I set the bool to true so the dialogue loop in the script can continue and move on to the next dialogue.

If you look at the 3rd print, you can see how the currentChatIndex moves from 2 to 4, then prints the same thing twice.

I don’t know if this is exactly what you want to know but to succeed in reading several infinite loops you need this: spaw(function())

The loops aren’t infinite. Sorry I forgot to add the tables;

local speakers = {
	Dummy = {
		"Dummy",
		
		"None", --ignore this. it's for something else
		
		resps = { --> this is the NPC dialogue
			"Hello!",
			"This a test!",
			{
				":D",
				">:("
			}
		},

		choices = { --> these are the player's choices
			{
				"Hello!"
			},
			
			{
				"Yessssssss",
				"Nah"
			}
		}
	}
}

return speakers

I don’t think I can help you sorry, I’m too beginner ! :sweat_smile::disappointed_relieved:
Have a good day ! :yum:

Thanks for trying to help though! :slight_smile:

1 Like