@kunosyn/shatterbox 0.0.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/src/index.d.ts ADDED
@@ -0,0 +1,617 @@
1
+ /// <reference types="@rbxts/types" />
2
+
3
+ import Effects from "./Effects";
4
+ import type {
5
+ HitboxObject,
6
+ DestructionParams,
7
+ Settings,
8
+ WorldInfo,
9
+ OnDestructCompleted,
10
+ ImaginaryDestructionParams,
11
+ ImaginaryVoxel,
12
+ DestroyedVoxelInfo,
13
+ OnVoxelDestruct,
14
+ VertexMath
15
+ } from "./types";
16
+
17
+ /**
18
+ * Shatterbox - A voxel destruction system for Roblox
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * const shatterbox = new Shatterbox();
23
+ * shatterbox.Start();
24
+ *
25
+ * // Destroy parts around a ball
26
+ * shatterbox.Destroy({
27
+ * CFrame: new CFrame(0, 10, 0),
28
+ * Size: new Vector3(5, 5, 5),
29
+ * Shape: Enum.PartType.Ball
30
+ * });
31
+ * ```
32
+ */
33
+ declare class Shatterbox {
34
+ /**
35
+ * Settings configuration for this Shatterbox instance
36
+ * @readonly
37
+ */
38
+ public readonly Settings: Settings;
39
+
40
+ /**
41
+ * Vertex math utilities for intersection testing
42
+ * @readonly
43
+ */
44
+ public readonly VertexMath: VertexMath;
45
+
46
+ /**
47
+ * Default Effects for Voxel Destruction.
48
+ * @readonly
49
+ */
50
+ public readonly DefaultEffects: Effects;
51
+
52
+ /**
53
+ * Whether the Shatterbox instance has been started
54
+ * @internal
55
+ */
56
+ private started: boolean;
57
+
58
+ /**
59
+ * Creates a new Shatterbox instance with optional custom settings
60
+ *
61
+ * @param settings Optional configuration settings. If not provided, uses default settings.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * // With default settings
66
+ * const shatterbox = new Shatterbox();
67
+ *
68
+ * // With custom settings
69
+ * const shatterbox = new Shatterbox({
70
+ * DefaultGridSize: 2,
71
+ * UseGreedyMeshing: true,
72
+ * MaxDivisionsPerFrame: 100
73
+ * });
74
+ * ```
75
+ */
76
+ constructor(settings?: Partial<Settings>);
77
+
78
+ /**
79
+ * Starts the Shatterbox system. Must be called before using any destruction methods.
80
+ * Can only be called once per instance.
81
+ *
82
+ * Initializes network events, effect registries, and starts the heartbeat worker.
83
+ *
84
+ * @throws Will warn if called more than once on the same instance
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * const shatterbox = new Shatterbox();
89
+ * shatterbox.Start();
90
+ * ```
91
+ */
92
+ public Start(): void;
93
+
94
+ /**
95
+ * Registers an effect callback for voxel destruction
96
+ *
97
+ * Effect callbacks are executed when voxels are destroyed, allowing you to:
98
+ * - Apply physics effects
99
+ * - Create particle effects
100
+ * - Play sounds
101
+ * - Add custom behavior to destroyed voxels
102
+ *
103
+ * @param name Unique name for the effect (case-insensitive)
104
+ * @param callback Function to execute when a voxel is destroyed
105
+ *
106
+ * @throws Asserts if name is not a string
107
+ * @throws Asserts if an effect with this name already exists
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * shatterbox.RegisterOnVoxelDestruct("explosion", (voxel, info) => {
112
+ * voxel.Anchored = false;
113
+ * voxel.Velocity = info.CuttingPart.CFrame.LookVector.mul(50);
114
+ *
115
+ * const explosion = new Instance("Explosion");
116
+ * explosion.Position = voxel.Position;
117
+ * explosion.Parent = game.Workspace;
118
+ * });
119
+ *
120
+ * // Use the effect in destruction
121
+ * shatterbox.Destroy(part, undefined, undefined, "explosion");
122
+ * ```
123
+ */
124
+ public RegisterOnVoxelDestruct(name: string, callback: OnVoxelDestruct): void;
125
+
126
+ /**
127
+ * Performs voxel destruction around the intersecting part.
128
+ * Operations are queued and processed over multiple frames to maintain performance.
129
+ *
130
+ * Supports multiple syntaxes:
131
+ * 1. Part/Model as cutting shape
132
+ * 2. WorldInfo table (CFrame, Size, Shape)
133
+ * 3. DestructionParams object with all options
134
+ *
135
+ * @param intersectingPart The cutting shape or params object
136
+ * @param FilterTagged Optional tag(s) to filter which parts can be destroyed
137
+ * @param CleanupDelay Time in seconds before destroyed parts are restored (0 = never restore)
138
+ * @param OnVoxelDestruct Name of the registered effect callback to execute
139
+ * @param GridSize Size of each voxel in studs (smaller = more detailed destruction)
140
+ * @param SkipEncapsulatedVoxels If true, don't create voxels fully inside the cutting part
141
+ * @param OnDestructCompleted Callback when destruction completes
142
+ * @param UserData Custom data passed to the effect callback
143
+ * @param ExcludePlayersReplication Players to exclude from replication (server-side only)
144
+ * @param SkipFloors If true, don't destroy floor surfaces
145
+ * @param SkipWalls If true, don't destroy wall surfaces
146
+ *
147
+ * @example
148
+ * ```ts
149
+ * // Simple destruction with a part
150
+ * shatterbox.Destroy(explosionPart);
151
+ *
152
+ * // Destruction with WorldInfo
153
+ * shatterbox.Destroy({
154
+ * CFrame: new CFrame(0, 10, 0),
155
+ * Size: new Vector3(10, 10, 10),
156
+ * Shape: Enum.PartType.Ball
157
+ * });
158
+ *
159
+ * // Full params with effect and cleanup
160
+ * shatterbox.Destroy(
161
+ * explosionPart,
162
+ * "Destructible", // Only destroy parts tagged "Destructible"
163
+ * 5, // Restore after 5 seconds
164
+ * "explosion", // Use "explosion" effect
165
+ * 2, // 2-stud voxels
166
+ * false,
167
+ * (count, groups) => print(`Destroyed ${count} voxels`)
168
+ * );
169
+ *
170
+ * // Using params object
171
+ * shatterbox.Destroy({
172
+ * CuttingPart: {
173
+ * CFrame: missile.CFrame,
174
+ * Size: new Vector3(5, 5, 5),
175
+ * Shape: Enum.PartType.Ball
176
+ * },
177
+ * OnVoxelDestruct: "explosion",
178
+ * GridSize: 1.5,
179
+ * CleanupDelay: 10,
180
+ * SkipFloors: true
181
+ * });
182
+ * ```
183
+ */
184
+ public Destroy(
185
+ intersectingPart: Part | Model | WorldInfo | DestructionParams,
186
+ FilterTagged?: string | Array<string>,
187
+ CleanupDelay?: number,
188
+ OnVoxelDestruct?: string,
189
+ GridSize?: number,
190
+ SkipEncapsulatedVoxels?: boolean,
191
+ OnDestructCompleted?: OnDestructCompleted,
192
+ UserData?: Record<string, unknown>,
193
+ ExcludePlayersReplication?: Array<Player>,
194
+ SkipFloors?: boolean,
195
+ SkipWalls?: boolean
196
+ ): void;
197
+
198
+ /**
199
+ * Performs instant voxel destruction, yielding until the next Heartbeat.
200
+ * Returns imaginary voxels (data structures) that can be instantiated as real parts.
201
+ *
202
+ * Use this when you want full control over how voxels are created, or when you need
203
+ * to inspect/modify voxels before instantiating them.
204
+ *
205
+ * @returns A tuple of [imaginaryVoxels, existingDebris]
206
+ * - imaginaryVoxels: Array of voxel data that can be instantiated with InstantiateImaginaryVoxel
207
+ * - existingDebris: Array of existing debris parts that were in the destruction area
208
+ *
209
+ * @example
210
+ * ```ts
211
+ * // Get imaginary voxels without creating them
212
+ * const [voxels, debris] = shatterbox.ImaginaryVoxels({
213
+ * CFrame: new CFrame(0, 10, 0),
214
+ * Size: new Vector3(5, 5, 5),
215
+ * Shape: Enum.PartType.Ball
216
+ * });
217
+ *
218
+ * print(`Would create ${voxels.size()} voxels`);
219
+ *
220
+ * // Create only edge voxels with custom properties
221
+ * for (const voxel of voxels) {
222
+ * if (voxel.isEdge) {
223
+ * const part = shatterbox.InstantiateImaginaryVoxel(voxel);
224
+ * part.BrickColor = BrickColor.Red();
225
+ * part.Anchored = false;
226
+ * }
227
+ * }
228
+ *
229
+ * // Handle existing debris
230
+ * for (const debris of debris) {
231
+ * debris.BrickColor = BrickColor.Yellow();
232
+ * }
233
+ * ```
234
+ */
235
+ public ImaginaryVoxels(
236
+ intersectingPart: Part | Model | WorldInfo | ImaginaryDestructionParams,
237
+ FilterTagged?: string | Array<string>,
238
+ CleanupDelay?: number,
239
+ GridSize?: number,
240
+ SkipEncapsulatedVoxels?: boolean,
241
+ ExcludePlayersReplication?: Array<Player>,
242
+ SkipFloors?: boolean,
243
+ SkipWalls?: boolean
244
+ ): LuaTuple<[Array<ImaginaryVoxel>, Array<BasePart>]>;
245
+
246
+ /**
247
+ * Instantiates an imaginary voxel as a real Part in the workspace.
248
+ *
249
+ * The created part will:
250
+ * - Be automatically parented to the appropriate location
251
+ * - Have the DebrisTag tag (unless doNotGiveDebrisTag is true)
252
+ * - Be registered in the dirty group system
253
+ *
254
+ * @param imaginaryVoxel The imaginary voxel data from ImaginaryVoxels()
255
+ * @param doNotGiveDebrisTag If true, don't add the DebrisTag to the created part
256
+ * @returns The created Part instance
257
+ *
258
+ * @example
259
+ * ```ts
260
+ * const [voxels] = shatterbox.ImaginaryVoxels(explosionPart);
261
+ *
262
+ * // Create all voxels as debris
263
+ * for (const voxel of voxels) {
264
+ * const part = shatterbox.InstantiateImaginaryVoxel(voxel);
265
+ * part.Anchored = false;
266
+ * }
267
+ *
268
+ * // Create voxels without debris tag (for custom tracking)
269
+ * for (const voxel of voxels) {
270
+ * const part = shatterbox.InstantiateImaginaryVoxel(voxel, true);
271
+ * // Add your own custom tag or tracking
272
+ * part.SetAttribute("CustomVoxel", true);
273
+ * }
274
+ * ```
275
+ */
276
+ public InstantiateImaginaryVoxel(
277
+ imaginaryVoxel: ImaginaryVoxel,
278
+ doNotGiveDebrisTag?: boolean
279
+ ): Part;
280
+
281
+ /**
282
+ * Makes a voxel fall realistically with physics and replicates it to clients.
283
+ *
284
+ * **Server-side only.**
285
+ *
286
+ * Puppets are physics-enabled parts that are efficiently replicated to clients
287
+ * using compressed state updates. The system automatically:
288
+ * - Manages network ownership
289
+ * - Compresses position/rotation data
290
+ * - Handles client-side interpolation
291
+ * - Auto-anchors when sleeping
292
+ * - Cleans up old puppets when limit is reached
293
+ *
294
+ * @param voxel The part to turn into a falling puppet
295
+ *
296
+ * @throws Asserts if called on client
297
+ * @throws Warns if network ownership cannot be set
298
+ *
299
+ * @example
300
+ * ```ts
301
+ * // Create falling debris
302
+ * const [voxels] = shatterbox.ImaginaryVoxels(explosionPart);
303
+ *
304
+ * for (const voxel of voxels) {
305
+ * const part = shatterbox.InstantiateImaginaryVoxel(voxel);
306
+ *
307
+ * // Apply force
308
+ * const direction = voxel.CFrame.Position.sub(explosionPart.Position).Unit;
309
+ * part.AssemblyLinearVelocity = direction.mul(50);
310
+ *
311
+ * // Make it fall with replication
312
+ * shatterbox.Puppeteer(part);
313
+ * }
314
+ * ```
315
+ */
316
+ public Puppeteer(voxel: Part): void;
317
+
318
+ /**
319
+ * Creates fake puppets on the client for testing purposes.
320
+ *
321
+ * **Client-side only.**
322
+ *
323
+ * Useful for testing destruction effects without server replication.
324
+ *
325
+ * @param fakePuppets Array of parts to treat as puppets
326
+ *
327
+ * @example
328
+ * ```ts
329
+ * // Client-side testing
330
+ * const testParts: Part[] = [];
331
+ * for (let i = 0; i < 10; i++) {
332
+ * const part = new Instance("Part");
333
+ * part.Size = new Vector3(2, 2, 2);
334
+ * part.Position = new Vector3(i * 3, 10, 0);
335
+ * part.Parent = game.Workspace;
336
+ * testParts.push(part);
337
+ * }
338
+ *
339
+ * shatterbox.FakeClientPuppets(testParts);
340
+ * ```
341
+ */
342
+ public FakeClientPuppets(fakePuppets: Array<Part>): void;
343
+
344
+ /**
345
+ * Creates a hitbox object with additional functionality for destruction.
346
+ *
347
+ * Hitboxes provide:
348
+ * - Welding to moving parts
349
+ * - Continuous destruction (raycast-like)
350
+ * - Velocity prediction for fast-moving objects
351
+ * - Start/Stop control
352
+ *
353
+ * @returns A hitbox object with methods for destruction and movement
354
+ *
355
+ * @example
356
+ * ```ts
357
+ * // Create a projectile hitbox
358
+ * const hitbox = shatterbox.CreateHitbox();
359
+ * hitbox.Size = new Vector3(2, 2, 2);
360
+ * hitbox.Shape = Enum.PartType.Ball;
361
+ * hitbox.OnVoxelDestruct = "explosion";
362
+ * hitbox.GridSize = 1;
363
+ *
364
+ * // Weld to projectile
365
+ * hitbox.WeldTo(projectile);
366
+ * hitbox.VelocityPrediction = true; // Enable for fast projectiles
367
+ * hitbox.Start();
368
+ *
369
+ * // Cleanup when done
370
+ * projectile.Touched.Connect(() => {
371
+ * hitbox.Stop();
372
+ * hitbox.DestroyHitbox();
373
+ * });
374
+ *
375
+ * // Manual control hitbox
376
+ * const controlledHitbox = shatterbox.CreateHitbox();
377
+ * controlledHitbox.DestructDelay = 0.1; // Destroy every 0.1 seconds
378
+ *
379
+ * game.GetService("RunService").Heartbeat.Connect(() => {
380
+ * controlledHitbox.CFrame = tool.Handle.CFrame;
381
+ * });
382
+ *
383
+ * controlledHitbox.Start();
384
+ * ```
385
+ */
386
+ public CreateHitbox(): HitboxObject;
387
+
388
+ /**
389
+ * Resets an area without reverting ownership of parts.
390
+ *
391
+ * Undoes all destruction within the specified area while maintaining the
392
+ * current dirty group structure. Useful for resetting specific zones without
393
+ * affecting the rest of the map.
394
+ *
395
+ * @param area The area to reset (Part or WorldInfo with CFrame and Size)
396
+ *
397
+ * @example
398
+ * ```ts
399
+ * // Reset using a part
400
+ * const resetZone = game.Workspace.WaitForChild("ResetZone") as Part;
401
+ * shatterbox.ResetArea(resetZone);
402
+ *
403
+ * // Reset using WorldInfo
404
+ * shatterbox.ResetArea({
405
+ * CFrame: new CFrame(0, 0, 0),
406
+ * Size: new Vector3(50, 50, 50)
407
+ * });
408
+ *
409
+ * // Reset around player
410
+ * game.GetService("Players").PlayerAdded.Connect((player) => {
411
+ * const character = player.Character || player.CharacterAdded.Wait()[0];
412
+ * const hrp = character.WaitForChild("HumanoidRootPart") as Part;
413
+ *
414
+ * shatterbox.ResetArea({
415
+ * CFrame: hrp.CFrame,
416
+ * Size: new Vector3(20, 20, 20)
417
+ * });
418
+ * });
419
+ * ```
420
+ */
421
+ public ResetArea(area: Part | WorldInfo): void;
422
+
423
+ /**
424
+ * Resets all modified parts to their original state.
425
+ *
426
+ * This will:
427
+ * - Cancel all queued operations
428
+ * - Terminate all greedy meshing workers
429
+ * - Clear smooth cleanup timers
430
+ * - Destroy or restore all puppets
431
+ * - Restore all original parts
432
+ *
433
+ * @param doNotRevertOwnership If true, keeps parts in their current ownership state
434
+ *
435
+ * @example
436
+ * ```ts
437
+ * // Complete reset
438
+ * shatterbox.Reset();
439
+ *
440
+ * // Reset but maintain dirty groups (for client-server sync)
441
+ * shatterbox.Reset(true);
442
+ *
443
+ * // Reset on round end
444
+ * function resetMap() {
445
+ * shatterbox.Reset();
446
+ * print("Map has been reset!");
447
+ * }
448
+ * ```
449
+ */
450
+ public Reset(doNotRevertOwnership?: boolean): void;
451
+
452
+ /**
453
+ * Cancels all ongoing destruction operations in the queue.
454
+ *
455
+ * This stops processing queued destructions but does not undo
456
+ * destructions that have already been processed.
457
+ *
458
+ * @example
459
+ * ```ts
460
+ * // Cancel all pending destructions
461
+ * shatterbox.ClearQueue();
462
+ *
463
+ * // Useful for stopping destruction on game state change
464
+ * function onRoundEnd() {
465
+ * shatterbox.ClearQueue(); // Stop processing destructions
466
+ * wait(1);
467
+ * shatterbox.Reset(); // Then reset the map
468
+ * }
469
+ * ```
470
+ */
471
+ public ClearQueue(): void;
472
+
473
+ /**
474
+ * Returns the original part associated with a DirtyGroupID.
475
+ *
476
+ * The original part is never destroyed by Shatterbox, just has its parent
477
+ * set to nil. This allows the system to restore parts efficiently.
478
+ *
479
+ * @param dirtyGroupID The ID of the dirty group
480
+ * @returns The original part, or undefined if not found
481
+ *
482
+ * @example
483
+ * ```ts
484
+ * // Get original part from a voxel
485
+ * const voxel = game.Workspace.FindFirstChild("Voxel") as Part;
486
+ * const dirtyGroupID = voxel.GetAttribute("DirtyGroupID") as string;
487
+ *
488
+ * if (dirtyGroupID) {
489
+ * const original = shatterbox.GetOriginalPart(dirtyGroupID);
490
+ * if (original) {
491
+ * print(`Original part: ${original.Name}`);
492
+ * print(`Original size: ${original.Size}`);
493
+ * }
494
+ * }
495
+ * ```
496
+ */
497
+ public GetOriginalPart(dirtyGroupID: string): Part | undefined;
498
+
499
+ /**
500
+ * Converts a point to voxel space distance relative to the voxel.
501
+ *
502
+ * Returns a vector indicating how many voxels away the point is on each
503
+ * local axis as fractional quantities. Useful for spatial queries and
504
+ * calculating voxel-based distances.
505
+ *
506
+ * @param voxel The reference voxel part
507
+ * @param point The world space point to convert
508
+ * @returns Distance vector in voxel space
509
+ *
510
+ * @example
511
+ * ```ts
512
+ * const voxel = game.Workspace.WaitForChild("Voxel") as Part;
513
+ * const targetPos = new Vector3(10, 10, 10);
514
+ *
515
+ * const distance = shatterbox.VoxelDistanceVector(voxel, targetPos);
516
+ * print(`Distance: ${distance.X}x, ${distance.Y}y, ${distance.Z}z voxels`);
517
+ *
518
+ * // Check if point is within 5 voxels
519
+ * if (distance.Magnitude < 5) {
520
+ * print("Point is close to voxel");
521
+ * }
522
+ * ```
523
+ */
524
+ public VoxelDistanceVector(voxel: Part, point: Vector3): Vector3;
525
+
526
+ /**
527
+ * Returns how many voxels a box has on each axis as fractional quantities.
528
+ *
529
+ * Minimum of 1 on each axis (a box is always at least 1 voxel large).
530
+ *
531
+ * @param voxel The reference voxel part (for voxel size)
532
+ * @param boxSize The size of the box to measure
533
+ * @returns Voxel count vector
534
+ *
535
+ * @example
536
+ * ```ts
537
+ * const voxel = game.Workspace.WaitForChild("Voxel") as Part;
538
+ * const testSize = new Vector3(10, 5, 8);
539
+ *
540
+ * const voxelCount = shatterbox.VoxelCountVector(voxel, testSize);
541
+ * print(`Would create ${voxelCount.X * voxelCount.Y * voxelCount.Z} voxels`);
542
+ *
543
+ * // Estimate performance impact
544
+ * const totalVoxels = voxelCount.X * voxelCount.Y * voxelCount.Z;
545
+ * if (totalVoxels > 1000) {
546
+ * warn("Large destruction - may impact performance");
547
+ * }
548
+ * ```
549
+ */
550
+ public VoxelCountVector(voxel: Part, boxSize: Vector3): Vector3;
551
+
552
+ /**
553
+ * Returns true if the contains part is fully encapsulated by the part.
554
+ *
555
+ * @param part The container part
556
+ * @param contains The part to check if it's contained (must be a Block part)
557
+ * @returns True if fully contained, false otherwise
558
+ *
559
+ * @throws Asserts if part is not a Part
560
+ * @throws Asserts if contains is not a Block Part
561
+ *
562
+ * @example
563
+ * ```ts
564
+ * const container = game.Workspace.WaitForChild("Container") as Part;
565
+ * const testPart = game.Workspace.WaitForChild("TestPart") as Part;
566
+ *
567
+ * if (shatterbox.PartEncapsulatesBlockPart(container, testPart)) {
568
+ * print("TestPart is fully inside Container");
569
+ * } else {
570
+ * print("TestPart is partially or not inside Container");
571
+ * }
572
+ * ```
573
+ */
574
+ public PartEncapsulatesBlockPart(part: Part, contains: Part): boolean;
575
+
576
+ /**
577
+ * Prints the current state of Shatterbox for debugging.
578
+ *
579
+ * Shows:
580
+ * - ShatterQueue length
581
+ * - Active greedy meshing workers
582
+ * - Dirty group counts
583
+ * - Operation processing counts
584
+ *
585
+ * Output is formatted for easy reading in the console.
586
+ *
587
+ * @example
588
+ * ```ts
589
+ * // Debug performance issues
590
+ * shatterbox.PrintState();
591
+ *
592
+ * // Monitor queue size
593
+ * game.GetService("RunService").Heartbeat.Connect(() => {
594
+ * if (frameCount % 60 === 0) {
595
+ * shatterbox.PrintState();
596
+ * }
597
+ * });
598
+ * ```
599
+ */
600
+ public PrintState(): void;
601
+
602
+ /**
603
+ * Gets a setting value, falling back to default if not set in instance settings.
604
+ *
605
+ * @param settingName Name of the setting to retrieve
606
+ * @returns The setting value
607
+ *
608
+ * @example
609
+ * ```ts
610
+ * const gridSize = shatterbox.UseSetting<number>("DefaultGridSize");
611
+ * const useGreedy = shatterbox.UseSetting<boolean>("UseGreedyMeshing");
612
+ * ```
613
+ */
614
+ public UseSetting<T>(settingName: keyof Settings): T;
615
+ }
616
+
617
+ export = Shatterbox;