NPCs API
Category: API Reference
Query and interact with NPC entities in the game world.
Functions
find_first
npcs:find_first(filters: table?) -> NPC?
Returns the first NPC matching the given filters.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
filters | table | No | Filter options |
Returns:
NPC?- First matching NPC, or nil if none found
Filters:
| Filter | Type | Description |
|---|---|---|
name | string | Exact name match |
name_contains | string | Name contains text |
name_matches_regex | string | Name matches regex pattern |
name_any | table | Match if name equals any in array of strings |
name_contains_any | table | Match if name contains any substring in array |
id | number | Exact ID match |
ids | table | Array of ID numbers |
within_distance_of_local | number | Distance from local player in tiles |
within_distance_of | table | Distance from position {x, y, floor, max_distance} |
at | table | Exact tile location {x, y, z} or [x, y, z] |
at_any | table | Array of tiles to check {{x, y, z}, ...} |
in_combat | boolean | Currently in combat |
not_in_combat | boolean | Not in combat |
is_interacting | boolean | Currently interacting |
not_interacting | boolean | Not interacting |
actions_contains | string | Has an action containing this text (case-insensitive) |
actions_equals | string | Has an action exactly matching this text (case-insensitive) |
Example:
-- Find by exact name
local guard = npcs:find_first({name = "Guard"})
-- Find by ID
local npc = npcs:find_first({id = 1234})
-- Find nearby NPC
local nearby = npcs:find_first({within_distance_of_local = 5})
-- Find NPC not in combat
local idle = npcs:find_first({
name_contains = "Banker",
not_in_combat = true
})
-- Find NPC with specific action
local pickpocketable = npcs:find_first({
name = "Guard",
actions_contains = "Pickpocket"
})
-- Find NPC at specific tile
local banker = npcs:find_first({
name = "Banker",
at = {3200, 3210, 0} -- or at = {x = 3200, y = 3210, floor = 0}
})
-- Find NPC at any of several tiles
local banker_any = npcs:find_first({
name = "Banker",
at_any = {
{3092, 3490, 0},
{3092, 3489, 0}
}
})
-- Find NPCs with any of multiple names
local npc = npcs:find_first({
name_any = {"Cow", "Chicken", "Goblin"}
})
-- Find NPCs with names containing any substring
local guard = npcs:find_first({
name_contains_any = {"Guard", "Knight", "Warrior"}
})
find_all
npcs:find_all(filters: table?) -> table
Returns all NPCs matching the given filters.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
filters | table | No | Filter options (same as find_first) |
Returns:
table- Array of NPC objects
Example:
-- Get all nearby NPCs
local nearby = npcs:find_all({within_distance_of_local = 20})
for _, npc in ipairs(nearby) do
logger:info("NPC: " .. npc:name() .. " (ID: " .. npc:id() .. ")")
end
-- Get all guards
local guards = npcs:find_all({name = "Guard"})
logger:info("Found " .. #guards .. " guards")
-- Get all NPCs in combat
local combat_npcs = npcs:find_all({in_combat = true})
find_nearest
npcs:find_nearest(filters: table?) -> NPC?
Returns the nearest NPC matching the given filters.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
filters | table | No | Filter options (same as find_first) |
Returns:
NPC?- Nearest matching NPC, or nil if none found
Example:
-- Find nearest NPC
local nearest = npcs:find_nearest()
if nearest then
logger:info("Nearest: " .. nearest:name() .. " (" .. nearest:distance_to_local_player() .. " tiles away)")
end
-- Find nearest banker
local banker = npcs:find_nearest({name = "Banker"})
-- Find nearest enemy
local enemy = npcs:find_nearest({in_combat = true})
Common Patterns
Interacting with Specific NPCs
local function interact_with_banker()
local banker = npcs:find_nearest({name = "Banker"})
if not banker then
logger:warn("No banker found nearby")
return false
end
logger:info("Interacting with banker at " .. banker:distance_to_local_player() .. " tiles")
return banker:smart_interact({action = "Bank"})
end
interact_with_banker()
Counting NPCs
local function count_npcs_by_name(name)
local npcs_list = npcs:find_all({name = name})
return #npcs_list
end
local guard_count = count_npcs_by_name("Guard")
logger:info("Guards nearby: " .. guard_count)
Finding Multiple NPC Types
local function find_important_npcs()
local npcs_list = {}
npcs_list.banker = npcs:find_first({name = "Banker"})
npcs_list.merchant = npcs:find_first({name = "Shop Keeper"})
npcs_list.quest_giver = npcs:find_first({name_contains = "Quest"})
return npcs_list
end
local npcs_list = find_important_npcs()
for role, npc in pairs(npcs_list) do
if npc then
logger:info(role .. ": " .. npc:name())
end
end
-- Or find any of multiple types at once
local any_important = npcs:find_first({
name_any = {"Banker", "Shop Keeper", "Grand Exchange Clerk"}
})
if any_important then
logger:info("Found: " .. any_important:name())
end
-- Find all NPCs of multiple types
local all_service_npcs = npcs:find_all({
name_contains_any = {"Bank", "Shop", "Exchange"}
})
logger:info("Found " .. #all_service_npcs .. " service NPCs")
Combat NPC Detection
local function is_under_attack()
local attacking = npcs:find_all({in_combat = true})
for _, npc in ipairs(attacking) do
if npc:distance_to_local_player() < 10 then
return true, npc:name()
end
end
return false
end
local under_attack, attacker = is_under_attack()
if under_attack then
logger:warn("Under attack by: " .. attacker)
end
Finding NPCs by ID
local function find_npc_by_any_id(id_list)
return npcs:find_first({ids = id_list})
end
-- Find any of these NPCs
local important_ids = {1, 2, 3, 123, 456}
local found = find_npc_by_any_id(important_ids)
if found then
logger:info("Found: " .. found:name())
end
NPC Distance Analysis
local function analyze_npc_distances()
local all_npcs = npcs:find_all()
logger:info("=== NPC Distance Analysis ===")
for _, npc in ipairs(all_npcs) do
local distance = npc:distance_to_local_player()
local status = npc:in_combat() and "(combat)" or "(idle)"
logger:info(npc:name() .. ": " .. distance .. " tiles " .. status)
end
end
analyze_npc_distances()
Waiting for NPC Interaction
local function wait_for_npc_interaction(npc_name, timeout)
local start = os.time()
while os.time() - start < timeout do
local npc = npcs:find_first({name = npc_name})
if npc and npc:is_interacting() then
logger:info("NPC is interacting with something")
return true
end
sleep(100)
end
return false
end
wait_for_npc_interaction("Banker", 30)
Safe NPC Interaction Pattern
local function safe_interact_npc(name, action)
-- Find the NPC
local npc = npcs:find_nearest({name = name})
if not npc then
logger:warn("NPC not found: " .. name)
return false
end
-- Check if valid
if not npc:is_valid() then
logger:warn("NPC is no longer valid")
return false
end
-- Check distance
local distance = npc:distance_to_local_player()
if distance > 15 then
logger:warn("NPC too far: " .. distance .. " tiles")
return false
end
logger:info("Interacting with " .. npc:name() .. " (" .. distance .. " tiles away)")
return npc:smart_interact({action = action})
end
safe_interact_npc("Banker", "Bank")
NPC State Monitoring
local function monitor_npc(name)
local npc = npcs:find_first({name = name})
if not npc then
logger:warn("NPC not found")
return
end
logger:info("=== NPC Info ===")
logger:info("Name: " .. npc:name())
logger:info("ID: " .. npc:id())
local health_pct = npc:health_percent()
local health_ratio = npc:health_ratio()
local health_scale = npc:health_scale()
if health_pct then
logger:info("Health: " .. health_pct .. "% (" .. (health_ratio or 0) .. "/" .. (health_scale or 0) .. ")")
else
logger:info("Health: Not visible (not in combat)")
end
logger:info("Combat: " .. tostring(npc:in_combat()))
logger:info("Animating: " .. tostring(npc:is_animating()))
logger:info("Moving: " .. tostring(npc:is_moving()))
logger:info("Distance: " .. npc:distance_to_local_player())
logger:info("Position: " .. npc:x() .. ", " .. npc:y())
logger:info("================")
end
monitor_npc("Guard")
Finding NPCs in Areas
local function find_npcs_in_area(area, npc_name)
local npcs_in_area = {}
local all_npcs = npcs:find_all({name = npc_name})
for _, npc in ipairs(all_npcs) do
if area:contains_tile(npc:tile()) then
table.insert(npcs_in_area, npc)
end
end
return npcs_in_area
end
local training_area = Area.new(
Tile.new(3200, 3200, 0),
Tile.new(3210, 3210, 0)
)
local guards = find_npcs_in_area(training_area, "Guard")
logger:info("Guards in training area: " .. #guards)
NPC Interaction Queue
local function interact_with_npcs(npc_names, action)
for _, name in ipairs(npc_names) do
local npc = npcs:find_nearest({name = name})
if npc then
logger:info("Interacting with: " .. npc:name())
npc:smart_interact({action = action})
sleep(1000) -- Wait between interactions
end
end
end
interact_with_npcs({"Guard", "Merchant"}, "Talk-to")
NPC Methods
Each NPC object returned by these functions has methods to access its properties:
-
Basic Info
name()→ stringid()→ numberanimation()→ number
-
Position
x(),y(),floor()→ numbertile()→ Tile objectdistance_to_local_player()→ number
-
State
is_animating()→ booleanis_moving()→ booleanis_interacting()→ booleanin_combat()→ booleanhealth_percent()→ number or nil (0-100)health_ratio()→ number or nil (current health value)health_scale()→ number or nil (maximum health value)
-
Interaction
click()→ booleaninteract(filters)→ booleansmart_interact(filters)→ boolean
See the methods listed above for complete NPC functionality.
Filter Combinations
Filters can be combined to create precise queries:
-- Find nearby idle bankers
local idle_bankers = npcs:find_all({
name = "Banker",
within_distance_of_local = 10,
not_in_combat = true,
not_interacting = true
})
-- Find NPCs by multiple IDs and distance
local npcs_by_ids = npcs:find_all({
ids = {1, 2, 3, 456},
within_distance_of_local = 15
})
-- Find combat NPCs in area
local combat_zone = npcs:find_all({
in_combat = true,
within_distance_of = {
x = 3200,
y = 3200,
floor = 0,
max_distance = 10
}
})
-- Find NPCs with name patterns
local quest_npcs = npcs:find_all({
name_matches_regex = ".*Quest.*",
not_in_combat = true
})
Related APIs
- Players API - Query and interact with players
- Game Objects API - Query game objects
- Combat API - Combat-related functionality
- Menu API - Interact with right-click menus