local function t(t,U,D)local j={}local u local L,T,b,P local k if D~=nil then if t:IsA("\077\111\100\101\108")then k=t.PrimaryPart if k==nil then error("\082\111\111\116 \077\111\100\101\108 \109\117\115\116 \104\097\118\101 \097 \080\114\105\109\097\114\121\080\097\114\116 \116\111 \117\115\101 \083\101\116\079\114\105\103\105\110\046")end elseif t:IsA("\066\097\115\101\080\097\114\116")then k=t else error("\082\111\111\116 \109\117\115\116 \098\101 \097 \066\097\115\101\080\097\114\116 \111\114 \077\111\100\101\108 \116\111 \117\115\101 \083\101\116\079\114\105\103\105\110\046")end u=k.CFrame:inverse()L=u.Position T=u-L b=D.Position P=D-b end VisitAllInstances(t,U,function(t,U)for U,L in pairs(U)do if D and t:IsA("\066\097\115\101\080\097\114\116")then local t=ApplyOrigin(U,L.InitialValue,u,D)if t then L.InitialValue=t end end local T=L.Keyframes local b=0 for L,T in ipairs(T)do if D and t:IsA("\066\097\115\101\080\097\114\116")then local t=ApplyOrigin(U,T.Value,u,D)if t then T.Value=t end end local P=TweenInfo.new(T.Time-b,T.EasingStyle,T.EasingDirection,0,false,0)local k={[U]=T.Value}local q=TweenService:Create(t,P,k)table.insert(j,{Tween=q,DelayTime=b})b=T.Time end end end)return j end local U={}U.__index=U function U.new(D,j)local u={running=false,root=D;tweenTable=j;tweens=t(D,j);origin=nil}setmetatable(u,U)return u end function U.ResetValues(t)if t.origin then if t.root:IsA("\077\111\100\101\108")then if t.root.PrimaryPart==nil then error("\082\111\111\116 \077\111\100\101\108 \109\117\115\116 \104\097\118\101 \097 \080\114\105\109\097\114\121\080\097\114\116 \116\111 \117\115\101 \083\101\116\079\114\105\103\105\110\046")end t.root:SetPrimaryPartCFrame(t.origin)elseif t.root:IsA("\066\097\115\101\080\097\114\116")then t.root.CFrame=t.origin else error("\082\111\111\116 \109\117\115\116 \098\101 \097 \066\097\115\101\080\097\114\116 \111\114 \077\111\100\101\108 \116\111 \117\115\101 \083\101\116\079\114\105\103\105\110\046")end end VisitAllInstances(t.root,t.tweenTable,function(t,U)for U,D in pairs(U)do local j=D.InitialValue t[U]=j end end)end function U.SetOrigin(D,U)if D.running then error("\067\097\110\110\111\116 \083\101\116\079\114\105\103\105\110 \119\104\105\108\101 \116\104\101 \097\110\105\109\097\116\105\111\110 \105\115 \114\117\110\110\105\110\103\046")end D.origin=U D.tweens=t(D.root,D.tweenTable,U)end function U.PlayTweens(U,t)if U.running then U:PauseTweens()end U.running=true local D={}for t,U in pairs(U.tweens)do local j=U.Tween local u=U.DelayTime if u==0 then j:Play()else table.insert(D,{Tween=j;DelayTime=u,Playing=false})end end local j local u=tick()local L=false j=RunService.Heartbeat:Connect(function()local T=tick()-u if not U.running then U.running=false j:Disconnect()if t then t()end return end if L then local D=true for t,U in pairs(U.tweens)do local j=U.Tween if j.PlaybackState==Enum.PlaybackState.Playing then D=false end end if D then U.running=false j:Disconnect()if t then t()end return end else local t=true for U,D in pairs(D)do if not D.Playing then if T>D.DelayTime then D.Playing=true D.Tween:Play()else t=false end end end L=t end end)end function U.PauseTweens(t)t.running=false for t,U in pairs(t.tweens)do local D=U.Tween D:Cancel()end end return U
-- Builds a set of runnable Tweens from a table of instances and values
local function BuildTweens(root, tweenTable, origin)
	local tweens = {}

	local initialFrame
	local initialPos, initialRot, originPos, originRot
	local center
	if origin ~= nil then
		if root:IsA("Model") then
			center = root.PrimaryPart
			if center == nil then
				error("Root Model must have a PrimaryPart to use SetOrigin.")
			end
		elseif root:IsA("BasePart") then
			center = root
		else
			error("Root must be a BasePart or Model to use SetOrigin.")
		end
		initialFrame = center.CFrame:inverse()
		initialPos = initialFrame.Position
		initialRot = initialFrame - initialPos
		originPos = origin.Position
		originRot = origin - originPos
	end

	VisitAllInstances(root, tweenTable, function(instance, props)
		for prop, values in pairs(props) do
			if origin and instance:IsA("BasePart") then
				local newValue = ApplyOrigin(prop, values.InitialValue, initialFrame, origin)
				if newValue then
					values.InitialValue = newValue
				end
			end

			local keyframes = values.Keyframes
			local lastTime = 0
			for _, keyframe in ipairs(keyframes) do
				if origin and instance:IsA("BasePart") then
					local newValue = ApplyOrigin(prop, keyframe.Value, initialFrame, origin)
					if newValue then
						keyframe.Value = newValue
					end
				end

				local tweenInfo =
					TweenInfo.new(keyframe.Time - lastTime, keyframe.EasingStyle, keyframe.EasingDirection, 0, false, 0)
				local propTable = {
					[prop] = keyframe.Value,
				}
				local tween = TweenService:Create(instance, tweenInfo, propTable)
				table.insert(tweens, {
					Tween = tween,
					DelayTime = lastTime,
				})
				lastTime = keyframe.Time
			end
		end
	end)

	return tweens
end

local TweenUtilities = {}
TweenUtilities.__index = TweenUtilities

function TweenUtilities.new(root, tweenTable)
	local self = {
		running = false,
		root = root,
		tweenTable = tweenTable,
		tweens = BuildTweens(root, tweenTable),
		origin = nil,
	}
	setmetatable(self, TweenUtilities)
	return self
end

function TweenUtilities:ResetValues()
	if self.origin then
		if self.root:IsA("Model") then
			if self.root.PrimaryPart == nil then
				error("Root Model must have a PrimaryPart to use SetOrigin.")
			end
			self.root:SetPrimaryPartCFrame(self.origin)
		elseif self.root:IsA("BasePart") then
			self.root.CFrame = self.origin
		else
			error("Root must be a BasePart or Model to use SetOrigin.")
		end
	end

	VisitAllInstances(self.root, self.tweenTable, function(instance, props)
		for prop, values in pairs(props) do
			local initialValue = values.InitialValue
			instance[prop] = initialValue
		end
	end)
end

function TweenUtilities:SetOrigin(origin)
	if self.running then
		error("Cannot SetOrigin while the animation is running.")
	end
	self.origin = origin
	self.tweens = BuildTweens(self.root, self.tweenTable, origin)
end

function TweenUtilities:PlayTweens(callback)
	if self.running then
		self:PauseTweens()
	end
	self.running = true

	local delayedTweens = {}
	for _, props in pairs(self.tweens) do
		local tween = props.Tween
		local delayTime = props.DelayTime
		if delayTime == 0 then
			tween:Play()
		else
			table.insert(delayedTweens, {
				Tween = tween,
				DelayTime = delayTime,
				Playing = false,
			})
		end
	end

	local heartbeatConnection
	local start = tick()
	local tweensDone = false
	heartbeatConnection = RunService.Heartbeat:Connect(function()
		local now = tick() - start
		if not self.running then
			self.running = false
			heartbeatConnection:Disconnect()
			if callback then
				callback()
			end
			return
		end
		if tweensDone then
			local allDone = true
			for _, props in pairs(self.tweens) do
				local tween = props.Tween
				if tween.PlaybackState == Enum.PlaybackState.Playing then
					allDone = false
				end
			end
			if allDone then
				self.running = false
				heartbeatConnection:Disconnect()
				if callback then
					callback()
				end
				return
			end
		else
			local allPlaying = true
			for _, props in pairs(delayedTweens) do
				if not props.Playing then
					if now > props.DelayTime then
						props.Playing = true
						props.Tween:Play()
					else
						allPlaying = false
					end
				end
			end
			tweensDone = allPlaying
		end
	end)
end

function TweenUtilities:PauseTweens()
	self.running = false
	for _, props in pairs(self.tweens) do
		local tween = props.Tween
		tween:Cancel()
	end
end

return TweenUtilities
