roblox-opencode 1.0.0 → 1.0.2

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 (246) hide show
  1. package/README.md +112 -122
  2. package/commands/setup-game.md +108 -108
  3. package/commands/sync-check.md +53 -53
  4. package/core/roblox-core.md +93 -93
  5. package/dist/server.js +189 -167
  6. package/package.json +35 -35
  7. package/skills/roblox-analytics/SKILL.md +277 -277
  8. package/skills/roblox-analytics/references/event-batcher.luau +75 -75
  9. package/skills/roblox-animation-vfx/SKILL.md +1325 -1325
  10. package/skills/roblox-architecture/SKILL.md +877 -863
  11. package/skills/roblox-architecture/references/combat-systems.md +1381 -1381
  12. package/skills/roblox-code-review/SKILL.md +686 -686
  13. package/skills/roblox-data/SKILL.md +889 -889
  14. package/skills/roblox-data/references/inventory-systems.md +1729 -1729
  15. package/skills/roblox-debug/SKILL.md +98 -98
  16. package/skills/roblox-gui/SKILL.md +1103 -1103
  17. package/skills/roblox-gui-fusion/SKILL.md +150 -150
  18. package/skills/roblox-gui-fusion/references/inventory.luau +427 -427
  19. package/skills/roblox-gui-fusion/references/settings-menu.luau +579 -579
  20. package/skills/roblox-gui-fusion/references/shop.luau +411 -411
  21. package/skills/roblox-luau-mastery/SKILL.md +1618 -1519
  22. package/skills/roblox-monetization/SKILL.md +1084 -1084
  23. package/skills/roblox-monetization/references/process-receipt.luau +131 -131
  24. package/skills/roblox-networking/SKILL.md +669 -669
  25. package/skills/roblox-networking/references/remote-validator.luau +193 -193
  26. package/skills/roblox-publish-checklist/SKILL.md +127 -127
  27. package/skills/roblox-runtime/SKILL.md +753 -753
  28. package/skills/roblox-sharp-edges/SKILL.md +294 -294
  29. package/skills/roblox-sync/SKILL.md +126 -126
  30. package/skills/roblox-testing/SKILL.md +943 -943
  31. package/skills/roblox-tooling/SKILL.md +149 -149
  32. package/vendor/LICENSES/ProfileStore-LICENSE +201 -201
  33. package/vendor/LICENSES/RbxUtil-LICENSE +7 -7
  34. package/vendor/LICENSES/promise-LICENSE +20 -20
  35. package/vendor/LICENSES/t-LICENSE +21 -21
  36. package/vendor/LICENSES/testez-LICENSE +200 -200
  37. package/vendor/README.md +83 -83
  38. package/vendor/fusion/Animation/ExternalTime.luau +83 -83
  39. package/vendor/fusion/Animation/Spring.luau +321 -321
  40. package/vendor/fusion/Animation/Stopwatch.luau +127 -127
  41. package/vendor/fusion/Animation/Tween.luau +187 -187
  42. package/vendor/fusion/Animation/getTweenDuration.luau +27 -27
  43. package/vendor/fusion/Animation/getTweenRatio.luau +47 -47
  44. package/vendor/fusion/Animation/lerpType.luau +163 -163
  45. package/vendor/fusion/Animation/packType.luau +99 -99
  46. package/vendor/fusion/Animation/springCoefficients.luau +80 -80
  47. package/vendor/fusion/Animation/unpackType.luau +102 -102
  48. package/vendor/fusion/Colour/Oklab.luau +70 -70
  49. package/vendor/fusion/Colour/sRGB.luau +54 -54
  50. package/vendor/fusion/External.luau +167 -167
  51. package/vendor/fusion/ExternalDebug.luau +69 -69
  52. package/vendor/fusion/Graph/Observer.luau +113 -113
  53. package/vendor/fusion/Graph/castToGraph.luau +28 -28
  54. package/vendor/fusion/Graph/change.luau +80 -80
  55. package/vendor/fusion/Graph/depend.luau +32 -32
  56. package/vendor/fusion/Graph/evaluate.luau +55 -55
  57. package/vendor/fusion/Instances/Attribute.luau +57 -57
  58. package/vendor/fusion/Instances/AttributeChange.luau +46 -46
  59. package/vendor/fusion/Instances/AttributeOut.luau +63 -63
  60. package/vendor/fusion/Instances/Child.luau +21 -21
  61. package/vendor/fusion/Instances/Children.luau +147 -147
  62. package/vendor/fusion/Instances/Hydrate.luau +32 -32
  63. package/vendor/fusion/Instances/New.luau +52 -52
  64. package/vendor/fusion/Instances/OnChange.luau +49 -49
  65. package/vendor/fusion/Instances/OnEvent.luau +53 -53
  66. package/vendor/fusion/Instances/Out.luau +69 -69
  67. package/vendor/fusion/Instances/applyInstanceProps.luau +148 -148
  68. package/vendor/fusion/Instances/defaultProps.luau +194 -194
  69. package/vendor/fusion/LICENSE +21 -21
  70. package/vendor/fusion/Logging/formatError.luau +48 -48
  71. package/vendor/fusion/Logging/messages.luau +51 -51
  72. package/vendor/fusion/Logging/parseError.luau +24 -24
  73. package/vendor/fusion/Memory/checkLifetime.luau +133 -133
  74. package/vendor/fusion/Memory/deriveScope.luau +23 -23
  75. package/vendor/fusion/Memory/deriveScopeImpl.luau +44 -44
  76. package/vendor/fusion/Memory/doCleanup.luau +78 -78
  77. package/vendor/fusion/Memory/innerScope.luau +33 -33
  78. package/vendor/fusion/Memory/legacyCleanup.luau +17 -17
  79. package/vendor/fusion/Memory/needsDestruction.luau +16 -16
  80. package/vendor/fusion/Memory/poisonScope.luau +33 -33
  81. package/vendor/fusion/Memory/scopePool.luau +54 -54
  82. package/vendor/fusion/Memory/scoped.luau +26 -26
  83. package/vendor/fusion/Memory/whichLivesLonger.luau +74 -74
  84. package/vendor/fusion/RobloxExternal.luau +97 -97
  85. package/vendor/fusion/State/Computed.luau +138 -138
  86. package/vendor/fusion/State/For/Disassembly.luau +210 -210
  87. package/vendor/fusion/State/For/ForTypes.luau +30 -30
  88. package/vendor/fusion/State/For/init.luau +109 -109
  89. package/vendor/fusion/State/ForKeys.luau +93 -93
  90. package/vendor/fusion/State/ForPairs.luau +96 -96
  91. package/vendor/fusion/State/ForValues.luau +93 -93
  92. package/vendor/fusion/State/Value.luau +87 -87
  93. package/vendor/fusion/State/castToState.luau +25 -25
  94. package/vendor/fusion/State/peek.luau +30 -30
  95. package/vendor/fusion/Types.luau +314 -314
  96. package/vendor/fusion/Utility/Contextual.luau +90 -90
  97. package/vendor/fusion/Utility/Safe.luau +22 -22
  98. package/vendor/fusion/Utility/isSimilar.luau +29 -29
  99. package/vendor/fusion/Utility/merge.luau +35 -35
  100. package/vendor/fusion/Utility/nameOf.luau +34 -34
  101. package/vendor/fusion/Utility/never.luau +13 -13
  102. package/vendor/fusion/Utility/nicknames.luau +10 -10
  103. package/vendor/fusion/Utility/xtypeof.luau +26 -26
  104. package/vendor/fusion/init.luau +82 -82
  105. package/vendor/profilestore/init.luau +2242 -2242
  106. package/vendor/promise/init.luau +1982 -1982
  107. package/vendor/rbxutil/buffer-util/Buffer.test.luau +25 -25
  108. package/vendor/rbxutil/buffer-util/BufferReader.luau +228 -228
  109. package/vendor/rbxutil/buffer-util/BufferWriter.luau +269 -269
  110. package/vendor/rbxutil/buffer-util/DataTypeBuffer.luau +223 -223
  111. package/vendor/rbxutil/buffer-util/Types.luau +60 -60
  112. package/vendor/rbxutil/buffer-util/index.d.ts +153 -153
  113. package/vendor/rbxutil/buffer-util/init.luau +41 -41
  114. package/vendor/rbxutil/buffer-util/package.json +16 -16
  115. package/vendor/rbxutil/buffer-util/wally.toml +9 -9
  116. package/vendor/rbxutil/comm/Client/ClientComm.luau +232 -232
  117. package/vendor/rbxutil/comm/Client/ClientRemoteProperty.luau +156 -156
  118. package/vendor/rbxutil/comm/Client/ClientRemoteSignal.luau +109 -109
  119. package/vendor/rbxutil/comm/Client/init.luau +135 -135
  120. package/vendor/rbxutil/comm/Server/RemoteProperty.luau +295 -295
  121. package/vendor/rbxutil/comm/Server/RemoteSignal.luau +211 -211
  122. package/vendor/rbxutil/comm/Server/ServerComm.luau +211 -211
  123. package/vendor/rbxutil/comm/Server/init.luau +140 -140
  124. package/vendor/rbxutil/comm/Types.luau +18 -18
  125. package/vendor/rbxutil/comm/Util.luau +27 -27
  126. package/vendor/rbxutil/comm/init.luau +35 -35
  127. package/vendor/rbxutil/comm/wally.toml +13 -13
  128. package/vendor/rbxutil/component/init.luau +759 -759
  129. package/vendor/rbxutil/component/init.test.luau +311 -311
  130. package/vendor/rbxutil/component/wally.toml +14 -14
  131. package/vendor/rbxutil/concur/init.luau +542 -542
  132. package/vendor/rbxutil/concur/init.test.luau +364 -364
  133. package/vendor/rbxutil/concur/wally.toml +8 -8
  134. package/vendor/rbxutil/enum-list/init.luau +101 -101
  135. package/vendor/rbxutil/enum-list/init.test.luau +91 -91
  136. package/vendor/rbxutil/enum-list/wally.toml +8 -8
  137. package/vendor/rbxutil/find/index.d.ts +20 -20
  138. package/vendor/rbxutil/find/init.luau +44 -44
  139. package/vendor/rbxutil/find/package.json +17 -17
  140. package/vendor/rbxutil/find/wally.toml +8 -8
  141. package/vendor/rbxutil/input/Gamepad.luau +559 -559
  142. package/vendor/rbxutil/input/Keyboard.luau +124 -124
  143. package/vendor/rbxutil/input/Mouse.luau +278 -278
  144. package/vendor/rbxutil/input/PreferredInput.luau +91 -91
  145. package/vendor/rbxutil/input/Touch.luau +120 -120
  146. package/vendor/rbxutil/input/init.luau +33 -33
  147. package/vendor/rbxutil/input/wally.toml +12 -12
  148. package/vendor/rbxutil/loader/index.d.ts +15 -15
  149. package/vendor/rbxutil/loader/init.luau +137 -137
  150. package/vendor/rbxutil/loader/wally.toml +8 -8
  151. package/vendor/rbxutil/log/index.d.ts +38 -38
  152. package/vendor/rbxutil/log/init.luau +746 -746
  153. package/vendor/rbxutil/log/wally.toml +8 -8
  154. package/vendor/rbxutil/net/init.luau +190 -190
  155. package/vendor/rbxutil/net/wally.toml +8 -8
  156. package/vendor/rbxutil/option/index.d.ts +44 -44
  157. package/vendor/rbxutil/option/init.luau +489 -489
  158. package/vendor/rbxutil/option/init.test.luau +342 -342
  159. package/vendor/rbxutil/option/wally.toml +8 -8
  160. package/vendor/rbxutil/pid/index.d.ts +53 -53
  161. package/vendor/rbxutil/pid/init.luau +195 -195
  162. package/vendor/rbxutil/pid/package.json +16 -16
  163. package/vendor/rbxutil/pid/wally.toml +9 -9
  164. package/vendor/rbxutil/quaternion/index.d.ts +117 -117
  165. package/vendor/rbxutil/quaternion/init.luau +570 -570
  166. package/vendor/rbxutil/quaternion/package.json +16 -16
  167. package/vendor/rbxutil/quaternion/wally.toml +9 -9
  168. package/vendor/rbxutil/query/index.d.ts +43 -43
  169. package/vendor/rbxutil/query/init.luau +117 -117
  170. package/vendor/rbxutil/query/package.json +18 -18
  171. package/vendor/rbxutil/query/wally.toml +9 -9
  172. package/vendor/rbxutil/sequent/index.d.ts +28 -28
  173. package/vendor/rbxutil/sequent/init.luau +340 -340
  174. package/vendor/rbxutil/sequent/package.json +16 -16
  175. package/vendor/rbxutil/sequent/wally.toml +9 -9
  176. package/vendor/rbxutil/ser/init.luau +175 -175
  177. package/vendor/rbxutil/ser/init.test.luau +50 -50
  178. package/vendor/rbxutil/ser/wally.toml +11 -11
  179. package/vendor/rbxutil/shake/index.d.ts +36 -36
  180. package/vendor/rbxutil/shake/init.luau +532 -532
  181. package/vendor/rbxutil/shake/init.test.luau +267 -267
  182. package/vendor/rbxutil/shake/package.json +16 -16
  183. package/vendor/rbxutil/shake/wally.toml +9 -9
  184. package/vendor/rbxutil/signal/index.d.ts +100 -100
  185. package/vendor/rbxutil/signal/init.luau +432 -432
  186. package/vendor/rbxutil/signal/init.test.luau +190 -190
  187. package/vendor/rbxutil/signal/package.json +17 -17
  188. package/vendor/rbxutil/signal/wally.toml +9 -9
  189. package/vendor/rbxutil/silo/TableWatcher.luau +65 -65
  190. package/vendor/rbxutil/silo/Util.luau +55 -55
  191. package/vendor/rbxutil/silo/init.luau +338 -338
  192. package/vendor/rbxutil/silo/init.test.luau +215 -215
  193. package/vendor/rbxutil/silo/wally.toml +8 -8
  194. package/vendor/rbxutil/spring/index.d.ts +40 -40
  195. package/vendor/rbxutil/spring/init.luau +97 -97
  196. package/vendor/rbxutil/spring/package.json +17 -17
  197. package/vendor/rbxutil/spring/wally.toml +8 -8
  198. package/vendor/rbxutil/stream/index.d.ts +88 -88
  199. package/vendor/rbxutil/stream/init.luau +597 -597
  200. package/vendor/rbxutil/stream/package.json +18 -18
  201. package/vendor/rbxutil/stream/wally.toml +9 -9
  202. package/vendor/rbxutil/streamable/Streamable.luau +202 -202
  203. package/vendor/rbxutil/streamable/StreamableUtil.luau +80 -80
  204. package/vendor/rbxutil/streamable/init.luau +8 -8
  205. package/vendor/rbxutil/streamable/wally.toml +12 -12
  206. package/vendor/rbxutil/symbol/init.luau +56 -56
  207. package/vendor/rbxutil/symbol/init.test.luau +37 -37
  208. package/vendor/rbxutil/symbol/wally.toml +8 -8
  209. package/vendor/rbxutil/table-util/init.luau +938 -938
  210. package/vendor/rbxutil/table-util/init.test.luau +439 -439
  211. package/vendor/rbxutil/task-queue/index.d.ts +27 -27
  212. package/vendor/rbxutil/task-queue/init.luau +97 -97
  213. package/vendor/rbxutil/task-queue/wally.toml +8 -8
  214. package/vendor/rbxutil/timer/index.d.ts +81 -81
  215. package/vendor/rbxutil/timer/init.luau +249 -249
  216. package/vendor/rbxutil/timer/init.test.luau +73 -73
  217. package/vendor/rbxutil/timer/wally.toml +11 -11
  218. package/vendor/rbxutil/tree/index.d.ts +15 -15
  219. package/vendor/rbxutil/tree/init.luau +137 -137
  220. package/vendor/rbxutil/tree/wally.toml +8 -8
  221. package/vendor/rbxutil/trove/index.d.ts +46 -46
  222. package/vendor/rbxutil/trove/init.luau +787 -787
  223. package/vendor/rbxutil/trove/init.test.luau +203 -203
  224. package/vendor/rbxutil/trove/wally.toml +8 -8
  225. package/vendor/rbxutil/typed-remote/init.luau +196 -196
  226. package/vendor/rbxutil/typed-remote/wally.toml +8 -8
  227. package/vendor/rbxutil/wait-for/index.d.ts +17 -17
  228. package/vendor/rbxutil/wait-for/init.luau +257 -257
  229. package/vendor/rbxutil/wait-for/init.test.luau +182 -182
  230. package/vendor/rbxutil/wait-for/wally.toml +11 -11
  231. package/vendor/t/t.lua +1350 -1350
  232. package/vendor/testez/Context.lua +26 -26
  233. package/vendor/testez/Expectation.lua +311 -311
  234. package/vendor/testez/ExpectationContext.lua +38 -38
  235. package/vendor/testez/LifecycleHooks.lua +89 -89
  236. package/vendor/testez/Reporters/TeamCityReporter.lua +101 -101
  237. package/vendor/testez/Reporters/TextReporter.lua +105 -105
  238. package/vendor/testez/Reporters/TextReporterQuiet.lua +96 -96
  239. package/vendor/testez/TestBootstrap.lua +146 -146
  240. package/vendor/testez/TestEnum.lua +27 -27
  241. package/vendor/testez/TestPlan.lua +304 -304
  242. package/vendor/testez/TestPlanner.lua +39 -39
  243. package/vendor/testez/TestResults.lua +111 -111
  244. package/vendor/testez/TestRunner.lua +188 -188
  245. package/vendor/testez/TestSession.lua +243 -243
  246. package/vendor/testez/init.lua +39 -39
@@ -1,322 +1,322 @@
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 physics to shape the motion.
9
-
10
- https://elttob.uk/Fusion/0.3/api-reference/animation/types/spring/
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 change = require(Package.Graph.change)
21
- local evaluate = require(Package.Graph.evaluate)
22
- -- State
23
- local castToState = require(Package.State.castToState)
24
- local peek = require(Package.State.peek)
25
- -- Animation
26
- local ExternalTime = require(Package.Animation.ExternalTime)
27
- local Stopwatch = require(Package.Animation.Stopwatch)
28
- local packType = require(Package.Animation.packType)
29
- local unpackType = require(Package.Animation.unpackType)
30
- local springCoefficients = require(Package.Animation.springCoefficients)
31
- -- Utility
32
- local nicknames = require(Package.Utility.nicknames)
33
-
34
- local EPSILON = 0.00001
35
-
36
- type Self<T> = Types.Spring<T> & {
37
- _activeDamping: number,
38
- _activeGoal: T,
39
- _activeLatestP: {number},
40
- _activeLatestV: {number},
41
- _activeNumSprings: number,
42
- _activeSpeed: number,
43
- _activeStartP: {number},
44
- _activeStartV: {number},
45
- _activeTargetP: {number},
46
- _activeType: string,
47
- _speed: Types.UsedAs<number>,
48
- _damping: Types.UsedAs<number>,
49
- _goal: Types.UsedAs<T>,
50
- _stopwatch: Stopwatch.Stopwatch
51
- }
52
-
53
- local class = {}
54
- class.type = "State"
55
- class.kind = "Spring"
56
- class.timeliness = "eager"
57
-
58
- local METATABLE = table.freeze {__index = class}
59
-
60
- local function Spring<T>(
61
- scope: Types.Scope<unknown>,
62
- goal: Types.UsedAs<T>,
63
- speed: Types.UsedAs<number>?,
64
- damping: Types.UsedAs<number>?
65
- ): Types.Spring<T>
66
- local createdAt = os.clock()
67
- if typeof(scope) ~= "table" or castToState(scope) ~= nil then
68
- External.logError("scopeMissing", nil, "Springs", "myScope:Spring(goalState, speed, damping)")
69
- end
70
-
71
- local goalState = castToState(goal)
72
- local stopwatch = nil
73
- if goalState ~= nil then
74
- stopwatch = Stopwatch(scope, ExternalTime(scope))
75
- stopwatch:unpause()
76
- end
77
-
78
- local speed = speed or 10
79
- local damping = damping or 1
80
-
81
- local self: Self<T> = setmetatable(
82
- {
83
- createdAt = createdAt,
84
- dependencySet = {},
85
- dependentSet = {},
86
- lastChange = nil,
87
- scope = scope,
88
- validity = "invalid",
89
- _activeDamping = -1,
90
- _activeGoal = nil,
91
- _activeLatestP = {},
92
- _activeLatestV = {},
93
- _activeNumSprings = 0,
94
- _activeSpeed = -1,
95
- _activeStartP = {},
96
- _activeStartV = {},
97
- _activeTargetP = {},
98
- _activeType = "",
99
- _damping = damping,
100
- _EXTREMELY_DANGEROUS_usedAsValue = peek(goal),
101
- _goal = goal,
102
- _speed = speed,
103
- _stopwatch = stopwatch
104
- },
105
- METATABLE
106
- ) :: any
107
- local destroy = function()
108
- self.scope = nil
109
- for dependency in pairs(self.dependencySet) do
110
- dependency.dependentSet[self] = nil
111
- end
112
- end
113
- self.oldestTask = destroy
114
- nicknames[self.oldestTask] = "Spring"
115
- table.insert(scope, destroy)
116
-
117
- if goalState ~= nil then
118
- checkLifetime.bOutlivesA(
119
- scope, self.oldestTask,
120
- goalState.scope, goalState.oldestTask,
121
- checkLifetime.formatters.animationGoal
122
- )
123
- end
124
- local speedState = castToState(speed)
125
- if speedState ~= nil then
126
- checkLifetime.bOutlivesA(
127
- scope, self.oldestTask,
128
- speedState.scope, speedState.oldestTask,
129
- checkLifetime.formatters.parameter, "speed"
130
- )
131
- end
132
- local dampingState = castToState(damping)
133
- if dampingState ~= nil then
134
- checkLifetime.bOutlivesA(
135
- scope, self.oldestTask,
136
- dampingState.scope, dampingState.oldestTask,
137
- checkLifetime.formatters.parameter, "damping"
138
- )
139
- end
140
-
141
- -- Eagerly evaluated objects need to evaluate themselves so that they're
142
- -- valid at all times.
143
- evaluate(self, true)
144
-
145
- return self
146
- end
147
-
148
- function class.addVelocity<T>(
149
- self: Self<T>,
150
- deltaValue: T
151
- ): ()
152
- evaluate(self, false) -- ensure the _active params are up to date
153
- local deltaType = typeof(deltaValue)
154
- if deltaType ~= self._activeType then
155
- External.logError("springTypeMismatch", nil, deltaType, self._activeType)
156
- end
157
- local newStartV = unpackType(deltaValue, deltaType)
158
- for index, velocity in self._activeLatestV do
159
- newStartV[index] += velocity
160
- end
161
- self._activeStartP = table.clone(self._activeLatestP)
162
- self._activeStartV = newStartV
163
- self._stopwatch:zero()
164
- self._stopwatch:unpause()
165
- change(self)
166
- end
167
-
168
- function class.get<T>(
169
- self: Self<T>
170
- ): never
171
- return External.logError("stateGetWasRemoved")
172
- end
173
-
174
- function class.setPosition<T>(
175
- self: Self<T>,
176
- newValue: T
177
- ): ()
178
- evaluate(self, false) -- ensure the _active params are up to date
179
- local newType = typeof(newValue)
180
- if newType ~= self._activeType then
181
- External.logError("springTypeMismatch", nil, newType, self._activeType)
182
- end
183
- self._activeStartP = unpackType(newValue, newType)
184
- self._activeStartV = table.clone(self._activeLatestV)
185
- self._stopwatch:zero()
186
- self._stopwatch:unpause()
187
- change(self)
188
- end
189
-
190
- function class.setVelocity<T>(
191
- self: Self<T>,
192
- newValue: T
193
- ): ()
194
- evaluate(self, false) -- ensure the _active params are up to date
195
- local newType = typeof(newValue)
196
- if newType ~= self._activeType then
197
- External.logError("springTypeMismatch", nil, newType, self._activeType)
198
- end
199
- self._activeStartP = table.clone(self._activeLatestP)
200
- self._activeStartV = unpackType(newValue, newType)
201
- self._stopwatch:zero()
202
- self._stopwatch:unpause()
203
- change(self)
204
- end
205
-
206
- function class._evaluate<T>(
207
- self: Self<T>
208
- ): boolean
209
- local goal = castToState(self._goal)
210
- -- Allow non-state goals to pass through transparently.
211
- if goal == nil then
212
- self._EXTREMELY_DANGEROUS_usedAsValue = self._goal :: T
213
- return false
214
- end
215
- -- depend(self, goal)
216
- local nextFrameGoal = peek(goal)
217
- -- Protect against NaN goals.
218
- if nextFrameGoal ~= nextFrameGoal then
219
- External.logWarn("springNanGoal")
220
- return false
221
- end
222
- local nextFrameGoalType = typeof(nextFrameGoal)
223
- local discontinuous = nextFrameGoalType ~= self._activeType
224
-
225
- local stopwatch = self._stopwatch :: Stopwatch.Stopwatch
226
- local elapsed = peek(stopwatch)
227
- depend(self, stopwatch)
228
-
229
- local oldValue = self._EXTREMELY_DANGEROUS_usedAsValue
230
- local newValue: T
231
-
232
- if discontinuous then
233
- -- Propagate changes in type instantly throughout the whole reactive
234
- -- graph, even if simulation is logically one frame behind, because it
235
- -- makes the whole graph behave more consistently.
236
- newValue = nextFrameGoal
237
- elseif elapsed <= 0 then
238
- newValue = oldValue
239
- else
240
- -- Calculate spring motion.
241
- -- IMPORTANT: use the parameters from last frame, not this frame. We're
242
- -- integrating the motion that happened over the last frame, after all.
243
- -- The stopwatch will have captured the length of time needed correctly.
244
- local posPos, posVel, velPos, velVel = springCoefficients(
245
- elapsed,
246
- self._activeDamping,
247
- self._activeSpeed
248
- )
249
- local isMoving = false
250
- for index = 1, self._activeNumSprings do
251
- local startP = self._activeStartP[index]
252
- local targetP = self._activeTargetP[index]
253
- local startV = self._activeStartV[index]
254
- local startD = startP - targetP
255
- local latestD = startD * posPos + startV * posVel
256
- local latestV = startD * velPos + startV * velVel
257
- if latestD ~= latestD or latestV ~= latestV then
258
- External.logWarn("springNanMotion")
259
- latestD, latestV = 0, 0
260
- end
261
- if math.abs(latestD) > EPSILON or math.abs(latestV) > EPSILON then
262
- isMoving = true
263
- end
264
- local latestP = latestD + targetP
265
- self._activeLatestP[index] = latestP
266
- self._activeLatestV[index] = latestV
267
- end
268
- -- Sleep and snap to goal if the motion has decayed to a negligible amount.
269
- if not isMoving then
270
- for index = 1, self._activeNumSprings do
271
- self._activeLatestP[index] = self._activeTargetP[index]
272
- end
273
- -- TODO: figure out how to do sleeping correctly for single frame
274
- -- changes
275
- -- stopwatch:pause()
276
- -- stopwatch:zero()
277
- end
278
- -- Pack springs into final value.
279
- newValue = packType(self._activeLatestP, self._activeType) :: any
280
- end
281
-
282
- -- Reconfigure spring when any of its parameters are changed.
283
- -- This should happen after integrating the last frame's motion.
284
- -- NOTE: don't need to add a dependency on these objects! they do not cause
285
- -- a spring to wake from sleep, so the stopwatch dependency is sufficient.
286
- local nextFrameSpeed = peek(self._speed) :: number
287
- local nextFrameDamping = peek(self._damping) :: number
288
- if
289
- discontinuous or
290
- nextFrameGoal ~= self._activeGoal or
291
- nextFrameSpeed ~= self._activeSpeed or
292
- nextFrameDamping ~= self._activeDamping
293
- then
294
- self._activeTargetP = unpackType(nextFrameGoal, nextFrameGoalType)
295
- self._activeNumSprings = #self._activeTargetP
296
- if discontinuous then
297
- self._activeStartP = table.clone(self._activeTargetP)
298
- self._activeLatestP = table.clone(self._activeTargetP)
299
- self._activeStartV = table.create(self._activeNumSprings, 0)
300
- self._activeLatestV = table.create(self._activeNumSprings, 0)
301
- else
302
- self._activeStartP = table.clone(self._activeLatestP)
303
- self._activeStartV = table.clone(self._activeLatestV)
304
- end
305
- self._activeType = nextFrameGoalType
306
- self._activeGoal = nextFrameGoal
307
- self._activeDamping = nextFrameDamping
308
- self._activeSpeed = nextFrameSpeed
309
- stopwatch:zero()
310
- stopwatch:unpause()
311
- end
312
-
313
- -- Push update and check for similarity.
314
- -- Don't need to use the similarity test here because this code doesn't
315
- -- deal with tables, and NaN is already guarded against, so the similarity
316
- -- test doesn't actually add any new safety here.
317
- self._EXTREMELY_DANGEROUS_usedAsValue = newValue
318
- return oldValue ~= newValue
319
- end
320
-
321
- table.freeze(class)
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 physics to shape the motion.
9
+
10
+ https://elttob.uk/Fusion/0.3/api-reference/animation/types/spring/
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 change = require(Package.Graph.change)
21
+ local evaluate = require(Package.Graph.evaluate)
22
+ -- State
23
+ local castToState = require(Package.State.castToState)
24
+ local peek = require(Package.State.peek)
25
+ -- Animation
26
+ local ExternalTime = require(Package.Animation.ExternalTime)
27
+ local Stopwatch = require(Package.Animation.Stopwatch)
28
+ local packType = require(Package.Animation.packType)
29
+ local unpackType = require(Package.Animation.unpackType)
30
+ local springCoefficients = require(Package.Animation.springCoefficients)
31
+ -- Utility
32
+ local nicknames = require(Package.Utility.nicknames)
33
+
34
+ local EPSILON = 0.00001
35
+
36
+ type Self<T> = Types.Spring<T> & {
37
+ _activeDamping: number,
38
+ _activeGoal: T,
39
+ _activeLatestP: {number},
40
+ _activeLatestV: {number},
41
+ _activeNumSprings: number,
42
+ _activeSpeed: number,
43
+ _activeStartP: {number},
44
+ _activeStartV: {number},
45
+ _activeTargetP: {number},
46
+ _activeType: string,
47
+ _speed: Types.UsedAs<number>,
48
+ _damping: Types.UsedAs<number>,
49
+ _goal: Types.UsedAs<T>,
50
+ _stopwatch: Stopwatch.Stopwatch
51
+ }
52
+
53
+ local class = {}
54
+ class.type = "State"
55
+ class.kind = "Spring"
56
+ class.timeliness = "eager"
57
+
58
+ local METATABLE = table.freeze {__index = class}
59
+
60
+ local function Spring<T>(
61
+ scope: Types.Scope<unknown>,
62
+ goal: Types.UsedAs<T>,
63
+ speed: Types.UsedAs<number>?,
64
+ damping: Types.UsedAs<number>?
65
+ ): Types.Spring<T>
66
+ local createdAt = os.clock()
67
+ if typeof(scope) ~= "table" or castToState(scope) ~= nil then
68
+ External.logError("scopeMissing", nil, "Springs", "myScope:Spring(goalState, speed, damping)")
69
+ end
70
+
71
+ local goalState = castToState(goal)
72
+ local stopwatch = nil
73
+ if goalState ~= nil then
74
+ stopwatch = Stopwatch(scope, ExternalTime(scope))
75
+ stopwatch:unpause()
76
+ end
77
+
78
+ local speed = speed or 10
79
+ local damping = damping or 1
80
+
81
+ local self: Self<T> = setmetatable(
82
+ {
83
+ createdAt = createdAt,
84
+ dependencySet = {},
85
+ dependentSet = {},
86
+ lastChange = nil,
87
+ scope = scope,
88
+ validity = "invalid",
89
+ _activeDamping = -1,
90
+ _activeGoal = nil,
91
+ _activeLatestP = {},
92
+ _activeLatestV = {},
93
+ _activeNumSprings = 0,
94
+ _activeSpeed = -1,
95
+ _activeStartP = {},
96
+ _activeStartV = {},
97
+ _activeTargetP = {},
98
+ _activeType = "",
99
+ _damping = damping,
100
+ _EXTREMELY_DANGEROUS_usedAsValue = peek(goal),
101
+ _goal = goal,
102
+ _speed = speed,
103
+ _stopwatch = stopwatch
104
+ },
105
+ METATABLE
106
+ ) :: any
107
+ local destroy = function()
108
+ self.scope = nil
109
+ for dependency in pairs(self.dependencySet) do
110
+ dependency.dependentSet[self] = nil
111
+ end
112
+ end
113
+ self.oldestTask = destroy
114
+ nicknames[self.oldestTask] = "Spring"
115
+ table.insert(scope, destroy)
116
+
117
+ if goalState ~= nil then
118
+ checkLifetime.bOutlivesA(
119
+ scope, self.oldestTask,
120
+ goalState.scope, goalState.oldestTask,
121
+ checkLifetime.formatters.animationGoal
122
+ )
123
+ end
124
+ local speedState = castToState(speed)
125
+ if speedState ~= nil then
126
+ checkLifetime.bOutlivesA(
127
+ scope, self.oldestTask,
128
+ speedState.scope, speedState.oldestTask,
129
+ checkLifetime.formatters.parameter, "speed"
130
+ )
131
+ end
132
+ local dampingState = castToState(damping)
133
+ if dampingState ~= nil then
134
+ checkLifetime.bOutlivesA(
135
+ scope, self.oldestTask,
136
+ dampingState.scope, dampingState.oldestTask,
137
+ checkLifetime.formatters.parameter, "damping"
138
+ )
139
+ end
140
+
141
+ -- Eagerly evaluated objects need to evaluate themselves so that they're
142
+ -- valid at all times.
143
+ evaluate(self, true)
144
+
145
+ return self
146
+ end
147
+
148
+ function class.addVelocity<T>(
149
+ self: Self<T>,
150
+ deltaValue: T
151
+ ): ()
152
+ evaluate(self, false) -- ensure the _active params are up to date
153
+ local deltaType = typeof(deltaValue)
154
+ if deltaType ~= self._activeType then
155
+ External.logError("springTypeMismatch", nil, deltaType, self._activeType)
156
+ end
157
+ local newStartV = unpackType(deltaValue, deltaType)
158
+ for index, velocity in self._activeLatestV do
159
+ newStartV[index] += velocity
160
+ end
161
+ self._activeStartP = table.clone(self._activeLatestP)
162
+ self._activeStartV = newStartV
163
+ self._stopwatch:zero()
164
+ self._stopwatch:unpause()
165
+ change(self)
166
+ end
167
+
168
+ function class.get<T>(
169
+ self: Self<T>
170
+ ): never
171
+ return External.logError("stateGetWasRemoved")
172
+ end
173
+
174
+ function class.setPosition<T>(
175
+ self: Self<T>,
176
+ newValue: T
177
+ ): ()
178
+ evaluate(self, false) -- ensure the _active params are up to date
179
+ local newType = typeof(newValue)
180
+ if newType ~= self._activeType then
181
+ External.logError("springTypeMismatch", nil, newType, self._activeType)
182
+ end
183
+ self._activeStartP = unpackType(newValue, newType)
184
+ self._activeStartV = table.clone(self._activeLatestV)
185
+ self._stopwatch:zero()
186
+ self._stopwatch:unpause()
187
+ change(self)
188
+ end
189
+
190
+ function class.setVelocity<T>(
191
+ self: Self<T>,
192
+ newValue: T
193
+ ): ()
194
+ evaluate(self, false) -- ensure the _active params are up to date
195
+ local newType = typeof(newValue)
196
+ if newType ~= self._activeType then
197
+ External.logError("springTypeMismatch", nil, newType, self._activeType)
198
+ end
199
+ self._activeStartP = table.clone(self._activeLatestP)
200
+ self._activeStartV = unpackType(newValue, newType)
201
+ self._stopwatch:zero()
202
+ self._stopwatch:unpause()
203
+ change(self)
204
+ end
205
+
206
+ function class._evaluate<T>(
207
+ self: Self<T>
208
+ ): boolean
209
+ local goal = castToState(self._goal)
210
+ -- Allow non-state goals to pass through transparently.
211
+ if goal == nil then
212
+ self._EXTREMELY_DANGEROUS_usedAsValue = self._goal :: T
213
+ return false
214
+ end
215
+ -- depend(self, goal)
216
+ local nextFrameGoal = peek(goal)
217
+ -- Protect against NaN goals.
218
+ if nextFrameGoal ~= nextFrameGoal then
219
+ External.logWarn("springNanGoal")
220
+ return false
221
+ end
222
+ local nextFrameGoalType = typeof(nextFrameGoal)
223
+ local discontinuous = nextFrameGoalType ~= self._activeType
224
+
225
+ local stopwatch = self._stopwatch :: Stopwatch.Stopwatch
226
+ local elapsed = peek(stopwatch)
227
+ depend(self, stopwatch)
228
+
229
+ local oldValue = self._EXTREMELY_DANGEROUS_usedAsValue
230
+ local newValue: T
231
+
232
+ if discontinuous then
233
+ -- Propagate changes in type instantly throughout the whole reactive
234
+ -- graph, even if simulation is logically one frame behind, because it
235
+ -- makes the whole graph behave more consistently.
236
+ newValue = nextFrameGoal
237
+ elseif elapsed <= 0 then
238
+ newValue = oldValue
239
+ else
240
+ -- Calculate spring motion.
241
+ -- IMPORTANT: use the parameters from last frame, not this frame. We're
242
+ -- integrating the motion that happened over the last frame, after all.
243
+ -- The stopwatch will have captured the length of time needed correctly.
244
+ local posPos, posVel, velPos, velVel = springCoefficients(
245
+ elapsed,
246
+ self._activeDamping,
247
+ self._activeSpeed
248
+ )
249
+ local isMoving = false
250
+ for index = 1, self._activeNumSprings do
251
+ local startP = self._activeStartP[index]
252
+ local targetP = self._activeTargetP[index]
253
+ local startV = self._activeStartV[index]
254
+ local startD = startP - targetP
255
+ local latestD = startD * posPos + startV * posVel
256
+ local latestV = startD * velPos + startV * velVel
257
+ if latestD ~= latestD or latestV ~= latestV then
258
+ External.logWarn("springNanMotion")
259
+ latestD, latestV = 0, 0
260
+ end
261
+ if math.abs(latestD) > EPSILON or math.abs(latestV) > EPSILON then
262
+ isMoving = true
263
+ end
264
+ local latestP = latestD + targetP
265
+ self._activeLatestP[index] = latestP
266
+ self._activeLatestV[index] = latestV
267
+ end
268
+ -- Sleep and snap to goal if the motion has decayed to a negligible amount.
269
+ if not isMoving then
270
+ for index = 1, self._activeNumSprings do
271
+ self._activeLatestP[index] = self._activeTargetP[index]
272
+ end
273
+ -- TODO: figure out how to do sleeping correctly for single frame
274
+ -- changes
275
+ -- stopwatch:pause()
276
+ -- stopwatch:zero()
277
+ end
278
+ -- Pack springs into final value.
279
+ newValue = packType(self._activeLatestP, self._activeType) :: any
280
+ end
281
+
282
+ -- Reconfigure spring when any of its parameters are changed.
283
+ -- This should happen after integrating the last frame's motion.
284
+ -- NOTE: don't need to add a dependency on these objects! they do not cause
285
+ -- a spring to wake from sleep, so the stopwatch dependency is sufficient.
286
+ local nextFrameSpeed = peek(self._speed) :: number
287
+ local nextFrameDamping = peek(self._damping) :: number
288
+ if
289
+ discontinuous or
290
+ nextFrameGoal ~= self._activeGoal or
291
+ nextFrameSpeed ~= self._activeSpeed or
292
+ nextFrameDamping ~= self._activeDamping
293
+ then
294
+ self._activeTargetP = unpackType(nextFrameGoal, nextFrameGoalType)
295
+ self._activeNumSprings = #self._activeTargetP
296
+ if discontinuous then
297
+ self._activeStartP = table.clone(self._activeTargetP)
298
+ self._activeLatestP = table.clone(self._activeTargetP)
299
+ self._activeStartV = table.create(self._activeNumSprings, 0)
300
+ self._activeLatestV = table.create(self._activeNumSprings, 0)
301
+ else
302
+ self._activeStartP = table.clone(self._activeLatestP)
303
+ self._activeStartV = table.clone(self._activeLatestV)
304
+ end
305
+ self._activeType = nextFrameGoalType
306
+ self._activeGoal = nextFrameGoal
307
+ self._activeDamping = nextFrameDamping
308
+ self._activeSpeed = nextFrameSpeed
309
+ stopwatch:zero()
310
+ stopwatch:unpause()
311
+ end
312
+
313
+ -- Push update and check for similarity.
314
+ -- Don't need to use the similarity test here because this code doesn't
315
+ -- deal with tables, and NaN is already guarded against, so the similarity
316
+ -- test doesn't actually add any new safety here.
317
+ self._EXTREMELY_DANGEROUS_usedAsValue = newValue
318
+ return oldValue ~= newValue
319
+ end
320
+
321
+ table.freeze(class)
322
322
  return Spring :: Types.SpringConstructor