@boyangsicwastaken/muchachohitbox 1.0.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/index.d.ts +53 -0
- package/out/DictDiff.luau +20 -0
- package/out/GoodSignal.luau +429 -0
- package/out/Types.luau +61 -0
- package/out/index.d.ts +1 -0
- package/out/init.luau +526 -0
- package/package.json +29 -0
package/index.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
interface SignalConnection {
|
|
2
|
+
Connected: boolean;
|
|
3
|
+
Disconnect(): void;
|
|
4
|
+
Destroy(): void;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface Signal<T extends unknown[]> {
|
|
8
|
+
Fire(...args: T): void;
|
|
9
|
+
FireDeferred(...args: T): void;
|
|
10
|
+
Connect(fn: (...args: T) => void): SignalConnection;
|
|
11
|
+
Once(fn: (...args: T) => void): SignalConnection;
|
|
12
|
+
DisconnectAll(): void;
|
|
13
|
+
GetConnections(): SignalConnection[];
|
|
14
|
+
Destroy(): void;
|
|
15
|
+
Wait(): LuaTuple<T>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type DetectionMode = "Default" | "ConstantDetection" | "HitOnce" | "HitParts";
|
|
19
|
+
|
|
20
|
+
interface Hitbox {
|
|
21
|
+
Visualizer: boolean;
|
|
22
|
+
VisualizerColor: Color3;
|
|
23
|
+
VisualizerTransparency: number;
|
|
24
|
+
|
|
25
|
+
DetectionMode: DetectionMode;
|
|
26
|
+
AutoDestroy: boolean;
|
|
27
|
+
Key: string;
|
|
28
|
+
|
|
29
|
+
OverlapParams: OverlapParams;
|
|
30
|
+
|
|
31
|
+
Size: Vector3 | number;
|
|
32
|
+
Shape: Enum.PartType;
|
|
33
|
+
CFrame: CFrame | BasePart;
|
|
34
|
+
Offset: CFrame;
|
|
35
|
+
|
|
36
|
+
VelocityPrediction: boolean;
|
|
37
|
+
VelocityPredictionTime: number;
|
|
38
|
+
|
|
39
|
+
Touched: Signal<[hit: BasePart, humanoid: Humanoid | undefined]>;
|
|
40
|
+
TouchEnded: Signal<[part: BasePart]>;
|
|
41
|
+
|
|
42
|
+
Start(): void;
|
|
43
|
+
Stop(): void;
|
|
44
|
+
Destroy(): void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface MuchachoHitbox {
|
|
48
|
+
CreateHitbox(): Hitbox;
|
|
49
|
+
FindHitbox(key: string): Hitbox | undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
declare const MuchachoHitbox: MuchachoHitbox;
|
|
53
|
+
export = MuchachoHitbox;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
local module = {}
|
|
2
|
+
|
|
3
|
+
local function find(a, tbl)
|
|
4
|
+
for _, a_ in ipairs(tbl) do
|
|
5
|
+
if a_==a then return true end
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
function module.difference(a, b)
|
|
10
|
+
local ret = {}
|
|
11
|
+
for _, v in ipairs(a) do
|
|
12
|
+
if not find(v,b) then table.insert(ret, v) end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
return ret
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
return module
|
|
20
|
+
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
-- -----------------------------------------------------------------------------
|
|
2
|
+
-- Batched Yield-Safe Signal Implementation --
|
|
3
|
+
-- This is a Signal class which has effectively identical behavior to a --
|
|
4
|
+
-- normal RBXScriptSignal, with the only difference being a couple extra --
|
|
5
|
+
-- stack frames at the bottom of the stack trace when an error is thrown. --
|
|
6
|
+
-- This implementation caches runner coroutines, so the ability to yield in --
|
|
7
|
+
-- the signal handlers comes at minimal extra cost over a naive signal --
|
|
8
|
+
-- implementation that either always or never spawns a thread. --
|
|
9
|
+
-- --
|
|
10
|
+
-- License: --
|
|
11
|
+
-- Licensed under the MIT license. --
|
|
12
|
+
-- --
|
|
13
|
+
-- Authors: --
|
|
14
|
+
-- stravant - July 31st, 2021 - Created the file. --
|
|
15
|
+
-- sleitnick - August 3rd, 2021 - Modified for Knit. --
|
|
16
|
+
-- -----------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
-- Signal types
|
|
19
|
+
export type Connection = {
|
|
20
|
+
Disconnect: (self: Connection) -> (),
|
|
21
|
+
Destroy: (self: Connection) -> (),
|
|
22
|
+
Connected: boolean,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type Signal<T...> = {
|
|
26
|
+
Fire: (self: Signal<T...>, T...) -> (),
|
|
27
|
+
FireDeferred: (self: Signal<T...>, T...) -> (),
|
|
28
|
+
Connect: (self: Signal<T...>, fn: (T...) -> ()) -> Connection,
|
|
29
|
+
Once: (self: Signal<T...>, fn: (T...) -> ()) -> Connection,
|
|
30
|
+
DisconnectAll: (self: Signal<T...>) -> (),
|
|
31
|
+
GetConnections: (self: Signal<T...>) -> { Connection },
|
|
32
|
+
Destroy: (self: Signal<T...>) -> (),
|
|
33
|
+
Wait: (self: Signal<T...>) -> T...,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
-- The currently idle thread to run the next handler on
|
|
37
|
+
local freeRunnerThread = nil
|
|
38
|
+
|
|
39
|
+
-- Function which acquires the currently idle handler runner thread, runs the
|
|
40
|
+
-- function fn on it, and then releases the thread, returning it to being the
|
|
41
|
+
-- currently idle one.
|
|
42
|
+
-- If there was a currently idle runner thread already, that's okay, that old
|
|
43
|
+
-- one will just get thrown and eventually GCed.
|
|
44
|
+
local function acquireRunnerThreadAndCallEventHandler(fn, ...)
|
|
45
|
+
local acquiredRunnerThread = freeRunnerThread
|
|
46
|
+
freeRunnerThread = nil
|
|
47
|
+
fn(...)
|
|
48
|
+
-- The handler finished running, this runner thread is free again.
|
|
49
|
+
freeRunnerThread = acquiredRunnerThread
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
-- Coroutine runner that we create coroutines of. The coroutine can be
|
|
53
|
+
-- repeatedly resumed with functions to run followed by the argument to run
|
|
54
|
+
-- them with.
|
|
55
|
+
local function runEventHandlerInFreeThread(...)
|
|
56
|
+
acquireRunnerThreadAndCallEventHandler(...)
|
|
57
|
+
while true do
|
|
58
|
+
acquireRunnerThreadAndCallEventHandler(coroutine.yield())
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
--[=[
|
|
63
|
+
@within Signal
|
|
64
|
+
@interface SignalConnection
|
|
65
|
+
.Connected boolean
|
|
66
|
+
.Disconnect (SignalConnection) -> ()
|
|
67
|
+
|
|
68
|
+
Represents a connection to a signal.
|
|
69
|
+
```lua
|
|
70
|
+
local connection = signal:Connect(function() end)
|
|
71
|
+
print(connection.Connected) --> true
|
|
72
|
+
connection:Disconnect()
|
|
73
|
+
print(connection.Connected) --> false
|
|
74
|
+
```
|
|
75
|
+
]=]
|
|
76
|
+
|
|
77
|
+
-- Connection class
|
|
78
|
+
local Connection = {}
|
|
79
|
+
Connection.__index = Connection
|
|
80
|
+
|
|
81
|
+
function Connection:Disconnect()
|
|
82
|
+
if not self.Connected then
|
|
83
|
+
return
|
|
84
|
+
end
|
|
85
|
+
self.Connected = false
|
|
86
|
+
|
|
87
|
+
-- Unhook the node, but DON'T clear it. That way any fire calls that are
|
|
88
|
+
-- currently sitting on this node will be able to iterate forwards off of
|
|
89
|
+
-- it, but any subsequent fire calls will not hit it, and it will be GCed
|
|
90
|
+
-- when no more fire calls are sitting on it.
|
|
91
|
+
if self._signal._handlerListHead == self then
|
|
92
|
+
self._signal._handlerListHead = self._next
|
|
93
|
+
else
|
|
94
|
+
local prev = self._signal._handlerListHead
|
|
95
|
+
while prev and prev._next ~= self do
|
|
96
|
+
prev = prev._next
|
|
97
|
+
end
|
|
98
|
+
if prev then
|
|
99
|
+
prev._next = self._next
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
Connection.Destroy = Connection.Disconnect
|
|
105
|
+
|
|
106
|
+
-- Make Connection strict
|
|
107
|
+
setmetatable(Connection, {
|
|
108
|
+
__index = function(_tb, key)
|
|
109
|
+
error(("Attempt to get Connection::%s (not a valid member)"):format(tostring(key)), 2)
|
|
110
|
+
end,
|
|
111
|
+
__newindex = function(_tb, key, _value)
|
|
112
|
+
error(("Attempt to set Connection::%s (not a valid member)"):format(tostring(key)), 2)
|
|
113
|
+
end,
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
--[=[
|
|
117
|
+
@within Signal
|
|
118
|
+
@type ConnectionFn (...any) -> ()
|
|
119
|
+
|
|
120
|
+
A function connected to a signal.
|
|
121
|
+
]=]
|
|
122
|
+
|
|
123
|
+
--[=[
|
|
124
|
+
@class Signal
|
|
125
|
+
|
|
126
|
+
A Signal is a data structure that allows events to be dispatched
|
|
127
|
+
and observed.
|
|
128
|
+
|
|
129
|
+
This implementation is a direct copy of the de facto standard, [GoodSignal](https://devforum.roblox.com/t/lua-signal-class-comparison-optimal-goodsignal-class/1387063),
|
|
130
|
+
with some added methods and typings.
|
|
131
|
+
|
|
132
|
+
For example:
|
|
133
|
+
```lua
|
|
134
|
+
local signal = Signal.new()
|
|
135
|
+
|
|
136
|
+
-- Subscribe to a signal:
|
|
137
|
+
signal:Connect(function(msg)
|
|
138
|
+
print("Got message:", msg)
|
|
139
|
+
end)
|
|
140
|
+
|
|
141
|
+
-- Dispatch an event:
|
|
142
|
+
signal:Fire("Hello world!")
|
|
143
|
+
```
|
|
144
|
+
]=]
|
|
145
|
+
local Signal = {}
|
|
146
|
+
Signal.__index = Signal
|
|
147
|
+
|
|
148
|
+
--[=[
|
|
149
|
+
Constructs a new Signal
|
|
150
|
+
|
|
151
|
+
@return Signal
|
|
152
|
+
]=]
|
|
153
|
+
function Signal.new<T...>(): Signal<T...>
|
|
154
|
+
local self = setmetatable({
|
|
155
|
+
_handlerListHead = false,
|
|
156
|
+
_proxyHandler = nil,
|
|
157
|
+
_yieldedThreads = nil,
|
|
158
|
+
}, Signal)
|
|
159
|
+
|
|
160
|
+
return self
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
--[=[
|
|
164
|
+
Constructs a new Signal that wraps around an RBXScriptSignal.
|
|
165
|
+
|
|
166
|
+
@param rbxScriptSignal RBXScriptSignal -- Existing RBXScriptSignal to wrap
|
|
167
|
+
@return Signal
|
|
168
|
+
|
|
169
|
+
For example:
|
|
170
|
+
```lua
|
|
171
|
+
local signal = Signal.Wrap(workspace.ChildAdded)
|
|
172
|
+
signal:Connect(function(part) print(part.Name .. " added") end)
|
|
173
|
+
Instance.new("Part").Parent = workspace
|
|
174
|
+
```
|
|
175
|
+
]=]
|
|
176
|
+
function Signal.Wrap<T...>(rbxScriptSignal: RBXScriptSignal): Signal<T...>
|
|
177
|
+
assert(
|
|
178
|
+
typeof(rbxScriptSignal) == "RBXScriptSignal",
|
|
179
|
+
"Argument #1 to Signal.Wrap must be a RBXScriptSignal; got " .. typeof(rbxScriptSignal)
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
local signal = Signal.new()
|
|
183
|
+
signal._proxyHandler = rbxScriptSignal:Connect(function(...)
|
|
184
|
+
signal:Fire(...)
|
|
185
|
+
end)
|
|
186
|
+
|
|
187
|
+
return signal
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
--[=[
|
|
191
|
+
Checks if the given object is a Signal.
|
|
192
|
+
|
|
193
|
+
@param obj any -- Object to check
|
|
194
|
+
@return boolean -- `true` if the object is a Signal.
|
|
195
|
+
]=]
|
|
196
|
+
function Signal.Is(obj: any): boolean
|
|
197
|
+
return type(obj) == "table" and getmetatable(obj) == Signal
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
--[=[
|
|
201
|
+
@param fn ConnectionFn
|
|
202
|
+
@return SignalConnection
|
|
203
|
+
|
|
204
|
+
Connects a function to the signal, which will be called anytime the signal is fired.
|
|
205
|
+
```lua
|
|
206
|
+
signal:Connect(function(msg, num)
|
|
207
|
+
print(msg, num)
|
|
208
|
+
end)
|
|
209
|
+
|
|
210
|
+
signal:Fire("Hello", 25)
|
|
211
|
+
```
|
|
212
|
+
]=]
|
|
213
|
+
function Signal:Connect(fn)
|
|
214
|
+
local connection = setmetatable({
|
|
215
|
+
Connected = true,
|
|
216
|
+
_signal = self,
|
|
217
|
+
_fn = fn,
|
|
218
|
+
_next = false,
|
|
219
|
+
}, Connection)
|
|
220
|
+
|
|
221
|
+
if self._handlerListHead then
|
|
222
|
+
connection._next = self._handlerListHead
|
|
223
|
+
self._handlerListHead = connection
|
|
224
|
+
else
|
|
225
|
+
self._handlerListHead = connection
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
return connection
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
--[=[
|
|
232
|
+
@deprecated v1.3.0 -- Use `Signal:Once` instead.
|
|
233
|
+
@param fn ConnectionFn
|
|
234
|
+
@return SignalConnection
|
|
235
|
+
]=]
|
|
236
|
+
function Signal:ConnectOnce(fn)
|
|
237
|
+
return self:Once(fn)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
--[=[
|
|
241
|
+
@param fn ConnectionFn
|
|
242
|
+
@return SignalConnection
|
|
243
|
+
|
|
244
|
+
Connects a function to the signal, which will be called the next time the signal fires. Once
|
|
245
|
+
the connection is triggered, it will disconnect itself.
|
|
246
|
+
```lua
|
|
247
|
+
signal:Once(function(msg, num)
|
|
248
|
+
print(msg, num)
|
|
249
|
+
end)
|
|
250
|
+
|
|
251
|
+
signal:Fire("Hello", 25)
|
|
252
|
+
signal:Fire("This message will not go through", 10)
|
|
253
|
+
```
|
|
254
|
+
]=]
|
|
255
|
+
function Signal:Once(fn)
|
|
256
|
+
local connection
|
|
257
|
+
local done = false
|
|
258
|
+
|
|
259
|
+
connection = self:Connect(function(...)
|
|
260
|
+
if done then
|
|
261
|
+
return
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
done = true
|
|
265
|
+
connection:Disconnect()
|
|
266
|
+
fn(...)
|
|
267
|
+
end)
|
|
268
|
+
|
|
269
|
+
return connection
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
function Signal:GetConnections()
|
|
273
|
+
local items = {}
|
|
274
|
+
|
|
275
|
+
local item = self._handlerListHead
|
|
276
|
+
while item do
|
|
277
|
+
table.insert(items, item)
|
|
278
|
+
item = item._next
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
return items
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
-- Disconnect all handlers. Since we use a linked list it suffices to clear the
|
|
285
|
+
-- reference to the head handler.
|
|
286
|
+
--[=[
|
|
287
|
+
Disconnects all connections from the signal.
|
|
288
|
+
```lua
|
|
289
|
+
signal:DisconnectAll()
|
|
290
|
+
```
|
|
291
|
+
]=]
|
|
292
|
+
function Signal:DisconnectAll()
|
|
293
|
+
local item = self._handlerListHead
|
|
294
|
+
while item do
|
|
295
|
+
item.Connected = false
|
|
296
|
+
item = item._next
|
|
297
|
+
end
|
|
298
|
+
self._handlerListHead = false
|
|
299
|
+
|
|
300
|
+
local yieldedThreads = rawget(self, "_yieldedThreads")
|
|
301
|
+
if yieldedThreads then
|
|
302
|
+
for thread in yieldedThreads do
|
|
303
|
+
if coroutine.status(thread) == "suspended" then
|
|
304
|
+
warn(debug.traceback(thread, "signal disconnected; yielded thread cancelled", 2))
|
|
305
|
+
task.cancel(thread)
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
table.clear(self._yieldedThreads)
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
-- Signal:Fire(...) implemented by running the handler functions on the
|
|
313
|
+
-- coRunnerThread, and any time the resulting thread yielded without returning
|
|
314
|
+
-- to us, that means that it yielded to the Roblox scheduler and has been taken
|
|
315
|
+
-- over by Roblox scheduling, meaning we have to make a new coroutine runner.
|
|
316
|
+
--[=[
|
|
317
|
+
@param ... any
|
|
318
|
+
|
|
319
|
+
Fire the signal, which will call all of the connected functions with the given arguments.
|
|
320
|
+
```lua
|
|
321
|
+
signal:Fire("Hello")
|
|
322
|
+
|
|
323
|
+
-- Any number of arguments can be fired:
|
|
324
|
+
signal:Fire("Hello", 32, {Test = "Test"}, true)
|
|
325
|
+
```
|
|
326
|
+
]=]
|
|
327
|
+
function Signal:Fire(...)
|
|
328
|
+
local item = self._handlerListHead
|
|
329
|
+
while item do
|
|
330
|
+
if item.Connected then
|
|
331
|
+
if not freeRunnerThread then
|
|
332
|
+
freeRunnerThread = coroutine.create(runEventHandlerInFreeThread)
|
|
333
|
+
end
|
|
334
|
+
task.spawn(freeRunnerThread, item._fn, ...)
|
|
335
|
+
end
|
|
336
|
+
item = item._next
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
--[=[
|
|
341
|
+
@param ... any
|
|
342
|
+
|
|
343
|
+
Same as `Fire`, but uses `task.defer` internally & doesn't take advantage of thread reuse.
|
|
344
|
+
```lua
|
|
345
|
+
signal:FireDeferred("Hello")
|
|
346
|
+
```
|
|
347
|
+
]=]
|
|
348
|
+
function Signal:FireDeferred(...)
|
|
349
|
+
local item = self._handlerListHead
|
|
350
|
+
while item do
|
|
351
|
+
local conn = item
|
|
352
|
+
task.defer(function(...)
|
|
353
|
+
if conn.Connected then
|
|
354
|
+
conn._fn(...)
|
|
355
|
+
end
|
|
356
|
+
end, ...)
|
|
357
|
+
item = item._next
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
--[=[
|
|
362
|
+
@return ... any
|
|
363
|
+
@yields
|
|
364
|
+
|
|
365
|
+
Yields the current thread until the signal is fired, and returns the arguments fired from the signal.
|
|
366
|
+
Yielding the current thread is not always desirable. If the desire is to only capture the next event
|
|
367
|
+
fired, using `Once` might be a better solution.
|
|
368
|
+
```lua
|
|
369
|
+
task.spawn(function()
|
|
370
|
+
local msg, num = signal:Wait()
|
|
371
|
+
print(msg, num) --> "Hello", 32
|
|
372
|
+
end)
|
|
373
|
+
signal:Fire("Hello", 32)
|
|
374
|
+
```
|
|
375
|
+
]=]
|
|
376
|
+
function Signal:Wait()
|
|
377
|
+
local yieldedThreads = rawget(self, "_yieldedThreads")
|
|
378
|
+
if not yieldedThreads then
|
|
379
|
+
yieldedThreads = {}
|
|
380
|
+
rawset(self, "_yieldedThreads", yieldedThreads)
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
local thread = coroutine.running()
|
|
384
|
+
yieldedThreads[thread] = true
|
|
385
|
+
|
|
386
|
+
self:Once(function(...)
|
|
387
|
+
yieldedThreads[thread] = nil
|
|
388
|
+
task.spawn(thread, ...)
|
|
389
|
+
end)
|
|
390
|
+
|
|
391
|
+
return coroutine.yield()
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
--[=[
|
|
395
|
+
Cleans up the signal.
|
|
396
|
+
|
|
397
|
+
Technically, this is only necessary if the signal is created using
|
|
398
|
+
`Signal.Wrap`. Connections should be properly GC'd once the signal
|
|
399
|
+
is no longer referenced anywhere. However, it is still good practice
|
|
400
|
+
to include ways to strictly clean up resources. Calling `Destroy`
|
|
401
|
+
on a signal will also disconnect all connections immediately.
|
|
402
|
+
```lua
|
|
403
|
+
signal:Destroy()
|
|
404
|
+
```
|
|
405
|
+
]=]
|
|
406
|
+
function Signal:Destroy()
|
|
407
|
+
self:DisconnectAll()
|
|
408
|
+
|
|
409
|
+
local proxyHandler = rawget(self, "_proxyHandler")
|
|
410
|
+
if proxyHandler then
|
|
411
|
+
proxyHandler:Disconnect()
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
-- Make signal strict
|
|
416
|
+
setmetatable(Signal, {
|
|
417
|
+
__index = function(_tb, key)
|
|
418
|
+
error(("Attempt to get Signal::%s (not a valid member)"):format(tostring(key)), 2)
|
|
419
|
+
end,
|
|
420
|
+
__newindex = function(_tb, key, _value)
|
|
421
|
+
error(("Attempt to set Signal::%s (not a valid member)"):format(tostring(key)), 2)
|
|
422
|
+
end,
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
return table.freeze({
|
|
426
|
+
new = Signal.new,
|
|
427
|
+
Wrap = Signal.Wrap,
|
|
428
|
+
Is = Signal.Is,
|
|
429
|
+
})
|
package/out/Types.luau
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
local Goodsignal = require(script.Parent.GoodSignal)
|
|
2
|
+
|
|
3
|
+
local types = {}
|
|
4
|
+
|
|
5
|
+
export type HitboxProperties = {
|
|
6
|
+
Visualizer: boolean,
|
|
7
|
+
DetectionMode: ("Default" | "ConstantDetection" | "HitOnce" | "HitParts"),
|
|
8
|
+
AutoDestroy: boolean,
|
|
9
|
+
Key: string,
|
|
10
|
+
|
|
11
|
+
OverlapParams: OverlapParams,
|
|
12
|
+
|
|
13
|
+
Size: Vector3,
|
|
14
|
+
Shape: Enum.PartType,
|
|
15
|
+
CFrame: CFrame,
|
|
16
|
+
Offset: CFrame,
|
|
17
|
+
|
|
18
|
+
VelocityPredictionTime: number?,
|
|
19
|
+
VelocityPrediction: boolean?,
|
|
20
|
+
|
|
21
|
+
Touched: Goodsignal.Signal<BasePart, Humanoid?>,
|
|
22
|
+
TouchEnded: Goodsignal.Signal<BasePart, Humanoid?>,
|
|
23
|
+
} & any
|
|
24
|
+
|
|
25
|
+
export type Hitbox = {
|
|
26
|
+
-- properties
|
|
27
|
+
Visualizer: boolean,
|
|
28
|
+
VisualizerColor: Color3?,
|
|
29
|
+
VisualizerTransparency: number,
|
|
30
|
+
|
|
31
|
+
DetectionMode: ("Default" | "ConstantDetection" | "HitOnce" | "HitParts"),
|
|
32
|
+
AutoDestroy: boolean,
|
|
33
|
+
Key: string,
|
|
34
|
+
|
|
35
|
+
OverlapParams: OverlapParams,
|
|
36
|
+
|
|
37
|
+
Size: Vector3,
|
|
38
|
+
Shape: Enum.PartType,
|
|
39
|
+
CFrame: CFrame,
|
|
40
|
+
Offset: CFrame,
|
|
41
|
+
|
|
42
|
+
VelocityPredictionTime: number?,
|
|
43
|
+
VelocityPrediction: boolean?,
|
|
44
|
+
|
|
45
|
+
-- events
|
|
46
|
+
Touched: Goodsignal.Signal<BasePart, Humanoid?>,
|
|
47
|
+
TouchEnded: Goodsignal.Signal<BasePart, Humanoid?>,
|
|
48
|
+
|
|
49
|
+
-- methods
|
|
50
|
+
Start: (self: Hitbox) -> (),
|
|
51
|
+
Stop: (self: Hitbox) -> (),
|
|
52
|
+
Destroy: (self: Hitbox) -> (boolean),
|
|
53
|
+
|
|
54
|
+
-- dev
|
|
55
|
+
HitList: {Model}?,
|
|
56
|
+
TouchingParts: {BasePart}?,
|
|
57
|
+
Connection: RBXScriptConnection?,
|
|
58
|
+
Box: BoxHandleAdornment? | SphereHandleAdornment?,
|
|
59
|
+
} & any
|
|
60
|
+
|
|
61
|
+
return types
|
package/out/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/out/init.luau
ADDED
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
|
|
3
|
+
--[[
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
_____ __ _ __ ___ __
|
|
7
|
+
/ ___/__ _______/ /_ (_) |/ /___ ______/ /____ _____
|
|
8
|
+
\__ \/ / / / ___/ __ \/ / /|_/ / __ `/ ___/ __/ _ \/ ___/
|
|
9
|
+
___/ / /_/ (__ ) / / / / / / / /_/ (__ ) /_/ __/ /
|
|
10
|
+
/____/\__,_/____/_/ /_/_/_/ /_/\__,_/____/\__/\___/_/
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
____________________________________________________________________________________________________________________________________________________________________________
|
|
17
|
+
|
|
18
|
+
[ UPDATE LOG v1.1 :]
|
|
19
|
+
1. New property!!
|
|
20
|
+
Hitbox.Key = "insert anything you want here"
|
|
21
|
+
--- This property will be used for the new function | module:FindHitbox(key)
|
|
22
|
+
|
|
23
|
+
2. New function!!
|
|
24
|
+
Module:FindHitbox(Key)
|
|
25
|
+
--- Returns a hitbox using specified Key, nil otherwise
|
|
26
|
+
|
|
27
|
+
3. New detection mode! | "ConstantDetection"
|
|
28
|
+
Hitbox.DetectionMode = "ConstantDetection"
|
|
29
|
+
--- The same as the default detection mode but no hit pool / debounce
|
|
30
|
+
--- You're free to customize the debounce anyway you want
|
|
31
|
+
|
|
32
|
+
4, Made the scripts cleaner
|
|
33
|
+
____________________________________________________________________________________________________________________________________________________________________________
|
|
34
|
+
|
|
35
|
+
[ UPDATE LOG v1.2 :]
|
|
36
|
+
1. Made the code better
|
|
37
|
+
|
|
38
|
+
____________________________________________________________________________________________________________________________________________________________________________
|
|
39
|
+
|
|
40
|
+
[ UPDATE LOG v1.3 :]
|
|
41
|
+
1. New property
|
|
42
|
+
HitboxObject.AutoDestroy = true (Default)
|
|
43
|
+
--- With the value being false you can keep using Stop()
|
|
44
|
+
and Start() without the hitbox being destroyed.
|
|
45
|
+
|
|
46
|
+
2. New metamethod
|
|
47
|
+
HitboxObject:Destroy()
|
|
48
|
+
--- This destroys the hitbox. You only need to use this
|
|
49
|
+
When having AutoDestroy's value set to false.
|
|
50
|
+
|
|
51
|
+
3. Minor bug fixes
|
|
52
|
+
|
|
53
|
+
____________________________________________________________________________________________________________________________________________________________________________
|
|
54
|
+
|
|
55
|
+
[ UPDATE LOG v1.4 Experimental:]
|
|
56
|
+
1. New event
|
|
57
|
+
HitboxObject.TouchEnded:Connect(instance)
|
|
58
|
+
Description
|
|
59
|
+
--- The event fires once a part stops touching the hitbox
|
|
60
|
+
Arguments
|
|
61
|
+
--- Instance part: Returns the part that the hitbox stopped touching
|
|
62
|
+
|
|
63
|
+
____________________________________________________________________________________________________________________________________________________________________________
|
|
64
|
+
|
|
65
|
+
UPDATE LOG v1.5 Stable:]
|
|
66
|
+
1. Reverted touch ended, will add back after the bug is fixed
|
|
67
|
+
|
|
68
|
+
____________________________________________________________________________________________________________________________________________________________________________
|
|
69
|
+
|
|
70
|
+
UPDATE LOG v1.6 Stable:]
|
|
71
|
+
1. Touch ended is back! It has been fixed
|
|
72
|
+
2. HitboxObject.Key is now generated automatically
|
|
73
|
+
3. Minor changes
|
|
74
|
+
|
|
75
|
+
____________________________________________________________________________________________________________________________________________________________________________
|
|
76
|
+
|
|
77
|
+
UPDATE LOG v2.0 Experimental:]
|
|
78
|
+
1. Added VelocityPrediction and VelocityPredictionTime property
|
|
79
|
+
2. You can now set the color and transparency of a hitbox
|
|
80
|
+
2. Minor fixes
|
|
81
|
+
3. Code now uses type checking
|
|
82
|
+
____________________________________________________________________________________________________________________________________________________________________________
|
|
83
|
+
|
|
84
|
+
Example code:
|
|
85
|
+
local module = require(game.ServerStorage.MuchachoHitbox)
|
|
86
|
+
|
|
87
|
+
local hitbox = module.CreateHitbox()
|
|
88
|
+
hitbox.Size = Vector3.new(10,10,10)
|
|
89
|
+
hitbox.CFrame = workspace.Part
|
|
90
|
+
|
|
91
|
+
-- IF YOU WANT TO ADD VELOCITY PREDICTION
|
|
92
|
+
hitbox.VelocityPrediction = true
|
|
93
|
+
hitbox.VelocityPredictionTime = .2
|
|
94
|
+
|
|
95
|
+
hitbox.Touched:Connect(function(hit, hum)
|
|
96
|
+
print(hit)
|
|
97
|
+
hum:TakeDamage(10)
|
|
98
|
+
end)
|
|
99
|
+
|
|
100
|
+
hitbox:Start()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
Alright thats all for the example code, its a pretty simple module, you could make a module similar to this yourself.
|
|
104
|
+
And maybe even make it better.
|
|
105
|
+
|
|
106
|
+
If you encounter any bugs, please tell me in the comment section, or you could DM me on discord
|
|
107
|
+
sushimaster#7840
|
|
108
|
+
|
|
109
|
+
❤ SushiMaster
|
|
110
|
+
____________________________________________________________________________________________________________________________________________________________________________
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
[MuchachoHitbox Documentation]
|
|
114
|
+
|
|
115
|
+
* local Module = require(MuchachoHitbox)
|
|
116
|
+
--- Require the module
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
[ FUNCTIONS ]
|
|
120
|
+
|
|
121
|
+
* Module.CreateHitbox()
|
|
122
|
+
Description
|
|
123
|
+
--- Creates a hitbox
|
|
124
|
+
|
|
125
|
+
* Module:FindHitbox(Key)
|
|
126
|
+
Description
|
|
127
|
+
--- Returns a hitbox with specified Key
|
|
128
|
+
|
|
129
|
+
* HitboxObject:Start()
|
|
130
|
+
Description
|
|
131
|
+
--- Starts the hitbox.
|
|
132
|
+
|
|
133
|
+
* HitboxObject:Stop()
|
|
134
|
+
Description
|
|
135
|
+
--- Stops the hitbox and resets the debounce.
|
|
136
|
+
|
|
137
|
+
* HitboxObject:Destroy()
|
|
138
|
+
Description
|
|
139
|
+
--- Destroys the hitbox. Use this when you have
|
|
140
|
+
HitboxObject.AutoDestroy set to false
|
|
141
|
+
|
|
142
|
+
[ EVENTS ]
|
|
143
|
+
|
|
144
|
+
* HitboxObject.Touched:Connect(hit, humanoid)
|
|
145
|
+
Description
|
|
146
|
+
--- If the hitbox touches a humanoid, it'll return information on them
|
|
147
|
+
--- The hitbox can detect parts depending on the detection mode
|
|
148
|
+
Arguments
|
|
149
|
+
--- Instance part: Returns the part that the hitbox hit first
|
|
150
|
+
--- Instance humanoid: Returns the Humanoid object
|
|
151
|
+
|
|
152
|
+
* HitboxObject.TouchEnded:Connect(instance)
|
|
153
|
+
Description
|
|
154
|
+
--- The event fires once a part stops touching the hitbox
|
|
155
|
+
Arguments
|
|
156
|
+
--- Instance part: Returns the part that the hitbox stopped touching
|
|
157
|
+
|
|
158
|
+
[ PROPERTIES ]
|
|
159
|
+
|
|
160
|
+
* HitboxObject.OverlapParams: OverlapParams
|
|
161
|
+
Description
|
|
162
|
+
--- Takes in a OverlapParams object
|
|
163
|
+
|
|
164
|
+
* HitboxObject.Visualizer: boolean
|
|
165
|
+
Description
|
|
166
|
+
--- Turns on or off the visualizer part
|
|
167
|
+
|
|
168
|
+
* HitboxObject.CFrame: CFrame / Instance
|
|
169
|
+
Description
|
|
170
|
+
--- Sets the hitbox CFrame to the CFrame
|
|
171
|
+
--- If its an instance, then the hitbox would follow the instance
|
|
172
|
+
|
|
173
|
+
* HitboxObject.Shape: Enum.PartType.Block / Enum.PartType.Ball
|
|
174
|
+
Description
|
|
175
|
+
--- Defaults to block
|
|
176
|
+
--- Sets the hitbox shape to the property
|
|
177
|
+
|
|
178
|
+
* HitboxObject.Size: Vector3 / number
|
|
179
|
+
Description
|
|
180
|
+
--- Sets the size of the hitbox
|
|
181
|
+
--- It uses Vector3 if the shape is block
|
|
182
|
+
--- It uses number if the shape is ball
|
|
183
|
+
|
|
184
|
+
* HitboxObject.Offset: CFrame
|
|
185
|
+
Description
|
|
186
|
+
--- Hitbox offset
|
|
187
|
+
|
|
188
|
+
* HitboxObject.DetectionMode: string | "Default" , "HitOnce" , "HitParts" , "ConstantDetection"
|
|
189
|
+
Description
|
|
190
|
+
--- Default value set to "Default"
|
|
191
|
+
--- Changes on how the detection works
|
|
192
|
+
|
|
193
|
+
* HitboxObject.Key: String
|
|
194
|
+
Description
|
|
195
|
+
--- The key property for the find hitbox function
|
|
196
|
+
--- MuchachoHitbox automatically generates a randomized key for you but you can change it. The module will save the hitbox, and can be found using | Module:FindHitbox(Key)
|
|
197
|
+
|
|
198
|
+
* HitboxObject.AutoDestroy: boolean
|
|
199
|
+
Description
|
|
200
|
+
--- Default value is set to true
|
|
201
|
+
--- When set to true, :Stop() atomatically destroys the hitbox.
|
|
202
|
+
--- Does not destroy the hitbox when set to false. You'll
|
|
203
|
+
have to use :Destroy() to delete the hitbox.
|
|
204
|
+
|
|
205
|
+
* HitboxObject.VelocityPrediction: boolean
|
|
206
|
+
Description
|
|
207
|
+
--- Default value is set to false
|
|
208
|
+
--- When set to true, hitbox automatically predicts the velocity of the CFrame property if it is an instance. By "VelocityPredictionTime" amount of time
|
|
209
|
+
|
|
210
|
+
* HitboxObject.VelocityPredictionTime: number
|
|
211
|
+
Description
|
|
212
|
+
--- Default value is set to 0.1
|
|
213
|
+
--- When "VelocityPrediction" is set to true, this property determines how far in the future the hitbox will check for parts.
|
|
214
|
+
|
|
215
|
+
* HitboxObject.VisualizerColor: Color3
|
|
216
|
+
Description
|
|
217
|
+
--- Sets the color of the visualizer part
|
|
218
|
+
|
|
219
|
+
* HitboxObject.VisualizerTransparency: number
|
|
220
|
+
Description
|
|
221
|
+
--- Sets the transparency of the visualizer part
|
|
222
|
+
|
|
223
|
+
[ DETECTION MODES ]
|
|
224
|
+
|
|
225
|
+
* Default
|
|
226
|
+
Description
|
|
227
|
+
--- Checks if a humanoid exists when this hitbox touches a part. The hitbox will not return humanoids it has already hit for the duration
|
|
228
|
+
--- the hitbox has been active.
|
|
229
|
+
|
|
230
|
+
* HitParts
|
|
231
|
+
Description
|
|
232
|
+
--- OnHit will return every hit part, regardless if it's ascendant has a humanoid or not.
|
|
233
|
+
--- OnHit will no longer return a humanoid so you will have to check it. The hitbox will not return parts it has already hit for the
|
|
234
|
+
--- duration the hitbox has been active.
|
|
235
|
+
|
|
236
|
+
* HitOnce
|
|
237
|
+
Description
|
|
238
|
+
--- Hitbox will stop as soon as it detects a humanoid
|
|
239
|
+
|
|
240
|
+
* ConstantDetection
|
|
241
|
+
Description
|
|
242
|
+
--- The default detection mode but no hitlist / debounce
|
|
243
|
+
|
|
244
|
+
____________________________________________________________________________________________________________________________________________________________________________
|
|
245
|
+
|
|
246
|
+
]]
|
|
247
|
+
local rs = game:GetService("RunService")
|
|
248
|
+
local hs = game:GetService("HttpService")
|
|
249
|
+
|
|
250
|
+
local GoodSignal = require(script.GoodSignal)
|
|
251
|
+
local DictDiff = require(script.DictDiff)
|
|
252
|
+
local Types = require(script.Types)
|
|
253
|
+
|
|
254
|
+
local muchacho_hitbox = {}
|
|
255
|
+
muchacho_hitbox.__index = muchacho_hitbox
|
|
256
|
+
|
|
257
|
+
local adornment_form = {
|
|
258
|
+
["Proportion"] = {
|
|
259
|
+
[Enum.PartType.Ball] = "Radius",
|
|
260
|
+
[Enum.PartType.Block] = "Size",
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
["Shape"] = {
|
|
264
|
+
[Enum.PartType.Ball] = "SphereHandleAdornment",
|
|
265
|
+
[Enum.PartType.Block] = "BoxHandleAdornment",
|
|
266
|
+
},
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
local get_CFrame = {
|
|
270
|
+
["Instance"] = function(point)
|
|
271
|
+
return point.CFrame
|
|
272
|
+
end,
|
|
273
|
+
|
|
274
|
+
["CFrame"] = function(point)
|
|
275
|
+
return point
|
|
276
|
+
end,
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
local hitboxes = {}
|
|
281
|
+
|
|
282
|
+
-- public functions
|
|
283
|
+
function muchacho_hitbox.CreateHitbox()
|
|
284
|
+
local self = setmetatable({}, muchacho_hitbox) :: Types.Hitbox
|
|
285
|
+
self.DetectionMode = "Default"
|
|
286
|
+
self.AutoDestroy = true
|
|
287
|
+
|
|
288
|
+
self.Visualizer = true
|
|
289
|
+
self.VisualizerColor = Color3.fromRGB(255,0,0)
|
|
290
|
+
self.VisualizerTransparency = .8
|
|
291
|
+
|
|
292
|
+
self.VelocityPrediction = false
|
|
293
|
+
self.VelocityPredictionTime = 0.1
|
|
294
|
+
|
|
295
|
+
self.OverlapParams = OverlapParams.new()
|
|
296
|
+
|
|
297
|
+
self.Size = Vector3.new(0,0,0)
|
|
298
|
+
self.Shape = Enum.PartType.Block
|
|
299
|
+
self.CFrame = CFrame.new(0,0,0)
|
|
300
|
+
self.Offset = CFrame.new(0,0,0)
|
|
301
|
+
|
|
302
|
+
self.Key = hs:GenerateGUID(false)
|
|
303
|
+
|
|
304
|
+
self.HitList = {}
|
|
305
|
+
self.TouchingParts = {}
|
|
306
|
+
|
|
307
|
+
self.Touched = GoodSignal.new()
|
|
308
|
+
self.TouchEnded = GoodSignal.new()
|
|
309
|
+
|
|
310
|
+
return self
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
function muchacho_hitbox:FindHitbox(key) -- deprecated
|
|
314
|
+
if hitboxes[key] then
|
|
315
|
+
return hitboxes[key]
|
|
316
|
+
else
|
|
317
|
+
return nil
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
-- public methods
|
|
322
|
+
function muchacho_hitbox.Start(self: Types.Hitbox)
|
|
323
|
+
if hitboxes[self.Key] then
|
|
324
|
+
error("A hitbox with this Key has already been started. Change the key if you want to start this hitbox.")
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
hitboxes[self.Key] = self
|
|
328
|
+
|
|
329
|
+
-- looping the hitbox
|
|
330
|
+
task.spawn(function()
|
|
331
|
+
self._Connection = rs.Heartbeat:Connect(function()
|
|
332
|
+
self:_visualize()
|
|
333
|
+
self:_cast()
|
|
334
|
+
end)
|
|
335
|
+
end)
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
function muchacho_hitbox.Stop(self: Types.Hitbox)
|
|
339
|
+
local hitbox = muchacho_hitbox:FindHitbox(self.Key)
|
|
340
|
+
|
|
341
|
+
if not hitbox then
|
|
342
|
+
error("Hitbox has already been stopped")
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
-- clear hitbox
|
|
346
|
+
self:_clear()
|
|
347
|
+
|
|
348
|
+
if not self.AutoDestroy then return end
|
|
349
|
+
|
|
350
|
+
-- terminate hitbox
|
|
351
|
+
self.Touched:DisconnectAll()
|
|
352
|
+
self.TouchEnded:DisconnectAll()
|
|
353
|
+
--setmetatable(self, nil)
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
function muchacho_hitbox:Destroy()
|
|
357
|
+
local hitbox: Types.Hitbox = muchacho_hitbox:FindHitbox(self.Key)
|
|
358
|
+
|
|
359
|
+
if not hitbox then
|
|
360
|
+
error("Hitbox has already been destroyed")
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
-- clear hitbox
|
|
364
|
+
self:_clear()
|
|
365
|
+
|
|
366
|
+
-- terminate hitbox
|
|
367
|
+
self.Touched:DisconnectAll()
|
|
368
|
+
self.TouchEnded:DisconnectAll()
|
|
369
|
+
--setmetatable(self, nil)
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
-- private methods
|
|
374
|
+
function muchacho_hitbox._CastSpatialQuery(self: Types.Hitbox) : {BasePart}?
|
|
375
|
+
local point_type: CFrame | string = typeof(self.CFrame)
|
|
376
|
+
local point_cframe: CFrame = self:_PredictVelocity() or get_CFrame[point_type](self.CFrame)
|
|
377
|
+
|
|
378
|
+
local parts
|
|
379
|
+
local hitboxCFrame: CFrame = point_cframe * self.Offset
|
|
380
|
+
|
|
381
|
+
if self.Shape == Enum.PartType.Block then
|
|
382
|
+
parts = workspace:GetPartBoundsInBox(hitboxCFrame, self.Size, self.OverlapParams)
|
|
383
|
+
elseif self.Shape == Enum.PartType.Ball then
|
|
384
|
+
parts = workspace:GetPartBoundsInRadius(hitboxCFrame.Position, self.Size, self.OverlapParams)
|
|
385
|
+
else
|
|
386
|
+
error("Part type: " .. self.Shape .. " isn't compatible with muchachoHitbox")
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
return parts
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
function muchacho_hitbox._cast(self: Types.Hitbox, part: BasePart)
|
|
393
|
+
local mode = self.DetectionMode
|
|
394
|
+
local parts = self:_CastSpatialQuery()
|
|
395
|
+
|
|
396
|
+
self:_FindTouchEnded(parts)
|
|
397
|
+
|
|
398
|
+
for _, hit in pairs(parts) do
|
|
399
|
+
local character: Model = hit:FindFirstAncestorOfClass("Model") or hit.Parent
|
|
400
|
+
local humanoid: Humanoid? = character:FindFirstChildOfClass("Humanoid")
|
|
401
|
+
|
|
402
|
+
-- detection mode
|
|
403
|
+
if mode == "Default" then
|
|
404
|
+
if humanoid and not table.find(self.HitList, humanoid) then
|
|
405
|
+
table.insert(self.HitList, humanoid)
|
|
406
|
+
|
|
407
|
+
self:_InsertTouchingParts(hit)
|
|
408
|
+
|
|
409
|
+
self.Touched:Fire(hit, humanoid)
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
elseif mode == "ConstantDetection" then
|
|
413
|
+
|
|
414
|
+
if humanoid then
|
|
415
|
+
self:_InsertTouchingParts(hit)
|
|
416
|
+
|
|
417
|
+
self.Touched:Fire(hit, humanoid)
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
elseif mode == "HitOnce" then
|
|
421
|
+
|
|
422
|
+
if humanoid then
|
|
423
|
+
self:_InsertTouchingParts(hit)
|
|
424
|
+
|
|
425
|
+
self.Touched:Fire(hit, humanoid)
|
|
426
|
+
self.TouchEnded:Fire(hit)
|
|
427
|
+
|
|
428
|
+
self:Destroy()
|
|
429
|
+
break
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
elseif mode == "HitParts" then
|
|
433
|
+
self:_InsertTouchingParts(hit)
|
|
434
|
+
|
|
435
|
+
self.Touched:Fire(hit, nil)
|
|
436
|
+
|
|
437
|
+
end
|
|
438
|
+
end
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
function muchacho_hitbox._visualize(self: Types.Hitbox)
|
|
442
|
+
if not self.Visualizer then return end
|
|
443
|
+
|
|
444
|
+
local predictedCFrame = self:_PredictVelocity()
|
|
445
|
+
|
|
446
|
+
local point_type: string = typeof(self.CFrame)
|
|
447
|
+
local point_cframe: CFrame = predictedCFrame or get_CFrame[point_type](self.CFrame)
|
|
448
|
+
|
|
449
|
+
local proportion = adornment_form.Proportion[self.Shape]
|
|
450
|
+
|
|
451
|
+
if not self._Box then
|
|
452
|
+
local newBox = Instance.new(adornment_form.Shape[self.Shape])
|
|
453
|
+
newBox.Name = "Visualizer"
|
|
454
|
+
newBox.Adornee = workspace.Terrain
|
|
455
|
+
newBox[proportion] = self.Size
|
|
456
|
+
newBox.CFrame = point_cframe * self.Offset
|
|
457
|
+
newBox.Color3 = self.VisualizerColor
|
|
458
|
+
newBox.Transparency = self.VisualizerTransparency
|
|
459
|
+
newBox.Parent = workspace.Terrain
|
|
460
|
+
self._Box = newBox
|
|
461
|
+
else
|
|
462
|
+
self._Box.CFrame = point_cframe * self.Offset
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
function muchacho_hitbox._PredictVelocity(self: Types.Hitbox): CFrame | nil
|
|
467
|
+
if self.VelocityPrediction then
|
|
468
|
+
local PredictionTime: number = self.VelocityPredictionTime
|
|
469
|
+
local part: BasePart = self.CFrame
|
|
470
|
+
local constant: number = 1/PredictionTime
|
|
471
|
+
|
|
472
|
+
if PredictionTime > 0 and typeof(part) == "Instance" then
|
|
473
|
+
--local velocityVector = part.CFrame:VectorToObjectSpace(part.AssemblyLinearVelocity) / constant
|
|
474
|
+
--local predictedCFrame = part.CFrame * CFrame.new(velocityVector)
|
|
475
|
+
local Velocity = part.AssemblyLinearVelocity --// Normally this would be their ping
|
|
476
|
+
local PredictedPosition = part.Position + Velocity * PredictionTime
|
|
477
|
+
local PredictedCFrame = CFrame.new(PredictedPosition) * (part.CFrame - part.Position)
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
return PredictedCFrame
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
return nil
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
function muchacho_hitbox:_clear()
|
|
488
|
+
self.HitList = {}
|
|
489
|
+
|
|
490
|
+
if self._Connection then
|
|
491
|
+
self._Connection:Disconnect()
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
if self.Key then
|
|
495
|
+
hitboxes[self.Key] = nil
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
if self._Box then
|
|
499
|
+
self._Box:Destroy()
|
|
500
|
+
self.Box = nil
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
function muchacho_hitbox._InsertTouchingParts(self: Types.Hitbox, part)
|
|
505
|
+
if table.find(self.TouchingParts, part) then return end
|
|
506
|
+
|
|
507
|
+
table.insert(self.TouchingParts, part)
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
function muchacho_hitbox._FindTouchEnded(self: Types.Hitbox, parts: {BasePart}?)
|
|
511
|
+
if #self.TouchingParts == 0 then return end
|
|
512
|
+
|
|
513
|
+
local mode = self.DetectionMode
|
|
514
|
+
local differences = DictDiff.difference(self.TouchingParts, parts)
|
|
515
|
+
|
|
516
|
+
if differences then
|
|
517
|
+
for _, diff in ipairs(differences) do
|
|
518
|
+
self.TouchEnded:Fire(diff)
|
|
519
|
+
table.remove(self.TouchingParts, table.find(self.TouchingParts, diff))
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
return muchacho_hitbox
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@boyangsicwastaken/muchachohitbox",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "typescript port for muchachohitbox module",
|
|
5
|
+
"main": "out/init.luau",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "rbxtsc",
|
|
8
|
+
"watch": "rbxtsc -w"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [],
|
|
11
|
+
"author": "",
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"types": "index.d.ts",
|
|
14
|
+
"files": [
|
|
15
|
+
"out",
|
|
16
|
+
"index.d.ts",
|
|
17
|
+
"!**/*.tsbuildinfo"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@rbxts/compiler-types": "^3.0.0-types.0",
|
|
24
|
+
"@rbxts/types": "^1.0.905",
|
|
25
|
+
"prettier": "^3.8.1",
|
|
26
|
+
"roblox-ts": "^3.0.0",
|
|
27
|
+
"typescript": "^5.9.3"
|
|
28
|
+
}
|
|
29
|
+
}
|