Filters Guide

PowBot Filters Guide


⚠️ API VERSION WARNING
This guide describes filter patterns and concepts that remain consistent.
Specific filter field names and method signatures may have changed.
Always verify current filter fields and methods in pow://api/reference.
Last pattern update: 2025-11-21

A comprehensive guide to using filters in the PowBot Desktop Lua API for querying and filtering game entities.

Table of Contents

  1. Filter Pattern Overview
  2. Common Filter Fields
  3. API-Specific Filters
  4. Filter Combinations
  5. Performance Tips
  6. Common Patterns
  7. Debugging Filters
  8. Empty vs Nil Filters

Filter Pattern Overview

PowBot uses a consistent filter pattern across all APIs. Filters are Lua tables that specify search criteria for finding game entities like NPCs, game objects, ground items, inventory items, and players.

The Basic Pattern

Most find methods accept an optional filter table:

-- General pattern
api:find_all(filter)      -- Find all matching entities
api:find_first(filter)    -- Find first matching entity
api:find_nearest(filter)  -- Find nearest matching entity (spatial APIs only)

When to Use Each Method

  • find_all: Returns an array of all matching entities. Use when you need to process multiple results.
  • find_first: Returns the first matching entity or nil. Faster than find_all when you only need one result.
  • find_nearest: Returns the closest matching entity to the local player. Only available for spatial entities (NPCs, GameObjects, GroundItems, Players).

Common Filter Fields

These filter fields are available across multiple APIs with consistent behavior.

Name Filters

name - Exact Match

Matches entities with exactly this name (case-insensitive).

-- Find NPCs named exactly "Cow"
local cows = npcs:find_all({ name = "Cow" })

-- Find items named exactly "Bones"
local bones = inventory:find_all({ name = "Bones" })

name_contains - Substring Match

Matches entities whose name contains the substring (case-insensitive).

-- Find all objects with "tree" in the name
local trees = game_objects:find_all({ name_contains = "tree" })

-- Find all ore in inventory
local ores = inventory:find_all({ name_contains = "ore" })

-- Find any pickaxe equipped
local pickaxe = equipment:find_first({ name_contains = "pickaxe" })

name_matches_regex - Regex Pattern Match

Matches entities using a case-insensitive regular expression pattern. Powerful for complex name matching.

-- Find items named "Cowhide" OR "Bones" OR "Raw beef"
local loot = ground_items:find_all({
    name_matches_regex = "Cowhide|Bones|Raw beef"
})

-- Find any stamina potion with charges (1-4)
local stamina = inventory:find_first({
    name_matches_regex = "Stamina potion\\([1-4]\\)"
})

-- Find logs ending with "logs" (Oak logs, Willow logs, etc.)
local logs = inventory:find_all({
    name_matches_regex = ".*logs$"
})

Important: In Lua strings, backslashes need to be escaped. Use \\ for regex special characters:

  • "\\(" for literal (
  • "\\)" for literal )
  • "\\." for literal .
  • "\\d" for digit matching

exclude - Exclusion-Based Filtering

Exclude entities matching the criteria in the nested filter table. The exclude table accepts the same filter fields as the main filter, and uses AND logic (all criteria in exclude must match for exclusion).

-- Find all NPCs except cows
local npcs = npcs:find_all({ exclude = {name = "Cow"} })

-- Find items that don't contain "ore" in name
local items = inventory:find_all({ exclude = {name_contains = "ore"} })

-- Find objects excluding specific names
local objects = game_objects:find_all({ 
    exclude = {name_any = {"Tree", "Rock", "Door"}}
})

-- Find items excluding specific IDs
local items = inventory:find_all({ exclude = {ids = {995, 996, 997}} })

-- Complex: exclude items matching multiple criteria
local items = inventory:find_all({ 
    name_contains = "sword",  -- Must contain "sword"
    exclude = {name_contains = "broken"}  -- But not "broken"
})

Note: The exclude table uses AND logic - all criteria in the exclude table must match for an entity to be excluded. Positive filters and exclude filters are combined with AND: an entity must match positive filters AND NOT match exclude filters.

ID Filters

id - Exact ID Match

Matches entities with exactly this game ID.

-- Find NPC with specific ID
local npc = npcs:find_first({ id = 2692 })

-- Find item by ID
local item = inventory:find_first({ id = 995 })  -- Coins

ids - Multiple ID Match

Matches entities whose ID is in the provided list.

-- Find any of several NPCs
local monsters = npcs:find_all({
    ids = {2692, 2693, 2694}  -- Different goblin variants
})

-- Find any food item
local food = inventory:find_first({
    ids = {379, 361, 373}  -- Lobster, Tuna, Swordfish
})

id_any - Multiple ID Match (Alias)

Alias for ids - behaves exactly the same way. Matches entities whose ID is in the provided list.

-- Find any of several NPCs using id_any
local monsters = npcs:find_all({
    id_any = {2692, 2693, 2694}  -- Different goblin variants
})

-- Find any food item using id_any
local food = inventory:find_first({
    id_any = {379, 361, 373}  -- Lobster, Tuna, Swordfish
})

Distance Filters

within_distance_of_local - Distance from Player

Matches entities within the specified distance from the local player.

-- Find NPCs within 10 tiles
local nearby_npcs = npcs:find_all({
    name = "Cow",
    within_distance_of_local = 10.0
})

-- Find ground items within 5 tiles
local nearby_items = ground_items:find_all({
    within_distance_of_local = 5.0
})

within_distance_of - Distance from Specific Tile

Matches entities within the specified distance from a specific tile. Accepts an array or table format.

-- Array format: {x, y, floor, max_distance}
local items_near_target = ground_items:find_all({
    name = "Cowhide",
    within_distance_of = {3200, 3200, 0, 4.0}
})

-- Table format: {x = x, y = y, floor = floor, distance = max_distance}
local npcs_near_tile = npcs:find_all({
    name = "Guard",
    within_distance_of = {
        x = 3100,
        y = 3500,
        floor = 0,
        distance = 10.0
    }
})

-- Using last known position
local last_combat_pos = { x = target:x(), y = target:y(), floor = target:floor() }
local loot = ground_items:find_nearest({
    name = "Bones",
    within_distance_of = {
        last_combat_pos.x,
        last_combat_pos.y,
        last_combat_pos.floor,
        2.0
    }
})

API-Specific Filters

NPCs (npcs:find_all, npcs:find_first, npcs:find_nearest)

--- @class NpcFilterInput
--- @field name string | nil              -- Exact name match
--- @field name_contains string | nil     -- Name substring match
--- @field name_matches_regex string | nil -- Name regex match
--- @field id integer | nil               -- Exact ID match
--- @field ids integer[] | nil            -- List of IDs to match
--- @field within_distance_of_local number | nil
--- @field within_distance_of table | nil
--- @field not_in_combat boolean | nil    -- Filter NPCs NOT in combat
--- @field in_combat boolean | nil        -- Filter NPCs in combat
--- @field not_interacting boolean | nil  -- Filter NPCs NOT interacting
--- @field is_interacting boolean | nil   -- Filter NPCs interacting
--- @field reachable boolean | nil        -- Filter NPCs reachable from player (true) or not reachable (false)

Examples

-- Find nearest idle cow
local cow = npcs:find_nearest({
    name = "Cow",
    not_in_combat = true,
    not_interacting = true,
    within_distance_of_local = 15.0
})

-- Find all chickens in combat (scavenging kills)
local fighting_chickens = npcs:find_all({
    name = "Chicken",
    in_combat = true
})

-- Find interacting NPCs (being attacked by someone)
local busy_npcs = npcs:find_all({
    is_interacting = true,
    within_distance_of_local = 20.0
})

Game Objects (game_objects:find_all, game_objects:find_first, game_objects:find_nearest)

--- @class GameObjectFilterInput
--- @field name string | nil
--- @field name_contains string | nil
--- @field name_matches_regex string | nil
--- @field id integer | nil
--- @field ids integer[] | nil
--- @field type string | nil              -- "interactive", "boundary", "floor", "wall"
--- @field within_distance_of_local number | nil
--- @field within_distance_of table | nil
--- @field reachable boolean | nil        -- Filter objects reachable from player (true) or not reachable (false)

Examples

-- Find nearest copper rock
local rock = game_objects:find_nearest({
    name_contains = "Copper",
    within_distance_of_local = 10.0
})

-- Find trees
local trees = game_objects:find_all({
    name = "Tree",
    within_distance_of_local = 15.0
})

-- Find doors (boundary type)
local door = game_objects:find_first({
    name_contains = "Door",
    type = "boundary",
    within_distance_of_local = 5.0
})

-- Find bank booth or chest
local bank_obj = game_objects:find_nearest({
    name_matches_regex = "Bank booth|Bank chest",
    within_distance_of_local = 10.0
})

-- Find interactive objects
local ladder = game_objects:find_first({
    name = "Ladder",
    type = "interactive"
})

Ground Items (ground_items:find_all, ground_items:find_first, ground_items:find_nearest)

--- @class GroundItemFilterInput
--- @field name string | nil
--- @field name_contains string | nil
--- @field name_matches_regex string | nil
--- @field id integer | nil
--- @field ids integer[] | nil
--- @field noted boolean | nil              -- Filter noted ground items (true) or unnoted ground items (false)
--- @field within_distance_of_local number | nil
--- @field within_distance_of table | nil
--- @field min_store_price integer | nil       -- Minimum store price (vendor value)
--- @field max_store_price integer | nil       -- Maximum store price (vendor value)
--- @field min_store_price_total integer | nil -- Minimum total store price (store_price × stack_size)
--- @field max_store_price_total integer | nil -- Maximum total store price (store_price × stack_size)
--- @field min_high_price integer | nil        -- Minimum high price from Grand Exchange
--- @field max_high_price integer | nil        -- Maximum high price from Grand Exchange
--- @field has_high_price boolean | nil        -- Filter items with high price data available
--- @field min_high_price_total integer | nil  -- Minimum total high price (high_price × stack_size)
--- @field max_high_price_total integer | nil  -- Maximum total high price (high_price × stack_size)
--- @field min_low_price integer | nil         -- Minimum low price from Grand Exchange
--- @field max_low_price integer | nil         -- Maximum low price from Grand Exchange
--- @field has_low_price boolean | nil         -- Filter items with low price data available
--- @field min_low_price_total integer | nil   -- Minimum total low price (low_price × stack_size)
--- @field max_low_price_total integer | nil   -- Maximum total low price (low_price × stack_size)
--- @field min_average_price integer | nil     -- Minimum average price (average of high and low)
--- @field max_average_price integer | nil     -- Maximum average price (average of high and low)
--- @field has_average_price boolean | nil     -- Filter items with average price data available
--- @field min_average_price_total integer | nil -- Minimum total average price (average_price × stack_size)
--- @field max_average_price_total integer | nil -- Maximum total average price (average_price × stack_size)
--- @field reachable boolean | nil        -- Filter ground items reachable from player (true) or not reachable (false)

Examples

-- Find bones near last kill
local bones = ground_items:find_first({
    name = "Bones",
    within_distance_of = {
        last_target.x,
        last_target.y,
        last_target.floor,
        3.0
    }
})

-- Find mark of grace
local mark = ground_items:find_nearest({
    name = "Mark of grace",
    within_distance_of_local = 20.0
})

-- Find multiple loot types using regex
local loot = ground_items:find_all({
    name_matches_regex = "Cowhide|Raw beef|Bones",
    within_distance_of_local = 5.0
})

-- Find valuable ground items by price
local valuable_loot = ground_items:find_all({
    min_high_price_total = 5000,
    has_high_price = true,
    within_distance_of_local = 10.0
})

Inventory (inventory:find_all, inventory:find_first)

--- @class ItemFilter
--- @field id integer | nil                    -- Exact item ID
--- @field ids integer[] | nil                 -- List of item IDs
--- @field name string | nil                   -- Exact name match
--- @field name_contains string | nil          -- Name substring match
--- @field min_stack integer | nil             -- Minimum stack size
--- @field max_stack integer | nil             -- Maximum stack size
--- @field slot integer | nil                  -- Specific slot index
--- @field slot_range {[1]: integer, [2]: integer} | nil  -- Slot range [min, max]
--- @field charges_eq integer | nil            -- Exact charges
--- @field charges_gt integer | nil            -- Greater than charges
--- @field charges_lt integer | nil            -- Less than charges
--- @field charges_gte integer | nil           -- Greater than or equal
--- @field charges_lte integer | nil           -- Less than or equal
--- @field min_store_price integer | nil       -- Minimum store price (vendor value)
--- @field max_store_price integer | nil       -- Maximum store price (vendor value)
--- @field min_store_price_total integer | nil -- Minimum total store price (store_price × stack_size)
--- @field max_store_price_total integer | nil -- Maximum total store price (store_price × stack_size)
--- @field min_high_price integer | nil        -- Minimum high price from Grand Exchange
--- @field max_high_price integer | nil        -- Maximum high price from Grand Exchange
--- @field has_high_price boolean | nil        -- Filter items with high price data available
--- @field min_high_price_total integer | nil  -- Minimum total high price (high_price × stack_size)
--- @field max_high_price_total integer | nil  -- Maximum total high price (high_price × stack_size)
--- @field min_low_price integer | nil         -- Minimum low price from Grand Exchange
--- @field max_low_price integer | nil         -- Maximum low price from Grand Exchange
--- @field has_low_price boolean | nil         -- Filter items with low price data available
--- @field min_low_price_total integer | nil   -- Minimum total low price (low_price × stack_size)
--- @field max_low_price_total integer | nil   -- Maximum total low price (low_price × stack_size)
--- @field min_average_price integer | nil     -- Minimum average price (average of high and low)
--- @field max_average_price integer | nil     -- Maximum average price (average of high and low)
--- @field has_average_price boolean | nil     -- Filter items with average price data available
--- @field min_average_price_total integer | nil -- Minimum total average price (average_price × stack_size)
--- @field max_average_price_total integer | nil -- Maximum total average price (average_price × stack_size)

Examples

-- Find item by name
local item = inventory:find_first({ name = "Bones" })

-- Find all logs
local logs = inventory:find_all({ name_contains = "logs" })

-- Find items in specific slots (first 4 slots)
local top_row = inventory:find_all({
    slot_range = {0, 3}
})

-- Find item in slot 5
local slot_item = inventory:find_first({ slot = 5 })

-- Find items with minimum stack size
local big_stacks = inventory:find_all({
    min_stack = 10
})

-- Find item with specific charges (Ring of dueling with 4 charges)
local ring = inventory:find_first({
    name_contains = "Ring of dueling",
    charges_eq = 4
})

-- Find items with at least 2 charges
local charged_items = inventory:find_all({
    charges_gte = 2
})

-- Count specific items
local bone_count = inventory:count({ name = "Bones" })
local all_count = inventory:count()  -- Count all items

-- Find valuable items by price
local valuable = inventory:find_all({
    min_high_price_total = 10000,
    has_high_price = true
})

-- Find items with price in range
local mid_value = inventory:find_all({
    min_average_price = 1000,
    max_average_price = 5000,
    has_average_price = true
})

-- Find cheap items for dropping
local junk = inventory:find_all({
    max_store_price = 100
})

Bank (bank:find_all, bank:find_first)

Bank uses the same ItemFilter as inventory:

-- Find food in bank
local food = bank:find_first({ name = "Lobster" })

-- Check if bank contains item
if bank:contains(995) then  -- Check by ID
    logger:info("Bank contains coins")
end

-- Count specific items in bank
local shark_count = bank:count_of(385)  -- Count by ID
local food_count = bank:count("Lobster")  -- Count by name

Equipment (equipment:find_all, equipment:find_first)

Equipment also uses ItemFilter:

-- Check if wearing specific item
local has_armor = equipment:find_first({
    name = "Bronze platebody"
})

-- Find any equipped weapon with "sword" in name
local sword = equipment:find_first({
    name_contains = "sword"
})

-- Get item in specific equipment slot
local weapon = equipment:get_item_in_slot("weapon")
local head_armor = equipment:get_item_in_slot("head")

-- Available slots: head, cape, neck, weapon, torso, shield, legs, hands, feet, ring, ammo

Players (players:find_all, players:find_first, players:find_nearest)

--- @class PlayerFilterInput
--- @field name string | nil
--- @field name_contains string | nil
--- @field name_matches_regex string | nil
--- @field within_distance_of_local number | nil
--- @field within_distance_of table | nil
--- @field combat_level integer | nil        -- Exact combat level
--- @field min_combat_level integer | nil    -- Minimum combat level
--- @field max_combat_level integer | nil    -- Maximum combat level
--- @field is_interacting boolean | nil
--- @field not_interacting boolean | nil
--- @field is_moving boolean | nil
--- @field is_animating boolean | nil
--- @field reachable boolean | nil        -- Filter players reachable from local player (true) or not reachable (false)

Examples

-- Find nearby players
local nearby_players = players:find_all({
    within_distance_of_local = 15.0
})

-- Find high level players
local high_levels = players:find_all({
    min_combat_level = 100,
    within_distance_of_local = 20.0
})

-- Find players in combat level range
local similar_levels = players:find_all({
    min_combat_level = 40,
    max_combat_level = 60
})

-- Find moving players
local moving = players:find_all({
    is_moving = true,
    within_distance_of_local = 10.0
})
--- @class MenuItemFilterInput
--- @field action string | nil              -- Action text (or pass string directly)
--- @field action_contains string | nil     -- Action substring match
--- @field action_equals string | nil       -- Exact action match
--- @field target string | nil              -- Target text
--- @field target_contains string | nil     -- Target substring match
--- @field target_equals string | nil       -- Exact target match
--- @field sub_menu_action string | nil    -- Open context menu with action then select this sub-menu option (e.g. item:interact({ action = "Rub", sub_menu_action = "Grand Exchange" }))
--- @field sub_menu_action_contains string | nil    -- Sub-menu option substring match

Examples

-- Simple string filter (checks action)
menu:interact_with("Attack")

-- Complex filter
menu:interact_with({
    action = "Use",
    target_contains = "Door"
})

-- Find menu item by action
local index = menu:find_item_index({
    action_contains = "Walk"
})

Filter Combinations

Filters are AND conditions - all specified criteria must match.

Simple Combinations

-- Name AND distance
local nearby_cows = npcs:find_all({
    name = "Cow",
    within_distance_of_local = 10.0
})

-- Name AND state
local idle_cows = npcs:find_all({
    name = "Cow",
    not_in_combat = true,
    not_interacting = true
})

Complex Combinations

-- Multiple conditions for precise targeting
local target = npcs:find_nearest({
    name = "Guard",
    not_in_combat = true,
    not_interacting = true,
    within_distance_of = {
        start_pos.x,
        start_pos.y,
        start_pos.z,
        15.0
    }
})

-- Inventory filtering with multiple criteria
local valuable_items = inventory:find_all({
    name_contains = "diamond",
    min_stack = 1,
    slot_range = {0, 13}  -- First two rows only
})

-- Player filtering with level range
local pk_targets = players:find_all({
    min_combat_level = my_level - 5,
    max_combat_level = my_level + 5,
    not_interacting = true,
    within_distance_of_local = 15.0
})

Using OR Logic with Regex

When you need OR logic (match A OR B), use name_matches_regex:

-- Find "Lobster" OR "Swordfish" OR "Shark"
local food = inventory:find_all({
    name_matches_regex = "Lobster|Swordfish|Shark"
})

-- Find multiple ore types
local ores = inventory:find_all({
    name_matches_regex = "Copper ore|Tin ore|Iron ore"
})

-- Find NPCs with multiple names
local monsters = npcs:find_all({
    name_matches_regex = "Goblin|Imp|Spider",
    not_in_combat = true
})

Manual OR Logic

For complex OR conditions that can't use regex, filter manually:

-- Find NPCs matching multiple IDs
local all_npcs = npcs:find_all({ within_distance_of_local = 20.0 })
local valid_npcs = {}

for _, npc in ipairs(all_npcs) do
    local npc_id = tostring(npc:id())

    -- Check if NPC ID is in our selection
    for _, selected_id in ipairs(selected_npc_ids) do
        if selected_id == npc_id then
            if not npc:in_combat() and not npc:interacting() then
                table.insert(valid_npcs, npc)
            end
            break
        end
    end
end

Performance Tips

1. Use Specific Filters First

More specific filters reduce the search space:

-- GOOD: Specific name + distance
local rock = game_objects:find_nearest({
    name = "Copper rocks",
    within_distance_of_local = 10.0
})

-- LESS EFFICIENT: Very broad search
local all_objects = game_objects:find_all({
    within_distance_of_local = 50.0
})

2. Distance Filters are Efficient

Always use distance filters for spatial queries:

-- GOOD: Limited search radius
local npcs = npcs:find_all({
    name = "Cow",
    within_distance_of_local = 15.0
})

-- AVOID: Searching entire world
local npcs = npcs:find_all({ name = "Cow" })  -- No distance limit

3. Use find_first When You Only Need One

-- GOOD: Stops after finding first match
local bones = inventory:find_first({ name = "Bones" })

-- WASTEFUL: Finds all then takes first
local all_bones = inventory:find_all({ name = "Bones" })
local bones = all_bones[1]

4. Use find_nearest for Spatial Entities

-- GOOD: Efficiently finds closest
local npc = npcs:find_nearest({
    name = "Cow",
    within_distance_of_local = 15.0
})

-- LESS EFFICIENT: Manual distance calculation
local all_npcs = npcs:find_all({ name = "Cow" })
local closest = nil
local closest_dist = math.huge
for _, npc in ipairs(all_npcs) do
    local dist = calculate_distance(npc)
    if dist < closest_dist then
        closest = npc
        closest_dist = dist
    end
end

5. Cache Filters When Reusing

-- GOOD: Define filter once, reuse
local cow_filter = {
    name = "Cow",
    not_in_combat = true,
    within_distance_of_local = 15.0
}

-- Use it multiple times
local cow1 = npcs:find_nearest(cow_filter)
-- ... later ...
local cow2 = npcs:find_nearest(cow_filter)

-- AVOID: Recreating filter each time
local cow1 = npcs:find_nearest({
    name = "Cow",
    not_in_combat = true,
    within_distance_of_local = 15.0
})
local cow2 = npcs:find_nearest({
    name = "Cow",
    not_in_combat = true,
    within_distance_of_local = 15.0
})

6. Use contains for Simple Checks

-- GOOD: Efficient boolean check
if bank:contains(995) then  -- Check by ID
    -- Has coins
end

-- LESS EFFICIENT: Unnecessary find
local coins = bank:find_first({ id = 995 })
if coins then
    -- Has coins
end

Common Patterns

Pattern 1: Combat Target Selection

function find_combat_target(npc_name, max_distance)
    return npcs:find_nearest({
        name = npc_name,
        not_in_combat = true,
        not_interacting = true,
        within_distance_of_local = max_distance
    })
end

Pattern 2: Loot Collection Near Last Kill

function collect_loot(last_kill_pos, loot_names)
    -- Build regex from loot list: "Bones|Cowhide|Raw beef"
    local pattern = table.concat(loot_names, "|")

    local item = ground_items:find_nearest({
        name_matches_regex = pattern,
        within_distance_of = {
            last_kill_pos.x,
            last_kill_pos.y,
            last_kill_pos.floor,
            3.0
        }
    })

    if item then
        item:interact({ action_contains = "Take" })
        return true
    end
    return false
end

Pattern 3: Resource Gathering (Mining, Woodcutting, Fishing)

function find_resource(resource_name)
    local resource = game_objects:find_nearest({
        name_contains = resource_name,
        within_distance_of_local = 15.0
    })

    if resource and resource:is_visible() then
        return resource
    end
    return nil
end

Pattern 4: Check Inventory for Multiple Items

function has_any_food()
    local food_pattern = "Lobster|Swordfish|Shark|Tuna"
    local food = inventory:find_first({
        name_matches_regex = food_pattern
    })
    return food ~= nil
end

function count_all_food()
    local food_pattern = "Lobster|Swordfish|Shark|Tuna"
    return inventory:count({
        name_matches_regex = food_pattern
    })
end

Pattern 5: Equipment Validation

function has_weapon_equipped()
    local weapon = equipment:find_first({
        slot = 3  -- Weapon slot
    })
    return weapon ~= nil
end

function has_pickaxe()
    -- Check equipment first
    local equipped = equipment:find_first({
        name_contains = "pickaxe"
    })

    if equipped then
        return true
    end

    -- Check inventory
    local in_inv = inventory:find_first({
        name_contains = "pickaxe"
    })

    return in_inv ~= nil
end

Pattern 6: Bank Withdrawals

function withdraw_food(food_names, amount)
    if not bank:opened() then
        return false
    end

    for _, food_name in ipairs(food_names) do
        local bank_item = bank:find_first({ name = food_name })

        if bank_item then
            local available = bank_item:stack_size()
            local to_withdraw = math.min(available, amount)

            if to_withdraw > 0 then
                bank:withdraw({ name = food_name }, to_withdraw)
                return true
            end
        end
    end

    return false
end

Pattern 7: Conditional Dropping

function drop_all_except_tools()
    -- Keep pickaxe, axe, and tinderbox
    local keep_pattern = "pickaxe|axe|Tinderbox"

    inventory:drop_all_except({
        name_matches_regex = keep_pattern
    })
end

Debugging Filters

Problem: Filter Returns No Results

-- Log what's actually available
local all_npcs = npcs:find_all({ within_distance_of_local = 20.0 })
logger:info("Found " .. #all_npcs .. " NPCs nearby")

for _, npc in ipairs(all_npcs) do
    logger:info("  - " .. npc:name() .. " (ID: " .. npc:id() .. ")")
end

-- Then apply your filter
local filtered = npcs:find_all({
    name = "Your Name Here",
    within_distance_of_local = 20.0
})
logger:info("Filtered results: " .. #filtered)

Problem: Case Sensitivity Issues

Remember: name and name_contains are case-insensitive, but check exact spelling:

-- These are equivalent:
local item1 = inventory:find_first({ name = "bones" })
local item2 = inventory:find_first({ name = "Bones" })
local item3 = inventory:find_first({ name = "BONES" })

Problem: Distance Filter Not Working

Ensure you're using correct tile coordinates:

local player = players:local_player()
logger:info("Player position: " .. player:x() .. ", " .. player:y() .. ", floor: " .. player:floor())

-- Then check target position
local target = npcs:find_nearest({ name = "Cow" })
if target then
    logger:info("Target position: " .. target:x() .. ", " .. target:y())

    local dx = target:x() - player:x()
    local dy = target:y() - player:y()
    local distance = math.sqrt(dx*dx + dy*dy)
    logger:info("Actual distance: " .. distance)
end

Problem: Regex Not Matching

Test your regex pattern:

-- Log all item names to see exact format
local items = inventory:find_all()
for _, item in ipairs(items) do
    logger:info("Item: '" .. item:name() .. "'")
end

-- Remember to escape special characters in Lua:
local pattern = "Ring of dueling\\([1-8]\\)"  -- Note the double backslashes

Problem: Entity No Longer Valid

Always check if entity is still valid before using:

local npc = npcs:find_first({ name = "Cow" })

if npc and npc:is_valid() then
    npc:interact({ action = "Attack" })
else
    logger:warn("NPC is no longer valid")
end

Empty vs Nil Filters

Passing nil

When you pass nil as a filter, the API returns all entities (no filtering):

-- Returns ALL inventory items
local all_items = inventory:find_all(nil)

-- Returns ALL NPCs within reasonable range
local all_npcs = npcs:find_all(nil)

-- Useful for counting total items
local total_items = inventory:count(nil)
local total_items = inventory:count()  -- Equivalent

Passing Empty Table {}

An empty table {} means "no criteria" and behaves the same as nil:

-- These are equivalent:
local items1 = inventory:find_all(nil)
local items2 = inventory:find_all({})

-- Both return all items

When to Use Each

-- Use nil for clarity when you want everything
function get_all_inventory_items()
    return inventory:find_all(nil)
end

-- Use {} when building dynamic filters
local filter = {}

if options.specific_name then
    filter.name = options.specific_name
end

if options.max_distance then
    filter.within_distance_of_local = options.max_distance
end

-- filter might be {} if no options set
local results = npcs:find_all(filter)

Counting Examples

-- Count all items
local total = inventory:count()
local total = inventory:count(nil)
local total = inventory:count({})  -- All equivalent

-- Count specific items
local bone_count = inventory:count({ name = "Bones" })
local ore_count = inventory:count({ name_contains = "ore" })

Quick Reference

Common Filter Combinations

-- NPCs
npcs:find_nearest({
    name = "Cow",
    not_in_combat = true,
    not_interacting = true,
    within_distance_of_local = 15.0
})

-- Game Objects
game_objects:find_nearest({
    name_contains = "Copper",
    within_distance_of_local = 10.0
})

-- Ground Items
ground_items:find_all({
    name_matches_regex = "Bones|Cowhide",
    within_distance_of_local = 5.0
})

-- Inventory
inventory:find_first({
    name_contains = "logs",
    min_stack = 1
})

-- Bank
bank:find_first({
    name = "Lobster"
})

-- Equipment
equipment:find_first({
    name_contains = "sword"
})

-- Players
players:find_all({
    min_combat_level = 50,
    max_combat_level = 80,
    within_distance_of_local = 15.0
})

Filter Field Summary

FieldTypeAPIsDescription
namestringAllExact name match (case-insensitive)
name_containsstringAllSubstring match (case-insensitive)
name_matches_regexstringAll except ComponentsRegex pattern match
idintegerNPCs, GameObjects, GroundItems, ItemsExact ID match
idsinteger[]NPCs, GameObjects, GroundItems, ItemsList of IDs
within_distance_of_localnumberSpatial entitiesMax distance from player
within_distance_oftableSpatial entitiesMax distance from tile
not_in_combatbooleanNPCsFilter NPCs not in combat
in_combatbooleanNPCsFilter NPCs in combat
not_interactingbooleanNPCs, PlayersNot interacting with anything
is_interactingbooleanNPCs, PlayersIs interacting
typestringGameObjectsObject type filter
min_stackintegerItemsMinimum stack size
max_stackintegerItemsMaximum stack size
slotintegerItemsSpecific slot index
slot_rangeinteger[]ItemsSlot range [min, max]
charges_eqintegerItemsExact charge count
charges_gt/gte/lt/lteintegerItemsCharge comparisons
notedbooleanItems, GroundItemsFilter noted or unnoted items
min_store_price / max_store_priceintegerItems, GroundItemsStore price range
min_store_price_total / max_store_price_totalintegerItems, GroundItemsTotal store price range
min_high_price / max_high_priceintegerItems, GroundItemsHigh price range (GE)
has_high_pricebooleanItems, GroundItemsHas high price data
min_low_price / max_low_priceintegerItems, GroundItemsLow price range (GE)
has_low_pricebooleanItems, GroundItemsHas low price data
min_average_price / max_average_priceintegerItems, GroundItemsAverage price range (GE)
has_average_pricebooleanItems, GroundItemsHas average price data
*_price_total variantsintegerItems, GroundItemsPrice × stack_size variants
combat_levelintegerPlayersExact combat level
min_combat_levelintegerPlayersMinimum combat level
max_combat_levelintegerPlayersMaximum combat level
is_movingbooleanPlayersIs currently moving
is_animatingbooleanPlayersIs currently animating
reachablebooleanNPCs, GameObjects, GroundItems, PlayersFilter entities reachable from player position

Best Practices Summary

  1. Always use distance filters for spatial queries to limit search space
  2. Use find_first or find_nearest when you only need one result
  3. Use regex for OR logic when matching multiple values
  4. Cache common filters to avoid recreating them
  5. Check if entities are valid before using them
  6. Log filter results when debugging
  7. Use contains() methods for simple existence checks
  8. Combine filters for precise results (AND logic)
  9. Use count() for quantity checks instead of finding and counting manually
  10. Test filters incrementally - start broad, then add criteria

Additional Resources


Happy Filtering!