Skip to main content

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:

FieldTypeEffect
namestringMatched against display_name, template_name, name
tagstringEntity must carry this tag
min_mob_distancenumberPrefer a match at least this far from the nearest mob
excludetableSet 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.

A failed read looks exactly like an empty zone

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

FieldTypeNotes
keystringStable identity. Use as a table key and in exclude.
idnumber
global_idstringEmpty for some entities
namestringRaw debug name
display_namestringLang-resolved player-facing name; empty if lang not loaded
display_keystringThe lang code, e.g. WizardMobs_00000553
template_namestringObjectTemplate name, e.g. WispHealth, GH-Bear-Scout-1-R3
template_idnumber
type_namestring
entity_typestringnpc, mob, wisp, player, object, interactable
distancenumberFrom the player
addressnumberMemory address
tagsstring[]
positionPosition
Use key verbatim

key 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 sounds

It 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.

Wisp tags key off template_name, not display_name

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

TagApplies to
playerOther players
npcInteractable characters
mobHostile creatures
wispAny wisp
wisp_healthHealth wisps
wisp_manaMana wisps
wisp_goldGold wisps
reagentGardening/crafting reagents
petPets
mountMounts
interactableGeneric 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.