Path of Exile Helper

@rebind / poe-helperv0.1.3Public

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

Files

main.luau7.5 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.6 KB.