-- Generated By Codes Otaku Plugin
	
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