@rbxts/vfx-forge 2.2.2-ts.1

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 (42) hide show
  1. package/LICENSE +82 -0
  2. package/README.md +39 -0
  3. package/out/forge-vfx/effects/beam.luau +312 -0
  4. package/out/forge-vfx/effects/bezier.luau +392 -0
  5. package/out/forge-vfx/effects/camera_shake.luau +200 -0
  6. package/out/forge-vfx/effects/lightning.luau +1183 -0
  7. package/out/forge-vfx/effects/mesh.luau +466 -0
  8. package/out/forge-vfx/effects/particle.luau +64 -0
  9. package/out/forge-vfx/effects/randomizer.luau +110 -0
  10. package/out/forge-vfx/effects/screen.luau +61 -0
  11. package/out/forge-vfx/effects/shockwave_debris.luau +277 -0
  12. package/out/forge-vfx/effects/shockwave_line.luau +356 -0
  13. package/out/forge-vfx/effects/shockwave_ring.luau +252 -0
  14. package/out/forge-vfx/effects/sound.luau +311 -0
  15. package/out/forge-vfx/effects/spin.luau +88 -0
  16. package/out/forge-vfx/effects/tweener.luau +122 -0
  17. package/out/forge-vfx/emitters.luau +387 -0
  18. package/out/forge-vfx/index.d.ts +356 -0
  19. package/out/forge-vfx/init.luau +279 -0
  20. package/out/forge-vfx/mod/attributes.luau +227 -0
  21. package/out/forge-vfx/mod/color/Oklab.luau +93 -0
  22. package/out/forge-vfx/mod/color/sRGB.luau +71 -0
  23. package/out/forge-vfx/mod/common/bezier.luau +372 -0
  24. package/out/forge-vfx/mod/common/flipbook.luau +102 -0
  25. package/out/forge-vfx/mod/lerp.luau +210 -0
  26. package/out/forge-vfx/mod/logger.luau +20 -0
  27. package/out/forge-vfx/mod/shape.luau +207 -0
  28. package/out/forge-vfx/mod/tween.luau +161 -0
  29. package/out/forge-vfx/mod/utility.luau +707 -0
  30. package/out/forge-vfx/obj/Bezier.luau +268 -0
  31. package/out/forge-vfx/obj/ObjectCache.luau +289 -0
  32. package/out/forge-vfx/services/caches.luau +62 -0
  33. package/out/forge-vfx/services/effects.luau +234 -0
  34. package/out/forge-vfx/services/enabled_effects.luau +120 -0
  35. package/out/forge-vfx/services/texture_loader.luau +174 -0
  36. package/out/forge-vfx/types.luau +43 -0
  37. package/out/index.d.ts +3 -0
  38. package/out/init.luau +5 -0
  39. package/out/shake.d.ts +2 -0
  40. package/out/shake.luau +6 -0
  41. package/out/tsconfig.tsbuildinfo +1 -0
  42. package/package.json +63 -0
@@ -0,0 +1,277 @@
1
+ local TS = _G[script.Parent.Parent.Parent]
2
+ local RunService = game:GetService("RunService")
3
+ local CollectionService = game:GetService("CollectionService")
4
+
5
+ local attr = require(script.Parent.Parent.mod.attributes)
6
+ local tween = require(script.Parent.Parent.mod.tween)
7
+ local types = require(script.Parent.Parent.types)
8
+ local utility = require(script.Parent.Parent.mod.utility)
9
+
10
+ local ObjectCache = require(script.Parent.Parent.obj.ObjectCache)
11
+
12
+ local rng = Random.new()
13
+
14
+ local debris = {}
15
+ local physicsParts = {}
16
+
17
+ local part_cache: ObjectCache.ObjectCache?
18
+ local physicsStepper: RBXScriptConnection?
19
+
20
+ function debris.init(cache)
21
+ if physicsStepper then
22
+ return
23
+ end
24
+
25
+ part_cache = cache
26
+
27
+ if utility.PLUGIN_CONTEXT then
28
+ physicsStepper = RunService.RenderStepped:Connect(function(deltaTime)
29
+ if #physicsParts == 0 then
30
+ return
31
+ end
32
+
33
+ workspace:StepPhysics(deltaTime, physicsParts)
34
+ end)
35
+ end
36
+ end
37
+
38
+ function debris.deinit()
39
+ if physicsStepper then
40
+ physicsStepper:Disconnect()
41
+ physicsStepper = nil
42
+ end
43
+
44
+ part_cache = nil
45
+
46
+ for _, part in physicsParts do
47
+ part:Destroy()
48
+ end
49
+
50
+ table.clear(physicsParts)
51
+ end
52
+
53
+ function debris.emit(origin: Attachment, ref: Part, scope: types.scope)
54
+ if not part_cache then
55
+ return
56
+ end
57
+
58
+ local params
59
+
60
+ local inherit = attr.get(ref, "InheritanceEnabled", true)
61
+ local inheritRadius = attr.get(ref, "InheritanceRadius", 5)
62
+
63
+ local rayColGroup = attr.get(origin, "RayCollisionGroup", "Default")
64
+
65
+ local rayFilterTag = attr.get(origin, "FilterTag", "")
66
+ local rayFilterType = attr.get(origin, "FilterType", "Exclude")
67
+
68
+ local rayIgnoreCanCollide = attr.get(origin, "IgnoreCanCollide", false)
69
+
70
+ if inherit then
71
+ local inheritMaxResults = attr.get(ref, "InheritanceMaxResults", 5)
72
+
73
+ params = OverlapParams.new()
74
+ params.MaxParts = inheritMaxResults
75
+ params.CollisionGroup = rayColGroup
76
+ params.RespectCanCollide = not rayIgnoreCanCollide
77
+ params.FilterType = Enum.RaycastFilterType[rayFilterType]
78
+ params.FilterDescendantsInstances = CollectionService:GetTagged(rayFilterTag)
79
+
80
+ if rayFilterType == "Exclude" then
81
+ params:AddToFilter({ workspace.Terrain })
82
+ end
83
+ end
84
+
85
+ local emitDelay = attr.get(ref, "EmitDelay", 0)
86
+
87
+ local amount = attr.getRange(ref, "Amount", NumberRange.new(5, 10), NumberRange.new(0, math.huge))
88
+ local lifetime = attr.getRange(ref, "Lifetime", NumberRange.new(2, 3), NumberRange.new(0, math.huge))
89
+ local airtime = attr.getRange(ref, "Airtime", NumberRange.new(0.5, 0.5), NumberRange.new(0, math.huge))
90
+
91
+ local linmag = attr.getRange(ref, "LinearMagnitude", NumberRange.new(15, 25))
92
+ local angmag = attr.getRange(ref, "AngularMagnitude", NumberRange.new(5, 15))
93
+
94
+ local sizeScaleEnd = attr.get(ref, "SizeScaleEnd", vector.zero)
95
+
96
+ local minSize = attr.get(ref, "MinSize", vector.create(2, 1, 2))
97
+ local maxSize = attr.get(ref, "MaxSize", vector.create(3, 2, 3))
98
+
99
+ local minDir = attr.get(ref, "MinDirection", -vector.one)
100
+ local maxDir = attr.get(ref, "MaxDirection", vector.one)
101
+
102
+ local count = rng:NextInteger(amount.Min, amount.Max)
103
+
104
+ local sizeCurve = attr.get(ref, "Size_Curve", utility.default_bezier)
105
+ local tpCurve = attr.get(ref, "Transparency_Curve", utility.default_bezier)
106
+
107
+ local tpDuration = attr.get(ref, "Transparency_Duration", 0.5)
108
+ local sizeDuration = attr.get(ref, "Size_Duration", 0.5)
109
+
110
+ local tpStart = attr.get(ref, "Transparency_Start", 0)
111
+ local tpEnd = attr.get(ref, "Transparency_End", 1)
112
+
113
+ local partData = {}
114
+ local promises = {}
115
+
116
+ ref.CanCollide = false
117
+
118
+ local originCFrame = utility.getTransformedOriginExtents(origin)
119
+ local originPosition = originCFrame.Position
120
+
121
+ if params then
122
+ local parts = workspace:GetPartBoundsInRadius(originPosition, inheritRadius, params)
123
+
124
+ for _, p in parts do
125
+ if p.Transparency == 1 then
126
+ continue
127
+ end
128
+
129
+ table.insert(partData, p)
130
+ end
131
+ end
132
+
133
+ task.wait(emitDelay)
134
+
135
+ for _ = 1, count do
136
+ local iref = #partData ~= 0 and partData[rng:NextInteger(1, #partData)]
137
+
138
+ local id = utility.getRandomId()
139
+
140
+ local part = part_cache:get(id)
141
+
142
+ local realPart = part._getReal() :: BasePart
143
+
144
+ utility.copyProperties(ref, realPart, utility.COPY_PART_PROPERTIES)
145
+ utility.copyProperties(ref, realPart, utility.COPY_EXTENDED_PART_PROPERTIES)
146
+
147
+ if #ref:GetChildren() ~= 0 then
148
+ local clone = ref:Clone()
149
+
150
+ for _, child in clone:GetChildren() do
151
+ child.Parent = realPart
152
+ end
153
+
154
+ clone:Destroy()
155
+ end
156
+
157
+ local emitOnFinish = scope.effects.prepareEmitOnFinish(realPart, scope)
158
+ local emitOnImpact = scope.effects.prepareEmitFolder(realPart, "EmitOnImpact", scope)
159
+
160
+ if emitOnImpact and #emitOnImpact:GetChildren() > 0 then
161
+ local conn
162
+ conn = realPart.Touched:Connect(function(hit)
163
+ if
164
+ (not rayIgnoreCanCollide and not hit:CanCollideWith(realPart)) -- ignore parts we can't collide with if they can be ignored
165
+ or hit:IsDescendantOf(workspace.Terrain) -- ignore all debris parts/other effects
166
+ or (rayFilterType == "Exclude" and hit:HasTag(rayFilterTag)) -- ignore all excluded tags
167
+ then
168
+ return
169
+ end
170
+
171
+ conn:Disconnect()
172
+
173
+ local env = scope.effects.emitFromFolder(emitOnImpact, realPart, scope.depth + 1)
174
+ table.insert(promises, env.Finished)
175
+ end)
176
+
177
+ table.insert(scope, function()
178
+ if conn then
179
+ conn:Disconnect()
180
+ conn = nil
181
+ end
182
+ end)
183
+ end
184
+
185
+ part.Anchored = false
186
+ part.CanCollide = true
187
+
188
+ if part.CollisionGroup == "Default" then
189
+ part.CollisionGroup = "ForgeDebris"
190
+ end
191
+
192
+ part.CFrame = CFrame.new(originPosition)
193
+
194
+ local size = Vector3.new(
195
+ rng:NextNumber(minSize.X, maxSize.X),
196
+ rng:NextNumber(minSize.Y, maxSize.Y),
197
+ rng:NextNumber(minSize.Z, maxSize.Z)
198
+ )
199
+
200
+ part.Size = size
201
+
202
+ if iref then
203
+ part.Material = iref.Material
204
+ part.Color = iref.Color
205
+ part.Transparency = iref.Transparency
206
+ else
207
+ part.Transparency = tpStart
208
+ end
209
+
210
+ table.insert(scope, function()
211
+ local index = table.find(physicsParts, realPart)
212
+
213
+ if index then
214
+ table.remove(physicsParts, index)
215
+ end
216
+
217
+ if part_cache then
218
+ part_cache:free(id)
219
+ end
220
+ end)
221
+
222
+ table.insert(physicsParts, realPart)
223
+
224
+ part.AssemblyLinearVelocity = originCFrame:VectorToWorldSpace(part.AssemblyLinearVelocity)
225
+
226
+ local relDir = utility.randomUnitVector(minDir, maxDir)
227
+ local worldDir = originCFrame:VectorToWorldSpace(relDir)
228
+
229
+ part:ApplyImpulse(
230
+ part.AssemblyMass
231
+ * utility.getImpulseForce(
232
+ originPosition,
233
+ originPosition + worldDir.Unit * rng:NextNumber(linmag.Min, linmag.Max),
234
+ rng:NextNumber(airtime.Min, airtime.Max)
235
+ )
236
+ )
237
+
238
+ part:ApplyAngularImpulse(part.AssemblyMass * rng:NextUnitVector() * rng:NextNumber(angmag.Min, angmag.Max))
239
+
240
+ local env = scope.effects.emitNested(realPart, scope.depth + 1)
241
+ table.insert(promises, env.Finished)
242
+
243
+ task.delay(rng:NextNumber(lifetime.Min, lifetime.Max), function()
244
+ local startSize = part.Size
245
+ local startTp = part.Transparency
246
+
247
+ if startSize ~= size * sizeScaleEnd then
248
+ table.insert(
249
+ scope,
250
+ tween.fromParams(sizeCurve, sizeDuration, function(alpha, deltaTime)
251
+ part.Size = startSize:Lerp(size * sizeScaleEnd, alpha)
252
+ return deltaTime
253
+ end)
254
+ )
255
+ end
256
+
257
+ if startTp ~= tpEnd then
258
+ table.insert(
259
+ scope,
260
+ tween.fromParams(tpCurve, tpDuration, function(alpha, deltaTime)
261
+ part.Transparency = utility.lerp(startTp, tpEnd, alpha)
262
+ return deltaTime
263
+ end)
264
+ )
265
+ end
266
+
267
+ local env = scope.effects.emitOnFinish(emitOnFinish, realPart, scope.depth + 1)
268
+ table.insert(promises, env.Finished)
269
+ end)
270
+ end
271
+
272
+ task.wait(lifetime.Max + math.max(tpDuration, sizeDuration))
273
+
274
+ TS.Promise.all(promises):await()
275
+ end
276
+
277
+ return debris
@@ -0,0 +1,356 @@
1
+ local TS = _G[script.Parent.Parent.Parent]
2
+ local CollectionService = game:GetService("CollectionService")
3
+
4
+ local attr = require(script.Parent.Parent.mod.attributes)
5
+ local tween = require(script.Parent.Parent.mod.tween)
6
+ local types = require(script.Parent.Parent.types)
7
+ local logger = require(script.Parent.Parent.mod.logger)
8
+ local utility = require(script.Parent.Parent.mod.utility)
9
+
10
+ local Bezier = require(script.Parent.Parent.obj.Bezier)
11
+ local ObjectCache = require(script.Parent.Parent.obj.ObjectCache)
12
+
13
+ local rng = Random.new()
14
+
15
+ local line = {}
16
+
17
+ local part_cache: ObjectCache.ObjectCache?
18
+
19
+ function line.init(cache)
20
+ part_cache = cache
21
+ end
22
+
23
+ function line.deinit()
24
+ part_cache = nil
25
+ end
26
+
27
+ function line.emit(origin: Attachment, ref: Part, scope: types.scope)
28
+ if not part_cache then
29
+ return
30
+ end
31
+
32
+ local raydir = attr.get(origin, "RayDirection", vector.create(0, -50, 0))
33
+
34
+ local rayColGroup = attr.get(origin, "RayCollisionGroup", "Default")
35
+ local rayFilterTag = attr.get(origin, "FilterTag", "")
36
+ local rayFilterType = attr.get(origin, "FilterType", "Exclude")
37
+
38
+ local rayIgnoreWater = attr.get(origin, "IgnoreWater", true)
39
+ local rayIgnoreCanCollide = attr.get(origin, "IgnoreCanCollide", false)
40
+
41
+ local params = RaycastParams.new()
42
+ params.CollisionGroup = rayColGroup
43
+ params.IgnoreWater = rayIgnoreWater
44
+ params.RespectCanCollide = not rayIgnoreCanCollide
45
+ params.FilterType = Enum.RaycastFilterType[rayFilterType]
46
+ params.FilterDescendantsInstances = CollectionService:GetTagged(rayFilterTag)
47
+
48
+ if rayFilterType == "Exclude" then
49
+ params:AddToFilter({ workspace.Terrain })
50
+ end
51
+
52
+ local emitDelay = attr.get(ref, "EmitDelay", 0)
53
+
54
+ local rateStart = attr.get(ref, "Rate_Start", 30)
55
+ local rateEnd = attr.get(ref, "Rate_End", 30)
56
+
57
+ local fixedAmount = attr.get(ref, "FixedAmount", 0)
58
+
59
+ local scaleStart = attr.get(ref, "Scale_Start", 1)
60
+ local scaleEnd = attr.get(ref, "Scale_End", 1)
61
+
62
+ local duration = attr.get(ref, "Duration", 1)
63
+ local direction = attr.get(ref, "Direction", vector.create(0, 0, -1))
64
+ local lineLength = attr.get(ref, "Length", 50)
65
+
66
+ local rot = attr.getRange(ref, "Rotation", NumberRange.new(-180, 180))
67
+
68
+ local old_partOffset = attr.get(ref, "PartOffset", vector.zero, true)
69
+
70
+ local baseOffset = attr.get(ref, "BaseOffset", old_partOffset)
71
+ local offsetStart = attr.get(ref, "Offset_Start", old_partOffset)
72
+ local offsetEnd = attr.get(ref, "Offset_End", old_partOffset)
73
+
74
+ local offsetStartDuration = attr.get(ref, "Offset_Start_Duration", 0.5)
75
+ local offsetEndDuration = attr.get(ref, "Offset_End_Duration", 0.5)
76
+
77
+ local lifetime = attr.getRange(ref, "Lifetime", NumberRange.new(2, 3), NumberRange.new(0, math.huge))
78
+
79
+ local sizeScaleStart = attr.get(ref, "SizeScaleStart", vector.zero)
80
+ local sizeScaleEnd = attr.get(ref, "SizeScaleEnd", vector.zero)
81
+
82
+ local minSize = attr.get(ref, "MinSize", vector.create(2, 1, 2))
83
+ local maxSize = attr.get(ref, "MaxSize", vector.create(3, 2, 3))
84
+
85
+ local old_sizeCurve = attr.get(ref, "Size_Curve", utility.default_bezier, true)
86
+ local old_sizeDuration = attr.get(ref, "Size_Duration", 0.5, true)
87
+
88
+ local sizeStartCurve = attr.get(ref, "Size_Start_Curve", old_sizeCurve)
89
+ local sizeEndCurve = attr.get(ref, "Size_End_Curve", old_sizeCurve)
90
+
91
+ local sizeStartDuration = attr.get(ref, "Size_Start_Duration", old_sizeDuration)
92
+ local sizeEndDuration = attr.get(ref, "Size_End_Duration", old_sizeDuration)
93
+
94
+ local tpDuration = attr.get(ref, "Transparency_Duration", 0.5)
95
+
96
+ local tpStart = attr.get(ref, "Transparency_Start", 0)
97
+ local tpEnd = attr.get(ref, "Transparency_End", 0)
98
+
99
+ local sync = attr.get(ref, "SyncPosition", false)
100
+
101
+ duration = math.max(duration, 0.001)
102
+
103
+ if fixedAmount > 0 then
104
+ local rate = fixedAmount / duration
105
+
106
+ rateStart = rate
107
+ rateEnd = rate
108
+ end
109
+
110
+ direction = direction.Unit
111
+
112
+ if direction ~= direction then
113
+ direction = -Vector3.zAxis
114
+ end
115
+
116
+ task.wait(emitDelay)
117
+
118
+ local promises = {}
119
+
120
+ local start = utility.getTransformedOriginExtents(origin)
121
+
122
+ local last = 0
123
+ local total = 0
124
+
125
+ local currentRate = rateStart
126
+ local currentScale = scaleStart
127
+
128
+ if scaleStart ~= scaleEnd then
129
+ table.insert(
130
+ scope,
131
+ tween.fromParams(attr.get(ref, "Scale_Curve", utility.default_bezier), duration, function(alpha, deltaTime)
132
+ currentScale = utility.lerp(scaleStart, scaleEnd, alpha)
133
+ return deltaTime
134
+ end)
135
+ )
136
+ end
137
+
138
+ if rateStart ~= rateEnd then
139
+ table.insert(
140
+ scope,
141
+ tween.fromParams(attr.get(ref, "Rate_Curve", utility.default_bezier), duration, function(alpha, deltaTime)
142
+ currentRate = utility.lerp(rateStart, rateEnd, alpha)
143
+ return deltaTime
144
+ end)
145
+ )
146
+ end
147
+
148
+ local pathCurve = attr.get(ref, "Path_Curve", utility.linear_bezier)
149
+
150
+ local ok, points = pcall(function()
151
+ return utility.deserializePath(pathCurve)
152
+ end)
153
+
154
+ if not ok then
155
+ logger.error(`failed to decode bezier path data with error: {points}`)
156
+ end
157
+
158
+ local bezier = Bezier.new(points, 0)
159
+
160
+ table.insert(
161
+ scope,
162
+ tween.fromParams(utility.linear_bezier, duration, function(_, deltaTime)
163
+ last += deltaTime
164
+
165
+ local threshold = 1 / currentRate
166
+ local max = duration * currentRate
167
+
168
+ if last < threshold or total >= max then
169
+ return deltaTime
170
+ end
171
+
172
+ local ticks = last // threshold
173
+
174
+ for i = 1, ticks do
175
+ if total >= max then
176
+ continue
177
+ end
178
+
179
+ total += 1
180
+
181
+ local t = math.clamp(total / max, 0, 1)
182
+ local alpha = 1 - bezier:getEase(t).y
183
+
184
+ if sync then
185
+ start = utility.getTransformedOriginExtents(origin)
186
+ end
187
+
188
+ local originCFrame = utility.getTransformedOriginExtents(origin)
189
+ local result = workspace:Raycast(
190
+ start.Position + start:VectorToWorldSpace(direction) * lineLength * alpha,
191
+ originCFrame:VectorToWorldSpace(raydir),
192
+ params
193
+ )
194
+
195
+ if not result then
196
+ last = 0
197
+ return deltaTime
198
+ end
199
+
200
+ local width = rng:NextNumber(minSize.X, maxSize.X)
201
+
202
+ local height = rng:NextNumber(minSize.Y, maxSize.Y)
203
+ local length = rng:NextNumber(minSize.Z, maxSize.Z)
204
+
205
+ local id = utility.getRandomId()
206
+
207
+ local part = part_cache:get(id)
208
+
209
+ local realPart = part._getReal()
210
+
211
+ utility.copyProperties(ref, realPart, utility.COPY_PART_PROPERTIES)
212
+ utility.copyProperties(ref, realPart, utility.COPY_EXTENDED_PART_PROPERTIES)
213
+
214
+ if #ref:GetChildren() ~= 0 then
215
+ local clone = ref:Clone()
216
+
217
+ for _, child in clone:GetChildren() do
218
+ child.Parent = realPart
219
+ end
220
+
221
+ clone:Destroy()
222
+ end
223
+
224
+ local emitOnFinish = scope.effects.prepareEmitOnFinish(realPart, scope)
225
+
226
+ table.insert(scope, function()
227
+ if part_cache then
228
+ part_cache:free(id)
229
+ end
230
+ end)
231
+
232
+ part.Color = result.Instance.Color
233
+ part.Material = result.Material
234
+ part.Transparency = result.Instance.Transparency
235
+
236
+ if part.Transparency == 0 then
237
+ part.Transparency = tpStart
238
+ end
239
+
240
+ part.Size = Vector3.zero
241
+
242
+ local angle = rng:NextNumber(rot.Min, rot.Max)
243
+
244
+ local rx = -math.cos(angle)
245
+ local rz = -math.sin(angle)
246
+
247
+ local dir = originCFrame:VectorToWorldSpace(Vector3.new(rx, 0, rz))
248
+ local right = dir:Cross(result.Normal).Unit
249
+
250
+ local rotCF = CFrame.fromMatrix(result.Position, right, result.Normal)
251
+ * CFrame.fromOrientation(-math.atan(height / length), 0, 0)
252
+
253
+ part.CFrame = CFrame.new(offsetStart) * rotCF
254
+
255
+ local sizeTween
256
+ local offsetTween
257
+
258
+ local currentOffset = Vector3.zero
259
+
260
+ if offsetStart ~= baseOffset then
261
+ offsetTween = tween.fromParams(
262
+ attr.get(ref, "Offset_Start_Curve", utility.default_bezier),
263
+ offsetStartDuration,
264
+ function(alpha, deltaTime)
265
+ currentOffset = offsetStart:Lerp(baseOffset, alpha)
266
+ part.CFrame = CFrame.new(currentOffset) * rotCF
267
+ return deltaTime
268
+ end
269
+ )
270
+
271
+ table.insert(scope, offsetTween)
272
+ end
273
+
274
+ local size = Vector3.new(width, height, length) * currentScale
275
+
276
+ if size * sizeScaleStart ~= size then
277
+ sizeTween = tween.fromParams(sizeStartCurve, sizeStartDuration, function(alpha, deltaTime)
278
+ part.Size = (size * sizeScaleStart):Lerp(size, alpha)
279
+ return deltaTime
280
+ end)
281
+
282
+ table.insert(scope, sizeTween)
283
+ else
284
+ part.Size = size * sizeScaleStart
285
+ end
286
+
287
+ local env = scope.effects.emitNested(realPart, scope.depth + 1)
288
+ table.insert(promises, env.Finished)
289
+
290
+ task.delay(rng:NextNumber(lifetime.Min, lifetime.Max), function()
291
+ if sizeTween then
292
+ sizeTween:Disconnect()
293
+ end
294
+
295
+ if offsetTween then
296
+ offsetTween:Disconnect()
297
+ end
298
+
299
+ local startSize = part.Size
300
+ local startTp = part.Transparency
301
+
302
+ if startSize ~= size * sizeScaleEnd then
303
+ table.insert(
304
+ scope,
305
+ tween.fromParams(sizeEndCurve, sizeEndDuration, function(alpha, deltaTime)
306
+ part.Size = startSize:Lerp(size * sizeScaleEnd, alpha)
307
+ return deltaTime
308
+ end)
309
+ )
310
+ end
311
+
312
+ if currentOffset ~= offsetEnd then
313
+ table.insert(
314
+ scope,
315
+ tween.fromParams(
316
+ attr.get(ref, "Offset_End_Curve", utility.default_bezier),
317
+ offsetEndDuration,
318
+ function(alpha, deltaTime)
319
+ part.CFrame = CFrame.new(currentOffset:Lerp(offsetEnd, alpha)) * rotCF
320
+ return deltaTime
321
+ end
322
+ )
323
+ )
324
+ end
325
+
326
+ if startTp ~= tpEnd then
327
+ table.insert(
328
+ scope,
329
+ tween.fromParams(
330
+ attr.get(ref, "Transparency_Curve", utility.default_bezier),
331
+ tpDuration,
332
+ function(alpha, deltaTime)
333
+ part.Transparency = utility.lerp(startTp, tpEnd, alpha)
334
+ return deltaTime
335
+ end
336
+ )
337
+ )
338
+ end
339
+
340
+ local env = scope.effects.emitOnFinish(emitOnFinish, realPart, scope.depth + 1)
341
+ table.insert(promises, env.Finished)
342
+ end)
343
+ end
344
+
345
+ last %= threshold
346
+
347
+ return deltaTime
348
+ end, nil, nil, true, utility.RENDER_PRIORITY + scope.depth)
349
+ )
350
+
351
+ task.wait(lifetime.Max + duration + math.max(sizeEndDuration, offsetEndDuration))
352
+
353
+ TS.Promise.all(promises):await()
354
+ end
355
+
356
+ return line