@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.
- package/LICENSE +82 -0
- package/README.md +39 -0
- package/out/forge-vfx/effects/beam.luau +312 -0
- package/out/forge-vfx/effects/bezier.luau +392 -0
- package/out/forge-vfx/effects/camera_shake.luau +200 -0
- package/out/forge-vfx/effects/lightning.luau +1183 -0
- package/out/forge-vfx/effects/mesh.luau +466 -0
- package/out/forge-vfx/effects/particle.luau +64 -0
- package/out/forge-vfx/effects/randomizer.luau +110 -0
- package/out/forge-vfx/effects/screen.luau +61 -0
- package/out/forge-vfx/effects/shockwave_debris.luau +277 -0
- package/out/forge-vfx/effects/shockwave_line.luau +356 -0
- package/out/forge-vfx/effects/shockwave_ring.luau +252 -0
- package/out/forge-vfx/effects/sound.luau +311 -0
- package/out/forge-vfx/effects/spin.luau +88 -0
- package/out/forge-vfx/effects/tweener.luau +122 -0
- package/out/forge-vfx/emitters.luau +387 -0
- package/out/forge-vfx/index.d.ts +356 -0
- package/out/forge-vfx/init.luau +279 -0
- package/out/forge-vfx/mod/attributes.luau +227 -0
- package/out/forge-vfx/mod/color/Oklab.luau +93 -0
- package/out/forge-vfx/mod/color/sRGB.luau +71 -0
- package/out/forge-vfx/mod/common/bezier.luau +372 -0
- package/out/forge-vfx/mod/common/flipbook.luau +102 -0
- package/out/forge-vfx/mod/lerp.luau +210 -0
- package/out/forge-vfx/mod/logger.luau +20 -0
- package/out/forge-vfx/mod/shape.luau +207 -0
- package/out/forge-vfx/mod/tween.luau +161 -0
- package/out/forge-vfx/mod/utility.luau +707 -0
- package/out/forge-vfx/obj/Bezier.luau +268 -0
- package/out/forge-vfx/obj/ObjectCache.luau +289 -0
- package/out/forge-vfx/services/caches.luau +62 -0
- package/out/forge-vfx/services/effects.luau +234 -0
- package/out/forge-vfx/services/enabled_effects.luau +120 -0
- package/out/forge-vfx/services/texture_loader.luau +174 -0
- package/out/forge-vfx/types.luau +43 -0
- package/out/index.d.ts +3 -0
- package/out/init.luau +5 -0
- package/out/shake.d.ts +2 -0
- package/out/shake.luau +6 -0
- package/out/tsconfig.tsbuildinfo +1 -0
- 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
|