Dev Spy

@rebindv0.2.0

See the pointer, window, and color under it while you write a script, record observations and macros, and try AI calls.

Files

Version 0.2.0 (current)

probe.luau

-- What the HUD reads: the pointer, its monitor, the active window, the color
-- under the pointer, held keys and modifiers, the last input and its device,
-- and the controller's axes in hardware mode.

local M = {}

local AXES = { "LeftX", "LeftY", "RightX", "RightY", "LT", "RT" }

-- monitor and device lists change rarely; read once a second
local monitors, devices, listed_at = {}, {}, -math.huge
-- the last color read, kept for up to a second while the pointer stays put
local color_at, color, color_t = nil, nil, -math.huge

-- the last input the hooks saw, and the measured interval of the HUD timer
M.last = nil
M.interval = 0

function M.input(text, device)
  M.last = { text = text, at = rb.system.time(), device = device }
end

-- pixel: read the color under the pointer; covers(x, y): true over the
-- spy's own panels, where a read would see them; busy: a menu is open
function M.read(pixel, covers, busy)
  local t = rb.system.time()
  if t - listed_at >= 1000 then
    listed_at = t
    local ok, list = pcall(rb.screen.list)
    monitors = ok and list or {}
    ok, list = pcall(rb.input.devices)
    devices = ok and list or {}
  end
  local x, y = rb.system.mouse()
  local r = {
    now = t,
    x = x,
    y = y,
    window = rb.system.window(),
    count = #monitors,
    keys = rb.input.keys(),
    mods = rb.input.modifiers(),
    hardware = #devices > 0,
    last = M.last,
    interval = M.interval,
  }
  for _, m in monitors do
    if x >= m.x and x < m.x + m.width and y >= m.y and y < m.y + m.height then
      r.monitor = m
    end
  end
  if pixel then
    local here = x .. "," .. y
    if busy or (covers ~= nil and covers(x, y, r.monitor)) then
      r.covered = true
    elseif here == color_at and t - color_t < 1000 then
      r.hex = color
    else
      local ok, hex = pcall(rb.screen.pixel_hex, x, y)
      color = ok and string.match(hex, "^%x%x%x%x%x%x$") or nil
      color_at, color_t = here, t
      r.hex = color
    end
  end
  if r.hardware then
    r.axes = {}
    for _, name in AXES do
      r.axes[name] = rb.input.axis(name)
    end
  end
  return r
end

M.AXES = AXES

return M