local F={}F.__index=F F.Easing=require(script:WaitForChild("\069\097\115\105\110\103"))function F.new(H,T)do assert(typeof(T)=="\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 F=typeof(H)assert(F=="\084\119\101\101\110\073\110\102\111"or F=="\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 F=="\116\097\098\108\101"then if H.Time==nil then H.Time=1 end if H.EasingStyle==nil then H.EasingStyle=Enum.EasingStyle.Quad end if H.EasingDirection==nil then H.EasingDirection=Enum.EasingDirection.Out end if H.RepeatCount==nil then H.RepeatCount=0 end if H.Reverses==nil then H.Reverses=false end if H.DelayTime==nil then H.DelayTime=0 end end end local C=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")local b=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")local A=setmetatable({TweenInfo=H,Callback=T;PlaybackState=Enum.PlaybackState.Begin;Completed=C.Event,PlaybackStateChanged=b.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=C,_playbackStateChanged=b;_elapsedTime=0;_repeated=0;_reversing=false;_elapsedDelayTime=0},F)return A end function F.ResetState(F)F._playing=false F._paused=false F._elapsedTime=0 F._repeated=0 F._reversing=false F._elapsedDelayTime=0 end function F.SetPlaybackState(H,F)local T=H.PlaybackState H.PlaybackState=F if F~=T then H._playbackStateChanged:Fire(F)end end function F.Play(H)if H._playing and not H._paused then return end H._playing=true H._paused=false local T if typeof(H.TweenInfo)=="\084\119\101\101\110\073\110\102\111"then T=F.Easing[H.TweenInfo.EasingDirection.Name][H.TweenInfo.EasingStyle.Name]else local C,b C=typeof(H.TweenInfo.EasingDirection)=="\069\110\117\109\073\116\101\109"and H.TweenInfo.EasingDirection.Name or H.TweenInfo.EasingDirection b=typeof(H.TweenInfo.EasingStyle)=="\069\110\117\109\073\116\101\109"and H.TweenInfo.EasingStyle.Name or H.TweenInfo.EasingStyle T=F.Easing[C][b]if not H.TweenInfo.RepeatCount then H.TweenInfo.RepeatCount=0 end end local C=tick local b=H._elapsedTime local A=H.TweenInfo.Time local E=C()local o=H.Callback local P=H.TweenInfo.Reverses local i=H._elapsedDelayTime local L=H.TweenInfo.DelayTime local Y=H._reversing local function B()o(T(A,0,1,A));(game:GetService("\082\117\110\083\101\114\118\105\099\101")):UnbindFromRenderStep(H._id)H.PlaybackState=Enum.PlaybackState.Completed H._completed:Fire(H.PlaybackState)H:ResetState()end local function W(F)if i>=L then return false end i=i+F H._elapsedDelayTime=i if i<L then H:SetPlaybackState(Enum.PlaybackState.Delayed)else H:SetPlaybackState(Enum.PlaybackState.Playing)end return i<L end;(game:GetService("\082\117\110\083\101\114\118\105\099\101")):BindToRenderStep(H._id,Enum.RenderPriority.Camera.Value-1,function()local F=C()local L=F-E E=F if W(L)then return end b=b+L H._elapsedTime=b local k=b<A if k then if Y then o(T(b,1,-1,A))else o(T(b,0,1,A))end else if H._repeated<H.TweenInfo.RepeatCount or Y then if P then Y=not Y H._reversing=Y end if not P or Y then H._repeated=H._repeated+1 end if not Y then H._elapsedDelayTime=0 i=0 end H._elapsedTime=0 b=0 else B()end end end)end function F.Pause(F)if not F._playing or F._paused then return end F._paused=true F:SetPlaybackState(Enum.PlaybackState.Paused);(game:GetService("\082\117\110\083\101\114\118\105\099\101")):UnbindFromRenderStep(F._id)end function F.Cancel(F)if not F._playing then return end;(game:GetService("\082\117\110\083\101\114\118\105\099\101")):UnbindFromRenderStep(F._id)F:ResetState()F:SetPlaybackState(Enum.PlaybackState.Cancelled)F._completed:Fire(F.PlaybackState)end function F.fromService(...)return(game:GetService("\084\119\101\101\110\083\101\114\118\105\099\101")):Create(...)end F.FromService=F.fromService F.New=F.new return F
--[[
	
	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
