roblox-opencode 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. package/README.md +122 -0
  2. package/commands/setup-game.md +108 -0
  3. package/commands/sync-check.md +53 -0
  4. package/core/roblox-core.md +93 -0
  5. package/dist/server.js +167 -0
  6. package/package.json +35 -0
  7. package/skills/roblox-analytics/SKILL.md +277 -0
  8. package/skills/roblox-analytics/references/event-batcher.luau +75 -0
  9. package/skills/roblox-animation-vfx/SKILL.md +1325 -0
  10. package/skills/roblox-architecture/SKILL.md +863 -0
  11. package/skills/roblox-architecture/references/combat-systems.md +1381 -0
  12. package/skills/roblox-code-review/SKILL.md +687 -0
  13. package/skills/roblox-data/SKILL.md +889 -0
  14. package/skills/roblox-data/references/inventory-systems.md +1729 -0
  15. package/skills/roblox-debug/SKILL.md +99 -0
  16. package/skills/roblox-gui/SKILL.md +1103 -0
  17. package/skills/roblox-gui-fusion/SKILL.md +150 -0
  18. package/skills/roblox-gui-fusion/references/inventory.luau +427 -0
  19. package/skills/roblox-gui-fusion/references/settings-menu.luau +579 -0
  20. package/skills/roblox-gui-fusion/references/shop.luau +411 -0
  21. package/skills/roblox-luau-mastery/SKILL.md +1519 -0
  22. package/skills/roblox-monetization/SKILL.md +1084 -0
  23. package/skills/roblox-monetization/references/process-receipt.luau +131 -0
  24. package/skills/roblox-networking/SKILL.md +669 -0
  25. package/skills/roblox-networking/references/remote-validator.luau +193 -0
  26. package/skills/roblox-publish-checklist/SKILL.md +128 -0
  27. package/skills/roblox-runtime/SKILL.md +753 -0
  28. package/skills/roblox-sharp-edges/SKILL.md +295 -0
  29. package/skills/roblox-sync/SKILL.md +126 -0
  30. package/skills/roblox-testing/SKILL.md +943 -0
  31. package/skills/roblox-tooling/SKILL.md +150 -0
  32. package/vendor/LICENSES/ProfileStore-LICENSE +201 -0
  33. package/vendor/LICENSES/RbxUtil-LICENSE +7 -0
  34. package/vendor/LICENSES/promise-LICENSE +21 -0
  35. package/vendor/LICENSES/t-LICENSE +21 -0
  36. package/vendor/LICENSES/testez-LICENSE +201 -0
  37. package/vendor/README.md +84 -0
  38. package/vendor/fusion/Animation/ExternalTime.luau +84 -0
  39. package/vendor/fusion/Animation/Spring.luau +322 -0
  40. package/vendor/fusion/Animation/Stopwatch.luau +128 -0
  41. package/vendor/fusion/Animation/Tween.luau +187 -0
  42. package/vendor/fusion/Animation/getTweenDuration.luau +27 -0
  43. package/vendor/fusion/Animation/getTweenRatio.luau +47 -0
  44. package/vendor/fusion/Animation/lerpType.luau +164 -0
  45. package/vendor/fusion/Animation/packType.luau +100 -0
  46. package/vendor/fusion/Animation/springCoefficients.luau +80 -0
  47. package/vendor/fusion/Animation/unpackType.luau +103 -0
  48. package/vendor/fusion/Colour/Oklab.luau +70 -0
  49. package/vendor/fusion/Colour/sRGB.luau +55 -0
  50. package/vendor/fusion/External.luau +168 -0
  51. package/vendor/fusion/ExternalDebug.luau +70 -0
  52. package/vendor/fusion/Graph/Observer.luau +114 -0
  53. package/vendor/fusion/Graph/castToGraph.luau +29 -0
  54. package/vendor/fusion/Graph/change.luau +81 -0
  55. package/vendor/fusion/Graph/depend.luau +33 -0
  56. package/vendor/fusion/Graph/evaluate.luau +56 -0
  57. package/vendor/fusion/Instances/Attribute.luau +58 -0
  58. package/vendor/fusion/Instances/AttributeChange.luau +47 -0
  59. package/vendor/fusion/Instances/AttributeOut.luau +63 -0
  60. package/vendor/fusion/Instances/Child.luau +21 -0
  61. package/vendor/fusion/Instances/Children.luau +148 -0
  62. package/vendor/fusion/Instances/Hydrate.luau +33 -0
  63. package/vendor/fusion/Instances/New.luau +53 -0
  64. package/vendor/fusion/Instances/OnChange.luau +50 -0
  65. package/vendor/fusion/Instances/OnEvent.luau +54 -0
  66. package/vendor/fusion/Instances/Out.luau +69 -0
  67. package/vendor/fusion/Instances/applyInstanceProps.luau +149 -0
  68. package/vendor/fusion/Instances/defaultProps.luau +194 -0
  69. package/vendor/fusion/LICENSE +21 -0
  70. package/vendor/fusion/Logging/formatError.luau +49 -0
  71. package/vendor/fusion/Logging/messages.luau +52 -0
  72. package/vendor/fusion/Logging/parseError.luau +25 -0
  73. package/vendor/fusion/Memory/checkLifetime.luau +134 -0
  74. package/vendor/fusion/Memory/deriveScope.luau +24 -0
  75. package/vendor/fusion/Memory/deriveScopeImpl.luau +45 -0
  76. package/vendor/fusion/Memory/doCleanup.luau +79 -0
  77. package/vendor/fusion/Memory/innerScope.luau +34 -0
  78. package/vendor/fusion/Memory/legacyCleanup.luau +18 -0
  79. package/vendor/fusion/Memory/needsDestruction.luau +17 -0
  80. package/vendor/fusion/Memory/poisonScope.luau +34 -0
  81. package/vendor/fusion/Memory/scopePool.luau +55 -0
  82. package/vendor/fusion/Memory/scoped.luau +27 -0
  83. package/vendor/fusion/Memory/whichLivesLonger.luau +75 -0
  84. package/vendor/fusion/RobloxExternal.luau +98 -0
  85. package/vendor/fusion/State/Computed.luau +139 -0
  86. package/vendor/fusion/State/For/Disassembly.luau +211 -0
  87. package/vendor/fusion/State/For/ForTypes.luau +30 -0
  88. package/vendor/fusion/State/For/init.luau +110 -0
  89. package/vendor/fusion/State/ForKeys.luau +94 -0
  90. package/vendor/fusion/State/ForPairs.luau +97 -0
  91. package/vendor/fusion/State/ForValues.luau +94 -0
  92. package/vendor/fusion/State/Value.luau +88 -0
  93. package/vendor/fusion/State/castToState.luau +26 -0
  94. package/vendor/fusion/State/peek.luau +31 -0
  95. package/vendor/fusion/State/updateAll.luau +1 -0
  96. package/vendor/fusion/Types.luau +314 -0
  97. package/vendor/fusion/Utility/Contextual.luau +91 -0
  98. package/vendor/fusion/Utility/Safe.luau +23 -0
  99. package/vendor/fusion/Utility/isSimilar.luau +29 -0
  100. package/vendor/fusion/Utility/merge.luau +35 -0
  101. package/vendor/fusion/Utility/nameOf.luau +35 -0
  102. package/vendor/fusion/Utility/never.luau +14 -0
  103. package/vendor/fusion/Utility/nicknames.luau +11 -0
  104. package/vendor/fusion/Utility/xtypeof.luau +27 -0
  105. package/vendor/fusion/init.luau +82 -0
  106. package/vendor/profilestore/init.luau +2243 -0
  107. package/vendor/promise/init.luau +1982 -0
  108. package/vendor/rbxutil/buffer-util/Buffer.test.luau +25 -0
  109. package/vendor/rbxutil/buffer-util/BufferReader.luau +228 -0
  110. package/vendor/rbxutil/buffer-util/BufferWriter.luau +269 -0
  111. package/vendor/rbxutil/buffer-util/DataTypeBuffer.luau +223 -0
  112. package/vendor/rbxutil/buffer-util/Types.luau +60 -0
  113. package/vendor/rbxutil/buffer-util/index.d.ts +153 -0
  114. package/vendor/rbxutil/buffer-util/init.luau +41 -0
  115. package/vendor/rbxutil/buffer-util/package.json +16 -0
  116. package/vendor/rbxutil/buffer-util/wally.toml +9 -0
  117. package/vendor/rbxutil/comm/Client/ClientComm.luau +232 -0
  118. package/vendor/rbxutil/comm/Client/ClientRemoteProperty.luau +156 -0
  119. package/vendor/rbxutil/comm/Client/ClientRemoteSignal.luau +109 -0
  120. package/vendor/rbxutil/comm/Client/init.luau +135 -0
  121. package/vendor/rbxutil/comm/Server/RemoteProperty.luau +295 -0
  122. package/vendor/rbxutil/comm/Server/RemoteSignal.luau +211 -0
  123. package/vendor/rbxutil/comm/Server/ServerComm.luau +211 -0
  124. package/vendor/rbxutil/comm/Server/init.luau +140 -0
  125. package/vendor/rbxutil/comm/Types.luau +18 -0
  126. package/vendor/rbxutil/comm/Util.luau +27 -0
  127. package/vendor/rbxutil/comm/init.luau +35 -0
  128. package/vendor/rbxutil/comm/wally.toml +13 -0
  129. package/vendor/rbxutil/component/init.luau +759 -0
  130. package/vendor/rbxutil/component/init.test.luau +311 -0
  131. package/vendor/rbxutil/component/wally.toml +14 -0
  132. package/vendor/rbxutil/concur/init.luau +542 -0
  133. package/vendor/rbxutil/concur/init.test.luau +364 -0
  134. package/vendor/rbxutil/concur/wally.toml +8 -0
  135. package/vendor/rbxutil/enum-list/init.luau +101 -0
  136. package/vendor/rbxutil/enum-list/init.test.luau +91 -0
  137. package/vendor/rbxutil/enum-list/wally.toml +8 -0
  138. package/vendor/rbxutil/find/index.d.ts +20 -0
  139. package/vendor/rbxutil/find/init.luau +44 -0
  140. package/vendor/rbxutil/find/package.json +17 -0
  141. package/vendor/rbxutil/find/wally.toml +8 -0
  142. package/vendor/rbxutil/input/Gamepad.luau +559 -0
  143. package/vendor/rbxutil/input/Keyboard.luau +124 -0
  144. package/vendor/rbxutil/input/Mouse.luau +278 -0
  145. package/vendor/rbxutil/input/PreferredInput.luau +91 -0
  146. package/vendor/rbxutil/input/Touch.luau +120 -0
  147. package/vendor/rbxutil/input/init.luau +33 -0
  148. package/vendor/rbxutil/input/wally.toml +12 -0
  149. package/vendor/rbxutil/loader/index.d.ts +15 -0
  150. package/vendor/rbxutil/loader/init.luau +137 -0
  151. package/vendor/rbxutil/loader/wally.toml +8 -0
  152. package/vendor/rbxutil/log/index.d.ts +38 -0
  153. package/vendor/rbxutil/log/init.luau +746 -0
  154. package/vendor/rbxutil/log/wally.toml +8 -0
  155. package/vendor/rbxutil/net/init.luau +190 -0
  156. package/vendor/rbxutil/net/wally.toml +8 -0
  157. package/vendor/rbxutil/option/index.d.ts +44 -0
  158. package/vendor/rbxutil/option/init.luau +489 -0
  159. package/vendor/rbxutil/option/init.test.luau +342 -0
  160. package/vendor/rbxutil/option/wally.toml +8 -0
  161. package/vendor/rbxutil/pid/index.d.ts +53 -0
  162. package/vendor/rbxutil/pid/init.luau +195 -0
  163. package/vendor/rbxutil/pid/package.json +16 -0
  164. package/vendor/rbxutil/pid/wally.toml +9 -0
  165. package/vendor/rbxutil/quaternion/index.d.ts +117 -0
  166. package/vendor/rbxutil/quaternion/init.luau +570 -0
  167. package/vendor/rbxutil/quaternion/package.json +16 -0
  168. package/vendor/rbxutil/quaternion/wally.toml +9 -0
  169. package/vendor/rbxutil/query/index.d.ts +43 -0
  170. package/vendor/rbxutil/query/init.luau +117 -0
  171. package/vendor/rbxutil/query/package.json +18 -0
  172. package/vendor/rbxutil/query/wally.toml +9 -0
  173. package/vendor/rbxutil/sequent/index.d.ts +28 -0
  174. package/vendor/rbxutil/sequent/init.luau +340 -0
  175. package/vendor/rbxutil/sequent/package.json +16 -0
  176. package/vendor/rbxutil/sequent/wally.toml +9 -0
  177. package/vendor/rbxutil/ser/init.luau +175 -0
  178. package/vendor/rbxutil/ser/init.test.luau +50 -0
  179. package/vendor/rbxutil/ser/wally.toml +11 -0
  180. package/vendor/rbxutil/shake/index.d.ts +36 -0
  181. package/vendor/rbxutil/shake/init.luau +532 -0
  182. package/vendor/rbxutil/shake/init.test.luau +267 -0
  183. package/vendor/rbxutil/shake/package.json +16 -0
  184. package/vendor/rbxutil/shake/wally.toml +9 -0
  185. package/vendor/rbxutil/signal/index.d.ts +100 -0
  186. package/vendor/rbxutil/signal/init.luau +432 -0
  187. package/vendor/rbxutil/signal/init.test.luau +190 -0
  188. package/vendor/rbxutil/signal/package.json +17 -0
  189. package/vendor/rbxutil/signal/wally.toml +9 -0
  190. package/vendor/rbxutil/silo/TableWatcher.luau +65 -0
  191. package/vendor/rbxutil/silo/Util.luau +55 -0
  192. package/vendor/rbxutil/silo/init.luau +338 -0
  193. package/vendor/rbxutil/silo/init.test.luau +215 -0
  194. package/vendor/rbxutil/silo/wally.toml +8 -0
  195. package/vendor/rbxutil/spring/index.d.ts +40 -0
  196. package/vendor/rbxutil/spring/init.luau +97 -0
  197. package/vendor/rbxutil/spring/package.json +17 -0
  198. package/vendor/rbxutil/spring/wally.toml +8 -0
  199. package/vendor/rbxutil/stream/index.d.ts +88 -0
  200. package/vendor/rbxutil/stream/init.luau +597 -0
  201. package/vendor/rbxutil/stream/package.json +18 -0
  202. package/vendor/rbxutil/stream/wally.toml +9 -0
  203. package/vendor/rbxutil/streamable/Streamable.luau +202 -0
  204. package/vendor/rbxutil/streamable/StreamableUtil.luau +80 -0
  205. package/vendor/rbxutil/streamable/init.luau +8 -0
  206. package/vendor/rbxutil/streamable/wally.toml +12 -0
  207. package/vendor/rbxutil/symbol/init.luau +56 -0
  208. package/vendor/rbxutil/symbol/init.test.luau +37 -0
  209. package/vendor/rbxutil/symbol/wally.toml +8 -0
  210. package/vendor/rbxutil/table-util/init.luau +938 -0
  211. package/vendor/rbxutil/table-util/init.test.luau +439 -0
  212. package/vendor/rbxutil/table-util/wally.toml +8 -0
  213. package/vendor/rbxutil/task-queue/index.d.ts +27 -0
  214. package/vendor/rbxutil/task-queue/init.luau +97 -0
  215. package/vendor/rbxutil/task-queue/wally.toml +8 -0
  216. package/vendor/rbxutil/timer/index.d.ts +81 -0
  217. package/vendor/rbxutil/timer/init.luau +249 -0
  218. package/vendor/rbxutil/timer/init.test.luau +73 -0
  219. package/vendor/rbxutil/timer/wally.toml +11 -0
  220. package/vendor/rbxutil/tree/index.d.ts +15 -0
  221. package/vendor/rbxutil/tree/init.luau +137 -0
  222. package/vendor/rbxutil/tree/wally.toml +8 -0
  223. package/vendor/rbxutil/trove/index.d.ts +46 -0
  224. package/vendor/rbxutil/trove/init.luau +787 -0
  225. package/vendor/rbxutil/trove/init.test.luau +203 -0
  226. package/vendor/rbxutil/trove/wally.toml +8 -0
  227. package/vendor/rbxutil/typed-remote/init.luau +196 -0
  228. package/vendor/rbxutil/typed-remote/wally.toml +8 -0
  229. package/vendor/rbxutil/wait-for/index.d.ts +17 -0
  230. package/vendor/rbxutil/wait-for/init.luau +257 -0
  231. package/vendor/rbxutil/wait-for/init.test.luau +182 -0
  232. package/vendor/rbxutil/wait-for/wally.toml +11 -0
  233. package/vendor/t/t.lua +1350 -0
  234. package/vendor/testez/Context.lua +26 -0
  235. package/vendor/testez/Expectation.lua +311 -0
  236. package/vendor/testez/ExpectationContext.lua +38 -0
  237. package/vendor/testez/LifecycleHooks.lua +89 -0
  238. package/vendor/testez/Reporters/TeamCityReporter.lua +102 -0
  239. package/vendor/testez/Reporters/TextReporter.lua +106 -0
  240. package/vendor/testez/Reporters/TextReporterQuiet.lua +97 -0
  241. package/vendor/testez/TestBootstrap.lua +147 -0
  242. package/vendor/testez/TestEnum.lua +28 -0
  243. package/vendor/testez/TestPlan.lua +304 -0
  244. package/vendor/testez/TestPlanner.lua +40 -0
  245. package/vendor/testez/TestResults.lua +112 -0
  246. package/vendor/testez/TestRunner.lua +188 -0
  247. package/vendor/testez/TestSession.lua +243 -0
  248. package/vendor/testez/init.lua +40 -0
@@ -0,0 +1,187 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ A specialised state object for following a goal state smoothly over time,
8
+ using a TweenInfo to shape the motion.
9
+
10
+ https://elttob.uk/Fusion/0.3/api-reference/animation/types/tween/
11
+ ]]
12
+
13
+ local Package = script.Parent.Parent
14
+ local Types = require(Package.Types)
15
+ local External = require(Package.External)
16
+ -- Memory
17
+ local checkLifetime = require(Package.Memory.checkLifetime)
18
+ -- Graph
19
+ local depend = require(Package.Graph.depend)
20
+ local evaluate = require(Package.Graph.evaluate)
21
+ -- State
22
+ local castToState = require(Package.State.castToState)
23
+ local peek = require(Package.State.peek)
24
+ -- Animation
25
+ local ExternalTime = require(Package.Animation.ExternalTime)
26
+ local Stopwatch = require(Package.Animation.Stopwatch)
27
+ local lerpType = require(Package.Animation.lerpType)
28
+ local getTweenRatio = require(Package.Animation.getTweenRatio)
29
+ local getTweenDuration = require(Package.Animation.getTweenDuration)
30
+ -- Utility
31
+ local nicknames = require(Package.Utility.nicknames)
32
+
33
+ export type Self<T> = Types.Tween<T> & {
34
+ _activeDuration: number,
35
+ _activeElapsed: number,
36
+ _activeFrom: T,
37
+ _activeTo: T,
38
+ _activeTweenInfo: TweenInfo,
39
+ _goal: Types.UsedAs<T>,
40
+ _stopwatch: Stopwatch.Stopwatch?,
41
+ _tweenInfo: Types.UsedAs<TweenInfo>,
42
+ }
43
+
44
+ local class = {}
45
+ class.type = "State"
46
+ class.kind = "Tween"
47
+ class.timeliness = "eager"
48
+
49
+ local METATABLE = table.freeze {__index = class}
50
+
51
+ local function Tween<T>(
52
+ scope: Types.Scope<unknown>,
53
+ goal: Types.UsedAs<T>,
54
+ tweenInfo: Types.UsedAs<TweenInfo>?
55
+ ): Types.Tween<T>
56
+ local createdAt = os.clock()
57
+ if castToState(scope) then
58
+ External.logError("scopeMissing", nil, "Tweens", "myScope:Tween(goalState, tweenInfo)")
59
+ end
60
+
61
+ local goalState = castToState(goal)
62
+ local stopwatch = nil
63
+ if goalState ~= nil then
64
+ stopwatch = Stopwatch(scope, ExternalTime(scope))
65
+ end
66
+
67
+ local self: Self<T> = setmetatable(
68
+ {
69
+ createdAt = createdAt,
70
+ dependencySet = {},
71
+ dependentSet = {},
72
+ lastChange = nil,
73
+ scope = scope,
74
+ validity = "invalid",
75
+ _activeDuration = nil,
76
+ _activeElapsed = nil,
77
+ _activeFrom = nil,
78
+ _activeTo = nil,
79
+ _activeTweenInfo = nil,
80
+ _EXTREMELY_DANGEROUS_usedAsValue = peek(goal),
81
+ _goal = goal,
82
+ _stopwatch = stopwatch,
83
+ _tweenInfo = tweenInfo or TweenInfo.new()
84
+ },
85
+ METATABLE
86
+ ) :: any
87
+ local destroy = function()
88
+ self.scope = nil
89
+ for dependency in pairs(self.dependencySet) do
90
+ dependency.dependentSet[self] = nil
91
+ end
92
+ end
93
+ self.oldestTask = destroy
94
+ nicknames[self.oldestTask] = "Tween"
95
+ table.insert(scope, destroy)
96
+
97
+ if goalState ~= nil then
98
+ checkLifetime.bOutlivesA(
99
+ scope, self.oldestTask,
100
+ goalState.scope, goalState.oldestTask,
101
+ checkLifetime.formatters.animationGoal
102
+ )
103
+ end
104
+
105
+ local tweenInfoState = castToState(tweenInfo)
106
+ if tweenInfoState ~= nil then
107
+ checkLifetime.bOutlivesA(
108
+ scope, self.oldestTask,
109
+ tweenInfoState.scope, tweenInfoState.oldestTask,
110
+ checkLifetime.formatters.parameter, "tween info"
111
+ )
112
+ end
113
+
114
+ -- Eagerly evaluated objects need to evaluate themselves so that they're
115
+ -- valid at all times.
116
+ evaluate(self, true)
117
+
118
+ return self
119
+ end
120
+
121
+ function class.get<T>(
122
+ self: Self<T>
123
+ ): never
124
+ return External.logError("stateGetWasRemoved")
125
+ end
126
+
127
+ function class._evaluate<T>(
128
+ self: Self<T>
129
+ ): boolean
130
+ local goal = castToState(self._goal)
131
+ -- Allow non-state goals to pass through transparently.
132
+ if goal == nil then
133
+ self._EXTREMELY_DANGEROUS_usedAsValue = self._goal :: T
134
+ return false
135
+ end
136
+ depend(self, goal)
137
+ local newTweenTo = peek(goal)
138
+ -- Protect against NaN goals.
139
+ if newTweenTo ~= newTweenTo then
140
+ External.logWarn("tweenNanGoal")
141
+ return false
142
+ end
143
+ local stopwatch = self._stopwatch :: Stopwatch.Stopwatch
144
+ local tweenInfo = peek(self._tweenInfo) :: TweenInfo
145
+ -- Restart animation when the goal changes, or if the tween info changes
146
+ -- partway through another animation.
147
+ if
148
+ self._activeTo ~= newTweenTo or
149
+ (self._activeElapsed < self._activeDuration and self._activeTweenInfo ~= tweenInfo)
150
+ then
151
+ self._activeDuration = getTweenDuration(tweenInfo)
152
+ self._activeFrom = self._EXTREMELY_DANGEROUS_usedAsValue
153
+ self._activeTo = newTweenTo
154
+ self._activeTweenInfo = tweenInfo
155
+ stopwatch:zero()
156
+ stopwatch:unpause()
157
+ end
158
+ depend(self, stopwatch)
159
+ self._activeElapsed = peek(stopwatch)
160
+ if
161
+ self._activeFrom == self._activeTo or -- endpoints match
162
+ self._activeElapsed >= self._activeDuration or -- animation is done
163
+ typeof(self._activeTo) ~= typeof(self._activeFrom) -- type difference
164
+ then
165
+ self._activeFrom = self._activeTo
166
+ self._activeElapsed = self._activeDuration
167
+ stopwatch:pause()
168
+ end
169
+ -- Compute actual tweened value.
170
+ local ratio = getTweenRatio(tweenInfo, self._activeElapsed)
171
+ local oldValue = self._EXTREMELY_DANGEROUS_usedAsValue
172
+ local newValue = lerpType(self._activeFrom, self._activeTo, ratio) :: T
173
+ -- Protect against NaN after motion.
174
+ if newValue ~= newValue then
175
+ External.logWarn("tweenNanMotion")
176
+ newValue = self._activeTo
177
+ end
178
+ -- Push update and check for similarity.
179
+ -- Don't need to use the similarity test here because this code doesn't
180
+ -- deal with tables, and NaN is already guarded against, so the similarity
181
+ -- test doesn't actually add any new safety here.
182
+ self._EXTREMELY_DANGEROUS_usedAsValue = newValue
183
+ return oldValue ~= newValue
184
+ end
185
+
186
+ table.freeze(class)
187
+ return Tween :: Types.TweenConstructor
@@ -0,0 +1,27 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ Given a `tweenInfo`, returns how many seconds it will take before the tween
8
+ finishes moving. The result may be infinite if the tween repeats forever.
9
+ ]]
10
+
11
+ local TweenService = game:GetService("TweenService")
12
+
13
+ local function getTweenDuration(
14
+ tweenInfo: TweenInfo
15
+ ): number
16
+ if tweenInfo.RepeatCount <= -1 then
17
+ return math.huge
18
+ end
19
+ local tweenDuration = tweenInfo.DelayTime + tweenInfo.Time
20
+ if tweenInfo.Reverses then
21
+ tweenDuration += tweenInfo.Time
22
+ end
23
+ tweenDuration *= tweenInfo.RepeatCount + 1
24
+ return tweenDuration
25
+ end
26
+
27
+ return getTweenDuration
@@ -0,0 +1,47 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ Given a `tweenInfo` and `currentTime`, returns a ratio which can be used to
8
+ tween between two values over time.
9
+ ]]
10
+
11
+ local TweenService = game:GetService("TweenService")
12
+
13
+ local function getTweenRatio(
14
+ tweenInfo: TweenInfo,
15
+ currentTime: number
16
+ ): number
17
+ local delay = tweenInfo.DelayTime
18
+ local duration = tweenInfo.Time
19
+ local reverses = tweenInfo.Reverses
20
+ local numCycles = 1 + tweenInfo.RepeatCount
21
+ local easeStyle = tweenInfo.EasingStyle
22
+ local easeDirection = tweenInfo.EasingDirection
23
+ local cycleDuration = delay + duration
24
+ if reverses then
25
+ cycleDuration += duration
26
+ end
27
+ -- If currentTime is infinity, then presumably the tween should be over.
28
+ -- This avoids NaN when the duration of an infinitely repeating tween is given.
29
+ if currentTime == math.huge then
30
+ return 1
31
+ end
32
+ if currentTime >= cycleDuration * numCycles and tweenInfo.RepeatCount > -1 then
33
+ return 1
34
+ end
35
+ local cycleTime = currentTime % cycleDuration
36
+ if cycleTime <= delay then
37
+ return 0
38
+ end
39
+ local tweenProgress = (cycleTime - delay) / duration
40
+ if tweenProgress > 1 then
41
+ tweenProgress = 2 - tweenProgress
42
+ end
43
+ local ratio = TweenService:GetValue(tweenProgress, easeStyle, easeDirection)
44
+ return ratio
45
+ end
46
+
47
+ return getTweenRatio
@@ -0,0 +1,164 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ Linearly interpolates the given animatable types by a ratio.
8
+ If the types are different or not animatable, then the first value will be
9
+ returned for ratios below 0.5, and the second value for 0.5 and above.
10
+ ]]
11
+
12
+ local Package = script.Parent.Parent
13
+ local Oklab = require(Package.Colour.Oklab)
14
+
15
+ local function lerpType(
16
+ from: unknown,
17
+ to: unknown,
18
+ ratio: number
19
+ ): unknown
20
+ local typeString = typeof(from)
21
+
22
+ if typeof(to) == typeString then
23
+ -- both types must match for interpolation to make sense
24
+ if typeString == "number" then
25
+ local to, from = to :: number, from :: number
26
+ return (to - from) * ratio + from
27
+
28
+ elseif typeString == "CFrame" then
29
+ local to, from = to :: CFrame, from :: CFrame
30
+ return from:Lerp(to, ratio)
31
+
32
+ elseif typeString == "Color3" then
33
+ local to, from = to :: Color3, from :: Color3
34
+ local fromLab = Oklab.fromSRGB(from)
35
+ local toLab = Oklab.fromSRGB(to)
36
+ return Oklab.toSRGB(
37
+ fromLab:Lerp(toLab, ratio),
38
+ false
39
+ )
40
+
41
+ elseif typeString == "ColorSequenceKeypoint" then
42
+ local to, from = to :: ColorSequenceKeypoint, from :: ColorSequenceKeypoint
43
+ local fromLab = Oklab.fromSRGB(from.Value)
44
+ local toLab = Oklab.fromSRGB(to.Value)
45
+ return ColorSequenceKeypoint.new(
46
+ (to.Time - from.Time) * ratio + from.Time,
47
+ Oklab.toSRGB(
48
+ fromLab:Lerp(toLab, ratio),
49
+ false
50
+ )
51
+ )
52
+
53
+ elseif typeString == "DateTime" then
54
+ local to, from = to :: DateTime, from :: DateTime
55
+ return DateTime.fromUnixTimestampMillis(
56
+ (to.UnixTimestampMillis - from.UnixTimestampMillis) * ratio + from.UnixTimestampMillis
57
+ )
58
+
59
+ elseif typeString == "NumberRange" then
60
+ local to, from = to :: NumberRange, from :: NumberRange
61
+ return NumberRange.new(
62
+ (to.Min - from.Min) * ratio + from.Min,
63
+ (to.Max - from.Max) * ratio + from.Max
64
+ )
65
+
66
+ elseif typeString == "NumberSequenceKeypoint" then
67
+ local to, from = to :: NumberSequenceKeypoint, from :: NumberSequenceKeypoint
68
+ return NumberSequenceKeypoint.new(
69
+ (to.Time - from.Time) * ratio + from.Time,
70
+ (to.Value - from.Value) * ratio + from.Value,
71
+ (to.Envelope - from.Envelope) * ratio + from.Envelope
72
+ )
73
+
74
+ elseif typeString == "PhysicalProperties" then
75
+ local to, from = to :: PhysicalProperties, from :: PhysicalProperties
76
+ return PhysicalProperties.new(
77
+ (to.Density - from.Density) * ratio + from.Density,
78
+ (to.Friction - from.Friction) * ratio + from.Friction,
79
+ (to.Elasticity - from.Elasticity) * ratio + from.Elasticity,
80
+ (to.FrictionWeight - from.FrictionWeight) * ratio + from.FrictionWeight,
81
+ (to.ElasticityWeight - from.ElasticityWeight) * ratio + from.ElasticityWeight
82
+ )
83
+
84
+ elseif typeString == "Ray" then
85
+ local to, from = to :: Ray, from :: Ray
86
+ return Ray.new(
87
+ from.Origin:Lerp(to.Origin, ratio),
88
+ from.Direction:Lerp(to.Direction, ratio)
89
+ )
90
+
91
+ elseif typeString == "Rect" then
92
+ local to, from = to :: Rect, from :: Rect
93
+ return Rect.new(
94
+ from.Min:Lerp(to.Min, ratio),
95
+ from.Max:Lerp(to.Max, ratio)
96
+ )
97
+
98
+ elseif typeString == "Region3" then
99
+ local to, from = to :: Region3, from :: Region3
100
+ -- FUTURE: support rotated Region3s if/when they become constructable
101
+ local position = from.CFrame.Position:Lerp(to.CFrame.Position, ratio)
102
+ local halfSize = from.Size:Lerp(to.Size, ratio) / 2
103
+ return Region3.new(position - halfSize, position + halfSize)
104
+
105
+ elseif typeString == "Region3int16" then
106
+ local to, from = to :: Region3int16, from :: Region3int16
107
+ return Region3int16.new(
108
+ Vector3int16.new(
109
+ (to.Min.X - from.Min.X) * ratio + from.Min.X,
110
+ (to.Min.Y - from.Min.Y) * ratio + from.Min.Y,
111
+ (to.Min.Z - from.Min.Z) * ratio + from.Min.Z
112
+ ),
113
+ Vector3int16.new(
114
+ (to.Max.X - from.Max.X) * ratio + from.Max.X,
115
+ (to.Max.Y - from.Max.Y) * ratio + from.Max.Y,
116
+ (to.Max.Z - from.Max.Z) * ratio + from.Max.Z
117
+ )
118
+ )
119
+
120
+ elseif typeString == "UDim" then
121
+ local to, from = to :: UDim, from :: UDim
122
+ return UDim.new(
123
+ (to.Scale - from.Scale) * ratio + from.Scale,
124
+ (to.Offset - from.Offset) * ratio + from.Offset
125
+ )
126
+
127
+ elseif typeString == "UDim2" then
128
+ local to, from = to :: UDim2, from :: UDim2
129
+ return from:Lerp(to, ratio)
130
+
131
+ elseif typeString == "Vector2" then
132
+ local to, from = to :: Vector2, from :: Vector2
133
+ return from:Lerp(to, ratio)
134
+
135
+ elseif typeString == "Vector2int16" then
136
+ local to, from = to :: Vector2int16, from :: Vector2int16
137
+ return Vector2int16.new(
138
+ (to.X - from.X) * ratio + from.X,
139
+ (to.Y - from.Y) * ratio + from.Y
140
+ )
141
+
142
+ elseif typeString == "Vector3" then
143
+ local to, from = to :: Vector3, from :: Vector3
144
+ return from:Lerp(to, ratio)
145
+
146
+ elseif typeString == "Vector3int16" then
147
+ local to, from = to :: Vector3int16, from :: Vector3int16
148
+ return Vector3int16.new(
149
+ (to.X - from.X) * ratio + from.X,
150
+ (to.Y - from.Y) * ratio + from.Y,
151
+ (to.Z - from.Z) * ratio + from.Z
152
+ )
153
+ end
154
+ end
155
+
156
+ -- fallback case: the types are different or not animatable
157
+ if ratio < 0.5 then
158
+ return from
159
+ else
160
+ return to
161
+ end
162
+ end
163
+
164
+ return lerpType
@@ -0,0 +1,100 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ Packs an array of numbers into a given animatable data type.
8
+ If the type is not animatable, nil will be returned.
9
+ ]]
10
+
11
+ local Package = script.Parent.Parent
12
+ local Types = require(Package.Types)
13
+ local Oklab = require(Package.Colour.Oklab)
14
+
15
+ local function packType(
16
+ numbers: {number},
17
+ typeString: string
18
+ ): Types.Animatable?
19
+ if typeString == "number" then
20
+ return numbers[1]
21
+
22
+ elseif typeString == "CFrame" then
23
+ return
24
+ CFrame.new(numbers[1], numbers[2], numbers[3]) *
25
+ CFrame.fromAxisAngle(
26
+ Vector3.new(numbers[4], numbers[5], numbers[6]).Unit,
27
+ numbers[7]
28
+ )
29
+
30
+ elseif typeString == "Color3" then
31
+ return Oklab.toSRGB(
32
+ Vector3.new(numbers[1], numbers[2], numbers[3]),
33
+ false
34
+ )
35
+
36
+ elseif typeString == "ColorSequenceKeypoint" then
37
+ return ColorSequenceKeypoint.new(
38
+ numbers[4],
39
+ Oklab.toSRGB(
40
+ Vector3.new(numbers[1], numbers[2], numbers[3]),
41
+ false
42
+ )
43
+ )
44
+
45
+ elseif typeString == "DateTime" then
46
+ return DateTime.fromUnixTimestampMillis(numbers[1])
47
+
48
+ elseif typeString == "NumberRange" then
49
+ return NumberRange.new(numbers[1], numbers[2])
50
+
51
+ elseif typeString == "NumberSequenceKeypoint" then
52
+ return NumberSequenceKeypoint.new(numbers[2], numbers[1], numbers[3])
53
+
54
+ elseif typeString == "PhysicalProperties" then
55
+ return PhysicalProperties.new(numbers[1], numbers[2], numbers[3], numbers[4], numbers[5])
56
+
57
+ elseif typeString == "Ray" then
58
+ return Ray.new(
59
+ Vector3.new(numbers[1], numbers[2], numbers[3]),
60
+ Vector3.new(numbers[4], numbers[5], numbers[6])
61
+ )
62
+
63
+ elseif typeString == "Rect" then
64
+ return Rect.new(numbers[1], numbers[2], numbers[3], numbers[4])
65
+
66
+ elseif typeString == "Region3" then
67
+ -- FUTURE: support rotated Region3s if/when they become constructable
68
+ local position = Vector3.new(numbers[1], numbers[2], numbers[3])
69
+ local halfSize = Vector3.new(numbers[4] / 2, numbers[5] / 2, numbers[6] / 2)
70
+ return Region3.new(position - halfSize, position + halfSize)
71
+
72
+ elseif typeString == "Region3int16" then
73
+ return Region3int16.new(
74
+ Vector3int16.new(numbers[1], numbers[2], numbers[3]),
75
+ Vector3int16.new(numbers[4], numbers[5], numbers[6])
76
+ )
77
+
78
+ elseif typeString == "UDim" then
79
+ return UDim.new(numbers[1], numbers[2])
80
+
81
+ elseif typeString == "UDim2" then
82
+ return UDim2.new(numbers[1], numbers[2], numbers[3], numbers[4])
83
+
84
+ elseif typeString == "Vector2" then
85
+ return Vector2.new(numbers[1], numbers[2])
86
+
87
+ elseif typeString == "Vector2int16" then
88
+ return Vector2int16.new(numbers[1], numbers[2])
89
+
90
+ elseif typeString == "Vector3" then
91
+ return Vector3.new(numbers[1], numbers[2], numbers[3])
92
+
93
+ elseif typeString == "Vector3int16" then
94
+ return Vector3int16.new(numbers[1], numbers[2], numbers[3])
95
+ else
96
+ return nil
97
+ end
98
+ end
99
+
100
+ return packType
@@ -0,0 +1,80 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ Returns a 2x2 matrix of coefficients for a given time, damping and angular
8
+ frequency (aka 'speed').
9
+
10
+ Specifically, this returns four coefficients - posPos, posVel, velPos, and
11
+ velVel - which can be multiplied with position and velocity like so:
12
+
13
+ local newPosition = oldPosition * posPos + oldVelocity * posVel
14
+ local newVelocity = oldPosition * velPos + oldVelocity * velVel
15
+
16
+ For speed = 1 and damping = 0, the result is a simple harmonic oscillator
17
+ with a period of tau.
18
+
19
+ Special thanks to AxisAngle for helping to improve numerical precision.
20
+ ]]
21
+
22
+ local function springCoefficients(
23
+ time: number,
24
+ damping: number,
25
+ speed: number
26
+ ): (number, number, number, number)
27
+ -- if time or speed is 0, then the spring won't move
28
+ if time == 0 or speed == 0 then
29
+ return 1, 0, 0, 1
30
+ end
31
+ local posPos, posVel, velPos, velVel
32
+
33
+ if damping > 1 then
34
+ -- overdamped spring
35
+
36
+ local alpha = math.sqrt(damping^2 - 1)
37
+ local negHalf_over_alpha_speed = -0.5 / (alpha * speed)
38
+ local z1 = speed * (alpha + damping) * -1
39
+ local z2 = speed * (alpha - damping)
40
+ local exp1 = math.exp(time * z1)
41
+ local exp2 = math.exp(time * z2)
42
+
43
+ posPos = (exp2 * z1 - exp1 * z2) * negHalf_over_alpha_speed
44
+ posVel = (exp1 - exp2) * negHalf_over_alpha_speed / speed
45
+ velPos = (exp2 - exp1) * negHalf_over_alpha_speed * speed
46
+ velVel = (exp1 * z1 - exp2 * z2) * negHalf_over_alpha_speed
47
+
48
+ elseif damping == 1 then
49
+ -- critically damped spring
50
+
51
+ local time_speed = time * speed
52
+ local time_speed_neg1 = time_speed * -1
53
+ local exp = math.exp(time_speed_neg1)
54
+
55
+ posPos = exp * (time_speed + 1)
56
+ posVel = exp * time
57
+ velPos = exp * (time_speed_neg1 * speed)
58
+ velVel = exp * (time_speed_neg1 + 1)
59
+ else
60
+ -- underdamped spring
61
+
62
+ local alpha = speed * math.sqrt(1 - damping^2)
63
+ local overAlpha = 1 / alpha
64
+ local exp = math.exp(-1 * time * speed * damping)
65
+ local sin = math.sin(alpha * time)
66
+ local cos = math.cos(alpha * time)
67
+ local exp_sin = exp * sin
68
+ local exp_cos = exp * cos
69
+ local exp_sin_speed_damping_overAlpha = exp_sin * speed * damping * overAlpha
70
+
71
+ posPos = exp_sin_speed_damping_overAlpha + exp_cos
72
+ posVel = exp_sin * overAlpha
73
+ velPos = -1 * ( exp_sin * alpha + speed * damping * exp_sin_speed_damping_overAlpha )
74
+ velVel = exp_cos - exp_sin_speed_damping_overAlpha
75
+ end
76
+
77
+ return posPos, posVel, velPos, velVel
78
+ end
79
+
80
+ return springCoefficients
@@ -0,0 +1,103 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ Unpacks an animatable type into an array of numbers.
8
+ If the type is not animatable, an empty array will be returned.
9
+ ]]
10
+
11
+ local Package = script.Parent.Parent
12
+ local Oklab = require(Package.Colour.Oklab)
13
+
14
+ local function unpackType(
15
+ value: unknown,
16
+ typeString: string
17
+ ): {number}
18
+ if typeString == "number" then
19
+ local value = value :: number
20
+ return {value}
21
+
22
+ elseif typeString == "CFrame" then
23
+ local value = value :: CFrame
24
+ -- FUTURE: is there a better way of doing this? doing distance
25
+ -- calculations on `angle` may be incorrect
26
+ local axis, angle = value:ToAxisAngle()
27
+ return {value.X, value.Y, value.Z, axis.X, axis.Y, axis.Z, angle}
28
+
29
+ elseif typeString == "Color3" then
30
+ local value = value :: Color3
31
+ local lab = Oklab.fromSRGB(value)
32
+ return {lab.X, lab.Y, lab.Z}
33
+
34
+ elseif typeString == "ColorSequenceKeypoint" then
35
+ local value = value :: ColorSequenceKeypoint
36
+ local lab = Oklab.fromSRGB(value.Value)
37
+ return {lab.X, lab.Y, lab.Z, value.Time}
38
+
39
+ elseif typeString == "DateTime" then
40
+ local value = value :: DateTime
41
+ return {value.UnixTimestampMillis}
42
+
43
+ elseif typeString == "NumberRange" then
44
+ local value = value :: NumberRange
45
+ return {value.Min, value.Max}
46
+
47
+ elseif typeString == "NumberSequenceKeypoint" then
48
+ local value = value :: NumberSequenceKeypoint
49
+ return {value.Value, value.Time, value.Envelope}
50
+
51
+ elseif typeString == "PhysicalProperties" then
52
+ local value = value :: PhysicalProperties
53
+ return {value.Density, value.Friction, value.Elasticity, value.FrictionWeight, value.ElasticityWeight}
54
+
55
+ elseif typeString == "Ray" then
56
+ local value = value :: Ray
57
+ return {value.Origin.X, value.Origin.Y, value.Origin.Z, value.Direction.X, value.Direction.Y, value.Direction.Z}
58
+
59
+ elseif typeString == "Rect" then
60
+ local value = value :: Rect
61
+ return {value.Min.X, value.Min.Y, value.Max.X, value.Max.Y}
62
+
63
+ elseif typeString == "Region3" then
64
+ local value = value :: Region3
65
+ -- FUTURE: support rotated Region3s if/when they become constructable
66
+ return {
67
+ value.CFrame.X, value.CFrame.Y, value.CFrame.Z,
68
+ value.Size.X, value.Size.Y, value.Size.Z
69
+ }
70
+
71
+ elseif typeString == "Region3int16" then
72
+ local value = value :: Region3int16
73
+ return {value.Min.X, value.Min.Y, value.Min.Z, value.Max.X, value.Max.Y, value.Max.Z}
74
+
75
+ elseif typeString == "UDim" then
76
+ local value = value :: UDim
77
+ return {value.Scale, value.Offset}
78
+
79
+ elseif typeString == "UDim2" then
80
+ local value = value :: UDim2
81
+ return {value.X.Scale, value.X.Offset, value.Y.Scale, value.Y.Offset}
82
+
83
+ elseif typeString == "Vector2" then
84
+ local value = value :: Vector2
85
+ return {value.X, value.Y}
86
+
87
+ elseif typeString == "Vector2int16" then
88
+ local value = value :: Vector2int16
89
+ return {value.X, value.Y}
90
+
91
+ elseif typeString == "Vector3" then
92
+ local value = value :: Vector3
93
+ return {value.X, value.Y, value.Z}
94
+
95
+ elseif typeString == "Vector3int16" then
96
+ local value = value :: Vector3int16
97
+ return {value.X, value.Y, value.Z}
98
+ else
99
+ return {}
100
+ end
101
+ end
102
+
103
+ return unpackType