Changing wait() to function breaks cutscene

I have an issue. There is my cutscene script. When i use wait() it works just as expected. But if i replace wait(3) to typewrite(“some text”, 3) it will wait for longer. How i can fix this?

function typewrite(text, time)
	local s = 0
	for i = 1,#text,1 do
		script.Parent.DialogueText.Text = string.sub (text,1,i)
		s += 0.01
		wait(0.01)
	end
	local q = time - s
	wait(q)
end

local cams = game.Workspace.cams
local obj = game.ReplicatedStorage.ass
local remotes = game.ReplicatedStorage.Remotes.Cutscenes
--local sounds = game.Workspace.Cutscenes.Sounds

function enableGui()
	script.Parent.down.Visible = true
	script.Parent.up.Visible = true
	script.Parent.DialogueText.Visible = true
end

function disableGui()
	script.Parent.down.Visible = false
	script.Parent.up.Visible = false
	script.Parent.DialogueText.Visible = false
end

function move(camxd, tix)
	local ti = {}
	ti.CFrame = camxd.CFrame
	local ts = TweenInfo.new(tix)
	local tween = game:GetService("TweenService"):Create(workspace.CurrentCamera, ts, ti)
	tween:Play()
end

remotes.StartCutscene.OnClientEvent:Connect(function ()
	local cam = workspace.CurrentCamera
	cam.CameraType = Enum.CameraType.Custom
	cam.CameraType = Enum.CameraType.Scriptable
	enableGui()
	local jot1 = obj.jotaro1:Clone()
	local jos1 = obj.joseph1:Clone()
	jos1.Parent = workspace
	jot1.Parent = workspace
	workspace.JotaJail:Play()
	move(cams.cam1, 0)
	wait(3.5)
	local car = obj.car2
	car.Parent = workspace
	move(cams.cam2, 0)
	wait(1)
	car:Destroy()
	move(cams.cam3, 0)
	wait(0.01)
	move(cams.cam4, 3)
	wait(3)
	move(cams.jotaface, 0)
	wait(2.2)
	move(cams.cam5, 0)
	wait(0.01)
	move(cams.cam6, 1)
	wait(2.5)
	move(cams.jotaface, 0)
	wait(0.7)
	jos1:Destroy()
	local joskick = obj.josephkick:Clone()
	joskick.Parent = workspace
	local cop1 = obj.cop1:Clone()
	cop1.Parent = workspace
	local cop2 = obj.cop2:Clone()
	cop2.Parent = workspace
	move(cams.cam7, 0)
	wait(0.5)
	move(cams.cam8, 0)
	wait(0.5)
	jot1:Destroy()
	local jot1 = obj.jotaro2:Clone()
	jot1.Parent = workspace
	wait(1)
	move(cams.josephkick, 0)
	wait(1.2)
	cop1:Destroy()
	wait(1.8)
	cop2:Destroy()
	
	wait(1.5)
	move(cams.cam9, 0)
	jot1:Destroy()
	joskick:Destroy()
	local jot1 = obj.jotaro3:Clone()
	jot1.Parent = workspace
	local jos = obj.joseph2:Clone()
	jos.Parent = workspace
	wait(0.1)
	move(cams.cam10, 1.5)
	wait(1.5)
	move(cams.cam11, 1)
	wait(2)
	move(cams.cam12, 0)
	wait(0.01)
	move(cams.cam13, 2)
	wait(3)
	move(cams.wt, 2)
	local wt = obj.wt:Clone()
	wt.Parent = workspace
	workspace.Sound:Play()
	wait(10)	
	game.ReplicatedStorage.Scenarios.JailLeave:FireServer()
	cam.CameraType = Enum.CameraType.Custom
	cam.CameraSubject = game.Players.LocalPlayer.Character.Humanoid
	disableGui()
	workspace.Sound:Stop()
	wait(28)
	jot1:Destroy()
	jos:Destroy()
end)

The reason why replacing wait(3) with typewrite("some text", 3) causes a longer wait is because the typewrite function takes longer than 3 seconds to complete. The typewrite function is using a loop to gradually display the text, and the time it takes to complete the loop depends on the length of the text.

To fix this, you need to adjust the time parameter in the typewrite function based on the length of the text. You can calculate the time it will take to display the text by multiplying the length of the text by the delay between each character display. In your code, the delay between each character display is set to 0.01 seconds.

Here’s an updated version of the typewrite function that calculates the time needed based on the length of the input text:

function typewrite(text)
    local s = 0
    for i = 1,#text,1 do
        script.Parent.DialogueText.Text = string.sub (text,1,i)
        s += 0.01
        wait(0.01)
    end
    return s
end

You can then call this function with the input text and use its return value to adjust the wait time in your main function. For example, if you want to wait for 3 seconds after the text is displayed, you can call typewrite with the input text and subtract its return value from the desired wait time:

local text = "some text"
local displayTime = typewrite(text)
wait(3 - displayTime)

This way, the total time waited will be 3 seconds, regardless of the length of the input text.

Tried you method, changed typewrite to

function typewrite(text, time)
	local s = 0
	for i = 1,#text,1 do
		script.Parent.DialogueText.Text = string.sub (text,1,i)
		s += 0.01
		wait(0.01)
	end
	return s
end

and changed part of the code to

local waittime = typewrite("H-how terrifying")
wait(3-waittime)

It still waits a bit longer, bc music is out of sync

If the music is out of sync, you can adjust the timing of the music instead of the typewriting function. One way to do this is to use the play() function of the Sound object with the optional timePosition parameter. This parameter specifies the time in seconds to start playing the sound from. Here’s an example:

local music = game.Workspace.Sound.Music
local dialogue = script.Parent.DialogueText

-- play the music and get the current time
music:play()
local startTime = music.TimePosition

-- type the text
local waittime = typewrite("H-how terrifying")

-- wait until the end of the text or the end of the music, whichever is longer
local endTime = startTime + math.max(waittime, music.TimeLength - startTime)
wait(endTime - music.TimePosition)

-- stop the music
music:stop()