Entity and EntityFinder
c.entity is always available. It queries the entities the client has currently loaded —
which is not the same as every entity in the zone. See
Finding entities for the practical guide; this page is
the reference.
EntityFinder
Nearest
c.entity:Nearest() --> Entity|nil
c.entity:Nearest('Troubled Warrior') --> Entity|nil
c.entity:Nearest(opts) --> Entity|nil
Options:
| Field | Type | Effect |
|---|---|---|
name | string | Matched against display_name, template_name, name |
tag | string | Entity must carry this tag |
min_mob_distance | number | Prefer a match at least this far from the nearest mob |
exclude | table | Set or list of Entity.key values to skip |
All filters are ANDed. Name matching squashes both sides to lowercase alphanumerics before a
substring test, so case, spaces, and hyphens are ignored — "Troubled Warrior" matches an
entity whose template_name is GH-Bear-Scout-1-R3 by way of its display_name.
A name that squashes to nothing but was not empty ("???", "---") matches nothing rather
than everything.
min_mob_distance is a preference. The finder returns the nearest match with that clearance
if one exists, otherwise the plain nearest match. An entity with no other mobs in the zone
counts as clear.
exclude accepts a set ({ [key] = true }) or a list ({ key1, key2 }). A set entry mapped
to false is ignored.
Other queries
c.entity:FindAll() --> Entity[]
c.entity:FindByName(name) --> Entity[]
c.entity:FindNearest(name) --> Entity|nil
c.entity:FindByTag(tag) --> Entity[]
c.entity:GetMobs() --> Entity[]
c.entity:GetNPCs() --> Entity[]
c.entity:GetWisps(type) --> Entity[]
GetWisps takes 'health', 'mana', or 'gold'. Anything else — including 'all', and
including a typo — returns every wisp, because the type filter falls through to the plain
wisp tag. So GetWisps('helth') quietly returns mana and gold wisps too.
FindNearest predates Nearest and only filters by name; called with no name it returns the
nearest entity of any kind. Prefer Nearest, which can also filter by tag and mob clearance.
FindByName('') returns an empty list rather than everything.
Every list query returns an empty table — and Nearest/FindNearest return nil — when
the entity list cannot be read. The reason is logged, not returned; there is no error value in
any of these signatures.
So "no mobs here" and "the read failed" are the same answer. A plugin that treats one empty result as "zone cleared, move on" will occasionally move on from a full zone. Require the empty result on two or three consecutive ticks before acting on it.
Entity
Fields
| Field | Type | Notes |
|---|---|---|
key | string | Stable identity. Use as a table key and in exclude. |
id | number | |
global_id | string | Empty for some entities |
name | string | Raw debug name |
display_name | string | Lang-resolved player-facing name; empty if lang not loaded |
display_key | string | The lang code, e.g. WizardMobs_00000553 |
template_name | string | ObjectTemplate name, e.g. WispHealth, GH-Bear-Scout-1-R3 |
template_id | number | |
type_name | string | |
entity_type | string | npc, mob, wisp, player, object, interactable |
distance | number | From the player |
address | number | Memory address |
tags | string[] | |
position | Position |
key verbatimkey is global_id when the game reports one, and addr:<address> otherwise. That fallback
lives in Go. Reconstructing the key in Lua — 'addr:' .. e.address, or using global_id
directly — produces a value that will not match, which silently breaks exclusion sets.
Methods
e:IsAlive() --> boolean
e:HasTag(tag) --> boolean
e:DistanceTo(target) --> number accepts Position or Entity
e:DistanceTo is 2D — X and Y only. p:DistanceTo on a bare Position is 3D and
includes Z, so the same two points can give you two different numbers depending on which
object you started from. Compare like with like, and prefer e.distance (also 2D, measured
from the player) when you just want "how far is that from me".
IsAlive is weaker than it soundsIt reports whether the entity carries a dead tag, nothing more. An entity with no tags at
all — which is what a partly-read entity looks like — reports alive. It is a filter for
corpses left in the entity list, not a health check. For a real one, read the participant in
combat or check the entity's health through the game UI.
Naming, in practice
The three name fields exist because none of them is universally available or universally meaningful:
local function identity_of(e)
local function present(v)
local s = tostring(v)
if s == '' or s == 'nil' then return nil end
return s
end
return present(e.display_name) or present(e.template_name) or present(e.name) or '<unnamed>'
end
For log lines, showing both is more useful than picking one:
local function label_of(e)
local display = present(e.display_name)
local internal = present(e.template_name) or present(e.name)
local name = display or internal or '<unnamed>'
if display and internal then name = display .. ' (' .. internal .. ')' end
return string.format('%s at %d', name, math.floor(tonumber(e.distance) or 0))
end
When a bot reports Troubled Warrior (GH-Bear-Scout-1-R3) at 412 you can immediately tell
whether the name resolution is working and whether the distance is plausible.
The wisp/wisp_health/wisp_mana/wisp_gold tags come from matching "wisp" (and
health/mana/gold) substrings against the entity's debug name, lang key, and template_name —
never display_name. The game ships wisps with template names like WispHealth, so tagging
works even when a wisp's display_name is empty.
Mob, NPC, and player tags come from the object's behavior list instead, not from any name
field. entity:Nearest's name filter still checks display_name, template_name, and
name together for every entity regardless of kind — the asymmetry is in how the tags get
assigned, not in how a name query matches.
Tags
| Tag | Applies to |
|---|---|
player | Other players |
npc | Interactable characters |
mob | Hostile creatures |
wisp | Any wisp |
wisp_health | Health wisps |
wisp_mana | Mana wisps |
wisp_gold | Gold wisps |
reagent | Gardening/crafting reagents |
pet | Pets |
mount | Mounts |
interactable | Generic interactable objects |
Tagging is derived from the object's attached behaviors (and its name, for wisps) and is not
perfect. A mob whose behaviors do not classify cleanly will not carry the mob tag, which is
why the fallback pattern exists:
c.entity:Nearest({ name = TARGET, tag = 'mob' }) or c.entity:Nearest({ name = TARGET })
Position
---@class Position
---@field x number
---@field y number
---@field z number
p:DistanceTo(other) --> number 3D, includes Z
Positions returned from entities and clients are plain tables with a metatable providing
DistanceTo. You can construct one yourself as { x = 100, y = 200, z = 0 } and pass it to
Teleport or InRange.
A hand-built table has no metatable, so own:DistanceTo(other) fails on it — call it on a
position the API gave you, or use utils.Distance, which takes
four numbers and is 2D.