Path of Exile Helper

@rebind / poe-helperv0.1.3Public

QoL shortcuts, inventory automation, and grid calibration for Path of Exile.

Files

Older release. Installs get 0.1.3.

main.luau7.1 KB

-- Path of Exile Helper: stash transfer, inventory and trade grid actions,
-- a portal key and chat shortcuts. Grid positions come from a four-step
-- calibration saved next to the script.

local cfg = UI.Schema({
  enable_right_click  = UI.Toggle(false, { label = "Enable Right-Click Combat Loop" }),
  enable_dump         = UI.Toggle(false, { label = "Enable Full Inventory Dump" }),
  combat_button       = UI.Keybind("Mouse2", { label = "Hold for Combat Loop", group = "Hotkeys" }),
  transfer_button     = UI.Keybind("Mouse1", { label = "Hold with Ctrl for Stash Transfer", group = "Hotkeys" }),
  win_remap_key       = UI.Keybind("LWin", { label = "Key That Sends Right Shift", group = "Hotkeys" }),
  cursor_key          = UI.Keybind("F12", { label = "Show Cursor Position", group = "Hotkeys" }),
  setup_key           = UI.Keybind("F10", { label = "Calibrate Coordinates" }),
  portal_key          = UI.Keybind("Grave", { label = "Instant Portal", group = "Hotkeys" }),
  dump_key            = UI.Keybind("Left", { label = "Dump Inventory (Ctrl+Click)", group = "Hotkeys" }),
  right_click_all_key = UI.Keybind("Right", { label = "Right-Click All Slots", group = "Hotkeys" }),
  inspect_trade_key   = UI.Keybind("Up", { label = "Inspect Trade Window", group = "Hotkeys" }),
  hideout_key         = UI.Keybind("F1", { label = "Go to Hideout", group = "Chat Commands" }),
  ty_key              = UI.Keybind("F2", { label = "Quick Reply 'ty'", group = "Chat Commands" }),
  leave_key           = UI.Keybind("F3", { label = "Leave Party", group = "Chat Commands" }),
  destroy_key         = UI.Keybind("F11", { label = "Destroy Held Item", group = "Chat Commands" }),
})

local COORDS_FILE = "coords.json"

local function load_coords()
  if File.Exists(COORDS_FILE) then
    local ok, data = pcall(File.ReadJSON, COORDS_FILE)
    if ok and data then return data end
  end
  return {
    inv_tl   = { x = 1780, y = 818 },
    inv_br   = { x = 2495, y = 1078 },
    trade_tl = { x = 494,  y = 324 },
    trade_br = { x = 1209, y = 584 },
  }
end

local function save_coords(data)
  File.WriteJSON(COORDS_FILE, data)
end

local coords = load_coords()
local setup_step = 0

local function get_inv_cell(col, row)
  local dx = (coords.inv_br.x - coords.inv_tl.x) / 11
  local dy = (coords.inv_br.y - coords.inv_tl.y) / 4
  return {
    x = math.floor(coords.inv_tl.x + col * dx),
    y = math.floor(coords.inv_tl.y + row * dy),
  }
end

local function get_trade_cell(col, row)
  local dx = (coords.trade_br.x - coords.trade_tl.x) / 11
  local dy = (coords.trade_br.y - coords.trade_tl.y) / 4
  return {
    x = math.floor(coords.trade_tl.x + col * dx),
    y = math.floor(coords.trade_tl.y + row * dy),
  }
end

local function send_chat(command)
  HID.Press("Enter", 15)
  Sleep(25)
  HID.Type(command)
  Sleep(25)
  HID.Press("Enter", 15)
end

-- Calibration hotkey
Bind(cfg.setup_key, function()
  local pos = Input.GetMousePos()

  if setup_step == 0 then
    setup_step = 1
    UI.Notify("Step 1/4: Hover TOP-LEFT inventory slot center, press key", "info")
  elseif setup_step == 1 then
    coords.inv_tl = { x = pos.x, y = pos.y }
    setup_step = 2
    UI.Notify(string.format("Saved TL (%d, %d). Step 2/4: Hover BOTTOM-RIGHT inventory slot, press key", pos.x, pos.y), "info")
  elseif setup_step == 2 then
    coords.inv_br = { x = pos.x, y = pos.y }
    setup_step = 3
    UI.Notify(string.format("Saved BR (%d, %d). Step 3/4: Open trade, hover TOP-LEFT trade slot, press key", pos.x, pos.y), "info")
  elseif setup_step == 3 then
    coords.trade_tl = { x = pos.x, y = pos.y }
    setup_step = 4
    UI.Notify(string.format("Saved Trade TL (%d, %d). Step 4/4: Hover BOTTOM-RIGHT trade slot, press key", pos.x, pos.y), "info")
  elseif setup_step == 4 then
    coords.trade_br = { x = pos.x, y = pos.y }
    save_coords(coords)
    setup_step = 0
    UI.Notify(string.format("Calibration saved to %s", COORDS_FILE), "success")
    Log.Info(string.format("Saved grid coordinates to %s", COORDS_FILE))
  end
end)

-- Key remapping
Bind.Remap(cfg.win_remap_key, "RShift")

-- Rapid Ctrl + Left Click (stash transfer loop)
Bind(cfg.transfer_button, {
  when = function()
    return Input.GetModifiers().ctrl
  end,
  action = Async(function()
    while Input.IsDown(cfg.transfer_button) and Input.GetModifiers().ctrl do
      HID.Press("Mouse1", 10)
      Sleep(15)
    end
  end),
})

-- Rapid Right Click combat sequence (Q + E skills)
Bind(cfg.combat_button, {
  when = function()
    return cfg.enable_right_click
  end,
  action = Async(function()
    while Input.IsDown(cfg.combat_button) and cfg.enable_right_click do
      HID.Press("Mouse2", 15)
      Sleep(50)
      if not Input.IsDown(cfg.combat_button) then break end
      Sleep(10)
      HID.Press("Q", 15)
      if not Input.IsDown(cfg.combat_button) then break end
      Sleep(10)
      HID.Press("E", 15)
      Sleep(10)
    end
  end),
})

-- Instant Portal: open inventory, right click bottom-right scroll slot, close inventory
Bind(cfg.portal_key, Async(function()
  local orig_pos = Input.GetMousePos()
  local portal_cell = get_inv_cell(11, 4)

  HID.Press("B", 15)
  Sleep(15)
  HID.MoveTo(portal_cell.x, portal_cell.y)
  Sleep(10)
  HID.Press("Mouse2", 15)
  Sleep(15)
  HID.Press("B", 15)
  Sleep(10)
  HID.MoveTo(orig_pos.x, orig_pos.y)
end))

-- Dump inventory (Ctrl + Click); off by default, since it chains actions
Bind(cfg.dump_key, {
  when = function()
    return cfg.enable_dump
  end,
  action = Async(function()
    HID.Down("LCtrl")
    for row = 0, 4 do
      for col = 0, 11 do
        local pos = get_inv_cell(col, row)
        HID.MoveTo(pos.x, pos.y)
        Sleep(10)
        HID.Press("Mouse1", 10)
        Sleep(10)
      end
    end
    HID.Up("LCtrl")
  end),
})

-- Right click all slots
Bind(cfg.right_click_all_key, Async(function()
  if not cfg.enable_right_click then
    HID.Press("Mouse2", 15)
    return
  end
  for row = 0, 4 do
    for col = 0, 11 do
      local pos = get_inv_cell(col, row)
      HID.MoveTo(pos.x, pos.y)
      Sleep(10)
      HID.Press("Mouse2", 10)
      Sleep(10)
    end
  end
end))

-- Inspect items in trade window
Bind(cfg.inspect_trade_key, Async(function()
  for row = 0, 4 do
    for col = 0, 11 do
      local pos = get_trade_cell(col, row)
      HID.MoveTo(pos.x, pos.y)
      Sleep(10)
    end
  end
end))

-- Chat shortcuts
Bind(cfg.hideout_key, Async(function()
  send_chat("/hideout")
end))

Bind(cfg.ty_key, Async(function()
  HID.Combo("LCtrl+Enter")
  Sleep(25)
  HID.Type("ty")
  Sleep(25)
  HID.Press("Enter", 15)
end))

Bind(cfg.leave_key, Async(function()
  send_chat("/leave")
end))

Bind(cfg.destroy_key, Async(function()
  send_chat("/destroy")
end))

-- Cursor position debugger
Bind(cfg.cursor_key, function()
  local pos = Input.GetMousePos()
  UI.Notify(string.format("Cursor: %d, %d", pos.x, pos.y), "info")
  Log.Info(string.format("Cursor: %d, %d", pos.x, pos.y))
end)

function OnStop()
  HID.Up("LCtrl")
  HID.Up("Mouse1")
  HID.Up("Mouse2")
end

function OnBlur()
  HID.Up("LCtrl")
  HID.Up("Mouse1")
  HID.Up("Mouse2")
end

3 files, 10.1 KB.