@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,466 @@
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 flipbook_common = require(script.Parent.Parent.mod.common.flipbook)
10
+
11
+ local Oklab = require(script.Parent.Parent.mod.color.Oklab)
12
+
13
+ local rng = Random.new()
14
+
15
+ local mesh = {}
16
+
17
+ function mesh.emit(ref: Model, obj: BasePart, scope: types.scope, scale: number, mustEmit: boolean?)
18
+ local start = ref:FindFirstChild("Start")
19
+ local goal = ref:FindFirstChild("End")
20
+
21
+ if
22
+ not start
23
+ or not goal
24
+ or not start:IsA("BasePart")
25
+ or not goal:IsA("BasePart")
26
+ or (ref:GetAttribute("Enabled") and not mustEmit)
27
+ then
28
+ return
29
+ end
30
+
31
+ local legacy_duration = attr.get(ref, "Duration", 1, true)
32
+
33
+ local durationRange = attr.getRange(
34
+ ref,
35
+ "EffectDuration",
36
+ NumberRange.new(legacy_duration, legacy_duration),
37
+ NumberRange.new(0, math.huge)
38
+ )
39
+
40
+ local duration = rng:NextNumber(durationRange.Min, durationRange.Max)
41
+
42
+ local emitDelay = attr.get(ref, "EmitDelay", 0)
43
+ local destroyDelay = attr.get(ref, "DestroyDelay", 0)
44
+ local emitDuration = attr.get(ref, "EmitDuration", 0)
45
+
46
+ local legacy_isFlipbook = attr.get(ref, "Flipbook", false, true)
47
+ local legacy_flipbookFadeOffset = attr.get(ref, "FlipbookFadeOffset", 0, true)
48
+
49
+ -- stone age compatibility
50
+ do
51
+ local prefix
52
+
53
+ if legacy_isFlipbook or not start:FindFirstChildOfClass("Decal") then
54
+ prefix = "Mesh_"
55
+ else
56
+ prefix = "Decal_"
57
+ end
58
+
59
+ local ts = ref:GetAttribute("StartTransparency")
60
+
61
+ if ts ~= nil then
62
+ ref:SetAttribute(prefix .. "StartTransparency", ts)
63
+ ref:SetAttribute("StartTransparency", nil)
64
+ end
65
+
66
+ local te = ref:GetAttribute("EndTransparency")
67
+
68
+ if te ~= nil then
69
+ ref:SetAttribute(prefix .. "EndTransparency", te)
70
+ ref:SetAttribute("EndTransparency", nil)
71
+ end
72
+ end
73
+
74
+ local useEmitDuration = ref:GetAttribute("Enabled") ~= nil and ref:HasTag(utility.ENABLED_VFX_TAG) and duration > 0
75
+
76
+ local legacy_decalST = attr.get(ref, "Decal_StartTransparency", 0, true)
77
+ local legacy_decalET = attr.get(ref, "Decal_EndTransparency", 0, true)
78
+
79
+ local partDefaultTp = 0
80
+
81
+ do
82
+ local decal = start and start:FindFirstChildOfClass("Decal")
83
+
84
+ if decal then
85
+ partDefaultTp = 1
86
+ end
87
+ end
88
+
89
+ local legacy_meshST = attr.get(ref, "Mesh_StartTransparency", partDefaultTp, true)
90
+ local legacy_meshET = attr.get(ref, "Mesh_EndTransparency", partDefaultTp, true)
91
+
92
+ local sync = attr.get(ref, "SyncPosition", false)
93
+
94
+ local speedStart = attr.get(ref, "Speed_Start", 1)
95
+ local speedEnd = attr.get(ref, "Speed_End", 1)
96
+
97
+ local tpStart = attr.get(ref, "Part_Transparency_Start", legacy_meshST)
98
+ local tpEnd = attr.get(ref, "Part_Transparency_End", legacy_meshET)
99
+
100
+ local spreadAngle = attr.get(ref, "SpreadAngle", vector.zero)
101
+
102
+ local rotSpeedStartRange = attr.getRange(ref, "Part_RotSpeed_Start", NumberRange.new(0, 0))
103
+ local rotSpeedEndRange = attr.getRange(ref, "Part_RotSpeed_End", NumberRange.new(0, 0))
104
+
105
+ local rotAroundOrigin = attr.get(ref, "RotAroundOrigin", false)
106
+
107
+ local minInitRot = attr.get(ref, "MinInitRot", vector.zero)
108
+ local maxInitRot = attr.get(ref, "MaxInitRot", vector.zero)
109
+
110
+ local rotSpeedStart = rng:NextNumber(rotSpeedStartRange.Min, rotSpeedStartRange.Max)
111
+ local rotSpeedEnd = rng:NextNumber(rotSpeedEndRange.Min, rotSpeedEndRange.Max)
112
+
113
+ local initRot = vector.create(
114
+ rng:NextNumber(minInitRot.x, maxInitRot.x),
115
+ rng:NextNumber(minInitRot.y, maxInitRot.y),
116
+ rng:NextNumber(minInitRot.z, maxInitRot.z)
117
+ ) * utility.DEG_TO_RAD
118
+
119
+ task.wait(emitDelay)
120
+
121
+ if useEmitDuration and not mustEmit then
122
+ attr.trigger(ref, "Enabled", true)
123
+
124
+ if speedStart ~= speedEnd then
125
+ attr.setState(ref, "SpeedTweening", true)
126
+
127
+ table.insert(
128
+ scope,
129
+ tween.fromParams(
130
+ attr.get(ref, "Speed_Curve", utility.default_bezier),
131
+ attr.get(ref, "Speed_Duration", 0.1),
132
+ function(alpha, deltaTime)
133
+ attr.setState(ref, "SpeedOverride", utility.lerp(speedStart, speedEnd, alpha))
134
+ return deltaTime
135
+ end,
136
+ nil,
137
+ function()
138
+ attr.setState(ref, "SpeedTweening", nil)
139
+ end
140
+ )
141
+ )
142
+ end
143
+
144
+ task.wait(emitDuration)
145
+
146
+ attr.trigger(ref, "Enabled", false)
147
+ attr.clearState(ref)
148
+
149
+ return
150
+ end
151
+
152
+ local parent = ref:FindFirstAncestorOfClass("Attachment") or start
153
+ local realObj = typeof(obj) == "table" and obj._getReal() or obj
154
+
155
+ local origin = utility.getTransformedOriginExtents(parent)
156
+ local goalCFrameOffset = start.CFrame:ToObjectSpace(goal.CFrame)
157
+
158
+ local currentSpeed = speedStart
159
+ local currentRotSpeed = rotSpeedStart
160
+
161
+ local currentRot = CFrame.fromOrientation(initRot.x, initRot.y, initRot.z)
162
+ local currentCFrame = CFrame.identity
163
+
164
+ local spread = CFrame.fromOrientation(
165
+ math.rad(rng:NextNumber(-spreadAngle.x, spreadAngle.x)),
166
+ math.rad(rng:NextNumber(-spreadAngle.y, spreadAngle.y)),
167
+ 0
168
+ )
169
+
170
+ local function updatePos(deltaTime: number)
171
+ local o = sync and utility.getTransformedOriginExtents(parent) or origin
172
+ local add = initRot:Sign() * currentRotSpeed * deltaTime
173
+
174
+ currentRot *= CFrame.fromOrientation(add.x, add.y, add.z)
175
+
176
+ local cf
177
+
178
+ if rotAroundOrigin then
179
+ cf = o * spread * currentRot * currentCFrame
180
+ else
181
+ cf = o * spread * currentCFrame * currentRot
182
+ end
183
+
184
+ cf *= currentRot
185
+
186
+ obj.CFrame = cf
187
+ end
188
+
189
+ updatePos(0)
190
+
191
+ do
192
+ local id = utility.getRandomId()
193
+
194
+ RunService:BindToRenderStep(id, utility.RENDER_PRIORITY + scope.depth, updatePos)
195
+
196
+ table.insert(scope, function()
197
+ RunService:UnbindFromRenderStep(id)
198
+ end)
199
+ end
200
+
201
+ local promises = {}
202
+
203
+ local emitOnFinish = scope.effects.prepareEmitOnFinish(obj, scope)
204
+
205
+ local env = scope.effects.emitNested(obj, scope.depth + 1)
206
+ table.insert(promises, env.Finished)
207
+
208
+ local speedTween
209
+
210
+ if speedStart ~= speedEnd and not attr.getState(ref, "SpeedOverride", nil) then
211
+ speedTween = tween.fromParams(
212
+ attr.get(ref, "Speed_Curve", utility.default_bezier),
213
+ attr.get(ref, "Speed_Duration", 0.1),
214
+ function(alpha, deltaTime)
215
+ currentSpeed = utility.lerp(speedStart, speedEnd, alpha)
216
+ return deltaTime
217
+ end
218
+ )
219
+
220
+ table.insert(scope, speedTween)
221
+ end
222
+
223
+ if goalCFrameOffset ~= CFrame.identity then
224
+ table.insert(
225
+ scope,
226
+ tween.fromParams(attr.get(ref, "Part_CFrame_Curve", utility.default_bezier), duration, function(alpha, deltaTime)
227
+ currentCFrame = CFrame.identity:Lerp(goalCFrameOffset, alpha)
228
+ return deltaTime * (attr.getState(ref, "SpeedOverride", currentSpeed))
229
+ end, speedTween)
230
+ )
231
+ end
232
+
233
+ if rotSpeedStart ~= rotSpeedEnd then
234
+ table.insert(
235
+ scope,
236
+ tween.fromParams(
237
+ attr.get(ref, "Part_RotSpeed_Curve", utility.default_bezier),
238
+ duration,
239
+ function(alpha, deltaTime)
240
+ currentRotSpeed = utility.lerp(rotSpeedStart, rotSpeedEnd, alpha)
241
+ return deltaTime * (attr.getState(ref, "SpeedOverride", currentSpeed))
242
+ end,
243
+ speedTween
244
+ )
245
+ )
246
+ end
247
+
248
+ if tpStart ~= tpEnd then
249
+ table.insert(
250
+ scope,
251
+ tween.fromParams(
252
+ attr.get(ref, "Part_Transparency_Curve", utility.default_bezier),
253
+ duration,
254
+ function(alpha, deltaTime)
255
+ obj.Transparency = utility.lerp(tpStart, tpEnd, alpha)
256
+ return deltaTime * (attr.getState(ref, "SpeedOverride", currentSpeed))
257
+ end,
258
+ speedTween
259
+ )
260
+ )
261
+ else
262
+ obj.Transparency = tpStart
263
+ end
264
+
265
+ local sizeStart, sizeEnd = start.Size * scale, goal.Size * scale
266
+
267
+ if sizeStart ~= sizeEnd then
268
+ local lastSizeAlpha = -1
269
+
270
+ table.insert(
271
+ scope,
272
+ tween.fromParams(attr.get(ref, "Part_Size_Curve", utility.default_bezier), duration, function(alpha, deltaTime)
273
+ local speed = attr.getState(ref, "SpeedOverride", currentSpeed)
274
+
275
+ -- reduce size writes
276
+ if math.abs(alpha - lastSizeAlpha) < 0.005 then
277
+ return deltaTime * speed
278
+ end
279
+
280
+ lastSizeAlpha = alpha
281
+ obj.Size = sizeStart:Lerp(sizeEnd, alpha)
282
+
283
+ return deltaTime * speed
284
+ end, speedTween)
285
+ )
286
+ else
287
+ obj.Size = sizeStart
288
+ end
289
+
290
+ local startMesh = start:FindFirstChildOfClass("SpecialMesh")
291
+ local goalMesh = goal:FindFirstChildOfClass("SpecialMesh")
292
+
293
+ local objMesh = obj:FindFirstChildOfClass("SpecialMesh")
294
+
295
+ if objMesh then
296
+ if startMesh and goalMesh then
297
+ local scaleStart, scaleEnd = startMesh.Scale * scale, goalMesh.Scale * scale
298
+
299
+ if scaleStart ~= scaleEnd then
300
+ table.insert(
301
+ scope,
302
+ tween.fromParams(
303
+ attr.get(ref, "Mesh_Scale_Curve", utility.default_bezier),
304
+ duration,
305
+ function(alpha, deltaTime)
306
+ objMesh.Scale = utility.lerp(scaleStart, scaleEnd, alpha)
307
+ return deltaTime * (attr.getState(ref, "SpeedOverride", currentSpeed))
308
+ end,
309
+ speedTween
310
+ )
311
+ )
312
+ end
313
+ else
314
+ objMesh.Parent = nil
315
+
316
+ table.insert(scope, function()
317
+ objMesh.Parent = realObj
318
+ end)
319
+ end
320
+ end
321
+
322
+ local decals, flipbooks, fromToMap = utility.getMeshDecals(ref, obj)
323
+
324
+ local longestFlipbookDuration = 0
325
+
326
+ if legacy_isFlipbook then
327
+ table.sort(decals, function(a, b)
328
+ local x = a.Name:match("%d+") or 0
329
+ local y = b.Name:match("%d+") or 0
330
+
331
+ return tonumber(x) < tonumber(y)
332
+ end)
333
+
334
+ local displayDecal = Instance.new("Decal")
335
+ displayDecal.Parent = realObj
336
+
337
+ table.insert(scope, displayDecal)
338
+
339
+ if legacy_flipbookFadeOffset > 0 then
340
+ if legacy_decalST ~= legacy_decalET then
341
+ table.insert(
342
+ scope,
343
+ tween.fromParams(utility.default_bezier, duration + legacy_flipbookFadeOffset, function(alpha, deltaTime)
344
+ displayDecal.Transparency = utility.lerp(legacy_decalST, legacy_decalET, alpha)
345
+ return deltaTime * (attr.getState(ref, "SpeedOverride", currentSpeed))
346
+ end, speedTween)
347
+ )
348
+ end
349
+ end
350
+
351
+ table.insert(
352
+ scope,
353
+ tween.fromParams(utility.default_bezier, duration, function(alpha, deltaTime)
354
+ local index = math.max(math.round(#decals * alpha), 1)
355
+ local target = decals[index]
356
+
357
+ displayDecal.Texture = target.Texture
358
+
359
+ displayDecal.Color3 = target.Color3
360
+ displayDecal.ZIndex = target.ZIndex
361
+
362
+ if legacy_flipbookFadeOffset == 0 then
363
+ displayDecal.Transparency = utility.lerp(legacy_decalST, legacy_decalET, alpha)
364
+ displayDecal.ZIndex = target.ZIndex
365
+ end
366
+
367
+ return deltaTime * (attr.getState(ref, "SpeedOverride", currentSpeed))
368
+ end, speedTween)
369
+ )
370
+ else
371
+ for _, decal in decals do
372
+ local from = decal
373
+ local to = fromToMap[from]
374
+
375
+ local decal_tpStart = attr.get(from, "Transparency_Start", legacy_decalST)
376
+ local decal_tpEnd = attr.get(from, "Transparency_End", legacy_decalET)
377
+
378
+ if decal_tpStart ~= decal_tpEnd then
379
+ table.insert(
380
+ scope,
381
+ tween.fromParams(
382
+ attr.get(from, "Transparency_Curve", utility.default_bezier),
383
+ duration,
384
+ function(alpha, deltaTime)
385
+ decal.Transparency = utility.lerp(decal_tpStart, decal_tpEnd, alpha)
386
+ return deltaTime * (attr.getState(ref, "SpeedOverride", currentSpeed))
387
+ end,
388
+ speedTween
389
+ )
390
+ )
391
+ else
392
+ decal.Transparency = decal_tpStart
393
+ end
394
+
395
+ if from.Color3 ~= to.Color3 then
396
+ table.insert(
397
+ scope,
398
+ tween.fromParams(attr.get(from, "Color_Curve", utility.default_bezier), duration, function(alpha, deltaTime)
399
+ local a = Oklab.fromSRGB(from.Color3)
400
+ local b = Oklab.fromSRGB(to.Color3)
401
+
402
+ decal.Color3 = Oklab.toSRGB(a:Lerp(b, alpha), true)
403
+
404
+ return deltaTime * (attr.getState(ref, "SpeedOverride", currentSpeed))
405
+ end, speedTween)
406
+ )
407
+ end
408
+
409
+ local flipbook = flipbooks[from]
410
+
411
+ if flipbook then
412
+ local config = {
413
+ ref = from,
414
+ frames = flipbook,
415
+ speedTween = speedTween,
416
+ effectDuration = duration,
417
+
418
+ curve = attr.get(from, "Flipbook_Change_Curve", utility.linear_bezier),
419
+ duration = attr.get(from, "Flipbook_Change_Duration", duration),
420
+
421
+ getSpeed = function()
422
+ return attr.getState(ref, "SpeedOverride", currentSpeed)
423
+ end,
424
+
425
+ setTexture = function(texture)
426
+ decal.Texture = texture
427
+ end,
428
+ }
429
+
430
+ local changeDuration = flipbook_common.getChangeDuration(config)
431
+
432
+ if changeDuration > longestFlipbookDuration then
433
+ longestFlipbookDuration = changeDuration
434
+ end
435
+
436
+ table.insert(
437
+ scope,
438
+ tween.fromParams(config.curve, changeDuration, flipbook_common.createUpdateCallback(config), speedTween)
439
+ )
440
+ end
441
+ end
442
+ end
443
+
444
+ tween.timer(
445
+ math.max(duration, longestFlipbookDuration) + legacy_flipbookFadeOffset + destroyDelay,
446
+ function(deltaTime, elapsed)
447
+ local speed = attr.getState(ref, "SpeedOverride", currentSpeed)
448
+
449
+ currentSpeed = speed
450
+
451
+ return if speed > 0
452
+ or (elapsed > 0 and if speedTween then speedTween.Connected else attr.getState(ref, "SpeedTweening", false))
453
+ then deltaTime * speed
454
+ else nil
455
+ end,
456
+ speedTween,
457
+ scope
458
+ )
459
+
460
+ TS.Promise.all(promises):await()
461
+
462
+ local env = scope.effects.emitOnFinish(emitOnFinish, realObj, scope.depth + 1)
463
+ env.Finished:await()
464
+ end
465
+
466
+ return mesh
@@ -0,0 +1,64 @@
1
+ local attr = require(script.Parent.Parent.mod.attributes)
2
+ local tween = require(script.Parent.Parent.mod.tween)
3
+ local types = require(script.Parent.Parent.types)
4
+ local utility = require(script.Parent.Parent.mod.utility)
5
+
6
+ local particle = {}
7
+
8
+ function particle.emit(ref: ParticleEmitter, obj: ParticleEmitter, scope: types.scope)
9
+ if ref.Enabled then
10
+ ref.Enabled = false
11
+ end
12
+
13
+ local delay = attr.get(ref, "EmitDelay", 0)
14
+ local count = attr.get(ref, "EmitCount", 1)
15
+ local duration = attr.get(ref, "EmitDuration", 0)
16
+
17
+ local useDuration = duration > 0
18
+
19
+ task.wait(delay)
20
+
21
+ if useDuration then
22
+ obj.Enabled = true
23
+ end
24
+
25
+ local timeScaleDuration = attr.get(ref, "TimeScale_Duration", 0.1)
26
+
27
+ local timeScaleStart = attr.get(ref, "TimeScale_Start", obj.TimeScale, true)
28
+ local timeScaleEnd = attr.get(ref, "TimeScale_End", obj.TimeScale, true)
29
+
30
+ local speedTween
31
+
32
+ if timeScaleStart ~= timeScaleEnd then
33
+ speedTween = tween.fromParams(
34
+ attr.get(ref, "TimeScale_Curve", utility.default_bezier),
35
+ timeScaleDuration,
36
+ function(alpha, deltaTime)
37
+ local speed = utility.lerp(timeScaleStart, timeScaleEnd, math.clamp(alpha, 0, 1))
38
+
39
+ obj.TimeScale = speed
40
+
41
+ return deltaTime
42
+ end
43
+ )
44
+
45
+ table.insert(scope, speedTween)
46
+ elseif timeScaleStart ~= obj.TimeScale then
47
+ obj.TimeScale = timeScaleStart
48
+ end
49
+
50
+ obj:Emit(count)
51
+
52
+ if useDuration then
53
+ task.wait(duration)
54
+ obj.Enabled = false
55
+ end
56
+
57
+ tween.timer(ref.Lifetime.Max, function(deltaTime, elapsed)
58
+ return if ref.TimeScale > 0 or (elapsed > 0 and speedTween and speedTween.Connected)
59
+ then deltaTime * ref.TimeScale
60
+ else nil
61
+ end, speedTween, scope)
62
+ end
63
+
64
+ return particle
@@ -0,0 +1,110 @@
1
+ local attr = require(script.Parent.Parent.mod.attributes)
2
+
3
+ local lerp = require(script.Parent.Parent.mod.lerp)
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 Bezier = require(script.Parent.Parent.obj.Bezier)
9
+
10
+ local rng = Random.new()
11
+
12
+ local randomizer = {}
13
+
14
+ local function getBezier(params: string): Bezier.Bezier?
15
+ local bezier = tween.bezier_cache[params]
16
+
17
+ if not bezier then
18
+ local success, points = pcall(function()
19
+ return utility.deserializePath(params)
20
+ end)
21
+
22
+ if not success then
23
+ return nil
24
+ end
25
+
26
+ bezier = Bezier.new(points, 0)
27
+ tween.bezier_cache[params] = bezier
28
+ end
29
+
30
+ return bezier
31
+ end
32
+
33
+ local function sampleWeighted(bezier: Bezier.Bezier): number
34
+ local t = rng:NextNumber()
35
+ local weight = 1 - bezier:getEase(t).y
36
+
37
+ return weight
38
+ end
39
+
40
+ function randomizer.emit(obj: RayValue, scope: types.scope, isAttribute: boolean)
41
+ local target = utility.getTarget(obj)
42
+
43
+ if not target then
44
+ return
45
+ end
46
+
47
+ local name = obj.Name
48
+
49
+ local startValue = attr.get(obj, "_START_VALUE", nil)
50
+ local endValue = attr.get(obj, "_END_VALUE", nil)
51
+
52
+ if startValue == nil or endValue == nil then
53
+ return
54
+ end
55
+
56
+ local typeName = typeof(startValue)
57
+
58
+ if typeName ~= typeof(endValue) then
59
+ return
60
+ end
61
+
62
+ local weightCurve = attr.get(obj, "Weight_Curve", utility.linear_bezier)
63
+ local bezier = getBezier(weightCurve)
64
+
65
+ if not bezier then
66
+ return
67
+ end
68
+
69
+ local originalValue
70
+
71
+ if isAttribute then
72
+ originalValue = attr.get(target, name, nil)
73
+ else
74
+ local ok, val = pcall(function()
75
+ return target[name]
76
+ end)
77
+
78
+ if not ok then
79
+ return
80
+ end
81
+
82
+ originalValue = val
83
+ end
84
+
85
+ local lfunc = lerp[typeName] or lerp.Other
86
+
87
+ local alpha = sampleWeighted(bezier)
88
+ local newValue = lfunc(startValue, endValue, alpha)
89
+
90
+ local resetOnFinish = attr.get(obj, "ResetOnFinish", true)
91
+
92
+ if isAttribute then
93
+ attr.set(target, name, newValue)
94
+ else
95
+ target[name] = newValue
96
+ end
97
+
98
+ if resetOnFinish then
99
+ -- reset on next frame (after the target effects are emitted)
100
+ task.defer(function()
101
+ if isAttribute then
102
+ attr.set(target, name, originalValue)
103
+ else
104
+ target[name] = originalValue
105
+ end
106
+ end)
107
+ end
108
+ end
109
+
110
+ return randomizer
@@ -0,0 +1,61 @@
1
+ local RunService = game:GetService("RunService")
2
+
3
+ local attr = require(script.Parent.Parent.mod.attributes)
4
+ local types = require(script.Parent.Parent.types)
5
+ local utility = require(script.Parent.Parent.mod.utility)
6
+
7
+ local screen = {}
8
+
9
+ function screen.emit(ref: BasePart, scope: types.scope)
10
+ if not ref:GetAttribute("Enabled") then
11
+ return
12
+ end
13
+
14
+ local scale = attr.get(ref, "PartScale", vector.one)
15
+ local distance = attr.get(ref, "PartDistance", 1.5)
16
+
17
+ local pos = attr.get(ref, "OffsetPosition", vector.zero)
18
+ local rot = attr.get(ref, "OffsetRotation", vector.zero) * utility.DEG_TO_RAD
19
+
20
+ local originalCF = ref.CFrame
21
+ local originalSize = ref.Size
22
+ local originalColGroup = ref.CollisionGroup
23
+
24
+ ref.CollisionGroup = "ForgeMouseIgnore"
25
+
26
+ table.insert(scope, function()
27
+ ref.Size = originalSize
28
+ ref.CFrame = originalCF
29
+ ref.CollisionGroup = originalColGroup ~= "" and originalColGroup or "Default"
30
+ end)
31
+
32
+ -- crazy how BindToRenderStep will always run before RenderStepped in studio
33
+ local function frame()
34
+ local camera = workspace.CurrentCamera
35
+
36
+ local halfFovRad = math.rad(camera.FieldOfView / 2)
37
+ local height = math.tan(halfFovRad) * distance * 2
38
+ local width = (camera.ViewportSize.X / camera.ViewportSize.Y) * height
39
+
40
+ local cf = camera.CFrame * CFrame.new(0, 0, -distance)
41
+
42
+ ref.Size = Vector3.new(width, height, ref.Size.Z) * scale
43
+ ref.CFrame = cf * CFrame.new(pos) * CFrame.fromOrientation(rot.x, rot.y, rot.z)
44
+ end
45
+
46
+ if not RunService:IsRunning() then
47
+ table.insert(scope, RunService.RenderStepped:Connect(frame))
48
+ else
49
+ local id = utility.getRandomId()
50
+
51
+ RunService:BindToRenderStep(id, Enum.RenderPriority.Last.Value + 2 + scope.depth, frame)
52
+
53
+ table.insert(scope, function()
54
+ RunService:UnbindFromRenderStep(id)
55
+ end)
56
+ end
57
+
58
+ return true
59
+ end
60
+
61
+ return screen