local function A(A,x,X)local o={}local F local H,Y,w,e local Q if X~=nil then if A:IsA("\077\111\100\101\108")then Q=A.PrimaryPart if Q==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 A:IsA("\066\097\115\101\080\097\114\116")then Q=A 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 F=Q.CFrame:inverse()H=F.Position Y=F-H w=X.Position e=X-w end VisitAllInstances(A,x,function(A,x)for x,H in pairs(x)do if X and A:IsA("\066\097\115\101\080\097\114\116")then local A=ApplyOrigin(x,H.InitialValue,F,X)if A then H.InitialValue=A end end local Y=H.Keyframes local w=0 for H,Y in ipairs(Y)do if X and A:IsA("\066\097\115\101\080\097\114\116")then local A=ApplyOrigin(x,Y.Value,F,X)if A then Y.Value=A end end local e=TweenInfo.new(Y.Time-w,Y.EasingStyle,Y.EasingDirection,0,false,0)local Q={[x]=Y.Value}local y=TweenService:Create(A,e,Q)table.insert(o,{Tween=y,DelayTime=w})w=Y.Time end end end)return o end local x={}x.__index=x function x.new(X,o)local F={running=false;root=X,tweenTable=o,tweens=A(X,o),origin=nil}setmetatable(F,x)return F end function x.ResetValues(A)if A.origin then if A.root:IsA("\077\111\100\101\108")then if A.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 A.root:SetPrimaryPartCFrame(A.origin)elseif A.root:IsA("\066\097\115\101\080\097\114\116")then A.root.CFrame=A.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(A.root,A.tweenTable,function(A,x)for x,X in pairs(x)do local o=X.InitialValue A[x]=o end end)end function x.SetOrigin(X,x)if X.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 X.origin=x X.tweens=A(X.root,X.tweenTable,x)end function x.PlayTweens(x,A)if x.running then x:PauseTweens()end x.running=true local X={}for A,x in pairs(x.tweens)do local o=x.Tween local F=x.DelayTime if F==0 then o:Play()else table.insert(X,{Tween=o;DelayTime=F;Playing=false})end end local o local F=tick()local H=false o=RunService.Heartbeat:Connect(function()local Y=tick()-F if not x.running then x.running=false o:Disconnect()if A then A()end return end if H then local X=true for A,x in pairs(x.tweens)do local o=x.Tween if o.PlaybackState==Enum.PlaybackState.Playing then X=false end end if X then x.running=false o:Disconnect()if A then A()end return end else local A=true for x,X in pairs(X)do if not X.Playing then if Y>X.DelayTime then X.Playing=true X.Tween:Play()else A=false end end end H=A end end)end function x.PauseTweens(A)A.running=false for A,x in pairs(A.tweens)do local X=x.Tween X:Cancel()end end return x

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
