pulse-rb 1.2.24
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 +225 -0
- package/adapters/linoria.lua +233 -0
- package/adapters/windui.llms.txt +366 -0
- package/adapters/windui.lua +505 -0
- package/bin/rb.js +2 -0
- package/dist/index.js +3285 -0
- package/package.json +59 -0
- package/pulse/dev/debuggui.lua +1206 -0
- package/pulse/dev/devconfig.lua +81 -0
- package/pulse/dev/ui/9_DevPanel.lua +384 -0
- package/pulse/helpers/aim.lua +193 -0
- package/pulse/helpers/cache.lua +68 -0
- package/pulse/helpers/cleaner.lua +110 -0
- package/pulse/helpers/conn.lua +33 -0
- package/pulse/helpers/cooldown.lua +47 -0
- package/pulse/helpers/draw.lua +122 -0
- package/pulse/helpers/hitbox.lua +63 -0
- package/pulse/helpers/input.lua +24 -0
- package/pulse/helpers/log.lua +228 -0
- package/pulse/helpers/loop.lua +58 -0
- package/pulse/helpers/memory.lua +48 -0
- package/pulse/helpers/narrate.lua +160 -0
- package/pulse/helpers/notify.lua +51 -0
- package/pulse/helpers/perf.lua +48 -0
- package/pulse/helpers/remote.lua +128 -0
- package/pulse/helpers/restore.lua +59 -0
- package/pulse/helpers/store.lua +39 -0
- package/pulse/helpers/team.lua +83 -0
- package/pulse/helpers/testmode.lua +80 -0
- package/pulse/helpers/trace.lua +111 -0
- package/pulse/helpers/track.lua +85 -0
- package/pulse/helpers/vec.lua +52 -0
- package/pulse/helpers/world.lua +51 -0
- package/pulse/runtime.lua +343 -0
- package/pulse/ui/linoria_settings.lua +55 -0
- package/pulse/ui/windui_settings.lua +87 -0
- package/templates/AGENTS.md +177 -0
- package/templates/CLAUDE.md +424 -0
- package/templates/deploy_config.example +17 -0
- package/templates/example_esp.rblua +69 -0
- package/templates/example_fov.rblua +20 -0
- package/templates/example_speed.rblua +25 -0
- package/templates/gitignore +4 -0
- package/templates/globals.lua +66 -0
- package/templates/layout.rblua +28 -0
- package/templates/module.lua +7 -0
- package/templates/module.rblua +32 -0
- package/templates/page_home.rblua +9 -0
- package/templates/remote.lua +6 -0
- package/templates/remotes.lua +14 -0
- package/vscode/language-configuration.json +35 -0
- package/vscode/package.json +35 -0
- package/vscode/src/extension.js +397 -0
- package/vscode/syntaxes/rblua.tmLanguage.json +126 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
-- ── Pulse.Cache ──────────────────────────────────────────────────────────────
|
|
2
|
+
-- Timed key-value cache and fetch-wrapper.
|
|
3
|
+
-- Usage:
|
|
4
|
+
-- local c = Pulse.Cache.new(2) -- 2-second TTL per key
|
|
5
|
+
-- c:set("titans", titanList)
|
|
6
|
+
-- local list = c:get("titans") -- nil after 2 s
|
|
7
|
+
--
|
|
8
|
+
-- local getTitans = Pulse.Cache.wrap(2, function()
|
|
9
|
+
-- return workspace.Titans.Alive:GetChildren()
|
|
10
|
+
-- end)
|
|
11
|
+
|
|
12
|
+
Pulse.Cache = (function()
|
|
13
|
+
local C = {}
|
|
14
|
+
|
|
15
|
+
-- Create a new timed key-value cache.
|
|
16
|
+
-- ttl: seconds until entries expire; pass nil for entries that never expire.
|
|
17
|
+
function C.new(ttl)
|
|
18
|
+
local self = { _d = {}, _t = {}, _ttl = ttl }
|
|
19
|
+
|
|
20
|
+
function self:get(key)
|
|
21
|
+
if self._ttl then
|
|
22
|
+
local ts = self._t[key]
|
|
23
|
+
if ts and (tick() - ts) > self._ttl then
|
|
24
|
+
self._d[key] = nil; self._t[key] = nil; return nil
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
return self._d[key]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
function self:set(key, value)
|
|
31
|
+
self._d[key] = value
|
|
32
|
+
if self._ttl then self._t[key] = tick() end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
function self:has(key) return self:get(key) ~= nil end
|
|
36
|
+
function self:del(key) self._d[key] = nil; self._t[key] = nil end
|
|
37
|
+
function self:clear() self._d = {}; self._t = {} end
|
|
38
|
+
|
|
39
|
+
function self:keys()
|
|
40
|
+
local result = {}
|
|
41
|
+
for k in pairs(self._d) do
|
|
42
|
+
if self:has(k) then result[#result + 1] = k end
|
|
43
|
+
end
|
|
44
|
+
return result
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
return self
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
-- Wrap a fetcher so its return value is cached for `ttl` seconds.
|
|
51
|
+
-- Passes through all arguments to fetcher on cache miss.
|
|
52
|
+
function C.wrap(ttl, fetcher)
|
|
53
|
+
local _v, _t = nil, 0
|
|
54
|
+
return function(...)
|
|
55
|
+
local now = tick()
|
|
56
|
+
if _v ~= nil and (now - _t) < ttl then return _v end
|
|
57
|
+
local ok, result = pcall(fetcher, ...)
|
|
58
|
+
if ok and result ~= nil then
|
|
59
|
+
_v = result; _t = now
|
|
60
|
+
else
|
|
61
|
+
_v = nil; _t = 0
|
|
62
|
+
end
|
|
63
|
+
return _v or {}
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
return C
|
|
68
|
+
end)()
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
-- ── Pulse.Cleaner ─────────────────────────────────────────────────────────────
|
|
2
|
+
-- Scheduled object-removal scheduler with optional filtering and a named registry.
|
|
3
|
+
-- Pulse.Loop is referenced lazily (inside start()), so load order doesn't matter.
|
|
4
|
+
--
|
|
5
|
+
-- Usage — one-shot cleaner:
|
|
6
|
+
-- local c = Pulse.Cleaner.new({
|
|
7
|
+
-- collect = function() return workspace.Map.Decor:GetChildren() end,
|
|
8
|
+
-- interval = 1.0,
|
|
9
|
+
-- })
|
|
10
|
+
-- c:start() c:stop() c:running()
|
|
11
|
+
--
|
|
12
|
+
-- Usage — named registry (cross-component access):
|
|
13
|
+
-- Pulse.Cleaner.register("mapDecor", { collect = ..., interval = 1 })
|
|
14
|
+
-- Pulse.Cleaner.get("mapDecor"):start()
|
|
15
|
+
-- Pulse.Cleaner.stopAll()
|
|
16
|
+
|
|
17
|
+
Pulse.Cleaner = (function()
|
|
18
|
+
local C = {}
|
|
19
|
+
local _registry = {}
|
|
20
|
+
|
|
21
|
+
-- Create a new cleaner.
|
|
22
|
+
-- opts:
|
|
23
|
+
-- collect — function() → {Instance,...} required
|
|
24
|
+
-- filter — function(inst) → bool optional; return false to skip removal
|
|
25
|
+
-- interval — seconds between sweeps default 1.0
|
|
26
|
+
-- onSweep — function(removedCount) optional callback after each sweep
|
|
27
|
+
function C.new(opts)
|
|
28
|
+
opts = opts or {}
|
|
29
|
+
local self = {
|
|
30
|
+
totalRemoved = 0,
|
|
31
|
+
_interval = opts.interval or 1.0,
|
|
32
|
+
_collect = opts.collect or function() return {} end,
|
|
33
|
+
_filter = opts.filter,
|
|
34
|
+
_onSweep = opts.onSweep,
|
|
35
|
+
_loop = nil,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function self:_sweep()
|
|
39
|
+
local ok, list = pcall(self._collect)
|
|
40
|
+
if not ok or type(list) ~= "table" or #list == 0 then return end
|
|
41
|
+
local removed = 0
|
|
42
|
+
for _, obj in ipairs(list) do
|
|
43
|
+
local skip = false
|
|
44
|
+
if self._filter then
|
|
45
|
+
local fok, keep = pcall(self._filter, obj)
|
|
46
|
+
skip = fok and not keep
|
|
47
|
+
end
|
|
48
|
+
if not skip then
|
|
49
|
+
if pcall(function() obj:Destroy() end) then
|
|
50
|
+
removed = removed + 1
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
self.totalRemoved = self.totalRemoved + removed
|
|
55
|
+
if removed > 0 and self._onSweep then
|
|
56
|
+
pcall(self._onSweep, removed)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
function self:start()
|
|
61
|
+
self:stop()
|
|
62
|
+
self._loop = Pulse.Loop.new(self._interval, function()
|
|
63
|
+
self:_sweep()
|
|
64
|
+
end)
|
|
65
|
+
self._loop:start()
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
function self:stop()
|
|
69
|
+
if self._loop then
|
|
70
|
+
self._loop:stop()
|
|
71
|
+
self._loop = nil
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
function self:running()
|
|
76
|
+
return self._loop ~= nil and self._loop:running()
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
function self:setInterval(n)
|
|
80
|
+
self._interval = n
|
|
81
|
+
if self._loop then self._loop:setInterval(n) end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
function self:reset()
|
|
85
|
+
self.totalRemoved = 0
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
return self
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
-- Named registry — register once, access from any component.
|
|
92
|
+
function C.register(name, opts)
|
|
93
|
+
local c = C.new(opts)
|
|
94
|
+
_registry[name] = c
|
|
95
|
+
return c
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
function C.get(name) return _registry[name] end
|
|
99
|
+
function C.startAll() for _, c in pairs(_registry) do c:start() end end
|
|
100
|
+
function C.stopAll() for _, c in pairs(_registry) do c:stop() end end
|
|
101
|
+
|
|
102
|
+
function C.registeredNames()
|
|
103
|
+
local names = {}
|
|
104
|
+
for k in pairs(_registry) do names[#names + 1] = k end
|
|
105
|
+
table.sort(names)
|
|
106
|
+
return names
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
return C
|
|
110
|
+
end)()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
-- ── Pulse.Conn ────────────────────────────────────────────────────────────────
|
|
2
|
+
-- Named connection manager — one live connection per name, auto-disconnects on rebind.
|
|
3
|
+
-- Replaces the manual func.BindConnection / func.Disconnect pattern.
|
|
4
|
+
-- Usage:
|
|
5
|
+
-- Pulse.Conn.bind("ESP", RunService.Heartbeat:Connect(fn))
|
|
6
|
+
-- Pulse.Conn.disconnect("ESP")
|
|
7
|
+
-- Pulse.Conn.disconnectAll()
|
|
8
|
+
|
|
9
|
+
Pulse.Conn = (function()
|
|
10
|
+
local C = {}
|
|
11
|
+
local _named = {}
|
|
12
|
+
|
|
13
|
+
function C.bind(name, connection)
|
|
14
|
+
if _named[name] then pcall(function() _named[name]:Disconnect() end) end
|
|
15
|
+
_named[name] = connection
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
function C.disconnect(name)
|
|
19
|
+
if _named[name] then
|
|
20
|
+
pcall(function() _named[name]:Disconnect() end)
|
|
21
|
+
_named[name] = nil
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
function C.disconnectAll()
|
|
26
|
+
for _, conn in pairs(_named) do
|
|
27
|
+
pcall(function() conn:Disconnect() end)
|
|
28
|
+
end
|
|
29
|
+
_named = {}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
return C
|
|
33
|
+
end)()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
-- ── Pulse.Cooldown ───────────────────────────────────────────────────────────
|
|
2
|
+
-- Per-entity (or global) cooldown tracking.
|
|
3
|
+
-- Replaces manual `lastFireTimes = {}` tables scattered across components.
|
|
4
|
+
-- Usage:
|
|
5
|
+
-- local cd = Pulse.Cooldown.new(1.0) -- single shared cooldown
|
|
6
|
+
-- if cd:ready() then cd:use(); fireRemote() end
|
|
7
|
+
--
|
|
8
|
+
-- local pcd = Pulse.Cooldown.perEntity(0.5) -- one timer per entity
|
|
9
|
+
-- if pcd:ready(player) then pcd:use(player); applyEffect(player) end
|
|
10
|
+
-- pcd:purge(isDead) -- drop entries for dead entities
|
|
11
|
+
|
|
12
|
+
Pulse.Cooldown = (function()
|
|
13
|
+
local C = {}
|
|
14
|
+
|
|
15
|
+
-- A single shared cooldown (one timestamp).
|
|
16
|
+
function C.new(duration)
|
|
17
|
+
local self = { _last = 0, _dur = duration }
|
|
18
|
+
function self:ready() return (tick() - self._last) >= self._dur end
|
|
19
|
+
function self:use() self._last = tick() end
|
|
20
|
+
function self:reset() self._last = 0 end
|
|
21
|
+
function self:set(d) self._dur = d end
|
|
22
|
+
return self
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
-- One cooldown timer per entity (keyed by any value).
|
|
26
|
+
function C.perEntity(duration)
|
|
27
|
+
local self = { _times = {}, _dur = duration }
|
|
28
|
+
function self:ready(key)
|
|
29
|
+
local t = self._times[key]
|
|
30
|
+
return not t or (tick() - t) >= self._dur
|
|
31
|
+
end
|
|
32
|
+
function self:use(key) self._times[key] = tick() end
|
|
33
|
+
function self:reset(key)
|
|
34
|
+
if key then self._times[key] = nil else self._times = {} end
|
|
35
|
+
end
|
|
36
|
+
function self:set(d) self._dur = d end
|
|
37
|
+
-- Remove entries where isDeadFn(entity) returns true.
|
|
38
|
+
function self:purge(isDeadFn)
|
|
39
|
+
for key in pairs(self._times) do
|
|
40
|
+
if isDeadFn(key) then self._times[key] = nil end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
return self
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
return C
|
|
47
|
+
end)()
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
-- ── Pulse.Draw ───────────────────────────────────────────────────────────────
|
|
2
|
+
-- Highlight / Drawing / SelectionBox creation helpers.
|
|
3
|
+
-- Usage:
|
|
4
|
+
-- local hl = Pulse.Draw.highlight(part, "HitboxESP", { fill = Color3.new(1,0,0), fillAlpha = 0.5 })
|
|
5
|
+
-- Pulse.Draw.removeHighlight(part, "HitboxESP")
|
|
6
|
+
-- local ring = Pulse.Draw.circle({ radius = 300, color = Color3.new(1,1,1) })
|
|
7
|
+
-- Pulse.Draw.remove(ring)
|
|
8
|
+
|
|
9
|
+
Pulse.Draw = (function()
|
|
10
|
+
local D = {}
|
|
11
|
+
|
|
12
|
+
-- Create or replace a named Highlight on an instance.
|
|
13
|
+
-- opts: fill, outline, fillAlpha (0–1), outlineAlpha (0–1), adornee, alwaysOnTop
|
|
14
|
+
function D.highlight(target, name, opts)
|
|
15
|
+
if not target then return nil end
|
|
16
|
+
opts = opts or {}
|
|
17
|
+
local old = target:FindFirstChild(name)
|
|
18
|
+
if old then old:Destroy() end
|
|
19
|
+
local h = Instance.new("Highlight")
|
|
20
|
+
h.Name = name
|
|
21
|
+
h.FillColor = opts.fill or Color3.fromRGB(255, 0, 0)
|
|
22
|
+
h.OutlineColor = opts.outline or Color3.fromRGB(255, 255, 255)
|
|
23
|
+
h.FillTransparency = opts.fillAlpha ~= nil and opts.fillAlpha or 0.5
|
|
24
|
+
h.OutlineTransparency = opts.outlineAlpha ~= nil and opts.outlineAlpha or 0
|
|
25
|
+
h.Adornee = opts.adornee or target
|
|
26
|
+
if opts.alwaysOnTop then
|
|
27
|
+
h.DepthMode = Enum.HighlightDepthMode.AlwaysOnTop
|
|
28
|
+
end
|
|
29
|
+
h.Parent = target
|
|
30
|
+
return h
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
-- Destroy a named Highlight on target (no-op if missing).
|
|
34
|
+
function D.removeHighlight(target, name)
|
|
35
|
+
if not target then return end
|
|
36
|
+
local h = target:FindFirstChild(name)
|
|
37
|
+
if h then h:Destroy() end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
-- Create a Drawing circle (FOV ring, radar dot, etc.).
|
|
41
|
+
-- opts: color, thickness, alpha, filled, radius, visible, sides, position
|
|
42
|
+
function D.circle(opts)
|
|
43
|
+
opts = opts or {}
|
|
44
|
+
local c = Drawing.new("Circle")
|
|
45
|
+
c.Color = opts.color or Color3.fromRGB(255, 255, 255)
|
|
46
|
+
c.Thickness = opts.thickness or 2
|
|
47
|
+
c.Transparency = opts.alpha ~= nil and opts.alpha or 0.8
|
|
48
|
+
c.Filled = opts.filled ~= nil and opts.filled or false
|
|
49
|
+
c.Radius = opts.radius or 100
|
|
50
|
+
c.NumSides = opts.sides or 64
|
|
51
|
+
c.Visible = opts.visible == nil and true or opts.visible
|
|
52
|
+
c.Position = opts.position or Vector2.new(0, 0)
|
|
53
|
+
return c
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
-- Safely remove any Drawing object.
|
|
57
|
+
function D.remove(drawing)
|
|
58
|
+
if drawing then pcall(function() drawing:Remove() end) end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
-- Create or replace a SelectionBox on target.
|
|
62
|
+
-- opts: color, thickness
|
|
63
|
+
function D.selectionBox(target, name, opts)
|
|
64
|
+
if not target then return nil end
|
|
65
|
+
opts = opts or {}
|
|
66
|
+
local old = target:FindFirstChild(name)
|
|
67
|
+
if old then old:Destroy() end
|
|
68
|
+
local sb = Instance.new("SelectionBox")
|
|
69
|
+
sb.Name = name
|
|
70
|
+
sb.Color3 = opts.color or Color3.fromRGB(255, 0, 0)
|
|
71
|
+
sb.LineThickness = opts.thickness or 0.05
|
|
72
|
+
sb.Adornee = target
|
|
73
|
+
sb.Parent = target
|
|
74
|
+
return sb
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
-- Create or replace a BillboardGui label on a part (name tag, distance, etc.).
|
|
78
|
+
-- opts: text, color, size (UDim2), offset (Vector3), alwaysOnTop, fontSize
|
|
79
|
+
function D.billboard(target, name, opts)
|
|
80
|
+
if not target then return nil end
|
|
81
|
+
opts = opts or {}
|
|
82
|
+
local old = target:FindFirstChild(name)
|
|
83
|
+
if old then old:Destroy() end
|
|
84
|
+
local bg = Instance.new("BillboardGui")
|
|
85
|
+
bg.Name = name
|
|
86
|
+
bg.Size = opts.size or UDim2.new(0, 120, 0, 28)
|
|
87
|
+
bg.StudsOffset = opts.offset or Vector3.new(0, 3, 0)
|
|
88
|
+
bg.AlwaysOnTop = opts.alwaysOnTop ~= false
|
|
89
|
+
bg.Adornee = target
|
|
90
|
+
bg.Parent = target
|
|
91
|
+
local lbl = Instance.new("TextLabel")
|
|
92
|
+
lbl.Size = UDim2.new(1, 0, 1, 0)
|
|
93
|
+
lbl.BackgroundTransparency = 1
|
|
94
|
+
lbl.Text = opts.text or ""
|
|
95
|
+
lbl.TextColor3 = opts.color or Color3.fromRGB(255, 255, 255)
|
|
96
|
+
lbl.TextScaled = true
|
|
97
|
+
lbl.Font = Enum.Font.GothamBold
|
|
98
|
+
lbl.Parent = bg
|
|
99
|
+
return bg
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
-- Destroy a named BillboardGui on target (no-op if missing).
|
|
103
|
+
function D.removeBillboard(target, name)
|
|
104
|
+
if not target then return end
|
|
105
|
+
local b = target:FindFirstChild(name)
|
|
106
|
+
if b then b:Destroy() end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
-- Destroy every child named `name` found anywhere under `parent`.
|
|
110
|
+
-- Useful for batch cleanup after e.g. a Highlights pass.
|
|
111
|
+
function D.removeAll(name, parent)
|
|
112
|
+
parent = parent or workspace
|
|
113
|
+
local list = parent:GetDescendants()
|
|
114
|
+
for i = 1, #list do
|
|
115
|
+
if list[i].Name == name then
|
|
116
|
+
pcall(function() list[i]:Destroy() end)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
return D
|
|
122
|
+
end)()
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
-- ── Pulse.Hitbox ──────────────────────────────────────────────────────────────
|
|
2
|
+
-- Uniform size-expansion manager for BaseParts (hitbox stretching).
|
|
3
|
+
-- Game-agnostic: works on any BasePart regardless of game/character structure.
|
|
4
|
+
-- Saves original sizes on first apply and restores them cleanly.
|
|
5
|
+
--
|
|
6
|
+
-- Usage:
|
|
7
|
+
-- local hm = Pulse.Hitbox.new("enemyHitbox")
|
|
8
|
+
-- hm:apply(part, 30) -- expand to 30×30×30, saving original
|
|
9
|
+
-- hm:apply(part, 30) -- idempotent: original already saved
|
|
10
|
+
-- hm:restore(part) -- restore this part to saved size
|
|
11
|
+
-- hm:restoreAll() -- restore all managed parts
|
|
12
|
+
-- hm:isExpanded(part) -- true if part is currently expanded
|
|
13
|
+
--
|
|
14
|
+
-- For visual indicators use Pulse.Draw.selectionBox / Pulse.Draw.highlight separately.
|
|
15
|
+
|
|
16
|
+
Pulse.Hitbox = {}
|
|
17
|
+
|
|
18
|
+
function Pulse.Hitbox.new(tag)
|
|
19
|
+
local self = {}
|
|
20
|
+
local _orig = {} -- part → Vector3 original Size
|
|
21
|
+
local _tag = tag or "hitbox"
|
|
22
|
+
|
|
23
|
+
-- Expand part to a uniform size³ cube, saving the original on first call.
|
|
24
|
+
function self:apply(part, size)
|
|
25
|
+
if not part or not part:IsA("BasePart") then return end
|
|
26
|
+
if not _orig[part] then
|
|
27
|
+
_orig[part] = part.Size
|
|
28
|
+
end
|
|
29
|
+
part.Size = Vector3.new(size, size, size)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
-- Restore a single part to its saved size. No-op if not managed.
|
|
33
|
+
function self:restore(part)
|
|
34
|
+
if not part then return end
|
|
35
|
+
local orig = _orig[part]
|
|
36
|
+
if orig then
|
|
37
|
+
if part.Parent then pcall(function() part.Size = orig end) end
|
|
38
|
+
_orig[part] = nil
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
-- Restore all managed parts and clear state.
|
|
43
|
+
function self:restoreAll()
|
|
44
|
+
local n = 0
|
|
45
|
+
for part, orig in pairs(_orig) do
|
|
46
|
+
if part and part.Parent then
|
|
47
|
+
pcall(function() part.Size = orig end)
|
|
48
|
+
end
|
|
49
|
+
n = n + 1
|
|
50
|
+
end
|
|
51
|
+
_orig = {}
|
|
52
|
+
if n > 0 and Pulse.Log then
|
|
53
|
+
Pulse.Log.debug(_tag, "restoreAll", { count = n })
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
-- Returns true if this part is currently expanded by this manager.
|
|
58
|
+
function self:isExpanded(part)
|
|
59
|
+
return _orig[part] ~= nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
return self
|
|
63
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
-- ── Pulse.Input ──────────────────────────────────────────────────────────────
|
|
2
|
+
-- Key-press simulation using VirtualInputManager + UserInputService.
|
|
3
|
+
-- Usage:
|
|
4
|
+
-- Pulse.Input.simulateKey(Enum.KeyCode.T)
|
|
5
|
+
|
|
6
|
+
Pulse.Input = (function()
|
|
7
|
+
local I = {}
|
|
8
|
+
|
|
9
|
+
function I.simulateKey(keyCode)
|
|
10
|
+
pcall(function()
|
|
11
|
+
_VIM:SendKeyEvent(true, keyCode, false, game)
|
|
12
|
+
task.wait(0.1)
|
|
13
|
+
_VIM:SendKeyEvent(false, keyCode, false, game)
|
|
14
|
+
end)
|
|
15
|
+
pcall(function()
|
|
16
|
+
local ev = { KeyCode = keyCode, UserInputType = Enum.UserInputType.Keyboard }
|
|
17
|
+
_PulseUIS.InputBegan:Fire(ev, false)
|
|
18
|
+
task.wait(0.05)
|
|
19
|
+
_PulseUIS.InputEnded:Fire(ev, false)
|
|
20
|
+
end)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
return I
|
|
24
|
+
end)()
|