@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,392 @@
1
+ local TS = _G[script.Parent.Parent.Parent]
2
+ --!nolint LocalShadow
3
+ local RunService = game:GetService("RunService")
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
+ local bezier_common = require(script.Parent.Parent.mod.common.bezier)
10
+
11
+ local Bezier = require(script.Parent.Parent.obj.Bezier)
12
+ local ObjectCache = require(script.Parent.Parent.obj.ObjectCache)
13
+
14
+ local bezier = {}
15
+
16
+ local part_cache: ObjectCache.ObjectCache?
17
+
18
+ function bezier.init(cache)
19
+ part_cache = cache
20
+ end
21
+
22
+ function bezier.deinit()
23
+ part_cache = nil
24
+ end
25
+
26
+ local function readBezierAttributes(ref: Attachment)
27
+ return {
28
+ -- animation
29
+ facePath = attr.get(ref, "FacePath", false),
30
+ arcSpace = attr.get(ref, "ArcSpace", false),
31
+
32
+ -- rotation
33
+ rotSpeedStart = attr.getRange(ref, "RotSpeed_Start", NumberRange.new(0, 0)),
34
+ rotSpeedEnd = attr.getRange(ref, "RotSpeed_End", NumberRange.new(0, 0)),
35
+ minInitRot = attr.get(ref, "MinInitRot", vector.zero),
36
+ maxInitRot = attr.get(ref, "MaxInitRot", vector.zero),
37
+
38
+ -- curves
39
+ speedCurve = attr.get(ref, "Speed_Curve", utility.default_bezier),
40
+ speedDuration = attr.get(ref, "Speed_Duration", 0.1),
41
+ easingCurve = attr.get(ref, "Easing_Curve", utility.linear_bezier),
42
+ }
43
+ end
44
+
45
+ function bezier.emit(ref: Attachment, refObj: Part, scope: types.scope, mustEmit: boolean?)
46
+ local root = ref:FindFirstChild("Points")
47
+
48
+ if not root or not root:IsA("Attachment") or not part_cache then
49
+ return
50
+ end
51
+
52
+ local common = bezier_common.readCommonAttributes(ref)
53
+ local attrs = readBezierAttributes(ref)
54
+
55
+ local drawFunc = bezier_common.drawFuncMap[common.shapeType]
56
+ and bezier_common.drawFuncMap[common.shapeType][common.shapeStyle]
57
+
58
+ if not drawFunc then
59
+ return
60
+ end
61
+
62
+ local useDuration = common.emitDuration > 0
63
+
64
+ task.wait(common.emitDelay)
65
+
66
+ if useDuration and not mustEmit then
67
+ attr.trigger(ref, "Enabled", true)
68
+
69
+ if common.speedStart ~= common.speedEnd then
70
+ attr.setState(ref, "SpeedTweening", true)
71
+
72
+ table.insert(
73
+ scope,
74
+ tween.fromParams(
75
+ attrs.speedCurve,
76
+ attrs.speedDuration,
77
+ function(alpha, deltaTime)
78
+ attr.setState(ref, "SpeedOverride", utility.lerp(common.speedStart, common.speedEnd, alpha))
79
+ return deltaTime
80
+ end,
81
+ nil,
82
+ function()
83
+ attr.setState(ref, "SpeedTweening", nil)
84
+ end
85
+ )
86
+ )
87
+ end
88
+
89
+ task.wait(common.emitDuration)
90
+
91
+ attr.trigger(ref, "Enabled", false)
92
+ attr.clearState(ref)
93
+
94
+ return
95
+ end
96
+
97
+ if common.emitCount <= 0 then
98
+ return
99
+ end
100
+
101
+ local parent = bezier_common.validateParent(ref)
102
+
103
+ if not parent then
104
+ return
105
+ end
106
+
107
+ local originCFrame, originSize = utility.getTransformedOriginExtents(parent)
108
+
109
+ if not originCFrame then
110
+ return
111
+ end
112
+
113
+ local endPoint, endT1 = bezier_common.findEndAttachments(ref)
114
+ local points = utility.getBezierPoints(root)
115
+
116
+ local rng = Random.new()
117
+ local baseBezier = not endPoint and Bezier.new(points)
118
+
119
+ -- Hitbox params
120
+ local hitboxParams = bezier_common.createHitboxParams({
121
+ enabled = common.hitboxEnabled,
122
+ collisionGroup = common.hitboxCollisionGroup,
123
+ filterTag = common.hitboxFilterTag,
124
+ filterType = common.hitboxFilterType,
125
+ ignoreCanCollide = common.hitboxIgnoreCanCollide,
126
+ }, parent, root)
127
+
128
+ local promises = {}
129
+
130
+ for _ = 1, common.emitCount do
131
+ local rotSpeedStart = rng:NextNumber(attrs.rotSpeedStart.Min, attrs.rotSpeedStart.Max)
132
+ local rotSpeedEnd = rng:NextNumber(attrs.rotSpeedEnd.Min, attrs.rotSpeedEnd.Max)
133
+
134
+ local initRot = vector.create(
135
+ rng:NextNumber(attrs.minInitRot.x, attrs.maxInitRot.x),
136
+ rng:NextNumber(attrs.minInitRot.y, attrs.maxInitRot.y),
137
+ rng:NextNumber(attrs.minInitRot.z, attrs.maxInitRot.z)
138
+ )
139
+
140
+ local duration = rng:NextNumber(common.duration.Min, common.duration.Max)
141
+ local lifetime = common.projectileEnabled
142
+ and rng:NextNumber(common.projectileLifetime.Min, common.projectileLifetime.Max)
143
+
144
+ table.insert(
145
+ promises,
146
+ TS.Promise.new(function(resolve)
147
+ -- Calculate emission CFrame
148
+ local cf = bezier_common.calculateEmissionCFrame(originCFrame, originSize, {
149
+ face = common.face,
150
+ spreadAngle = common.spreadAngle,
151
+ mirror = common.mirror,
152
+ mirrorRot = common.mirrorRot,
153
+ partial = common.partial,
154
+ emissionDirection = common.emissionDirection,
155
+ }, drawFunc, rng, endPoint, parent:IsA("Attachment"))
156
+
157
+ -- create bezier
158
+ local pathBezier = baseBezier or bezier_common.createBezierWithEndpoint(points, cf, endPoint, endT1)
159
+ local getPos = bezier_common.createPosGetter(pathBezier, points, cf, endPoint, attrs.arcSpace)
160
+
161
+ -- get part from cache
162
+ local cacheId = utility.getRandomId()
163
+
164
+ if not part_cache then
165
+ resolve()
166
+ return
167
+ end
168
+
169
+ local objAbstr = part_cache:get(cacheId)
170
+ objAbstr.CFrame = CFrame.new(getPos(0))
171
+
172
+ local obj = objAbstr._getReal()
173
+
174
+ utility.copyProperties(refObj, obj, utility.COPY_PART_PROPERTIES)
175
+ utility.copyProperties(refObj, obj, utility.COPY_EXTENDED_PART_PROPERTIES)
176
+
177
+ -- clone children from reference
178
+ do
179
+ local clone = refObj:Clone()
180
+ for _, child in clone:GetChildren() do
181
+ child.Parent = obj
182
+ end
183
+ clone:Destroy()
184
+ end
185
+
186
+ local emitOnFinish = scope.effects.prepareEmitOnFinish(obj, scope)
187
+ local env = scope.effects.emitNested(obj, scope.depth + 1)
188
+
189
+ table.insert(promises, env.Finished)
190
+
191
+ table.insert(scope, function()
192
+ if part_cache then
193
+ part_cache:free(cacheId)
194
+ end
195
+ end)
196
+
197
+ -- animation state
198
+ local lastPos = Vector3.zero
199
+ local lastVelocity = Vector3.zero
200
+
201
+ local didHit = false
202
+ local finished = false
203
+
204
+ local prevCF = CFrame.identity
205
+ local currentSpeed = common.speedStart
206
+
207
+ local currentRot = CFrame.fromOrientation(initRot.x, initRot.y, initRot.z)
208
+ local currentRotSpeed = rotSpeedStart
209
+
210
+ local function onFinish()
211
+ if finished then
212
+ return
213
+ end
214
+
215
+ if common.syncPosition then
216
+ local initOrigin = utility.getTransformedOriginExtents(parent)
217
+ local initPartPos = objAbstr.CFrame
218
+
219
+ local function updatePos()
220
+ objAbstr.CFrame = utility.getTransformedOriginExtents(parent) * initOrigin:ToObjectSpace(initPartPos)
221
+ end
222
+
223
+ local id = utility.getRandomId()
224
+
225
+ RunService:BindToRenderStep(id, utility.RENDER_PRIORITY + scope.depth, updatePos)
226
+
227
+ table.insert(scope, function()
228
+ RunService:UnbindFromRenderStep(id)
229
+ end)
230
+ end
231
+
232
+ finished = true
233
+
234
+ local finishEnv = scope.effects.emitOnFinish(emitOnFinish, obj, scope.depth + 1)
235
+ finishEnv.Finished:finally(function()
236
+ resolve()
237
+ end)
238
+ end
239
+
240
+ local function shapecast()
241
+ if not common.hitboxEnabled then
242
+ return false
243
+ end
244
+
245
+ local result = workspace:GetPartsInPart(obj, hitboxParams)
246
+ return result[1] ~= nil
247
+ end
248
+
249
+ -- Speed tween
250
+ local speedTween
251
+
252
+ if common.speedStart ~= common.speedEnd and not attr.getState(ref, "SpeedOverride", nil) then
253
+ speedTween = tween.fromParams(attrs.speedCurve, attrs.speedDuration, function(alpha, deltaTime)
254
+ currentSpeed = utility.lerp(common.speedStart, common.speedEnd, alpha)
255
+ return deltaTime
256
+ end)
257
+
258
+ table.insert(scope, speedTween)
259
+ end
260
+
261
+ local function getEffectiveSpeed()
262
+ return attr.getState(ref, "SpeedOverride", currentSpeed)
263
+ end
264
+
265
+ local function isSpeedTweening()
266
+ return if speedTween then speedTween.Connected else attr.getState(ref, "SpeedTweening", false)
267
+ end
268
+
269
+ -- Rotation speed tween
270
+ bezier_common.createPropertyTween(scope, ref, "RotSpeed", duration, rotSpeedStart, rotSpeedEnd, function(v)
271
+ currentRotSpeed = v
272
+ end, function()
273
+ return getEffectiveSpeed()
274
+ end, speedTween)
275
+
276
+ -- main animation
277
+ table.insert(
278
+ scope,
279
+ tween.fromParams(
280
+ attrs.easingCurve,
281
+ duration,
282
+ function(alpha, deltaTime, elapsed)
283
+ local pos1 = getPos(alpha)
284
+ local newCF = CFrame.new(pos1)
285
+
286
+ if attrs.facePath then
287
+ local pos2 = getPos(math.clamp((elapsed + (1.6666666666666665E-2)) / duration, 0, 1))
288
+
289
+ if pos1 ~= pos2 then
290
+ newCF = CFrame.lookAt(pos1, pos2)
291
+ else
292
+ newCF *= prevCF.Rotation
293
+ end
294
+ end
295
+
296
+ local add = initRot:Sign() * currentRotSpeed * utility.DEG_TO_RAD * deltaTime
297
+ currentRot *= CFrame.fromOrientation(add.x, add.y, add.z)
298
+
299
+ -- save current cframe before applying rotation
300
+ prevCF = newCF
301
+ newCF *= currentRot
302
+
303
+ if common.syncPosition then
304
+ objAbstr.CFrame = utility.getTransformedOriginExtents(parent) * originCFrame:ToObjectSpace(newCF)
305
+ else
306
+ objAbstr.CFrame = newCF
307
+ end
308
+
309
+ lastVelocity = (pos1 - lastPos) / deltaTime
310
+ lastPos = pos1
311
+
312
+ if shapecast() then
313
+ onFinish()
314
+ didHit = true
315
+ return nil
316
+ end
317
+
318
+ local speed = getEffectiveSpeed()
319
+ currentSpeed = speed
320
+
321
+ if speed == 0 and not isSpeedTweening() then
322
+ return nil
323
+ end
324
+
325
+ if (common.projectileEnabled and alpha * duration < lifetime) or not common.projectileEnabled then
326
+ return deltaTime * speed
327
+ else
328
+ onFinish()
329
+ return nil
330
+ end
331
+ end,
332
+ speedTween,
333
+ function(organic)
334
+ if not common.projectileEnabled or didHit then
335
+ if organic then
336
+ onFinish()
337
+ end
338
+ return
339
+ end
340
+
341
+ -- Projectile mode
342
+ local dir = if common.projectileMatchEnd and endPoint
343
+ then endPoint.WorldCFrame.LookVector
344
+ else lastVelocity.Unit
345
+
346
+ if dir ~= dir then
347
+ dir = Vector3.zero
348
+ end
349
+
350
+ lastVelocity = dir * common.projectileSpeed
351
+
352
+ local projectileOrigin = obj.Position
353
+ local originCFrameAtProjectileInit = utility.getTransformedOriginExtents(parent)
354
+
355
+ tween.timer(lifetime, function(deltaTime, elapsed)
356
+ if common.syncPosition then
357
+ objAbstr.CFrame = utility.getTransformedOriginExtents(parent)
358
+ * originCFrameAtProjectileInit:ToObjectSpace(CFrame.new(projectileOrigin + lastVelocity * elapsed))
359
+ else
360
+ objAbstr.CFrame = CFrame.new(projectileOrigin + lastVelocity * elapsed) * obj.CFrame.Rotation
361
+ end
362
+
363
+ if shapecast() then
364
+ onFinish()
365
+ didHit = true
366
+ return nil
367
+ end
368
+
369
+ local speed = getEffectiveSpeed()
370
+ currentSpeed = speed
371
+
372
+ return if speed > 0 or (elapsed > 0 and isSpeedTweening()) then deltaTime * speed else nil
373
+ end, speedTween, scope, utility.RENDER_PRIORITY + scope.depth)
374
+
375
+ if not didHit then
376
+ onFinish()
377
+ end
378
+ end,
379
+ true, -- start at zero, important for facepath to work on short durations
380
+ utility.RENDER_PRIORITY + scope.depth
381
+ )
382
+ )
383
+ end)
384
+ )
385
+ end
386
+
387
+ TS.Promise.all(promises):await()
388
+
389
+ task.wait(common.destroyDelay)
390
+ end
391
+
392
+ return bezier
@@ -0,0 +1,200 @@
1
+ local RunService = game:GetService("RunService")
2
+
3
+ local attr = require(script.Parent.Parent.mod.attributes)
4
+ local tween = require(script.Parent.Parent.mod.tween)
5
+ local types = require(script.Parent.Parent.types)
6
+ local utility = require(script.Parent.Parent.mod.utility)
7
+
8
+ local Shake = require(script.Parent.Parent.Parent.shake)
9
+
10
+ local activeShakes = {}
11
+ local totalShakeCF = CFrame.identity
12
+
13
+ local function updateCamera()
14
+ if #activeShakes == 0 then
15
+ return
16
+ end
17
+
18
+ local camera = workspace.CurrentCamera
19
+
20
+ if not RunService:IsRunning() then
21
+ camera.CFrame *= totalShakeCF:Inverse()
22
+
23
+ -- I <3 THE STUDIO CAMERA SYSTEM !!!! YAYAY AIHU;S H;GDFAH GSDFL
24
+ local cf = camera.CFrame
25
+ local rx, ry = cf:ToOrientation()
26
+
27
+ -- NO HAX ALLOWED!!!!!!!
28
+ camera.CFrame = CFrame.new(cf.Position) * CFrame.fromOrientation(rx, ry, 0)
29
+ end
30
+
31
+ local totalPos = vector.zero
32
+ local totalRot = CFrame.identity
33
+
34
+ local stillActive = {}
35
+
36
+ for _, v in activeShakes do
37
+ if v.done then
38
+ continue
39
+ end
40
+
41
+ totalPos += v.pos
42
+ totalRot *= CFrame.Angles(v.rot.x, v.rot.y, v.rot.z)
43
+
44
+ table.insert(stillActive, v)
45
+ end
46
+
47
+ activeShakes = stillActive
48
+
49
+ local total = CFrame.new(totalPos) * totalRot
50
+
51
+ if not RunService:IsRunning() then
52
+ totalShakeCF = total
53
+ end
54
+
55
+ camera.CFrame *= total
56
+ end
57
+
58
+ local camera_shake = {}
59
+
60
+ local conn
61
+ local lastCameraCf
62
+
63
+ function camera_shake.init()
64
+ if conn then
65
+ return
66
+ end
67
+
68
+ if RunService:IsRunning() then
69
+ conn = RunService.Heartbeat:Connect(function()
70
+ if not lastCameraCf then
71
+ return
72
+ end
73
+
74
+ workspace.CurrentCamera.CFrame = lastCameraCf
75
+ end)
76
+ end
77
+
78
+ RunService:BindToRenderStep("forge_updateCameraShake", Enum.RenderPriority.Last.Value + 1, updateCamera)
79
+ end
80
+
81
+ function camera_shake.deinit()
82
+ if conn then
83
+ conn:Disconnect()
84
+ conn = nil
85
+ end
86
+
87
+ RunService:UnbindFromRenderStep("forge_updateCameraShake")
88
+ end
89
+
90
+ function camera_shake.emit(ref: RayValue, scope: types.scope)
91
+ local emitDelay = attr.get(ref, "EmitDelay", 0)
92
+ local emitDuration = attr.get(ref, "EmitDuration", 0)
93
+
94
+ local falloff = attr.get(ref, "Falloff", 30)
95
+
96
+ local amplitude = attr.get(ref, "Amplitude", 2.5)
97
+ local frequency = attr.get(ref, "Frequency", 0.2)
98
+
99
+ local fadeInTime = attr.get(ref, "FadeInTime", 0.3)
100
+ local fadeOutTime = attr.get(ref, "FadeOutTime", 2)
101
+ local sustainTime = attr.get(ref, "SustainTime", 1)
102
+
103
+ local posInfluence = attr.get(ref, "PositionInfluence", vector.one)
104
+ local rotInfluence = attr.get(ref, "RotationInfluence", vector.one * 0.2)
105
+
106
+ local speedStart = attr.get(ref, "Speed_Start", 1)
107
+ local speedEnd = attr.get(ref, "Speed_End", 1)
108
+
109
+ local sustained = emitDuration > 0
110
+
111
+ local shake = Shake.new()
112
+ shake.Sustain = sustained
113
+ shake.Amplitude = amplitude
114
+ shake.Frequency = frequency
115
+ shake.FadeInTime = fadeInTime
116
+ shake.FadeOutTime = fadeOutTime
117
+ shake.SustainTime = sustainTime
118
+ shake.PositionInfluence = posInfluence
119
+ shake.RotationInfluence = rotInfluence
120
+
121
+ local currentSpeed = 1
122
+
123
+ local elapsed = 0
124
+
125
+ shake.TimeFunction = function()
126
+ return elapsed
127
+ end
128
+
129
+ if speedStart ~= speedEnd then
130
+ local speedTween = tween.fromParams(
131
+ attr.get(ref, "Speed_Curve", utility.default_bezier),
132
+ attr.get(ref, "Speed_Duration", 0.1),
133
+ function(alpha, deltaTime)
134
+ currentSpeed = utility.lerp(speedStart, speedEnd, alpha)
135
+ return deltaTime
136
+ end
137
+ )
138
+
139
+ table.insert(scope, speedTween)
140
+ end
141
+
142
+ table.insert(
143
+ scope,
144
+ RunService.RenderStepped:Connect(function(deltaTime)
145
+ elapsed += deltaTime * currentSpeed
146
+ end)
147
+ )
148
+
149
+ local obj = {
150
+ done = false,
151
+ pos = vector.zero,
152
+ rot = vector.zero,
153
+ }
154
+
155
+ task.wait(emitDelay)
156
+
157
+ table.insert(activeShakes, obj)
158
+
159
+ local thread = coroutine.running()
160
+
161
+ shake:BindToRenderStep(Shake.NextRenderName(), utility.RENDER_PRIORITY + scope.depth, function(pos, rot, isDone)
162
+ local camera = workspace.CurrentCamera
163
+
164
+ lastCameraCf = camera.CFrame
165
+
166
+ local origin = ref:FindFirstAncestorOfClass("Attachment") or ref:FindFirstAncestorWhichIsA("BasePart")
167
+
168
+ if origin then
169
+ local originCFrame = utility.getTransformedOriginExtents(origin)
170
+ local originPosition = originCFrame and originCFrame.Position or origin.Position
171
+
172
+ local alpha = 1 - math.clamp((camera.CFrame.Position - originPosition).Magnitude / falloff, 0, 1)
173
+
174
+ pos *= alpha
175
+ rot *= alpha
176
+ end
177
+
178
+ obj.pos = pos
179
+ obj.rot = rot
180
+ obj.done = isDone
181
+
182
+ if isDone then
183
+ lastCameraCf = nil
184
+ task.spawn(thread)
185
+ end
186
+ end)
187
+
188
+ shake:Start()
189
+
190
+ if sustained then
191
+ task.wait(emitDuration)
192
+ shake:StopSustain()
193
+ end
194
+
195
+ if not obj.done then
196
+ coroutine.yield()
197
+ end
198
+ end
199
+
200
+ return camera_shake