Components API
The components API provides access to UI widgets and interface elements in the game. Components can be buttons, text labels, inventory slots, dialog boxes, or any other UI element.
Overview
Components are organized hierarchically:
- Group ID (Widget ID): Top-level interface (e.g., 149 for inventory)
- Component ID: Specific component within the group (e.g., 0 for inventory container)
- Child ID: Optional child components (e.g., individual inventory slots)
Methods
get
components:get(group_id: number, component_id: number, child_id: number?) -> Component?
Gets a component by its group ID and component ID. Optionally accepts a child ID to retrieve nested child components.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | number | Yes | The interface group/widget ID |
component_id | number | Yes | The component ID within the group |
child_id | number | No | The child component ID for nested components |
Returns:
Component?: The component, or nil if not found
Example:
-- Get parent component (inventory container)
local inventory = components:get(149, 0)
if inventory and inventory:visible() then
logger:info("Inventory is visible")
end
-- Get child component (first inventory slot) with optional 3rd parameter
local first_slot = components:get(149, 0, 0)
if first_slot then
local item_id = first_slot:item_id()
if item_id ~= -1 then
logger:info("First slot contains item: " .. item_id)
end
end
-- Get specific inventory slot
local slot_5 = components:get(149, 0, 5)
if slot_5 then
logger:info("Slot 5 item: " .. slot_5:item_id())
end
-- Get bank container
local bank = components:get(12, 2)
if bank and bank:visible() then
logger:info("Bank is open")
end
Alternative: You can also use the component:child() method to navigate from a parent component to its children:
local inventory = components:get(149, 0)
if inventory then
local first_slot = inventory:child(0)
end
find_first
components:find_first(filter: table?) -> Component?
Finds the first component matching the specified filters.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
filter | table | No | Filter options (see Filter Options below) |
Returns:
Component?: The first matching component, or nil if none found
Example:
-- Find continue button by text
local continue = components:find_first({
text_contains = "Click here to continue"
})
-- Find visible components in a group
local dialog = components:find_first({
group_id = 231,
visible_only = true
})
-- Find component by action
local drop_slot = components:find_first({
group_id = 149,
actions_contains = "Drop"
})
find_all
components:find_all(filter: table?) -> Component[]
Finds all components matching the specified filters.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
filter | table | No | Filter options (see Filter Options below) |
Returns:
Component[]: Array of matching components (empty if none found)
Example:
-- Find all visible inventory slots
local visible_slots = components:find_all({
group_id = 149,
visible_only = true
})
-- Filter to only slots with items
local items = {}
for _, slot in ipairs(visible_slots) do
if slot:item_id() ~= -1 then
table.insert(items, slot)
end
end
logger:info("Found " .. #items .. " items in inventory")
Filter Options
The find_first and find_all methods accept a filter table with the following options:
| Option | Type | Description |
|---|---|---|
text_contains | string | Component text contains this string (case-insensitive) |
text_equals | string | Component text exactly matches this string (case-insensitive) |
visible_only | boolean | Only return visible components |
group_id | number | Filter by specific group/widget ID |
content_type | number | Filter by content type (0=graphic, 1=text, 2=filled box, etc.) |
min_width | number | Minimum width in pixels |
min_height | number | Minimum height in pixels |
parent_id | number | Filter by parent component ID |
with_text | boolean | Only components that have non-empty text |
actions_contains | string | Component actions contain this string |
actions_equals | string | Component actions exactly match this string |
Example:
-- Find large visible buttons with text
local buttons = components:find_all({
visible_only = true,
with_text = true,
min_width = 50,
min_height = 20,
content_type = 2 -- Filled box
})
-- Find specific dialog option
local option = components:find_first({
group_id = 219,
text_contains = "Yes",
visible_only = true
})
Common Component Groups
Here are some commonly used component group IDs:
| Group ID | Interface | Description |
|---|---|---|
| 149 | Inventory | Player inventory (28 slots) |
| 12 | Bank | Bank interface |
| 162 | Chatbox | Chat messages and input |
| 231 | Dialog | NPC dialogs and continue buttons |
| 387 | Equipment | Worn equipment |
| 218 | Spellbook | Magic spellbook |
| 593 | Grand Exchange | GE interface |
| 219 | Dialog Options | Multiple choice dialog options |
| 465 | Bank Inventory | Inventory shown when bank is open |
Common Patterns
Checking if Interface is Open
local function is_bank_open()
local bank = components:get(12, 2)
return bank and bank:visible()
end
local function is_inventory_open()
local inventory = components:get(149, 0)
return inventory and inventory:visible()
end
if is_bank_open() then
logger:info("Bank is open")
end
Getting All Inventory Slots
local function get_all_inventory_slots()
local slots = {}
for i = 0, 27 do
local slot = components:get(149, 0, i)
if slot then
table.insert(slots, slot)
end
end
return slots
end
local slots = get_all_inventory_slots()
logger:info("Found " .. #slots .. " inventory slots")
Finding Items in Inventory
local function find_item_in_inventory(item_id)
for i = 0, 27 do
local slot = components:get(149, 0, i)
if slot and slot:item_id() == item_id then
return slot, i
end
end
return nil, -1
end
-- Find coins (item ID 995)
local coin_slot, index = find_item_in_inventory(995)
if coin_slot then
local amount = coin_slot:item_stack_size()
logger:info("Found " .. amount .. " coins in slot " .. index)
end
Clicking Dialog Buttons
local function click_continue()
local continue = components:find_first({
text_contains = "Click here to continue",
visible_only = true
})
if continue then
continue:click()
return true
end
return false
end
local function select_dialog_option(text)
local option = components:find_first({
group_id = 219,
text_contains = text,
visible_only = true
})
if option then
option:click()
return true
end
return false
end
-- Use in script
if click_continue() then
logger:info("Clicked continue")
end
if select_dialog_option("Yes") then
logger:info("Selected 'Yes' option")
end
Waiting for Component to Appear
local function wait_for_component(group_id, component_id, timeout)
local start = os.time()
while os.time() - start < timeout do
local comp = components:get(group_id, component_id)
if comp and comp:is_valid() and comp:visible() then
return comp
end
sleep(100)
end
return nil
end
-- Wait up to 5 seconds for bank to open
local bank = wait_for_component(12, 2, 5)
if bank then
logger:info("Bank opened!")
else
logger:warn("Bank did not open within 5 seconds")
end
Reading Component Text
local function get_dialog_text()
-- Try common dialog component IDs
local dialog_ids = {
{231, 5},
{217, 5},
{231, 6}
}
for _, ids in ipairs(dialog_ids) do
local comp = components:get(ids[1], ids[2])
if comp and comp:visible() then
local text = comp:text()
if text and text ~= "" then
return text
end
end
end
return nil
end
local text = get_dialog_text()
if text then
logger:info("NPC says: " .. text)
end
Interacting with Component Actions
local function drop_item_in_slot(slot_index)
local slot = components:get(149, 0, slot_index)
if slot and slot:item_id() ~= -1 then
-- Use interact to right-click and select "Drop"
return slot:interact("Drop")
end
return false
end
-- Drop items in slots 0-5
for i = 0, 5 do
if drop_item_in_slot(i) then
logger:info("Dropped item in slot " .. i)
sleep(100)
end
end
Checking Component Bounds
local function is_point_in_component(comp, x, y)
if not comp or not comp:is_valid() then
return false
end
local cx = comp:x()
local cy = comp:y()
local cw = comp:width()
local ch = comp:height()
return x >= cx and x <= cx + cw and
y >= cy and y <= cy + ch
end
-- Check if mouse is over inventory
local inventory = components:get(149, 0)
if inventory then
local mouse_x, mouse_y = mouse:position()
if is_point_in_component(inventory, mouse_x, mouse_y) then
logger:info("Mouse is over inventory")
end
end
Finding Empty Inventory Slots
local function count_empty_slots()
local empty = 0
for i = 0, 27 do
local slot = components:get(149, 0, i)
if slot and slot:item_id() == -1 then
empty = empty + 1
end
end
return empty
end
local function find_first_empty_slot()
for i = 0, 27 do
local slot = components:get(149, 0, i)
if slot and slot:item_id() == -1 then
return slot, i
end
end
return nil, -1
end
local empty_count = count_empty_slots()
logger:info("Inventory has " .. empty_count .. " empty slots")
local empty_slot, index = find_first_empty_slot()
if empty_slot then
logger:info("First empty slot is at index " .. index)
end
Advanced Examples
Smart Component Search
local function find_clickable_button(text_hint)
-- Find buttons that are visible, have reasonable size, and contain text
local buttons = components:find_all({
visible_only = true,
with_text = true,
min_width = 30,
min_height = 15
})
-- Filter by text hint if provided
if text_hint then
for _, button in ipairs(buttons) do
local text = button:text()
if text and text:lower():find(text_hint:lower()) then
return button
end
end
end
return buttons[1]
end
local button = find_clickable_button("Accept")
if button then
button:click()
end
Component State Monitoring
local function monitor_component_text(group_id, component_id, callback, interval)
local last_text = ""
while true do
local comp = components:get(group_id, component_id)
if comp and comp:visible() then
local current_text = comp:text()
if current_text ~= last_text then
callback(current_text, last_text)
last_text = current_text
end
end
sleep(interval or 100)
end
end
-- Example usage
monitor_component_text(231, 5, function(new_text, old_text)
logger:info("Dialog text changed from '" .. old_text .. "' to '" .. new_text .. "'")
end, 200)
Component Content Types
Component content types determine how the component is rendered:
| Value | Type | Description |
|---|---|---|
| 0 | GRAPHIC | Image/sprite |
| 1 | TEXT | Text label |
| 2 | FILLED_BOX | Solid rectangle |
| 3 | MODEL | 3D model |
| 4 | TEXT_INPUT | Text input field |
| 5 | CONTAINER | Container for other components |
| 6 | MODEL_LIST | List of 3D models |
| 9 | LINE | Line separator |
Example:
local function find_text_components()
return components:find_all({
content_type = 1, -- TEXT
visible_only = true
})
end
local text_comps = find_text_components()
for _, comp in ipairs(text_comps) do
logger:info("Text: " .. comp:text())
end
Best Practices
Always Validate Components
-- Bad
local comp = components:get(149, 0)
comp:click() -- May error if comp is nil
-- Good
local comp = components:get(149, 0)
if comp and comp:is_valid() and comp:visible() then
comp:click()
else
logger:warn("Component not available")
end
Use Appropriate Access Methods
-- For parent components - use get()
local inventory = components:get(149, 0)
-- For child components - use get_child()
local first_slot = components:get_child(149, 0, 0)
-- Or navigate from parent to child
local inventory = components:get(149, 0)
if inventory then
local first_slot = inventory:child(0)
end
Cache Component Lookups
-- Bad: Repeated lookups
for i = 1, 10 do
local comp = components:get(149, 0)
if comp:visible() then
-- Do something
end
sleep(100)
end
-- Good: Cache the lookup
local comp = components:get(149, 0)
for i = 1, 10 do
if comp and comp:is_valid() and comp:visible() then
-- Do something
end
sleep(100)
end
Use Filters Efficiently
-- Bad: Get all then filter manually
local all = components:find_all({})
local visible = {}
for _, comp in ipairs(all) do
if comp:visible() and comp:text() ~= "" then
table.insert(visible, comp)
end
end
-- Good: Use filter options
local visible = components:find_all({
visible_only = true,
with_text = true
})
See Also
- Mouse - Mouse interaction
- Inventory - Higher-level inventory API
- Bank - Higher-level bank API
- Menu - Context menu interaction