local c={}c.__index=c c.Easing=require(script:WaitForChild("\069\097\115\105\110\103"))function c.new(K,w)do assert(typeof(w)=="\102\117\110\099\116\105\111\110","\067\097\108\108\098\097\099\107 \097\114\103\117\109\101\110\116 \109\117\115\116 \098\101 \097 \102\117\110\099\116\105\111\110")local c=typeof(K)assert(c=="\084\119\101\101\110\073\110\102\111"or c=="\116\097\098\108\101","\084\119\101\101\110\073\110\102\111 \109\117\115\116 \098\101 \111\102 \116\121\112\101 \084\119\101\101\110\073\110\102\111 \111\114 \116\097\098\108\101")if c=="\116\097\098\108\101"then if K.Time==nil then K.Time=1 end if K.EasingStyle==nil then K.EasingStyle=Enum.EasingStyle.Quad end if K.EasingDirection==nil then K.EasingDirection=Enum.EasingDirection.Out end if K.RepeatCount==nil then K.RepeatCount=0 end if K.Reverses==nil then K.Reverses=false end if K.DelayTime==nil then K.DelayTime=0 end end end local Z=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")local m=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")local D=setmetatable({TweenInfo=K;Callback=w;PlaybackState=Enum.PlaybackState.Begin;Completed=Z.Event;PlaybackStateChanged=m.Event,_id="\116\119\101\101\110\095"..(game:GetService("\072\116\116\112\083\101\114\118\105\099\101")):GenerateGUID(false),_playing=false;_paused=false;_completed=Z,_playbackStateChanged=m,_elapsedTime=0;_repeated=0,_reversing=false,_elapsedDelayTime=0},c)return D end function c.ResetState(c)c._playing=false c._paused=false c._elapsedTime=0 c._repeated=0 c._reversing=false c._elapsedDelayTime=0 end function c.SetPlaybackState(K,c)local w=K.PlaybackState K.PlaybackState=c if c~=w then K._playbackStateChanged:Fire(c)end end function c.Play(K)if K._playing and not K._paused then return end K._playing=true K._paused=false local w if typeof(K.TweenInfo)=="\084\119\101\101\110\073\110\102\111"then w=c.Easing[K.TweenInfo.EasingDirection.Name][K.TweenInfo.EasingStyle.Name]else local Z,m Z=typeof(K.TweenInfo.EasingDirection)=="\069\110\117\109\073\116\101\109"and K.TweenInfo.EasingDirection.Name or K.TweenInfo.EasingDirection m=typeof(K.TweenInfo.EasingStyle)=="\069\110\117\109\073\116\101\109"and K.TweenInfo.EasingStyle.Name or K.TweenInfo.EasingStyle w=c.Easing[Z][m]if not K.TweenInfo.RepeatCount then K.TweenInfo.RepeatCount=0 end end local Z=tick local m=K._elapsedTime local D=K.TweenInfo.Time local C=Z()local P=K.Callback local n=K.TweenInfo.Reverses local N=K._elapsedDelayTime local J=K.TweenInfo.DelayTime local t=K._reversing local function H()P(w(D,0,1,D));(game:GetService("\082\117\110\083\101\114\118\105\099\101")):UnbindFromRenderStep(K._id)K.PlaybackState=Enum.PlaybackState.Completed K._completed:Fire(K.PlaybackState)K:ResetState()end local function T(c)if N>=J then return false end N=N+c K._elapsedDelayTime=N if N<J then K:SetPlaybackState(Enum.PlaybackState.Delayed)else K:SetPlaybackState(Enum.PlaybackState.Playing)end return N<J end;(game:GetService("\082\117\110\083\101\114\118\105\099\101")):BindToRenderStep(K._id,Enum.RenderPriority.Camera.Value-1,function()local c=Z()local J=c-C C=c if T(J)then return end m=m+J K._elapsedTime=m local I=m<D if I then if t then P(w(m,1,-1,D))else P(w(m,0,1,D))end else if K._repeated<K.TweenInfo.RepeatCount or t then if n then t=not t K._reversing=t end if not n or t then K._repeated=K._repeated+1 end if not t then K._elapsedDelayTime=0 N=0 end K._elapsedTime=0 m=0 else H()end end end)end function c.Pause(c)if not c._playing or c._paused then return end c._paused=true c:SetPlaybackState(Enum.PlaybackState.Paused);(game:GetService("\082\117\110\083\101\114\118\105\099\101")):UnbindFromRenderStep(c._id)end function c.Cancel(c)if not c._playing then return end;(game:GetService("\082\117\110\083\101\114\118\105\099\101")):UnbindFromRenderStep(c._id)c:ResetState()c:SetPlaybackState(Enum.PlaybackState.Cancelled)c._completed:Fire(c.PlaybackState)end function c.fromService(...)return(game:GetService("\084\119\101\101\110\083\101\114\118\105\099\101")):Create(...)end c.FromService=c.fromService c.New=c.new return c
-- Tween
-- Crazyman32
-- June 21, 2017

--[[
	
	Native:
		
		tween = Tween.FromService(instance, tweenInfo, properties)
		
		See Wiki page on Tween object for methods and such
		
		
	Custom:
	
		tween = Tween.new(tweenInfo, callbackFunction)
		
		tween.TweenInfo
		tween.Callback
		tween.PlaybackState
		
		tween:Play()
		tween:Pause()
		tween:Cancel()
		
		tween.Completed(playbackState)
		tween.PlaybackStateChanged(playbackState)
	
	
	Custom Example:
	
		tween = Tween.new(TweenInfo.new(2, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, 0, true, 0), function(n)
			print(n)
		end)
		
		tween:Play()
		tween.Completed:Wait()
	
--]]



local Tween = {}
Tween.__index = Tween


Tween.Easing = require(script:WaitForChild("Easing"))


function Tween.new(tweenInfo, callback)
	
	do
		-- Verify callback is function:
		assert(typeof(callback) == "function", "Callback argument must be a function")
		
		-- Verify tweenInfo:
		local typeOfTweenInfo = typeof(tweenInfo)
		assert(typeOfTweenInfo == "TweenInfo" or typeOfTweenInfo == "table", "TweenInfo must be of type TweenInfo or table")
		
		-- Defaults:
		if (typeOfTweenInfo == "table") then
			if (tweenInfo.Time == nil) then tweenInfo.Time = 1 end
			if (tweenInfo.EasingStyle == nil) then tweenInfo.EasingStyle = Enum.EasingStyle.Quad end
			if (tweenInfo.EasingDirection == nil) then tweenInfo.EasingDirection = Enum.EasingDirection.Out end
			if (tweenInfo.RepeatCount == nil) then tweenInfo.RepeatCount = 0 end
			if (tweenInfo.Reverses == nil) then tweenInfo.Reverses = false end
			if (tweenInfo.DelayTime == nil) then tweenInfo.DelayTime = 0 end
		end
		
	end
	
	local completed = Instance.new("BindableEvent")
	local playbackStateChanged = Instance.new("BindableEvent")
	
	local self = setmetatable({
		
		TweenInfo = tweenInfo;
		Callback = callback;
		PlaybackState = Enum.PlaybackState.Begin;
		Completed = completed.Event;
		PlaybackStateChanged = playbackStateChanged.Event;
		
		_id = "tween_" .. game:GetService("HttpService"):GenerateGUID(false);
		_playing = false;
		_paused = false;
		_completed = completed;
		_playbackStateChanged = playbackStateChanged;
		_elapsedTime = 0;
		_repeated = 0;
		_reversing = false;
		_elapsedDelayTime = 0;
		
	}, Tween)
	
	return self
	
end


function Tween:ResetState()
	self._playing = false
	self._paused = false
	self._elapsedTime = 0
	self._repeated = 0
	self._reversing = false
	self._elapsedDelayTime = 0
end


function Tween:SetPlaybackState(state)
	local lastState = self.PlaybackState
	self.PlaybackState = state
	if (state ~= lastState) then
		self._playbackStateChanged:Fire(state)
	end
end


function Tween:Play()
	
	if (self._playing and not self._paused) then return end
	self._playing = true
	self._paused = false
	
	-- Resolve easing function:
	local easingFunc
	if (typeof(self.TweenInfo) == "TweenInfo") then
		easingFunc = Tween.Easing[self.TweenInfo.EasingDirection.Name][self.TweenInfo.EasingStyle.Name]
	else
		local dir, style
		dir = typeof(self.TweenInfo.EasingDirection) == "EnumItem" and self.TweenInfo.EasingDirection.Name or self.TweenInfo.EasingDirection
		style = typeof(self.TweenInfo.EasingStyle) == "EnumItem" and self.TweenInfo.EasingStyle.Name or self.TweenInfo.EasingStyle
		easingFunc = Tween.Easing[dir][style]
		if (not self.TweenInfo.RepeatCount) then
			self.TweenInfo.RepeatCount = 0
		end
	end
	
	local tick = tick
	
	local elapsed = self._elapsedTime
	local duration = self.TweenInfo.Time
	local last = tick()
	local callback = self.Callback
	local reverses = self.TweenInfo.Reverses
	
	local elapsedDelay = self._elapsedDelayTime
	local durationDelay = self.TweenInfo.DelayTime
	
	local reversing = self._reversing
	
	local function OnCompleted()
		callback(easingFunc(duration, 0, 1, duration))
		game:GetService("RunService"):UnbindFromRenderStep(self._id)
		self.PlaybackState = Enum.PlaybackState.Completed
		self._completed:Fire(self.PlaybackState)
		self:ResetState()
	end
	
	local function IsDelayed(dt)
		if (elapsedDelay >= durationDelay) then return false end
		elapsedDelay = (elapsedDelay + dt)
		self._elapsedDelayTime = elapsedDelay
		if (elapsedDelay < durationDelay) then
			self:SetPlaybackState(Enum.PlaybackState.Delayed)
		else
			self:SetPlaybackState(Enum.PlaybackState.Playing)
		end
		return (elapsedDelay < durationDelay)
	end
	
	-- Tween:
	game:GetService("RunService"):BindToRenderStep(self._id, Enum.RenderPriority.Camera.Value - 1, function()
		local now = tick()
		local dt = (now - last)
		last = now
		if (IsDelayed(dt)) then return end
		elapsed = (elapsed + dt)
		self._elapsedTime = elapsed
		local notDone = (elapsed < duration)
		if (notDone) then
			if (reversing) then
				callback(easingFunc(elapsed, 1, -1, duration))
			else
				callback(easingFunc(elapsed, 0, 1, duration))
			end
		else
			if ((self._repeated < self.TweenInfo.RepeatCount) or reversing) then
				if (reverses) then
					reversing = (not reversing)
					self._reversing = reversing
				end
				if ((not reverses) or (reversing)) then
					self._repeated = (self._repeated + 1)
				end
				if (not reversing) then
					self._elapsedDelayTime = 0
					elapsedDelay = 0
				end
				self._elapsedTime = 0
				elapsed = 0
			else
				OnCompleted()
			end
		end
	end)
	
end


function Tween:Pause()
	if ((not self._playing) or (self._paused)) then return end
	self._paused = true
	self:SetPlaybackState(Enum.PlaybackState.Paused)
	game:GetService("RunService"):UnbindFromRenderStep(self._id)
end


function Tween:Cancel()
	if (not self._playing) then return end
	game:GetService("RunService"):UnbindFromRenderStep(self._id)
	self:ResetState()
	self:SetPlaybackState(Enum.PlaybackState.Cancelled)
	self._completed:Fire(self.PlaybackState)
end


function Tween.fromService(...)
	return game:GetService("TweenService"):Create(...)
end


Tween.FromService = Tween.fromService
Tween.New = Tween.new


return Tween