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.