local t={}t.__index=t local J=game:GetService("\084\119\101\101\110\083\101\114\118\105\099\101")local a=game:GetService("\082\117\110\083\101\114\118\105\099\101")local f={}local u=false if not u then for t,J in pairs(workspace.Cutscenes:GetDescendants())do if J:IsA("\066\097\115\101\080\097\114\116")then J.Transparency=1 end end end local R={NIL_DATA="\067\117\116\115\099\101\110\101 \100\097\116\097 \099\097\110\'\116 \098\101 \110\105\108",INVALID_DATA="\067\117\116\115\099\101\110\101 \100\097\116\097 \099\097\110\'\116 \098\101 \105\110\118\097\108\105\100",NO_CAMERA="\067\117\116\115\099\101\110\101 \099\097\109\101\114\097 \110\111\116 \115\101\116"}local function v(t,J)return pcall(function()local a=t[J]end)end local function G(t,J)if t and v(t,J)then if typeof(t[J])=="\073\110\115\116\097\110\099\101"then return t[J].Value else return t[J]end end end function t.new(J,a,u,R,v)local G={}G.Camera=J G.Data=v or{}G.Looped=a or false G.Speed=u or 1 G.Instruction=0 G.Playing=false G.Paused=false G.CurrentTween=nil G.CurrentFOVTween=nil G.ID=#f+1 G.Loaded=false G.FreezeControls=R or false G.ENextKeyframe=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")G.ELoaded=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")G.EPlay=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")G.EPause=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")G.EStop=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")G.EResume=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")G.DidLoop=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")G.Counter=0 G.Elapsed=0 G.NextInstruction=0 t[G.ID]=G return setmetatable(G,t)end function t.parse(t,J)local f={}f[1]={}f[2]={}local u=t:FindFirstChild("\067\097\109\101\114\097\115")local v=t:FindFirstChild("\080\114\111\103\114\097\109")if u and v then for t=1,#u:GetChildren(),1 do if J then a.RenderStepped:wait()end local R=u:FindFirstChild(tostring(t))if R then f[1][t]={}f[1][t][1]=G(R,"\067\070\114\097\109\101")or CFrame.new()f[1][t][2]=G(R,"\070\079\086")or 70 f[1][t][3]=Enum.EasingStyle[G(R,"\084\114\097\110\115\105\116\105\111\110\084\121\112\101")or 1]f[1][t][4]=G(R,"\084\114\097\110\115\105\116\105\111\110\068\117\114\097\116\105\111\110")or 1 f[1][t][5]=G(R,"\084\114\097\110\115\105\116\105\111\110\068\105\114\101\099\116\105\111\110")or 1 f[1][t][6]=G(R,"\084\114\097\110\115\105\116\105\111\110\068\101\108\097\121")or 0 end end else warn(R.INVALID_DATA)end return f end function t.Load(v,J,a,f,u)if J then if type(J)=="\116\097\098\108\101"then v.Data=J elseif J:IsA("\070\111\108\100\101\114")then if f then spawn(function()v.Data=t.parse(J,u)if a then J:Destroy()end v.Loaded=true v.ELoaded:Fire()end)else v.Data=t.parse(J,u)if a then J:Destroy()end v.Loaded=true v.ELoaded:Fire()end else warn(R.INVALID_DATA)end else warn(R.NIL_DATA)end end function t.Play(f,t,J,a)f.Speed=t or f.Speed if type(J)=="\098\111\111\108\101\097\110"then f.Looped=J end f.Camera=f.Camera or a if f.Camera and f.Playing==false then if f.FreezeControls then local t=(require((game:GetService("\080\108\097\121\101\114\115")).LocalPlayer.PlayerScripts.PlayerModule)):GetControls()t:Disable()end f.Camera.CameraType=Enum.CameraType.Scriptable f.Playing=true f.EPlay:Fire()else warn(R.NO_CAMERA)end end function t.Resume(t)if t.Playing and t.Paused then t.Camera.CameraType=Enum.CameraType.Scriptable t.CurrentTween:Play()t.CurrentFOVTween:Play()t.Paused=false t.EResume:Fire()end end function t.Pause(t)if t.Playing==true and t.Paused==false then t.CurrentTween:Pause()t.CurrentFOVTween:Pause()t.Paused=true t.EPause:Fire()end end function t.Stop(t)if t.Playing==true then t.Playing=false t.Paused=false t.Instruction=0 if t.CurrentTween then t.CurrentTween:Cancel()end t.CurrentTween=nil if t.CurrentFOVTween then t.CurrentFOVTween:Cancel()end t.CurrentFOVTween=nil t.NextInstruction=0 t.Elapsed=0 t.Camera.CameraType=Enum.CameraType.Custom local J=(require((game:GetService("\080\108\097\121\101\114\115")).LocalPlayer.PlayerScripts.PlayerModule)):GetControls()J:Enable()local a=(game:GetService("\080\108\097\121\101\114\115")).LocalPlayer local f=a.Character if f then local J=f:FindFirstChild("\072\117\109\097\110\111\105\100")if J then t.Camera.CameraSubject=J end end t.EStop:Fire()end end function t.GetDuration(J,t)t=t or 1 local a=0 for t,J in pairs(J.Data[1])do a=(a+J[4])+J[6]end return a/t end function t.Update(a,t)if a.Playing==true and(a.Paused==false and a.Camera)then a.Camera.CameraType=Enum.CameraType.Scriptable if a.Counter>=a.NextInstruction then a.Instruction=a.Instruction+1 local t=a.Data[1][a.Instruction]if t then if t[4]==0 then a.Camera.FieldOfView=t[2]a.Camera.CFrame=t[1]end local f=TweenInfo.new(t[4]/a.Speed,t[3],t[5],0,false,t[6]/a.Speed)local u=TweenInfo.new(t[4]/a.Speed,Enum.EasingStyle.Sine,Enum.EasingDirection.InOut,0,false,t[6]/a.Speed)local R={CFrame=t[1]}a.CurrentTween=J:Create(a.Camera,f,R)a.CurrentFOVTween=J:Create(a.Camera,u,{FieldOfView=t[2]})a.CurrentTween:Play()a.CurrentFOVTween:Play()a.Counter=0 a.NextInstruction=(t[4]+t[6])/a.Speed a.ENextKeyframe:Fire(a.Instruction,a.NextInstruction,t)else if a.Looped then a.Instruction=0 a.Counter=0 a.Elapsed=0 a.NextInstruction=0 a.DidLoop:Fire()else a:Stop()end end end a.Counter=a.Counter+t a.Elapsed=a.Elapsed+t end end function t.Toggle(t)if t.Playing then if t.Paused then t:Resume()else t:Pause()end end end function t.Destroy(t)if t.Playing then t:Stop()end t.CurrentTween:Destroy()t.CurrentFOVTween:Destroy()t.EPlay:Destroy()t.EPause:Destroy()t.EStop:Destroy()t.EResume:Destroy()t.ELoaded:Destroy()f[t.ID]=nil t=nil end return t

	
local Cutscene = {}
Cutscene.__index = Cutscene
local TS = game:GetService("TweenService")
local RuS = game:GetService("RunService")
local Cutscenes = {}

local DEBUG_MODE = false

if not DEBUG_MODE then
	for i, v in pairs(workspace.Cutscenes:GetDescendants()) do
		if v:IsA("BasePart") then
			v.Transparency = 1
		end
	end
end

local ERROR = {
	NIL_DATA = "Cutscene data can't be nil",
	INVALID_DATA = "Cutscene data can't be invalid",
	NO_CAMERA = "Cutscene camera not set"
}

local function hasProperty(object, prop)
    return pcall(function() local t = object[prop]  end)
end

local function GetValue(Parent, Name)
	if Parent and hasProperty(Parent, Name) then
		if typeof(Parent[Name]) == "Instance" then
			return Parent[Name].Value
		else
			return Parent[Name]
		end
	end
end

function Cutscene.new(Camera, Looped, Speed, FreezeControls, Data)
	local Self = {}
	
	Self.Camera = Camera
	Self.Data = Data or {}
	Self.Looped = Looped or false
	Self.Speed = Speed or 1
	Self.Instruction = 0
	Self.Playing = false
	Self.Paused = false
	Self.CurrentTween = nil
	Self.CurrentFOVTween = nil
	Self.ID = #Cutscenes+1
	Self.Loaded = false
	Self.FreezeControls = FreezeControls or false
	
	Self.ENextKeyframe = Instance.new("BindableEvent")
	Self.ELoaded = Instance.new("BindableEvent")
	Self.EPlay = Instance.new("BindableEvent")
	Self.EPause = Instance.new("BindableEvent")
	Self.EStop = Instance.new("BindableEvent")
	Self.EResume = Instance.new("BindableEvent")
	Self.DidLoop = Instance.new("BindableEvent")
	
	Self.Counter = 0
	Self.Elapsed = 0
	
	Self.NextInstruction = 0
	
	Cutscene[Self.ID] = Self
	
	return setmetatable(Self, Cutscene)
end

function Cutscene.parse(DataFolder, SafeMode)
	local Data = {}
	Data[1] = {} -- Cameras
	Data[2] = {} -- Program
	local Cameras = DataFolder:FindFirstChild("Cameras")
	local Program = DataFolder:FindFirstChild("Program")
	if Cameras and Program then
		for i=1,#Cameras:GetChildren(),1 do
			if SafeMode then
				RuS.RenderStepped:wait()
			end
			local Camera = Cameras:FindFirstChild(tostring(i))
			if Camera then
				Data[1][i] = {}
				Data[1][i][1] = GetValue(Camera, "CFrame") or CFrame.new()
				Data[1][i][2] = GetValue(Camera, "FOV") or 70
				Data[1][i][3] = Enum.EasingStyle[GetValue(Camera, "TransitionType") or 1]
				Data[1][i][4] = GetValue(Camera, "TransitionDuration") or 1
				Data[1][i][5] = GetValue(Camera, "TransitionDirection") or 1
				Data[1][i][6] = GetValue(Camera, "TransitionDelay") or 0
			end
		end
	else
		warn(ERROR.INVALID_DATA)
	end
	return Data
end

function Cutscene:Load(Data, Destroy, NoYield, SafeMode)
	if Data then
		if type(Data) == "table" then
			self.Data = Data
		elseif Data:IsA("Folder") then
			if NoYield then
				spawn(function()
					self.Data = Cutscene.parse(Data, SafeMode)
					if Destroy then
						Data:Destroy()
					end
					self.Loaded = true
					self.ELoaded:Fire()
				end)
			else
				self.Data = Cutscene.parse(Data, SafeMode)
				if Destroy then
					Data:Destroy()
				end
				self.Loaded = true
				self.ELoaded:Fire()
			end
		else
			warn(ERROR.INVALID_DATA)
		end
	else
		warn(ERROR.NIL_DATA)
	end
end

function Cutscene:Play(Speed, Looped, Camera)
	self.Speed = Speed or self.Speed
	if type(Looped) == "boolean" then
		self.Looped = Looped
	end
	self.Camera = self.Camera or Camera
	if self.Camera and self.Playing == false then
		if self.FreezeControls then
			local controls = require(game:GetService("Players").LocalPlayer.PlayerScripts.PlayerModule):GetControls()
			controls:Disable()
		end
		self.Camera.CameraType = Enum.CameraType.Scriptable
		self.Playing = true
		self.EPlay:Fire()
	else
		warn(ERROR.NO_CAMERA)
	end
end

function Cutscene:Resume()
	if self.Playing and self.Paused then
		self.Camera.CameraType = Enum.CameraType.Scriptable
		self.CurrentTween:Play()
		self.CurrentFOVTween:Play()
		self.Paused = false
		self.EResume:Fire()
	end
end

function Cutscene:Pause()
	if self.Playing == true and self.Paused == false then
		self.CurrentTween:Pause()
		self.CurrentFOVTween:Pause()
		self.Paused = true
		self.EPause:Fire()
	end
end

function Cutscene:Stop()
	if self.Playing == true then
		self.Playing = false
		self.Paused = false
		self.Instruction = 0
		if self.CurrentTween then
			self.CurrentTween:Cancel()
		end
		self.CurrentTween = nil
		if self.CurrentFOVTween then
			self.CurrentFOVTween:Cancel()
		end
		self.CurrentFOVTween = nil
		self.NextInstruction = 0
		self.Elapsed = 0
		self.Camera.CameraType = Enum.CameraType.Custom
		local controls = require(game:GetService("Players").LocalPlayer.PlayerScripts.PlayerModule):GetControls()
		controls:Enable()
		local Player = game:GetService("Players").LocalPlayer
		local Character = Player.Character
		if Character then
			local Humanoid = Character:FindFirstChild("Humanoid")
			if Humanoid then
				self.Camera.CameraSubject = Humanoid
			end
		end
		self.EStop:Fire()
	end
end

function Cutscene:GetDuration(Speed)
	Speed = Speed or 1
	local count = 0
	for i,X in pairs(self.Data[1]) do
		count = count+X[4]+X[6]
	end
	return count/Speed
end

function Cutscene:Update(delta)
	if self.Playing == true and self.Paused == false and self.Camera then
		self.Camera.CameraType = Enum.CameraType.Scriptable
		if self.Counter >= self.NextInstruction then
			self.Instruction = self.Instruction + 1
			local X = self.Data[1][self.Instruction]
			if X then
				if X[4] == 0 then
					self.Camera.FieldOfView = X[2]
					self.Camera.CFrame = X[1]
				end
				local TI = TweenInfo.new(X[4]/self.Speed, X[3], X[5], 0, false, X[6]/self.Speed)
				local TI2 = TweenInfo.new(X[4]/self.Speed, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, 0, false, X[6]/self.Speed)
				local Properties = {
					["CFrame"] = X[1]
				}
				self.CurrentTween = TS:Create(self.Camera, TI, Properties)
				self.CurrentFOVTween = TS:Create(self.Camera, TI2, {FieldOfView = X[2]})
				self.CurrentTween:Play()
				self.CurrentFOVTween:Play()
				self.Counter = 0
				self.NextInstruction = (X[4]+X[6])/self.Speed
				self.ENextKeyframe:Fire(self.Instruction, self.NextInstruction, X)
			else
				if self.Looped then
					self.Instruction = 0
					self.Counter = 0
					self.Elapsed = 0
					self.NextInstruction = 0
					self.DidLoop:Fire()
				else
					self:Stop()
				end
			end
		end
		self.Counter = self.Counter + delta
		self.Elapsed = self.Elapsed + delta
	end
end

function Cutscene:Toggle()
	if self.Playing then
		if self.Paused then
			self:Resume()
		else
			self:Pause()
		end
	end
end

function Cutscene:Destroy()
	if self.Playing then
		self:Stop()
	end
	self.CurrentTween:Destroy()
	self.CurrentFOVTween:Destroy()
	self.EPlay:Destroy()
	self.EPause:Destroy()
	self.EStop:Destroy()
	self.EResume:Destroy()
	self.ELoaded:Destroy()
	Cutscenes[self.ID] = nil
	self = nil
end

return Cutscene
