@rbxts/sound-manager 2.0.1 β 2.2.0
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/README.md +64 -7
- package/out/core/createSoundCategoryRegistry.d.ts +17 -1
- package/out/core/createSoundCategoryRegistry.luau +384 -2
- package/out/core/createSoundRegistry.d.ts +17 -6
- package/out/core/createSoundRegistry.luau +153 -53
- package/out/core/createSpatialHandle.d.ts +7 -0
- package/out/core/createSpatialHandle.luau +59 -0
- package/out/core/options.d.ts +12 -0
- package/out/utils/functions.d.ts +13 -0
- package/out/utils/functions.luau +47 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,15 +1,48 @@
|
|
|
1
|
-
|
|
1
|
+
<h1 align="center">
|
|
2
|
+
<a href="https://www.npmjs.com/package/@rbxts/sound-manager">
|
|
3
|
+
<img src="public/logo.png" alt="Sound-Manager" width="200" />
|
|
4
|
+
</a>
|
|
5
|
+
<br />
|
|
6
|
+
<b>Sound Manager</b>
|
|
7
|
+
|
|
8
|
+
</h1>
|
|
2
9
|
|
|
3
|
-
|
|
10
|
+
<div align="center">
|
|
11
|
+
|
|
12
|
+
  
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## π Sound Manager
|
|
22
|
+
|
|
23
|
+
**Sound Manager** is a roblox sound-management library built with Roblox-TS, designed to simplify sound handling in your roblox projects.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## π¦ Installation
|
|
4
28
|
|
|
5
|
-
## Getting Started
|
|
6
29
|
To use the Sound Manager library in your Roblox-TS project, install it via npm:
|
|
30
|
+
|
|
7
31
|
```bash
|
|
8
32
|
npm install @rbxts/sound-manager
|
|
9
33
|
```
|
|
10
34
|
|
|
11
|
-
|
|
12
|
-
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## π Quick Start
|
|
38
|
+
|
|
39
|
+
[See the Documentation ->](https://dev-lukas0.github.io/Sound-Manager-Docs/)
|
|
40
|
+
|
|
41
|
+
### β‘ Starting with Sound Manager
|
|
42
|
+
|
|
43
|
+
Sound-Manager uses [`createSoundRegistry`]() and [`createSoundCategoryRegistry`]() to create sounds and categories.
|
|
44
|
+
|
|
45
|
+
```ts
|
|
13
46
|
import { createSoundRegistry } from "@rbxts/sound-manager";
|
|
14
47
|
|
|
15
48
|
const Sounds = createSoundRegistry({
|
|
@@ -17,13 +50,37 @@ const Sounds = createSoundRegistry({
|
|
|
17
50
|
id: "rbxassetid://4714389545",
|
|
18
51
|
volume: 1,
|
|
19
52
|
loop: false,
|
|
20
|
-
}
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
Test: {
|
|
56
|
+
id: "rbxassetid://17771398985",
|
|
57
|
+
volume: 1,
|
|
58
|
+
loop: true,
|
|
59
|
+
}
|
|
21
60
|
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### π΅ Playing Sounds
|
|
22
64
|
|
|
23
|
-
|
|
65
|
+
To play a sound, you can use the [`play`](https://dev-lukas0.github.io/Sound-Manager-Docs/docs/API/play) method on the sound registry:
|
|
24
66
|
|
|
67
|
+
```ts
|
|
25
68
|
Sounds.play("SCP096");
|
|
26
69
|
```
|
|
27
70
|
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
## πΊοΈ Roadmap
|
|
74
|
+
-------
|
|
75
|
+
See the [Roadmap](./Roadmap.md) for planned features and current progress.
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
## π License
|
|
80
|
+
|
|
81
|
+
**Sound Manager** is licensed under the [MIT LICENSE](./LICENSE).
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
28
85
|
For more Details:
|
|
29
86
|
https://dev-lukas0.github.io/Sound-Manager-Docs/
|
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
import { CategoryOptions } from "./options";
|
|
2
|
+
import { SoundHandle } from "./options";
|
|
2
3
|
/**
|
|
3
4
|
* Create a sound category Registry
|
|
4
5
|
* @param definitions Define the Categorys
|
|
5
6
|
*/
|
|
6
7
|
export declare function createSoundCategoryRegistry<T extends Record<string, CategoryOptions>>(definitions: T): {
|
|
7
8
|
loadCategory: (name: keyof T) => void;
|
|
8
|
-
playCategory: <C extends keyof T>(name: C
|
|
9
|
+
playCategory: <C extends keyof T>(name: C, spatial?: {
|
|
10
|
+
emitters: BasePart[];
|
|
11
|
+
}) => Map<string, SoundHandle> | undefined;
|
|
12
|
+
stopCategory: <C extends keyof T>(name: C) => void;
|
|
13
|
+
stopAllCategories: () => void;
|
|
14
|
+
setCategoryVolume: <C extends keyof T>(category: C, volume: number) => void;
|
|
15
|
+
setGlobalCategoryVolume: (volume: number) => void;
|
|
16
|
+
isCategoryPlaying: <C extends keyof T>(category: C) => boolean;
|
|
17
|
+
playSoundFromCategory: <C extends keyof T, S extends keyof T[C]["sounds"]>(category: C, sound: S) => void;
|
|
18
|
+
resetCategory: <C extends keyof T>(category: C) => void;
|
|
19
|
+
resetAllCategories: <C extends keyof T>(category: C) => void;
|
|
20
|
+
preloadAllCategories: () => void;
|
|
21
|
+
preloadCategory: <C extends keyof T>(category: C) => void;
|
|
22
|
+
fadeInCategory: <C extends keyof T>(category: C, duration: number, volume: number) => void;
|
|
23
|
+
fadeOutCategory: <C extends keyof T>(category: C, duration: number, targetVolume?: number) => void;
|
|
24
|
+
onCategoryEnd: <C extends keyof T>(category: C, callback: () => void) => void;
|
|
9
25
|
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local createSpatialHandle = TS.import(script, script.Parent, "createSpatialHandle").createSpatialHandle
|
|
2
4
|
--[[
|
|
3
5
|
*
|
|
4
6
|
* Create a sound category Registry
|
|
@@ -6,6 +8,7 @@
|
|
|
6
8
|
|
|
7
9
|
]]
|
|
8
10
|
local function createSoundCategoryRegistry(definitions)
|
|
11
|
+
local spatialHandles = {}
|
|
9
12
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
10
13
|
local folder = ReplicatedStorage:FindFirstChild("Sounds")
|
|
11
14
|
if not folder then
|
|
@@ -53,8 +56,35 @@ local function createSoundCategoryRegistry(definitions)
|
|
|
53
56
|
* @param name Sound Category
|
|
54
57
|
|
|
55
58
|
]]
|
|
56
|
-
local function playCategory(name)
|
|
59
|
+
local function playCategory(name, spatial)
|
|
57
60
|
loadCategory(name)
|
|
61
|
+
local config = definitions[name]
|
|
62
|
+
local categoryFolder = folder:FindFirstChild(config.category)
|
|
63
|
+
if not categoryFolder then
|
|
64
|
+
return nil
|
|
65
|
+
end
|
|
66
|
+
for _, sound in categoryFolder:GetChildren() do
|
|
67
|
+
if not sound:IsA("Sound") then
|
|
68
|
+
continue
|
|
69
|
+
end
|
|
70
|
+
if spatial and #spatial.emitters > 0 then
|
|
71
|
+
local handle = createSpatialHandle(sound.SoundId, spatial.emitters, sound.Volume)
|
|
72
|
+
local _name = sound.Name
|
|
73
|
+
spatialHandles[_name] = handle
|
|
74
|
+
handle:play()
|
|
75
|
+
else
|
|
76
|
+
sound:Play()
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
return spatialHandles
|
|
80
|
+
end
|
|
81
|
+
--[[
|
|
82
|
+
*
|
|
83
|
+
* Stop every Sound from a Sound Category
|
|
84
|
+
* @param name Sound Category
|
|
85
|
+
|
|
86
|
+
]]
|
|
87
|
+
local function stopCategory(name)
|
|
58
88
|
local config = definitions[name]
|
|
59
89
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
60
90
|
local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
|
|
@@ -75,13 +105,365 @@ local function createSoundCategoryRegistry(definitions)
|
|
|
75
105
|
_result = _result:IsA("Sound")
|
|
76
106
|
end
|
|
77
107
|
if _result then
|
|
78
|
-
_sound:
|
|
108
|
+
_sound:Stop()
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
--[[
|
|
113
|
+
*
|
|
114
|
+
* Stops every Sound from every Sound Category
|
|
115
|
+
|
|
116
|
+
]]
|
|
117
|
+
local function stopAllCategories()
|
|
118
|
+
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
119
|
+
local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
|
|
120
|
+
if not soundsFolder then
|
|
121
|
+
return nil
|
|
122
|
+
end
|
|
123
|
+
for _, category in soundsFolder:GetChildren() do
|
|
124
|
+
if not category:IsA("Folder") then
|
|
125
|
+
continue
|
|
126
|
+
end
|
|
127
|
+
for _1, sound in category:GetChildren() do
|
|
128
|
+
if not sound:IsA("Sound") then
|
|
129
|
+
continue
|
|
130
|
+
end
|
|
131
|
+
sound:Stop()
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
--[[
|
|
136
|
+
*
|
|
137
|
+
* Set the Volume of a Sound Category
|
|
138
|
+
* @param category Sound Category
|
|
139
|
+
* @param volume Volume
|
|
140
|
+
|
|
141
|
+
]]
|
|
142
|
+
local function setCategoryVolume(category, volume)
|
|
143
|
+
local config = definitions[category]
|
|
144
|
+
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
145
|
+
local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
|
|
146
|
+
if not soundsFolder then
|
|
147
|
+
return nil
|
|
148
|
+
end
|
|
149
|
+
local categoryFolder = soundsFolder:FindFirstChild(config.category)
|
|
150
|
+
if not categoryFolder then
|
|
151
|
+
return nil
|
|
152
|
+
end
|
|
153
|
+
for _, sound in categoryFolder:GetChildren() do
|
|
154
|
+
if not sound:IsA("Sound") then
|
|
155
|
+
continue
|
|
156
|
+
end
|
|
157
|
+
sound.Volume = volume
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
--[[
|
|
161
|
+
*
|
|
162
|
+
* Set the Global Volume of every Sound Category
|
|
163
|
+
* @param volume Volume
|
|
164
|
+
|
|
165
|
+
]]
|
|
166
|
+
local function setGlobalCategoryVolume(volume)
|
|
167
|
+
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
168
|
+
local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
|
|
169
|
+
if not soundsFolder then
|
|
170
|
+
return nil
|
|
171
|
+
end
|
|
172
|
+
for _, category in soundsFolder:GetChildren() do
|
|
173
|
+
if not category:IsA("Folder") then
|
|
174
|
+
continue
|
|
175
|
+
end
|
|
176
|
+
for _1, sound in category:GetChildren() do
|
|
177
|
+
if not sound:IsA("Sound") then
|
|
178
|
+
continue
|
|
179
|
+
end
|
|
180
|
+
sound.Volume = volume
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
--[[
|
|
185
|
+
*
|
|
186
|
+
* Whether a Category is playing or not
|
|
187
|
+
* @param category Sound Category
|
|
188
|
+
* @returns Whether a Category is playing or not
|
|
189
|
+
|
|
190
|
+
]]
|
|
191
|
+
local function isCategoryPlaying(category)
|
|
192
|
+
local config = definitions[category]
|
|
193
|
+
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
194
|
+
local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
|
|
195
|
+
if not soundsFolder then
|
|
196
|
+
return false
|
|
197
|
+
end
|
|
198
|
+
local categoryFolder = soundsFolder:FindFirstChild(config.category)
|
|
199
|
+
if not categoryFolder then
|
|
200
|
+
return false
|
|
201
|
+
end
|
|
202
|
+
local _exp = categoryFolder:GetChildren()
|
|
203
|
+
-- βΌ ReadonlyArray.filter βΌ
|
|
204
|
+
local _newValue = {}
|
|
205
|
+
local _callback = function(sound)
|
|
206
|
+
return sound:IsA("Sound")
|
|
207
|
+
end
|
|
208
|
+
local _length = 0
|
|
209
|
+
for _k, _v in _exp do
|
|
210
|
+
if _callback(_v, _k - 1, _exp) == true then
|
|
211
|
+
_length += 1
|
|
212
|
+
_newValue[_length] = _v
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
-- β² ReadonlyArray.filter β²
|
|
216
|
+
local sounds = _newValue
|
|
217
|
+
if #sounds == 0 then
|
|
218
|
+
return false
|
|
219
|
+
end
|
|
220
|
+
for _, sound in sounds do
|
|
221
|
+
if not sound.IsPlaying then
|
|
222
|
+
return false
|
|
79
223
|
end
|
|
80
224
|
end
|
|
225
|
+
return true
|
|
226
|
+
end
|
|
227
|
+
--[[
|
|
228
|
+
*
|
|
229
|
+
* Play a Sound from a Category
|
|
230
|
+
* @param category Sound Category
|
|
231
|
+
* @param sound Sound Instance
|
|
232
|
+
|
|
233
|
+
]]
|
|
234
|
+
local function playSoundFromCategory(category, sound)
|
|
235
|
+
loadCategory(category)
|
|
236
|
+
local config = definitions[category]
|
|
237
|
+
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
238
|
+
local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
|
|
239
|
+
if not soundsFolder then
|
|
240
|
+
return nil
|
|
241
|
+
end
|
|
242
|
+
local categoryFolder = soundsFolder:FindFirstChild(config.category)
|
|
243
|
+
if not categoryFolder then
|
|
244
|
+
return nil
|
|
245
|
+
end
|
|
246
|
+
local soundInstance = categoryFolder:FindFirstChild(sound)
|
|
247
|
+
local _result = soundInstance
|
|
248
|
+
if _result ~= nil then
|
|
249
|
+
_result = _result:IsA("Sound")
|
|
250
|
+
end
|
|
251
|
+
if _result then
|
|
252
|
+
soundInstance:Play()
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
--[[
|
|
256
|
+
*
|
|
257
|
+
* Reset a Sound Category Timeposition to 0
|
|
258
|
+
* @param category Sound Category
|
|
259
|
+
|
|
260
|
+
]]
|
|
261
|
+
local function resetCategory(category)
|
|
262
|
+
local config = definitions[category]
|
|
263
|
+
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
264
|
+
local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
|
|
265
|
+
if not soundsFolder then
|
|
266
|
+
return nil
|
|
267
|
+
end
|
|
268
|
+
local categoryFolder = soundsFolder:FindFirstChild(config.category)
|
|
269
|
+
if not categoryFolder then
|
|
270
|
+
return nil
|
|
271
|
+
end
|
|
272
|
+
for _, sound in categoryFolder:GetChildren() do
|
|
273
|
+
if not sound:IsA("Sound") then
|
|
274
|
+
continue
|
|
275
|
+
end
|
|
276
|
+
sound.TimePosition = 0
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
--[[
|
|
280
|
+
*
|
|
281
|
+
* Resets every Sound from every Sound Category
|
|
282
|
+
* @param category Sound Category
|
|
283
|
+
|
|
284
|
+
]]
|
|
285
|
+
local function resetAllCategories(category)
|
|
286
|
+
local config = definitions[category]
|
|
287
|
+
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
288
|
+
local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
|
|
289
|
+
if not soundsFolder then
|
|
290
|
+
return nil
|
|
291
|
+
end
|
|
292
|
+
for _, folder in soundsFolder:GetChildren() do
|
|
293
|
+
if not folder:IsA("Folder") then
|
|
294
|
+
continue
|
|
295
|
+
end
|
|
296
|
+
for _1, sound in folder:GetChildren() do
|
|
297
|
+
if not sound:IsA("Sound") then
|
|
298
|
+
continue
|
|
299
|
+
end
|
|
300
|
+
sound.TimePosition = 0
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
--[[
|
|
305
|
+
*
|
|
306
|
+
* Preloads a Sound Category
|
|
307
|
+
* @param category Sound Category
|
|
308
|
+
|
|
309
|
+
]]
|
|
310
|
+
local function preloadCategory(category)
|
|
311
|
+
loadCategory(category)
|
|
312
|
+
end
|
|
313
|
+
--[[
|
|
314
|
+
*
|
|
315
|
+
* Preloads every Sound Category
|
|
316
|
+
|
|
317
|
+
]]
|
|
318
|
+
local function preloadAllCategories()
|
|
319
|
+
for category in pairs(definitions) do
|
|
320
|
+
loadCategory(category)
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
--[[
|
|
324
|
+
*
|
|
325
|
+
* Smoothly fade in a Sound Category
|
|
326
|
+
* @param category Sound Category
|
|
327
|
+
* @param duration Duration
|
|
328
|
+
* @param volume Volume
|
|
329
|
+
|
|
330
|
+
]]
|
|
331
|
+
local function fadeInCategory(category, duration, volume)
|
|
332
|
+
local config = definitions[category]
|
|
333
|
+
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
334
|
+
local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
|
|
335
|
+
if not soundsFolder then
|
|
336
|
+
return nil
|
|
337
|
+
end
|
|
338
|
+
local categoryFolder = soundsFolder:FindFirstChild(config.category)
|
|
339
|
+
if not categoryFolder then
|
|
340
|
+
return nil
|
|
341
|
+
end
|
|
342
|
+
for _, sound in categoryFolder:GetChildren() do
|
|
343
|
+
if not sound:IsA("Sound") then
|
|
344
|
+
continue
|
|
345
|
+
end
|
|
346
|
+
sound.Volume = 0
|
|
347
|
+
sound:Play()
|
|
348
|
+
local step = 0.05
|
|
349
|
+
local interval = duration * step
|
|
350
|
+
task.spawn(function()
|
|
351
|
+
local vol = 0
|
|
352
|
+
while vol < volume do
|
|
353
|
+
vol += step
|
|
354
|
+
sound.Volume = math.clamp(vol, 0, volume)
|
|
355
|
+
task.wait(interval)
|
|
356
|
+
end
|
|
357
|
+
end)
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
--[[
|
|
361
|
+
*
|
|
362
|
+
* Smoothly fade out a Sound Category
|
|
363
|
+
* @param category Sound Category
|
|
364
|
+
* @param duration Duration
|
|
365
|
+
* @param targetVolume Target Volume
|
|
366
|
+
|
|
367
|
+
]]
|
|
368
|
+
local function fadeOutCategory(category, duration, targetVolume)
|
|
369
|
+
local config = definitions[category]
|
|
370
|
+
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
371
|
+
local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
|
|
372
|
+
if not soundsFolder then
|
|
373
|
+
return nil
|
|
374
|
+
end
|
|
375
|
+
local categoryFolder = soundsFolder:FindFirstChild(config.category)
|
|
376
|
+
if not categoryFolder then
|
|
377
|
+
return nil
|
|
378
|
+
end
|
|
379
|
+
for _, sound in categoryFolder:GetChildren() do
|
|
380
|
+
if not sound:IsA("Sound") then
|
|
381
|
+
continue
|
|
382
|
+
end
|
|
383
|
+
local startVolume = sound.Volume
|
|
384
|
+
local _condition = targetVolume
|
|
385
|
+
if _condition == nil then
|
|
386
|
+
_condition = 0
|
|
387
|
+
end
|
|
388
|
+
local endVolume = _condition
|
|
389
|
+
local step = 0.05
|
|
390
|
+
local interval = duration * step
|
|
391
|
+
task.spawn(function()
|
|
392
|
+
local vol = startVolume
|
|
393
|
+
while vol > endVolume do
|
|
394
|
+
vol = math.clamp(vol - step, endVolume, startVolume)
|
|
395
|
+
sound.Volume = vol
|
|
396
|
+
task.wait(interval)
|
|
397
|
+
end
|
|
398
|
+
sound.Volume = endVolume
|
|
399
|
+
if endVolume == 0 then
|
|
400
|
+
sound:Stop()
|
|
401
|
+
end
|
|
402
|
+
end)
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
--[[
|
|
406
|
+
*
|
|
407
|
+
* Does something on Category end
|
|
408
|
+
* @param category Sound Category
|
|
409
|
+
* @param callback Callback
|
|
410
|
+
|
|
411
|
+
]]
|
|
412
|
+
local function onCategoryEnd(category, callback)
|
|
413
|
+
local config = definitions[category]
|
|
414
|
+
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
415
|
+
local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
|
|
416
|
+
if not soundsFolder then
|
|
417
|
+
return nil
|
|
418
|
+
end
|
|
419
|
+
local categoryFolder = soundsFolder:FindFirstChild(config.category)
|
|
420
|
+
if not categoryFolder then
|
|
421
|
+
return nil
|
|
422
|
+
end
|
|
423
|
+
local _exp = categoryFolder:GetChildren()
|
|
424
|
+
-- βΌ ReadonlyArray.filter βΌ
|
|
425
|
+
local _newValue = {}
|
|
426
|
+
local _callback = function(s)
|
|
427
|
+
return s:IsA("Sound")
|
|
428
|
+
end
|
|
429
|
+
local _length = 0
|
|
430
|
+
for _k, _v in _exp do
|
|
431
|
+
if _callback(_v, _k - 1, _exp) == true then
|
|
432
|
+
_length += 1
|
|
433
|
+
_newValue[_length] = _v
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
-- β² ReadonlyArray.filter β²
|
|
437
|
+
local sounds = _newValue
|
|
438
|
+
if #sounds == 0 then
|
|
439
|
+
return nil
|
|
440
|
+
end
|
|
441
|
+
local remaining = #sounds
|
|
442
|
+
for _, sound in sounds do
|
|
443
|
+
sound.Ended:Connect(function()
|
|
444
|
+
remaining -= 1
|
|
445
|
+
if remaining <= 0 then
|
|
446
|
+
callback()
|
|
447
|
+
end
|
|
448
|
+
end)
|
|
449
|
+
end
|
|
81
450
|
end
|
|
82
451
|
return {
|
|
83
452
|
loadCategory = loadCategory,
|
|
84
453
|
playCategory = playCategory,
|
|
454
|
+
stopCategory = stopCategory,
|
|
455
|
+
stopAllCategories = stopAllCategories,
|
|
456
|
+
setCategoryVolume = setCategoryVolume,
|
|
457
|
+
setGlobalCategoryVolume = setGlobalCategoryVolume,
|
|
458
|
+
isCategoryPlaying = isCategoryPlaying,
|
|
459
|
+
playSoundFromCategory = playSoundFromCategory,
|
|
460
|
+
resetCategory = resetCategory,
|
|
461
|
+
resetAllCategories = resetAllCategories,
|
|
462
|
+
preloadAllCategories = preloadAllCategories,
|
|
463
|
+
preloadCategory = preloadCategory,
|
|
464
|
+
fadeInCategory = fadeInCategory,
|
|
465
|
+
fadeOutCategory = fadeOutCategory,
|
|
466
|
+
onCategoryEnd = onCategoryEnd,
|
|
85
467
|
}
|
|
86
468
|
end
|
|
87
469
|
return {
|
|
@@ -1,21 +1,32 @@
|
|
|
1
|
-
import { SoundOptions } from "./options";
|
|
1
|
+
import { SoundHandle, SoundOptions } from "./options";
|
|
2
2
|
/**
|
|
3
3
|
* Create a sound Registry
|
|
4
4
|
* @param definitions Define the Sounds
|
|
5
5
|
*/
|
|
6
6
|
export declare function createSoundRegistry<T extends Record<string, SoundOptions>>(definitions: T): {
|
|
7
|
-
play: (name: keyof T
|
|
8
|
-
|
|
7
|
+
play: (name: keyof T, spatial?: {
|
|
8
|
+
emitters: BasePart[];
|
|
9
|
+
}) => SoundHandle | undefined;
|
|
10
|
+
stop: (name: keyof T, spatial?: {
|
|
11
|
+
emitters: BasePart[];
|
|
12
|
+
}) => void;
|
|
9
13
|
preloadAll: () => void;
|
|
10
14
|
load: (name: keyof T) => void;
|
|
11
|
-
fadeIn: (soundName: keyof T, duration: number, volume: number
|
|
12
|
-
|
|
15
|
+
fadeIn: (soundName: keyof T, duration: number, volume: number, spatial?: {
|
|
16
|
+
emitters: BasePart[];
|
|
17
|
+
}) => void;
|
|
18
|
+
fadeOut: (soundName: keyof T, duration: number, targetVolume?: number, spatial?: {
|
|
19
|
+
emitters: BasePart[];
|
|
20
|
+
}) => void;
|
|
13
21
|
reset: (sound: keyof T) => void;
|
|
14
22
|
setTimePosition: (sound: keyof T, timePosition: number) => void;
|
|
15
23
|
stopAll: (reset?: true) => void;
|
|
16
24
|
preload: (sound: keyof T) => void;
|
|
17
25
|
setGlobalVolume: (volume: number) => void;
|
|
18
|
-
setVolume: (sound: keyof T, volume: number
|
|
26
|
+
setVolume: (sound: keyof T, volume: number, spatial?: {
|
|
27
|
+
emitters: BasePart[];
|
|
28
|
+
}) => void;
|
|
19
29
|
resetAll: (sound: keyof T) => void;
|
|
20
30
|
onEnd: (sound: keyof T, callback: () => void) => void;
|
|
31
|
+
isPlaying: (sound: keyof T) => boolean;
|
|
21
32
|
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local createSpatialHandle = TS.import(script, script.Parent, "createSpatialHandle").createSpatialHandle
|
|
2
4
|
--[[
|
|
3
5
|
*
|
|
4
6
|
* Create a sound Registry
|
|
@@ -6,6 +8,7 @@
|
|
|
6
8
|
|
|
7
9
|
]]
|
|
8
10
|
local function createSoundRegistry(definitions)
|
|
11
|
+
local spatialHandles = {}
|
|
9
12
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
10
13
|
local folder = ReplicatedStorage:FindFirstChild("Sounds")
|
|
11
14
|
if not folder then
|
|
@@ -43,28 +46,58 @@ local function createSoundRegistry(definitions)
|
|
|
43
46
|
*
|
|
44
47
|
* Plays a Sound
|
|
45
48
|
* @param name Define which Sound should be played
|
|
49
|
+
* @param spatial Array of Baseparts
|
|
46
50
|
|
|
47
51
|
]]
|
|
48
|
-
local function play(name)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
_result
|
|
52
|
+
local function play(name, spatial)
|
|
53
|
+
local config = definitions[name]
|
|
54
|
+
if not spatial or not spatial.emitters then
|
|
55
|
+
load(name)
|
|
56
|
+
local sound = folder:FindFirstChild(name)
|
|
57
|
+
local _result = sound
|
|
58
|
+
if _result ~= nil then
|
|
59
|
+
_result:Play()
|
|
60
|
+
end
|
|
61
|
+
return nil
|
|
62
|
+
else
|
|
63
|
+
local emittersArray = spatial.emitters
|
|
64
|
+
local _exp = config.id
|
|
65
|
+
local _condition = config.volume
|
|
66
|
+
if _condition == nil then
|
|
67
|
+
_condition = 1
|
|
68
|
+
end
|
|
69
|
+
local handle = createSpatialHandle(_exp, emittersArray, _condition)
|
|
70
|
+
local _name = name
|
|
71
|
+
spatialHandles[_name] = handle
|
|
72
|
+
handle:play()
|
|
73
|
+
return handle
|
|
54
74
|
end
|
|
55
75
|
end
|
|
56
76
|
--[[
|
|
57
77
|
*
|
|
58
78
|
* Stops a Sound
|
|
59
79
|
* @param name Define which Sound should be stopped
|
|
80
|
+
* @param spatial Array of Baseparts
|
|
60
81
|
|
|
61
82
|
]]
|
|
62
|
-
local function stop(name)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
_result
|
|
83
|
+
local function stop(name, spatial)
|
|
84
|
+
if not spatial then
|
|
85
|
+
local sound = folder:FindFirstChild(name)
|
|
86
|
+
local _result = sound
|
|
87
|
+
if _result ~= nil then
|
|
88
|
+
_result:Stop()
|
|
89
|
+
end
|
|
90
|
+
return nil
|
|
91
|
+
end
|
|
92
|
+
local _name = name
|
|
93
|
+
local handle = spatialHandles[_name]
|
|
94
|
+
if not handle then
|
|
95
|
+
return nil
|
|
67
96
|
end
|
|
97
|
+
handle:stop()
|
|
98
|
+
handle:destroy()
|
|
99
|
+
local _name_1 = name
|
|
100
|
+
spatialHandles[_name_1] = nil
|
|
68
101
|
end
|
|
69
102
|
--[[
|
|
70
103
|
*
|
|
@@ -82,22 +115,44 @@ local function createSoundRegistry(definitions)
|
|
|
82
115
|
* @param soundName Sound Instance
|
|
83
116
|
* @param duration Time in Seconds
|
|
84
117
|
* @param volume Volume
|
|
118
|
+
* @param spatial Array of Baseparts
|
|
85
119
|
|
|
86
120
|
]]
|
|
87
|
-
local function fadeIn(soundName, duration, volume)
|
|
88
|
-
local
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
local
|
|
95
|
-
|
|
96
|
-
vol
|
|
97
|
-
|
|
98
|
-
|
|
121
|
+
local function fadeIn(soundName, duration, volume, spatial)
|
|
122
|
+
local config = definitions[soundName]
|
|
123
|
+
if not spatial or not spatial.emitters then
|
|
124
|
+
local sound = folder:FindFirstChild(soundName)
|
|
125
|
+
sound.Volume = 0
|
|
126
|
+
sound:Play()
|
|
127
|
+
local step = 0.05
|
|
128
|
+
local interval = duration * step
|
|
129
|
+
task.spawn(function()
|
|
130
|
+
local vol = 0
|
|
131
|
+
while vol < volume do
|
|
132
|
+
vol += step
|
|
133
|
+
sound.Volume = math.clamp(vol, 0, volume)
|
|
134
|
+
task.wait(interval)
|
|
135
|
+
end
|
|
136
|
+
end)
|
|
137
|
+
else
|
|
138
|
+
local emittersArray = spatial.emitters
|
|
139
|
+
local _exp = config.id
|
|
140
|
+
local _condition = config.volume
|
|
141
|
+
if _condition == nil then
|
|
142
|
+
_condition = 1
|
|
143
|
+
end
|
|
144
|
+
local handle = createSpatialHandle(_exp, emittersArray, _condition)
|
|
145
|
+
local _self = handle
|
|
146
|
+
local _result = _self.fadeIn
|
|
147
|
+
if _result ~= nil then
|
|
148
|
+
_result(_self, duration, volume)
|
|
99
149
|
end
|
|
100
|
-
|
|
150
|
+
if not config.loop == true then
|
|
151
|
+
handle:played(function()
|
|
152
|
+
handle:destroy()
|
|
153
|
+
end)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
101
156
|
end
|
|
102
157
|
--[[
|
|
103
158
|
*
|
|
@@ -105,33 +160,55 @@ local function createSoundRegistry(definitions)
|
|
|
105
160
|
* @param soundName Sound name from Registry
|
|
106
161
|
* @param duration Time in seconds
|
|
107
162
|
* @param targetVolume Optional target volume (default 0)
|
|
163
|
+
* @param spatial Array of Baseparts
|
|
108
164
|
|
|
109
165
|
]]
|
|
110
|
-
local function fadeOut(soundName, duration, targetVolume)
|
|
111
|
-
local
|
|
112
|
-
if not
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
local _condition = targetVolume
|
|
117
|
-
if _condition == nil then
|
|
118
|
-
_condition = 0
|
|
119
|
-
end
|
|
120
|
-
local endVolume = _condition
|
|
121
|
-
local step = 0.05
|
|
122
|
-
local interval = duration * step
|
|
123
|
-
task.spawn(function()
|
|
124
|
-
local vol = startVolume
|
|
125
|
-
while vol > endVolume do
|
|
126
|
-
vol = math.clamp(vol - step, endVolume, startVolume)
|
|
127
|
-
sound.Volume = vol
|
|
128
|
-
task.wait(interval)
|
|
166
|
+
local function fadeOut(soundName, duration, targetVolume, spatial)
|
|
167
|
+
local config = definitions[soundName]
|
|
168
|
+
if not spatial or not spatial.emitters then
|
|
169
|
+
local sound = folder:FindFirstChild(soundName)
|
|
170
|
+
if not sound then
|
|
171
|
+
return nil
|
|
129
172
|
end
|
|
130
|
-
sound.Volume
|
|
131
|
-
|
|
132
|
-
|
|
173
|
+
local startVolume = sound.Volume
|
|
174
|
+
local _condition = targetVolume
|
|
175
|
+
if _condition == nil then
|
|
176
|
+
_condition = 0
|
|
133
177
|
end
|
|
134
|
-
|
|
178
|
+
local endVolume = _condition
|
|
179
|
+
local step = 0.05
|
|
180
|
+
local interval = duration * step
|
|
181
|
+
task.spawn(function()
|
|
182
|
+
local vol = startVolume
|
|
183
|
+
while vol > endVolume do
|
|
184
|
+
vol = math.clamp(vol - step, endVolume, startVolume)
|
|
185
|
+
sound.Volume = vol
|
|
186
|
+
task.wait(interval)
|
|
187
|
+
end
|
|
188
|
+
sound.Volume = endVolume
|
|
189
|
+
if endVolume == 0 then
|
|
190
|
+
sound:Stop()
|
|
191
|
+
end
|
|
192
|
+
end)
|
|
193
|
+
else
|
|
194
|
+
local emittersArray = spatial.emitters
|
|
195
|
+
local _exp = config.id
|
|
196
|
+
local _condition = config.volume
|
|
197
|
+
if _condition == nil then
|
|
198
|
+
_condition = 1
|
|
199
|
+
end
|
|
200
|
+
local handle = createSpatialHandle(_exp, emittersArray, _condition)
|
|
201
|
+
local _self = handle
|
|
202
|
+
local _result = _self.fadeOut
|
|
203
|
+
if _result ~= nil then
|
|
204
|
+
_result(_self, duration)
|
|
205
|
+
end
|
|
206
|
+
if not config.loop == true then
|
|
207
|
+
handle:played(function()
|
|
208
|
+
handle:destroy()
|
|
209
|
+
end)
|
|
210
|
+
end
|
|
211
|
+
end
|
|
135
212
|
end
|
|
136
213
|
--[[
|
|
137
214
|
*
|
|
@@ -195,14 +272,21 @@ local function createSoundRegistry(definitions)
|
|
|
195
272
|
* Set Sound Volume
|
|
196
273
|
* @param sound Sound Instance
|
|
197
274
|
* @param volume Sound Volume
|
|
275
|
+
* @param spatial Array of Baseparts
|
|
198
276
|
|
|
199
277
|
]]
|
|
200
|
-
local function setVolume(sound, volume)
|
|
201
|
-
local
|
|
202
|
-
if not
|
|
203
|
-
|
|
278
|
+
local function setVolume(sound, volume, spatial)
|
|
279
|
+
local config = definitions[sound]
|
|
280
|
+
if not spatial or spatial.emitters then
|
|
281
|
+
local _sound = folder:FindFirstChild(sound)
|
|
282
|
+
if not (sound ~= 0 and sound == sound and sound ~= "" and sound) then
|
|
283
|
+
return nil
|
|
284
|
+
end
|
|
285
|
+
_sound.Volume = volume
|
|
286
|
+
else
|
|
287
|
+
local emittersArray = spatial.emitters
|
|
288
|
+
local handle = createSpatialHandle(config.id, emittersArray, volume)
|
|
204
289
|
end
|
|
205
|
-
_sound.Volume = volume
|
|
206
290
|
end
|
|
207
291
|
--[[
|
|
208
292
|
*
|
|
@@ -222,7 +306,7 @@ local function createSoundRegistry(definitions)
|
|
|
222
306
|
end
|
|
223
307
|
--[[
|
|
224
308
|
*
|
|
225
|
-
*
|
|
309
|
+
* Does something on Sound end
|
|
226
310
|
* @param name Sound Name
|
|
227
311
|
* @param callback Callback
|
|
228
312
|
|
|
@@ -243,6 +327,21 @@ local function createSoundRegistry(definitions)
|
|
|
243
327
|
local function preload(sound)
|
|
244
328
|
load(sound)
|
|
245
329
|
end
|
|
330
|
+
--[[
|
|
331
|
+
*
|
|
332
|
+
* Check whether a Sound is playing
|
|
333
|
+
* @param sound Sound Instance
|
|
334
|
+
* @returns Boolean
|
|
335
|
+
|
|
336
|
+
]]
|
|
337
|
+
local function isPlaying(sound)
|
|
338
|
+
local _sound = folder:WaitForChild(sound)
|
|
339
|
+
if _sound.IsPlaying == true then
|
|
340
|
+
return true
|
|
341
|
+
else
|
|
342
|
+
return false
|
|
343
|
+
end
|
|
344
|
+
end
|
|
246
345
|
return {
|
|
247
346
|
play = play,
|
|
248
347
|
stop = stop,
|
|
@@ -258,6 +357,7 @@ local function createSoundRegistry(definitions)
|
|
|
258
357
|
setVolume = setVolume,
|
|
259
358
|
resetAll = resetAll,
|
|
260
359
|
onEnd = onEnd,
|
|
360
|
+
isPlaying = isPlaying,
|
|
261
361
|
}
|
|
262
362
|
end
|
|
263
363
|
return {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _functions = TS.import(script, script.Parent.Parent, "utils", "functions")
|
|
4
|
+
local fadeIn = _functions.fadeIn
|
|
5
|
+
local fadeOut = _functions.fadeOut
|
|
6
|
+
--[[
|
|
7
|
+
*
|
|
8
|
+
* Create a Spatial Handle
|
|
9
|
+
* @param assetId Asset ID
|
|
10
|
+
* @param emitters Emitter
|
|
11
|
+
|
|
12
|
+
]]
|
|
13
|
+
local function createSpatialHandle(assetId, emitters, volume)
|
|
14
|
+
local player = Instance.new("AudioPlayer")
|
|
15
|
+
player.Asset = assetId
|
|
16
|
+
player.Volume = volume
|
|
17
|
+
player.Parent = game:GetService("ReplicatedStorage")
|
|
18
|
+
local emitterInstances = {}
|
|
19
|
+
for _, part in emitters do
|
|
20
|
+
local emitter = Instance.new("AudioEmitter")
|
|
21
|
+
emitter.Parent = part
|
|
22
|
+
local wire = Instance.new("Wire")
|
|
23
|
+
wire.SourceInstance = player
|
|
24
|
+
wire.TargetInstance = emitter
|
|
25
|
+
wire.Parent = emitter
|
|
26
|
+
table.insert(emitterInstances, emitter)
|
|
27
|
+
end
|
|
28
|
+
return {
|
|
29
|
+
play = function(self)
|
|
30
|
+
player:Play()
|
|
31
|
+
end,
|
|
32
|
+
stop = function(self)
|
|
33
|
+
player:Stop()
|
|
34
|
+
end,
|
|
35
|
+
fadeIn = function(self, duration, volume)
|
|
36
|
+
fadeIn(player, volume, duration)
|
|
37
|
+
end,
|
|
38
|
+
fadeOut = function(self, duration)
|
|
39
|
+
fadeOut(player, duration)
|
|
40
|
+
end,
|
|
41
|
+
destroy = function(self)
|
|
42
|
+
player:Destroy()
|
|
43
|
+
-- βΌ ReadonlyArray.forEach βΌ
|
|
44
|
+
local _callback = function(e)
|
|
45
|
+
return e:Destroy()
|
|
46
|
+
end
|
|
47
|
+
for _k, _v in emitterInstances do
|
|
48
|
+
_callback(_v, _k - 1, emitterInstances)
|
|
49
|
+
end
|
|
50
|
+
-- β² ReadonlyArray.forEach β²
|
|
51
|
+
end,
|
|
52
|
+
played = function(self, callback)
|
|
53
|
+
player.Ended:Connect(callback)
|
|
54
|
+
end,
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
return {
|
|
58
|
+
createSpatialHandle = createSpatialHandle,
|
|
59
|
+
}
|
package/out/core/options.d.ts
CHANGED
|
@@ -2,6 +2,18 @@ export interface SoundOptions {
|
|
|
2
2
|
volume?: number;
|
|
3
3
|
loop?: boolean;
|
|
4
4
|
id: string;
|
|
5
|
+
spatial?: {
|
|
6
|
+
attenuation?: number;
|
|
7
|
+
directional?: boolean;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export interface SoundHandle {
|
|
11
|
+
play(): void;
|
|
12
|
+
stop(): void;
|
|
13
|
+
fadeIn?(duration: number, volume: number): void;
|
|
14
|
+
fadeOut?(duration: number): void;
|
|
15
|
+
destroy(): void;
|
|
16
|
+
played(callback: () => void): void;
|
|
5
17
|
}
|
|
6
18
|
interface SoundDefinition {
|
|
7
19
|
id: string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fades in a Sound
|
|
3
|
+
* @param player AudioPlayer
|
|
4
|
+
* @param targetVolume Target volume
|
|
5
|
+
* @param duration Duration
|
|
6
|
+
*/
|
|
7
|
+
export declare function fadeIn(player: AudioPlayer, targetVolume: number, duration: number): void;
|
|
8
|
+
/**
|
|
9
|
+
* Smoothly fades out a Sound
|
|
10
|
+
* @param player AudioPlayer
|
|
11
|
+
* @param duration Duration
|
|
12
|
+
*/
|
|
13
|
+
export declare function fadeOut(player: AudioPlayer, duration: number): void;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
--[[
|
|
3
|
+
*
|
|
4
|
+
* Fades in a Sound
|
|
5
|
+
* @param player AudioPlayer
|
|
6
|
+
* @param targetVolume Target volume
|
|
7
|
+
* @param duration Duration
|
|
8
|
+
|
|
9
|
+
]]
|
|
10
|
+
local function fadeIn(player, targetVolume, duration)
|
|
11
|
+
player.Volume = 0
|
|
12
|
+
player:Play()
|
|
13
|
+
local steps = 20
|
|
14
|
+
task.spawn(function()
|
|
15
|
+
for i = 0, steps do
|
|
16
|
+
player.Volume = (i / steps) * targetVolume
|
|
17
|
+
task.wait(duration / steps)
|
|
18
|
+
end
|
|
19
|
+
end)
|
|
20
|
+
end
|
|
21
|
+
--[[
|
|
22
|
+
*
|
|
23
|
+
* Smoothly fades out a Sound
|
|
24
|
+
* @param player AudioPlayer
|
|
25
|
+
* @param duration Duration
|
|
26
|
+
|
|
27
|
+
]]
|
|
28
|
+
local function fadeOut(player, duration)
|
|
29
|
+
local _condition = player.Volume
|
|
30
|
+
if _condition == nil then
|
|
31
|
+
_condition = 1
|
|
32
|
+
end
|
|
33
|
+
local startVolume = _condition
|
|
34
|
+
local steps = 20
|
|
35
|
+
local interval = duration / steps
|
|
36
|
+
task.spawn(function()
|
|
37
|
+
for i = steps, 0, -1 do
|
|
38
|
+
player.Volume = (i / steps) * startVolume
|
|
39
|
+
task.wait(interval)
|
|
40
|
+
end
|
|
41
|
+
player:Stop()
|
|
42
|
+
end)
|
|
43
|
+
end
|
|
44
|
+
return {
|
|
45
|
+
fadeIn = fadeIn,
|
|
46
|
+
fadeOut = fadeOut,
|
|
47
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rbxts/sound-manager",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "A sound manager for Roblox-Typescript projects.",
|
|
5
5
|
"main": "out/init.lua",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "rbxtsc",
|
|
8
8
|
"watch": "rbxtsc -w",
|
|
9
|
-
"prepublishOnly": "npm run build"
|
|
9
|
+
"prepublishOnly": "npm run build",
|
|
10
|
+
"start": "npm pack && npm run watch"
|
|
10
11
|
},
|
|
11
12
|
"keywords": [],
|
|
12
13
|
"author": "Easy-Build-Studio",
|