@rbxts/vfx-forge 2.2.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 (38) hide show
  1. package/LICENSE +82 -0
  2. package/README.md +39 -0
  3. package/out/effects/beam.luau +312 -0
  4. package/out/effects/bezier.luau +392 -0
  5. package/out/effects/camera_shake.luau +200 -0
  6. package/out/effects/lightning.luau +1183 -0
  7. package/out/effects/mesh.luau +466 -0
  8. package/out/effects/particle.luau +64 -0
  9. package/out/effects/randomizer.luau +110 -0
  10. package/out/effects/screen.luau +61 -0
  11. package/out/effects/shockwave_debris.luau +277 -0
  12. package/out/effects/shockwave_line.luau +356 -0
  13. package/out/effects/shockwave_ring.luau +252 -0
  14. package/out/effects/sound.luau +311 -0
  15. package/out/effects/spin.luau +88 -0
  16. package/out/effects/tweener.luau +122 -0
  17. package/out/emitters.luau +387 -0
  18. package/out/index.d.ts +341 -0
  19. package/out/init.luau +279 -0
  20. package/out/mod/attributes.luau +227 -0
  21. package/out/mod/color/Oklab.luau +93 -0
  22. package/out/mod/color/sRGB.luau +71 -0
  23. package/out/mod/common/bezier.luau +372 -0
  24. package/out/mod/common/flipbook.luau +102 -0
  25. package/out/mod/lerp.luau +210 -0
  26. package/out/mod/logger.luau +20 -0
  27. package/out/mod/shape.luau +207 -0
  28. package/out/mod/tween.luau +161 -0
  29. package/out/mod/utility.luau +707 -0
  30. package/out/obj/Bezier.luau +268 -0
  31. package/out/obj/ObjectCache.luau +289 -0
  32. package/out/services/caches.luau +62 -0
  33. package/out/services/effects.luau +234 -0
  34. package/out/services/enabled_effects.luau +120 -0
  35. package/out/services/texture_loader.luau +174 -0
  36. package/out/tsconfig.tsbuildinfo +1 -0
  37. package/out/types.luau +43 -0
  38. package/package.json +63 -0
package/LICENSE ADDED
@@ -0,0 +1,82 @@
1
+ SPDX-License-Identifier: VFX-DL-1.1
2
+
3
+ Preamble.
4
+ Disclaimer: nothing stated in this preamble is legal advice.
5
+
6
+ This is a simple license that allows anyone to use the emit module in their Roblox games.
7
+
8
+ 1. You cannot use the module in anything that isn't a Roblox game. For example, it means
9
+ that you cannot create a plugin using this module.
10
+
11
+ 2. You are allowed to use the module commercially in your games.
12
+
13
+ 3. You are free to share the module with other developers so they can use it in their games,
14
+ as long as the license is retained.
15
+
16
+ 4. You can modify and share the module as long as you follow the terms of the license. All
17
+ derivates of the module must have the same license.
18
+
19
+ 5. If you break the terms and conditions of the license, you are required to stop the use and
20
+ distribution of the module immediately.
21
+
22
+ End of preamble.
23
+
24
+ VFX Forge Developer License (VFX-DL) Version 1.1
25
+
26
+ Copyright (c) 2025 zilibobi
27
+
28
+ 1. Definitions.
29
+ - "Module" means the source code and any compiled form of the Emit Module.
30
+ - "You" means any individual or organization exercising the rights granted herein.
31
+ - "Derivative Work" means any work based upon or incorporating the Module.
32
+ - "Roblox Experience" means a user-facing game or simulation that runs on the
33
+ Roblox platform, created using Roblox Studio and played via the Roblox client.
34
+
35
+ 2. Grant of Rights.
36
+ Permission is hereby granted, free of charge, to You to use, reproduce,
37
+ prepare Derivative Works of, publicly display, publicly perform, sublicense,
38
+ and distribute the Module, **solely when embedded in the runtime environment of
39
+ a Roblox Experience** (including playtesting in Roblox Studio), and **not** when
40
+ loaded by any Roblox Studio plugin or other developer tool, running in any context
41
+ that has access to API methods or permissions unavailable to runtime scripts within
42
+ Roblox Experiences (excluding the Roblox Studio command bar feature, when used for
43
+ the purpose of viewing finished effects). You may distribute the Module or Derivative
44
+ Work to other developers, provided they use it only within the permitted runtime
45
+ context described above.
46
+
47
+ 3. Conditions.
48
+ a. Copyleft.
49
+ Any Derivative Work, including modified or extended versions of the Module, that
50
+ You distribute must be licensed under the exact same terms as this VFX-DL.
51
+ b. Redistribution.
52
+ You must include a copy of this license text with any distribution of the Module or Derivative Work.
53
+ c. No Other Contexts.
54
+ You may not embed, load, or distribute the Module within any Roblox Studio
55
+ plugin, editor extension, command-line tool, test harness, or any code running
56
+ under Studio outside of normal game runtime.
57
+
58
+ 4. Disclaimer of Warranty.
59
+ The Module is provided "AS IS," without warranty of any kind, express or implied,
60
+ including but not limited to the warranties of merchantability, fitness for a
61
+ particular purpose, and noninfringement.
62
+
63
+ 5. Limitation of Liability.
64
+ In no event shall the authors or copyright holders be liable for any claim,
65
+ damages, or other liability arising from, out of, or in connection with the
66
+ Module or the use or other dealings in the Module.
67
+
68
+ 6. Termination.
69
+ If You violate the terms of this License, Your rights under this VFX-DL will
70
+ terminate automatically. Upon termination, You must cease all use and distribution
71
+ of the Module and any Derivative Works.
72
+
73
+ 7. Versioning.
74
+ This license applies to Version 1.0 of the VFX-DL. Future versions may be published
75
+ by the copyright holder.
76
+
77
+ 8. Commercial use.
78
+ This License permits commercial use within the scope defined in Section 2.
79
+
80
+ 9. Trademarks and branding.
81
+ Nothing in this License grants permission to use the trade names, trademarks, service marks,
82
+ or product names of the Licensor.
package/README.md ADDED
@@ -0,0 +1,39 @@
1
+ ![Banner](/forge-banner.png)
2
+
3
+ <div align="center">
4
+ <a href="https://docs.zilibobi.dev/vfx-forge"><img alt="View Documentation" src="https://img.shields.io/badge/Documentation-View?style=for-the-badge&label=View&color=c4a7e7"></a>
5
+ <a href="https://discord.gg/Qme9KPN3pF"><img alt="Join Discord" src="https://img.shields.io/discord/1401872327162986626?logo=discord&style=for-the-badge&color=c4a7e7&logoColor=ffffff&label=Join%20Discord"></a>
6
+ </div>
7
+
8
+ # Emit Module
9
+
10
+ The is the official repository for the emit module provided by the <a href="https://devforum.roblox.com/t/3867553">VFX Forge</a> Roblox plugin.
11
+
12
+ ## License
13
+ The emit module is licensed under a custom source-available copyleft license which **<mark>only allows usage within Roblox games</mark>**. That said, feel free to contribute and report bugs.
14
+
15
+ ## Installation
16
+
17
+ #### Using Wally
18
+
19
+ ```sh
20
+ ForgeVFX = "zilibobi/forge-vfx@2.2.2"
21
+ ```
22
+
23
+ #### Manual
24
+
25
+ Check the [releases page](https://github.com/zilibobi/forge-vfx/releases/latest) for prebuilt .rbxm files.
26
+
27
+ ## Usage
28
+
29
+ The module needs to be initialized before emitting any effects.
30
+ After that it is also exposed inside `shared.vfx`.
31
+
32
+ ```luau
33
+ local ReplicatedStorage = game:GetService("ReplicatedStorage")
34
+
35
+ local vfx = require(ReplicatedStorage.ForgeVFX)
36
+
37
+ vfx.init()
38
+ vfx.emit(workspace.Effect)
39
+ ```
@@ -0,0 +1,312 @@
1
+ --!nolint LocalShadow
2
+ local attr = require("@mod/attributes")
3
+ local tween = require("@mod/tween")
4
+ local types = require("@root/types")
5
+ local utility = require("@mod/utility")
6
+ local flipbook_common = require("@mod/common/flipbook")
7
+
8
+ local Promise = require("@pkg/Promise")
9
+
10
+ local rng = Random.new()
11
+
12
+ local function getLegacyWidths(obj: Beam, scale: number)
13
+ local w0 = attr.get(obj, "Width0", obj.Width0, true) * scale
14
+ local w1 = attr.get(obj, "Width1", obj.Width1, true) * scale
15
+
16
+ local s_w0 = attr.get(obj, "StartWidth0", obj.Width0, true) * scale
17
+ local s_w1 = attr.get(obj, "StartWidth1", obj.Width1, true) * scale
18
+
19
+ return w0, w1, s_w0, s_w1
20
+ end
21
+
22
+ local beam = {}
23
+
24
+ function beam.emit(ref: Beam, obj: Beam, scope: types.scope, scale: number)
25
+ local emitOnFinish = scope.effects.prepareEmitOnFinish(obj, scope)
26
+
27
+ local a1, a2, a3, a4 = getLegacyWidths(ref, scale)
28
+
29
+ local w1 = attr.get(ref, "Width0_Start", a3)
30
+ local w2 = attr.get(ref, "Width0_End", a1)
31
+ local w3 = attr.get(ref, "Width1_Start", a4)
32
+ local w4 = attr.get(ref, "Width1_End", a2)
33
+
34
+ local c1 = attr.get(ref, "CurveSize0_Start", obj.CurveSize0)
35
+ local c2 = attr.get(ref, "CurveSize0_End", obj.CurveSize0)
36
+ local c3 = attr.get(ref, "CurveSize1_Start", obj.CurveSize1)
37
+ local c4 = attr.get(ref, "CurveSize1_End", obj.CurveSize1)
38
+
39
+ local emitDelay = attr.get(ref, "EmitDelay", 0)
40
+
41
+ local legacy_duration = attr.get(ref, "Duration", 1, true)
42
+ local legacy_tpScale = attr.get(ref, "EndTransparencyScale", 1, true)
43
+
44
+ local durationRange = attr.getRange(
45
+ ref,
46
+ "EffectDuration",
47
+ NumberRange.new(legacy_duration, legacy_duration),
48
+ NumberRange.new(0, math.huge)
49
+ )
50
+
51
+ local lengthScaleRangeStart = attr.get(ref, "Length_Scale_Start", NumberRange.new(1, 1))
52
+ local lengthScaleRangeEnd = attr.get(ref, "Length_Scale_End", NumberRange.new(1, 1))
53
+
54
+ local texLengthStart = attr.get(ref, "Length_Texture_Start", ref.TextureLength)
55
+ local textLengthEnd = attr.get(ref, "Length_Texture_End", ref.TextureLength)
56
+
57
+ local duration = rng:NextNumber(durationRange.Min, durationRange.Max)
58
+
59
+ local lengthScaleEnd = rng:NextNumber(lengthScaleRangeEnd.Min, lengthScaleRangeEnd.Max)
60
+ local lengthScaleStart = rng:NextNumber(lengthScaleRangeStart.Min, lengthScaleRangeStart.Max)
61
+
62
+ local tStart, tEnd =
63
+ attr.get(ref, "Transparency_Scale_Start", 1), attr.get(ref, "Transparency_Scale_End", legacy_tpScale)
64
+
65
+ local texSpeedStart = attr.get(ref, "Speed_Texture_Start", ref.TextureSpeed)
66
+ local texSpeedEnd = attr.get(ref, "Speed_Texture_End", ref.TextureSpeed)
67
+
68
+ local speedStart = attr.get(ref, "Speed_Start", 1)
69
+ local speedEnd = attr.get(ref, "Speed_End", 1)
70
+
71
+ local baseTexSpeed = obj.TextureSpeed
72
+
73
+ task.wait(emitDelay)
74
+
75
+ obj.Enabled = true
76
+
77
+ local promises = {}
78
+ local currentSpeed = 1
79
+
80
+ local speedTween
81
+
82
+ local env = scope.effects.emitNested(obj, scope.depth + 1)
83
+ table.insert(promises, env.Finished)
84
+
85
+ if texSpeedStart ~= texSpeedEnd then
86
+ tween.fromParams(attr.get(ref, "Speed_Texture_Curve", utility.default_bezier), duration, function(alpha, deltaTime)
87
+ baseTexSpeed = utility.lerp(texSpeedStart, texSpeedEnd, alpha)
88
+ obj.TextureSpeed = baseTexSpeed * currentSpeed
89
+
90
+ return deltaTime
91
+ end)
92
+ end
93
+
94
+ if speedStart ~= speedEnd then
95
+ speedTween = tween.fromParams(
96
+ attr.get(ref, "Speed_Curve", utility.default_bezier),
97
+ attr.get(obj, "Speed_Duration", 0.1),
98
+ function(alpha, deltaTime)
99
+ currentSpeed = utility.lerp(speedStart, speedEnd, alpha)
100
+
101
+ obj.TextureSpeed = baseTexSpeed * currentSpeed
102
+
103
+ return deltaTime
104
+ end
105
+ )
106
+
107
+ table.insert(scope, speedTween)
108
+ end
109
+
110
+ local refTpKp = ref.Transparency.Keypoints
111
+
112
+ local numKeypoints = #refTpKp
113
+ local scaledKeypoints = table.create(numKeypoints)
114
+
115
+ local keypointData = table.create(numKeypoints)
116
+
117
+ for i, kp in refTpKp do
118
+ keypointData[i] = {
119
+ time = kp.Time,
120
+ value = kp.Value,
121
+ envelope = kp.Envelope,
122
+ }
123
+ end
124
+
125
+ local lastTScale = nil
126
+
127
+ local function setTScale(tscale: number)
128
+ if lastTScale and math.abs(tscale - lastTScale) < 0.001 then
129
+ return
130
+ end
131
+
132
+ lastTScale = tscale
133
+
134
+ local offset = tscale > 1 and tscale - 1 or 1 - tscale
135
+
136
+ for i, kd in keypointData do
137
+ local value = kd.value
138
+ local base = tscale > 1 and (1 - value) or -value
139
+
140
+ scaledKeypoints[i] = NumberSequenceKeypoint.new(kd.time, value + base * offset, kd.envelope)
141
+ end
142
+
143
+ obj.Transparency = NumberSequence.new(scaledKeypoints)
144
+ end
145
+
146
+ local att0, att1 = obj.Attachment0, obj.Attachment1
147
+ local cf0, cf1 = att0 and att0.CFrame, att1 and att1.CFrame
148
+
149
+ local function setLengthScale(scale: number)
150
+ if not cf0 or not cf1 then
151
+ return
152
+ end
153
+
154
+ -- calculate the original offset vector from att0 to att1
155
+ local offset = cf1.Position - cf0.Position
156
+ local halfOffset = offset * 0.5
157
+
158
+ -- calculate center point
159
+ local center = cf0.Position + halfOffset
160
+
161
+ -- scale the half-offsets and apply from center
162
+ local scaledHalfOffset = halfOffset * scale
163
+
164
+ att0.CFrame = CFrame.new(center - scaledHalfOffset) * (cf0 - cf0.Position)
165
+ att1.CFrame = CFrame.new(center + scaledHalfOffset) * (cf1 - cf1.Position)
166
+ end
167
+
168
+ table.insert(scope, function()
169
+ setLengthScale(1)
170
+ end)
171
+
172
+ if tStart ~= tEnd then
173
+ table.insert(
174
+ scope,
175
+ tween.fromParams(
176
+ attr.get(ref, "Transparency_Scale_Curve", utility.default_bezier),
177
+ duration,
178
+ function(alpha, deltaTime)
179
+ setTScale(utility.lerp(tStart, tEnd, alpha))
180
+ return deltaTime * currentSpeed
181
+ end,
182
+ speedTween
183
+ )
184
+ )
185
+ elseif tStart ~= 1 then
186
+ setTScale(tStart)
187
+ end
188
+
189
+ if texLengthStart ~= textLengthEnd then
190
+ table.insert(
191
+ scope,
192
+ tween.fromParams(
193
+ attr.get(ref, "Length_Texture_Curve", utility.default_bezier),
194
+ duration,
195
+ function(alpha, deltaTime)
196
+ obj.TextureLength = utility.lerp(texLengthStart, textLengthEnd, alpha)
197
+ return deltaTime * currentSpeed
198
+ end,
199
+ speedTween
200
+ )
201
+ )
202
+ end
203
+
204
+ if lengthScaleStart ~= lengthScaleEnd then
205
+ table.insert(
206
+ scope,
207
+ tween.fromParams(attr.get(ref, "Length_Scale_Curve", utility.default_bezier), duration, function(alpha, deltaTime)
208
+ local x = utility.lerp(lengthScaleStart, lengthScaleEnd, alpha)
209
+
210
+ setLengthScale(x)
211
+
212
+ return deltaTime * currentSpeed
213
+ end, speedTween)
214
+ )
215
+ elseif texLengthStart ~= 1 then
216
+ setLengthScale(lengthScaleStart)
217
+ end
218
+
219
+ if w1 ~= w2 then
220
+ table.insert(
221
+ scope,
222
+ tween.fromParams(attr.get(ref, "Width0_Curve", utility.default_bezier), duration, function(alpha, deltaTime)
223
+ obj.Width0 = utility.lerp(w1, w2, alpha)
224
+ return deltaTime * currentSpeed
225
+ end, speedTween)
226
+ )
227
+ else
228
+ obj.Width0 = w1
229
+ end
230
+
231
+ if w3 ~= w4 then
232
+ table.insert(
233
+ scope,
234
+ tween.fromParams(attr.get(ref, "Width1_Curve", utility.default_bezier), duration, function(alpha, deltaTime)
235
+ obj.Width1 = utility.lerp(w3, w4, alpha)
236
+ return deltaTime * currentSpeed
237
+ end, speedTween)
238
+ )
239
+ else
240
+ obj.Width1 = w3
241
+ end
242
+
243
+ if c1 ~= c2 then
244
+ table.insert(
245
+ scope,
246
+ tween.fromParams(attr.get(ref, "CurveSize0_Curve", utility.default_bezier), duration, function(alpha, deltaTime)
247
+ obj.CurveSize0 = utility.lerp(c1, c2, alpha)
248
+ return deltaTime * currentSpeed
249
+ end, speedTween)
250
+ )
251
+ else
252
+ obj.Width0 = w1
253
+ end
254
+
255
+ if c3 ~= c4 then
256
+ table.insert(
257
+ scope,
258
+ tween.fromParams(attr.get(ref, "CurveSize1_Curve", utility.default_bezier), duration, function(alpha, deltaTime)
259
+ obj.CurveSize1 = utility.lerp(c3, c4, alpha)
260
+ return deltaTime * currentSpeed
261
+ end, speedTween)
262
+ )
263
+ else
264
+ obj.Width1 = w3
265
+ end
266
+
267
+ -- flipbook support
268
+ local flipbookDuration = 0
269
+ local flipbook = flipbook_common.getFlipbookData(ref)
270
+
271
+ if flipbook then
272
+ local config = {
273
+ ref = ref,
274
+ frames = flipbook,
275
+ speedTween = speedTween,
276
+ effectDuration = duration,
277
+
278
+ curve = attr.get(ref, "Flipbook_Change_Curve", utility.linear_bezier),
279
+ duration = attr.get(ref, "Flipbook_Change_Duration", duration),
280
+
281
+ getSpeed = function()
282
+ return currentSpeed
283
+ end,
284
+
285
+ setTexture = function(texture)
286
+ obj.Texture = texture
287
+ end,
288
+ }
289
+
290
+ flipbookDuration = flipbook_common.getChangeDuration(config)
291
+
292
+ table.insert(
293
+ scope,
294
+ tween.fromParams(config.curve, flipbookDuration, flipbook_common.createUpdateCallback(config), speedTween)
295
+ )
296
+ end
297
+
298
+ tween.timer(math.max(duration, flipbookDuration), function(deltaTime, elapsed)
299
+ return if currentSpeed > 0 or (elapsed > 0 and speedTween and speedTween.Connected)
300
+ then deltaTime * currentSpeed
301
+ else nil
302
+ end, speedTween, scope)
303
+
304
+ if emitOnFinish then
305
+ local env = scope.effects.emitOnFinish(emitOnFinish, obj.Parent or workspace.Terrain, scope.depth + 1)
306
+ table.insert(promises, env.Finished)
307
+ end
308
+
309
+ Promise.all(promises):await()
310
+ end
311
+
312
+ return beam