General purpose modules for Roblox development. [Read-only Codeberg mirror]
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 201 lines 5.5 kB view raw
1--!optimize 2 2--!strict 3local RunService = game:GetService("RunService") 4 5type TweenSequenceItem = number | Tween | { Tween } | () -> () 6export type TweenSequenceType = { TweenSequenceItem } 7 8type CurrentState = { 9 thread: thread?, 10 index: number, 11 elapsed: number, 12 heartbeat: RBXScriptConnection?, 13 activeTweenIndices: { number }, 14} 15 16type TweenSequenceImpl = { 17 __index: TweenSequenceImpl, 18 __tostring: (self: TweenSequence) -> string, 19 new: (sequence: TweenSequenceType) -> TweenSequence, 20 _connectHeartbeat: (self: TweenSequence) -> (), 21 _disconnectHeartbeat: (self: TweenSequence) -> (), 22 _runItem: (self: TweenSequence, item: TweenSequenceItem) -> (), 23 _resumeItem: (self: TweenSequence, item: TweenSequenceItem) -> (), 24 Play: (self: TweenSequence) -> (), 25 Pause: (self: TweenSequence) -> (), 26 Resume: (self: TweenSequence) -> (), 27} 28 29export type TweenSequence = typeof(setmetatable( 30 {} :: { 31 _sequence: TweenSequenceType, 32 _current: CurrentState, 33 Playing: boolean, 34 }, 35 {} :: TweenSequenceImpl 36 )) 37 38local TweenSequence = {} :: TweenSequenceImpl 39TweenSequence.__index = TweenSequence 40 41-- Creates a new <code>TweenSequence</code> from a sequence of items. 42-- Items can be <code>number</code> (wait), <code>Tween</code>, <code>{Tween}</code> (parallel), or <code>() -> ()</code> (callback). 43function TweenSequence.new(sequence: TweenSequenceType): TweenSequence 44 local self = setmetatable({}, TweenSequence) 45 self._sequence = sequence 46 self.Playing = false 47 self._current = { 48 thread = nil, 49 index = 0, 50 elapsed = 0, 51 heartbeat = nil, 52 activeTweenIndices = {}, 53 } 54 return self 55end 56 57function TweenSequence:_connectHeartbeat() 58 self._current.heartbeat = RunService.Heartbeat:Connect(function(dt: number) 59 self._current.elapsed += dt 60 end) 61end 62 63function TweenSequence:_disconnectHeartbeat() 64 if self._current.heartbeat then 65 self._current.heartbeat:Disconnect() 66 self._current.heartbeat = nil 67 end 68end 69 70function TweenSequence:_runItem(item: TweenSequenceItem) 71 if typeof(item) == "number" then 72 task.wait(item) 73 elseif typeof(item) == "table" then 74 self._current.activeTweenIndices = {} 75 for tweenIndex, tween in item do 76 tween:Play() 77 table.insert(self._current.activeTweenIndices, tweenIndex) 78 tween.Completed:Connect(function() 79 local idx = table.find(self._current.activeTweenIndices, tweenIndex) 80 if idx then 81 table.remove(self._current.activeTweenIndices, idx) 82 end 83 end) 84 end 85 local longest: Tween = item[1] 86 for _, tween in item do 87 if tween.TweenInfo.Time > longest.TweenInfo.Time then 88 longest = tween 89 end 90 end 91 longest.Completed:Wait() 92 elseif typeof(item) == "function" then 93 item() 94 else 95 item:Play() 96 item.Completed:Wait() 97 end 98end 99 100function TweenSequence:_resumeItem(item: TweenSequenceItem) 101 if typeof(item) == "number" then 102 local remaining = item - self._current.elapsed 103 if remaining > 0 then 104 task.wait(remaining) 105 end 106 elseif typeof(item) == "table" then 107 for _, tweenIndex in self._current.activeTweenIndices do 108 item[tweenIndex]:Play() 109 end 110 if #self._current.activeTweenIndices > 0 then 111 local longest: Tween = item[self._current.activeTweenIndices[1]] 112 for _, tweenIndex in self._current.activeTweenIndices do 113 local tween: Tween = item[tweenIndex] 114 if tween.TweenInfo.Time > longest.TweenInfo.Time then 115 longest = tween 116 end 117 end 118 longest.Completed:Wait() 119 end 120 else 121 local tween = item :: Tween 122 tween:Play() 123 tween.Completed:Wait() 124 end 125end 126 127-- Plays the sequence from the beginning. 128-- Sets <code>Playing</code> to <code>true</code> for the duration of playback. 129function TweenSequence:Play() 130 self._current.thread = task.spawn(function() 131 self.Playing = true 132 for index, item in self._sequence do 133 self._current.index = index 134 self._current.elapsed = 0 135 self._current.activeTweenIndices = {} 136 self:_connectHeartbeat() 137 self:_runItem(item) 138 self:_disconnectHeartbeat() 139 end 140 self.Playing = false 141 end) 142end 143 144-- Pauses the sequence at the current step. 145-- Pauses any actively playing <code>Tween</code> instances at that step. 146function TweenSequence:Pause() 147 self:_disconnectHeartbeat() 148 if self._current.thread then 149 task.cancel(self._current.thread) 150 self._current.thread = nil 151 end 152 local item = self._sequence[self._current.index] 153 if item then 154 if typeof(item) == "table" then 155 for _, tween in item do 156 if tween.PlaybackState == Enum.PlaybackState.Playing then 157 tween:Pause() 158 end 159 end 160 elseif typeof(item) ~= "number" and typeof(item) ~= "function" then 161 item:Pause() 162 end 163 end 164 self.Playing = false 165end 166 167-- Resumes the sequence from where it was paused. 168-- Completes the remaining duration of the current step before advancing. 169function TweenSequence:Resume() 170 if self._current.index == 0 or self.Playing then 171 return 172 end 173 self._current.thread = task.spawn(function() 174 self.Playing = true 175 local startIndex = self._current.index 176 self:_connectHeartbeat() 177 self:_resumeItem(self._sequence[startIndex]) 178 self:_disconnectHeartbeat() 179 for index = startIndex + 1, #self._sequence do 180 self._current.index = index 181 self._current.elapsed = 0 182 self._current.activeTweenIndices = {} 183 self:_connectHeartbeat() 184 self:_runItem(self._sequence[index]) 185 self:_disconnectHeartbeat() 186 end 187 self.Playing = false 188 end) 189end 190 191function TweenSequence:__tostring(): string 192 return ("TweenSequence(Playing=%s, step=%d/%d)"):format( 193 tostring(self.Playing), 194 self._current.index, 195 #self._sequence 196 ) 197end 198 199return { 200 Create = TweenSequence.new, 201}