Skip to main content

Mouse and Keyboard Input

Time to make your plugin respond to what players do! This guide covers capturing mouse positions, clicks, and keyboard input.

Mouse Input

Getting Mouse Position

-- InputGetMouseX()
-- Returns the horizontal position of the mouse cursor
local mouseX = InputGetMouseX()
LogPrint("Mouse X: " .. mouseX)

-- InputGetMouseY()
-- Returns the vertical position of the mouse cursor
local mouseY = InputGetMouseY()
LogPrint("Mouse Y: " .. mouseY)

Checking if Mouse is Over an Area

InputCheckMousePos(x, y, w, h)

Parameters:

  • x, y - Area position
  • w, h - Area size

Returns: true if mouse is inside the area

local isInside = InputCheckMousePos(100, 100, 200, 50)
if isInside then
LogPrint("Mouse is inside the area!")
end

Example in Render Function

function MyPlugin.renderWindow(window)
UIRenderWindow(window, 0, 0, window.w, window.h, 0)

-- Check if mouse is over a specific button
local buttonX = 20
local buttonY = 50
local buttonW = 100
local buttonH = 30

-- Convert to global coordinates
local globalX = window.x + buttonX
local globalY = window.y + buttonY

if InputCheckMousePos(globalX, globalY, buttonW, buttonH) then
-- Mouse is over the button - highlight it
window:renderColor(buttonX, buttonY, buttonW, buttonH, RGBA(100, 100, 100, 255))
else
-- Mouse is not over the button - normal
window:renderColor(buttonX, buttonY, buttonW, buttonH, RGBA(50, 50, 50, 255))
end

window:renderText("Button", buttonX, buttonY, buttonW, buttonH, 4,
RGBA(255,255,255,255), FontM)
end

Window Hover Check Method

Windows have a convenient method for checking hover with relative coordinates:

window:checkHover(x, y, w, h)

This is the same as InputCheckMousePos but uses coordinates relative to the window.

function MyPlugin.renderWindow(window)
UIRenderWindow(window, 0, 0, window.w, window.h, 0)

-- Check hover more easily
if window:checkHover(20, 50, 100, 30) then
window:renderColor(20, 50, 100, 30, RGBA(100, 100, 100, 255))
else
window:renderColor(20, 50, 100, 30, RGBA(50, 50, 50, 255))
end
end

Keyboard Input

InputCheckKeyPress(index, value)

Parameters:

  • index - VK code of the key (use VK_* constants)
  • value - Check type:
    • 0 = Is the key being held down?
    • 1 = Was the key just pressed this frame?

Returns: true if the condition is met

Examples

-- Check if key is being held
if InputCheckKeyPress(VK_SPACE, 0) then
LogPrint("Space is pressed!")
end

-- Check if key was just pressed (single press)
if InputCheckKeyPress(VK_F1, 1) then
LogPrint("F1 was pressed!")
MyPlugin:toggleWindow()
end

Practical Example - Open Window with Key

function MyPlugin.updateWindow(window)
-- Check F2 key to open/close
if InputCheckKeyPress(VK_F2, 1) then
if window.state == 0 then
window:setState(1)
else
window:setState(0)
end
end

return window.state == 1
end

Virtual Key Constants

Here are all the key codes you can use:

Mouse Buttons

VK_LBUTTON  = 1   -- Left mouse button
VK_RBUTTON = 2 -- Right mouse button
VK_MBUTTON = 4 -- Middle mouse button
VK_XBUTTON1 = 5 -- Mouse extra button 1
VK_XBUTTON2 = 6 -- Mouse extra button 2

Special Keys

VK_BACK     = 8   -- Backspace
VK_TAB = 9 -- Tab
VK_RETURN = 13 -- Enter
VK_SHIFT = 16 -- Shift
VK_CONTROL = 17 -- Ctrl
VK_MENU = 18 -- Alt
VK_PAUSE = 19 -- Pause
VK_CAPITAL = 20 -- Caps Lock
VK_ESCAPE = 27 -- Esc
VK_SPACE = 32 -- Space
VK_PRIOR    = 33  -- Page Up
VK_NEXT = 34 -- Page Down
VK_END = 35 -- End
VK_HOME = 36 -- Home
VK_LEFT = 37 -- Left Arrow
VK_UP = 38 -- Up Arrow
VK_RIGHT = 39 -- Right Arrow
VK_DOWN = 40 -- Down Arrow
VK_INSERT = 45 -- Insert
VK_DELETE = 46 -- Delete

Number Keys (Main Keyboard)

VK_0 = 48
VK_1 = 49
VK_2 = 50
VK_3 = 51
VK_4 = 52
VK_5 = 53
VK_6 = 54
VK_7 = 55
VK_8 = 56
VK_9 = 57

Letter Keys (A-Z)

VK_A = 65
VK_B = 66
VK_C = 67
VK_D = 68
VK_E = 69
VK_F = 70
VK_G = 71
VK_H = 72
VK_I = 73
VK_J = 74
VK_K = 75
VK_L = 76
VK_M = 77
VK_N = 78
VK_O = 79
VK_P = 80
VK_Q = 81
VK_R = 82
VK_S = 83
VK_T = 84
VK_U = 85
VK_V = 86
VK_W = 87
VK_X = 88
VK_Y = 89
VK_Z = 90

Windows Keys

VK_LWIN     = 91  -- Left Windows key
VK_RWIN = 92 -- Right Windows key

Numpad Keys

VK_NUMPAD0  = 96
VK_NUMPAD1 = 97
VK_NUMPAD2 = 98
VK_NUMPAD3 = 99
VK_NUMPAD4 = 100
VK_NUMPAD5 = 101
VK_NUMPAD6 = 102
VK_NUMPAD7 = 103
VK_NUMPAD8 = 104
VK_NUMPAD9 = 105
VK_MULTIPLY = 106 -- *
VK_ADD = 107 -- +
VK_SUBTRACT = 109 -- -
VK_DECIMAL = 110 -- .
VK_DIVIDE = 111 -- /

Function Keys (F1-F12)

VK_F1  = 112
VK_F2 = 113
VK_F3 = 114
VK_F4 = 115
VK_F5 = 116
VK_F6 = 117
VK_F7 = 118
VK_F8 = 119
VK_F9 = 120
VK_F10 = 121
VK_F11 = 122
VK_F12 = 123

Lock Keys

VK_NUMLOCK  = 144  -- Num Lock
VK_SCROLL = 145 -- Scroll Lock

Left/Right Shift

VK_LSHIFT   = 160
VK_RSHIFT = 161

Checking if Chat is Open

This is super important! You don't want hotkeys triggering while the player is typing in chat:

InputCheckChatOpen()

Returns: true if chat window is open

function MyPlugin.updateWindow(window)
-- Only process keys if chat is not open
if not InputCheckChatOpen() then
if InputCheckKeyPress(VK_F1, 1) then
window:setState(window.state == 0 and 1 or 0)
end
end

return window.state == 1
end

Complete Example - Hotkey System

Here's a full hotkey manager:

HotkeySystem = {
index = GetWindowIndex(),
group = UI_GROUP_INGAME,
hotkeys = {
{key = VK_F1, name = "Inventory", action = function() LogPrint("Opening inventory") end},
{key = VK_F2, name = "Skills", action = function() LogPrint("Opening skills") end},
{key = VK_F3, name = "Party", action = function() LogPrint("Opening party") end},
{key = VK_F4, name = "Guild", action = function() LogPrint("Opening guild") end},
}
}

function HotkeySystem.renderWindow(window)
UIRenderWindow(window, 0, 0, window.w, window.h, 0)

-- Title
window:renderText("Hotkeys", 0, 10, window.w, 30, 4,
RGBA(255,215,0,255), FontL)

-- List hotkeys
local y = 50
for i, hotkey in ipairs(HotkeySystem.hotkeys) do
-- Key name
local keyName = "F" .. i
window:renderText(keyName, 20, y, 50, 25, 1,
RGBA(255,255,255,255), FontM)

-- Action name
window:renderText(hotkey.name, 80, y, 150, 25, 1,
RGBA(192,192,192,255), FontM)

y = y + 30
end

-- Instructions
window:renderText("Press ESC to close", 0, window.h-30, window.w, 25, 4,
RGBA(150,150,150,255), FontS)
end

function HotkeySystem.updateWindow(window)
-- Don't process input if chat is open
if InputCheckChatOpen() then
return window.state == 1
end

-- Check each hotkey
for i, hotkey in ipairs(HotkeySystem.hotkeys) do
if InputCheckKeyPress(hotkey.key, 1) then
hotkey.action()
end
end

-- ESC to close
if InputCheckKeyPress(VK_ESCAPE, 1) and window.state == 1 then
window:setState(0)
end

-- F12 to open/close hotkey menu
if InputCheckKeyPress(VK_F12, 1) then
window:setState(window.state == 0 and 1 or 0)
end

return window.state == 1
end

Example - Custom Movement Control

MovementDemo = {
index = GetWindowIndex(),
group = UI_GROUP_INGAME,
playerX = 200,
playerY = 200,
speed = 5,
}

function MovementDemo.renderWindow(window)
UIRenderWindow(window, 0, 0, window.w, window.h, 0)

-- Title
window:renderText("Movement Demo", 0, 10, window.w, 30, 4,
RGBA(255,255,255,255), FontL)

-- Game area
window:renderColor(20, 50, window.w-40, window.h-70, RGBA(30,30,30,255))

-- Player (red circle)
window:renderColor(
MovementDemo.playerX - 5,
MovementDemo.playerY - 5,
10, 10,
RGBA(255, 0, 0, 255)
)

-- Instructions
window:renderText("Use WASD to move", 20, window.h-25, window.w-40, 20, 4,
RGBA(200,200,200,255), FontS)
end

function MovementDemo.updateWindow(window)
if window.state == 0 then
return false
end

-- Don't process if chat is open
if InputCheckChatOpen() then
return true
end

-- Movement controls (hold key)
if InputCheckKeyPress(VK_W, 0) then
MovementDemo.playerY = MovementDemo.playerY - MovementDemo.speed
end
if InputCheckKeyPress(VK_S, 0) then
MovementDemo.playerY = MovementDemo.playerY + MovementDemo.speed
end
if InputCheckKeyPress(VK_A, 0) then
MovementDemo.playerX = MovementDemo.playerX - MovementDemo.speed
end
if InputCheckKeyPress(VK_D, 0) then
MovementDemo.playerX = MovementDemo.playerX + MovementDemo.speed
end

-- Limit movement to game area
local minX = 25
local maxX = window.w - 45
local minY = 55
local maxY = window.h - 75

if MovementDemo.playerX < minX then MovementDemo.playerX = minX end
if MovementDemo.playerX > maxX then MovementDemo.playerX = maxX end
if MovementDemo.playerY < minY then MovementDemo.playerY = minY end
if MovementDemo.playerY > maxY then MovementDemo.playerY = maxY end

return true
end

Example - Key Combinations

KeyComboDemo = {
index = GetWindowIndex(),
group = UI_GROUP_INGAME,
}

function KeyComboDemo.updateWindow(window)
if window.state == 0 then
return false
end

if InputCheckChatOpen() then
return true
end

-- Ctrl + S to save
if InputCheckKeyPress(VK_CONTROL, 0) and InputCheckKeyPress(VK_S, 1) then
NoticeSend(1, "Saving...")
end

-- Ctrl + Shift + D for debug
if InputCheckKeyPress(VK_CONTROL, 0) and
InputCheckKeyPress(VK_SHIFT, 0) and
InputCheckKeyPress(VK_D, 1) then
NoticeSend(1, "Debug mode activated!")
end

-- Alt + F4 to close (example, not recommended)
if InputCheckKeyPress(VK_MENU, 0) and InputCheckKeyPress(VK_F4, 1) then
window:setState(0)
NoticeSend(1, "Closing window...")
end

return true
end

Best Practices

  1. ALWAYS check InputCheckChatOpen() before processing hotkeys - Prevents keys from triggering while typing in chat

  2. Use value=1 for single actions (open menu, activate skill) - Prevents multiple activations while key is held

  3. Use value=0 for continuous actions (movement, holding button) - Detects key being held down

  4. Check modifiers first when using key combinations - Always check Ctrl, Shift, Alt with value=0, then the action key with value=1

  5. Consider game conflicts - Some keys might already be used by the game client - Test your hotkeys

  6. Performance matters - InputCheckKeyPress is fast, but don't check hundreds of keys - Only check the keys you actually need

That's input handling! Now you can make plugins that respond to player actions.