MobEntity¶
Inherits from: LivingEntity
Represents a mobile entity within the game, inheriting from LivingEntity and adding movement, looking and fighting.
A mob normally acts on its own. A spell takes over by giving it a task: while a task runs, the mob's own behavior is suspended, and when the task ends the mob carries on as before. Only one task runs at a time, so starting another one replaces it.
Starting a task
Every start... function returns true when the task started, or false and a reason when it did
not, like "no_path" when no route to the target could be found within searchRange. The
TaskReason type lists all reasons.
Watching a task
busy tells whether a task is running. task describes the task this entity is executing, or the
last one it executed, and keeps that description after the task has ended:
-
nameis the TaskName, like "path" or "attack". -
stateis the TaskState: "running", "finished", "failed" or "interrupted". -
reasonis the TaskReason of a state of "failed" or "interrupted". -
ageis the number of ticks since the task started. -
the options the task was started with, and the values the task computed. See each function, and MobTask for all of them.
A task that fails to start does not change task: read the reason from the return value instead.
awaitTask() waits until the task has ended and reports how, the same way the start...
functions do.
Hands
A mob has no inventory, only its two hands. The block tasks work with what the mob holds:
startBreakBlock uses the main hand as its tool, and startPlaceBlock, startPutInto and
startTakeFrom take their items from, or put them into, the hand named by their hand option.
Use setEquipment to hand a mob something, and getEquipment to see what it holds.
Misspelling an option is an error, not a wish: { speeed = 2 } is rejected rather than ignored.
Example
local pig = spell:summon("pig") --[[@as MobEntity]]
local ok, reason = pig:startPathTo(pig.pos + Vec3(0, 0, 10))
if ok then
ok, reason = pig:awaitTask() -- sleeps until the task has ended
end
print(ok and "The pig got there." or "The pig did not get there: " .. reason)
Fields¶
ai: boolean — Read/Write¶
Indicates whether this entity's own artificial intelligence is enabled. While it is disabled the entity neither acts on its own nor executes tasks, and it does not move at all, not even falling. This setting is persisted.
busy: boolean — Read-Only¶
Indicates whether this entity is currently executing a task.
canPickUpLoot: boolean — Read/Write¶
Indicates whether this entity picks up items it walks over. This setting is persisted.
lookAt: Entity|Vec3|nil — Read/Write¶
A reference to the entity this entity keeps looking at, a copy of the position it keeps looking at, or nil if it looks wherever it wants. Assigning a value overrides the direction its task would look in.
target: LivingEntity? — Read-Only¶
A reference to the entity this entity is attacking, or nil if it attacks none. The entity may have chosen this target itself.
task: MobTask? — Read-Only¶
A copy of the task this entity is executing, or of the last task it executed. nil if it has not executed one yet.
Methods¶
awaitTask(timeout?: number) → ok: boolean, reason: TaskReason|"timeout"|nil¶
Waits until the task this entity is executing has ended, and tells how it ended.
Returns true if the task finished, and false and the reason if it failed or was interrupted,
the same way a start... function reports a task that could not start. So a failed start and a
failed task can be handled in one place:
It waits for the task that runs when it is called. If something replaces that task, for example
another spell, the wait ends with false and "replaced", and the new task runs on. A task that
has already ended gives its result at once, and an entity that never had a task gives true.
With a timeout, the wait ends after that many ticks with false and "timeout", but the task
itself keeps running. A timeout of 0 only looks.
The spell sleeps while it waits. An entity only moves while its chunk is loaded, so for a long
walk away from any player, move spell.pos along with the entity in a loop instead.
In Lua, awaitTask would look much like this:
local function awaitTask(mob, timeout)
if mob.task == nil then
return true -- it never had a task, so there is nothing to wait for
end
local waited = 0
while mob.busy do
if timeout and waited >= timeout then
return false, "timeout" -- only the wait ends; the task keeps running
end
sleep(1)
waited = waited + 1
end
if mob.task.state == "finished" then
return true
end
return false, mob.task.reason -- e.g. "unreachable" or "replaced"
end
There is one difference: awaitTask keeps to the task that was running when it was called. If
another task replaces it, awaitTask returns false and "replaced" at once, while the loop above
would go on waiting for the new task.
Example: Fetching coal and fueling a furnace
local zombie = spell:summon("zombie") --[[@as MobEntity]]
local chest, furnace = spell.pos + Vec3(3, 0, 0), spell.pos + Vec3(3, 0, 3)
local ok, reason = zombie:startTakeFrom(chest, { item = "coal" })
if ok then
ok, reason = zombie:awaitTask(20 * 20)
end
if ok then
ok, reason = zombie:startPutInto(furnace)
if ok then
ok, reason = zombie:awaitTask(20 * 20)
end
end
print(ok and "The furnace is fueled." or "Gave up: " .. reason)
Parameters
- timeout?: number — The maximum number of ticks to wait, an integer. Defaults to no limit.
Returns
- ok: boolean —
trueif the task finished,falseotherwise. - reason: TaskReason|"timeout"|nil — Why the task did not finish, or "timeout" if the wait ended first.
findPath(target: Vec3|Vec3[ ], options?: {targetRadius?:number, searchRange?:number}) → path: MobPath?¶
Searches for the shortest path from where this entity is to the given position, and returns it without moving: the entity stays where it is, and its task, if it has one, goes on.
The path is the one startPathTo would start on with the same options, found the way this kind
of entity finds its ways: a spider climbs, a fish swims, a villager goes through doors. So a
spell can look before the entity goes: how long the way is, whether it gets there at all, and
where it leads. startPathAlong then moves the entity along it.
Given a list of positions, the path leads to each of them in order, and ends at the last: a
route of the spell's own choice, like a patrol, or a way around a place the entity should
avoid. The positions only say where the path comes by. The way from each to the next is
searched like any path, with the same options, and the entity walks all of it in one go.
targetRadius tells how close the path comes to each position; with 0 it leads through their
blocks. If a position can't be reached, the path ends there, short of it, and reachable is
false.
An entity can only search a path where it can set off: a walking one on the ground, a fish in
water. Otherwise, and when no path exists at all, the function returns nil.
Example: The nearer of two places
local a, b = villager:findPath(well), villager:findPath(farm)
local path = a
if not (a and a.reachable) or (b and b.reachable and b.length < a.length) then
path = b
end
if path and path.reachable then
villager:startPathAlong(path)
villager:awaitTask()
end
Example: A patrol around a square
local golem = spell:summon("iron_golem") --[[@as MobEntity]]
local corner = golem.pos
local route = { corner + Vec3(8, 0, 0), corner + Vec3(8, 0, 8), corner + Vec3(0, 0, 8), corner }
while true do
local path = golem:findPath(route, { targetRadius = 0 })
if not (path and path.reachable and golem:startPathAlong(path) and golem:awaitTask()) then
break
end
end
Parameters
- target: Vec3|Vec3[ ] — The position to search a path to, or a list of positions to come by in order, the last being the target.
- options?: {targetRadius?:number, searchRange?:number} —
targetRadiusandsearchRangeare as forstartPathTo.
Returns
- path: MobPath? — The path, or
nilif there is none at all. A path that stops short of the target hasreachableset tofalse.
startAttack(target: LivingEntity, options?: StartAttackOptions) → ok: boolean, reason: TaskReason?¶
Starts a task that walks this entity to the given entity and attacks it.
The entity chases its target, recomputing its route every few ticks as the target moves, and
attacks once every cooldown ticks while the target is close enough. Where its path ends short
of the target, it searches wider before it gives up, as for startFollow.
An entity that hits walks as close as it can get. An entity that shoots stops at distance blocks
and backs off when the target comes nearer, so a skeleton keeps its bow at a useful range instead
of walking into the enemy. Set chase to false to make the entity hold its ground: it then never
moves, turns to face its target and attacks whatever comes within range, which is what a guard on a
wall or a tower does.
The task finishes when the target dies. It fails when the target can no longer be reached, which
includes the target moving further away than searchRange, and when the target dies out of sight,
leaves the world or changes dimension. An entity holding its ground never fails for distance: it
waits for the target to come back.
This entity attacks with the weapon it is holding and deals the damage its attack damage attribute
says, enchantments included. Peaceful entities such as pigs have no attack damage attribute; for
them damage is required, and calling this function without it raises an error.
Like any entity other than a player, this entity does not wear out its weapon. Set wearTool to
true to make hitting and shooting wear it out as they would in a player's hand.
With a mode of "auto" this entity shoots if it is able to shoot and holds a bow, a crossbow or a
trident, and hits otherwise. An entity that shoots but cannot hit at all, like a snow golem, always
shoots, and needs no damage. Use "melee" or "ranged" to decide instead of it, for example to make
a witch throw potions rather than punch. An entity that cannot shoot hits even with "ranged": a
zombie, and a skeleton without a bow.
While this task runs, task also carries target, mode, damage, reach, cooldown,
distance, chase, wearTool, speed and searchRange, and hits, the number of hits that
landed and shots fired.
Example: A wolf that hunts down the nearest zombie
local wolf = spell:summon("wolf") --[[@as MobEntity]]
local zombies = spell:findEntities("@e[type=zombie,limit=1,sort=nearest]")
if #zombies > 0 then
wolf:startAttack(zombies[1], { searchRange = 30 })
wolf:awaitTask()
print("Hits landed: " .. wolf.task.hits)
end
Example: A skeleton that shoots from a safe distance
local skeleton = spell:summon("skeleton") --[[@as MobEntity]]
skeleton:setItemInHand("mainhand", Item:new("bow"))
skeleton:startAttack(spell.owner, { distance = 12 })
Example: A guard that holds its post
local skeleton = spell:summon("skeleton") --[[@as MobEntity]]
skeleton:startAttack(spell.owner, { chase = false })
Example: A pig with a grudge
local pig = spell:summon("pig") --[[@as MobEntity]]
pig:startAttack(spell.owner, { damage = 4, speed = 1.3 })
Parameters
- target: LivingEntity — The entity to attack.
- options?: StartAttackOptions — The options of the task.
Returns
- ok: boolean —
trueif the task started,falseotherwise. - reason: TaskReason? — The reason the task did not start.
startBreakBlock(pos: Vec3, options?: StartBreakBlockOptions) → ok: boolean, reason: TaskReason?¶
Starts a task that walks this entity to the given block and breaks it.
The entity walks until the block is within reach, then hits it until it breaks. Players see
the block crack as it does when they mine it. The time it takes is computed the way it is for a
player, from the block's hardness and the tool in this entity's main hand: a zombie needs 15
ticks for dirt, 150 for stone without a pickaxe and 8 with an iron one. duration overrides it.
The block drops what it would drop for a player with the same tool: stone broken without a
pickaxe drops nothing, and enchantments like silk touch and fortune on the tool apply. The tool
wears out as it would in a player's hand. This task ignores the mobGriefing game rule.
The task finishes when the block is gone, also when something else removed it. It fails with "target_changed" if the block is replaced by a different one. Gravel and sand above the block fall into its place right after it breaks, so check the block again before moving on.
While this task runs, task also carries target, duration (the break time in ticks),
progress (from 0 to 1), drops, wearTool, reach, searchRange and speed.
Example
local zombie = spell:summon("zombie") --[[@as MobEntity]]
zombie:setEquipment("mainhand", Item:new("iron_pickaxe"))
local ok, reason = zombie:startBreakBlock(spell.pos + Vec3(3, 0, 0))
if ok then
ok, reason = zombie:awaitTask()
end
print(ok and "Broken." or "The zombie could not break it: " .. reason)
Parameters
- pos: Vec3 — The position of the block to break.
- options?: StartBreakBlockOptions — The options of the task.
Returns
- ok: boolean —
trueif the task started,falseotherwise. - reason: TaskReason? — The reason the task did not start: "unbreakable" for blocks like bedrock, "invalid_target" for air and fluids, or one of the reasons every task has.
startFleeFrom(entity: Entity, options?: StartFleeFromOptions) → ok: boolean, reason: TaskReason?¶
Starts a task that moves this entity away from the given entity.
The entity repeatedly looks for a reachable spot leading away from the given entity and runs there,
as an animal does when it panics. The task finishes as soon as the two are distance blocks apart,
and fails when there is nowhere to run, for example in a closed room.
While this task runs, task also carries target, distance, searchRange and speed.
Example
local sheep = spell:summon("sheep") --[[@as MobEntity]]
sheep:startFleeFrom(spell.owner, { distance = 15, speed = 1.5 })
if sheep:awaitTask() then
print("The sheep is safe again.")
end
Parameters
- entity: Entity — The entity to move away from.
- options?: StartFleeFromOptions — The options of the task.
Returns
- ok: boolean —
trueif the task started,falseotherwise. - reason: TaskReason? — The reason the task did not start.
startFollow(target: Entity, options?: StartFollowOptions) → ok: boolean, reason: TaskReason?¶
Starts a task that follows the given entity at a distance of distance blocks.
The entity walks after its target whenever the gap grows larger than distance and waits when it
is closer. The task runs until it is stopped or replaced. It fails if the target dies, leaves the
world or moves somewhere this entity cannot reach.
Where its path ends short of the target, at the foot of a cliff or at a wall, the entity doesn't
give up at once: it searches again from there, up to 4 times as far as searchRange, since a
way around may be longer than that. Only when that search finds no way either, the task fails
with "unreachable".
While this task runs, task also carries target, distance, searchRange and speed.
Example
local wolf = spell:summon("wolf") --[[@as MobEntity]]
wolf:startFollow(spell.owner, { distance = 3, speed = 1.2 })
wolf.lookAt = spell.owner
sleep(60 * 20)
wolf:stopTask()
Parameters
- target: Entity — The entity to follow.
- options?: StartFollowOptions — The options of the task.
Returns
- ok: boolean —
trueif the task started,falseotherwise. - reason: TaskReason? — The reason the task did not start.
startPathAlong(path: MobPath, options?: {speed?:number}) → ok: boolean, reason: TaskReason?¶
Starts a task that moves this entity along a path that findPath returned.
The entity walks this path and no other. It doesn't search again: if it is held up, for example
by a block placed in its way, the task fails with "unreachable", and the spell decides what to
do next. The task finishes when the entity is at the end of the path, also at the end of a path
that stops short of its target, as path.reachable tells.
The entity must be on the path to start: at its first node, or at any other, from where it walks the rest. So a path can be walked again, or by another entity of the same kind, as long as the blocks along it stay as they were.
While this task runs, task carries the same fields as for startPathTo, with the target and
the search options of the path.
Example
local pig = spell:summon("pig") --[[@as MobEntity]]
local path = pig:findPath(pig.pos + Vec3(20, 0, 0))
if path and path.reachable and path.length < 40 then
local ok, reason = pig:startPathAlong(path, { speed = 1.2 })
if ok then
ok, reason = pig:awaitTask()
end
print(ok and "The pig got there." or "The pig did not get there: " .. reason)
end
Parameters
- path: MobPath — The path to walk along.
- options?: {speed?:number} —
speedis as forstartPathTo.
Returns
- ok: boolean —
trueif the task started,falseotherwise. - reason: TaskReason? — The reason the task did not start: "off_path" if the entity isn't on the path, or "other_dimension" if the path was searched in another world.
startPathTo(target: Vec3, options?: StartPathToOptions) → ok: boolean, reason: TaskReason?¶
Searches for the shortest path to the given position and starts a task that moves this entity along it.
The task finishes as soon as the entity is within targetRadius of the target. A path search
looks searchRange blocks ahead, so a path to a target further away, or along a long, winding
way, may end short of it. Then the entity searches again from where that path ends, and so on,
as long as each such end is at least a block nearer the target than the last. The task fails if
the entity gets stuck, or if it gets no nearer, which happens when the target cannot be reached.
On a winding way, the end of a short path can be a dead end, which costs a detour or fails; a
larger searchRange finds the better way at once, but takes longer to search.
A speed well above 1 can make the entity overshoot turns and the target: the task finishes
when the entity reaches the target, and the entity may slide on from there. In tight spaces, keep
it close to 1.
An entity that can open doors, like a villager, opens wooden doors on its way and closes them behind itself.
While this task runs, task also carries target, targetRadius, searchRange and speed as
they were passed, traveled, the distance in blocks the entity has come along its path so far,
remainingDistance, the length in blocks of the rest of the path, and reachable, which is
false when the path stops short of the target; the entity may still get there, see above.
The path is computed anew when blocks change along the way, or when the entity was held up. So
remainingDistance and reachable can change while the entity walks, while traveled only grows.
Example
local pig = spell:summon("pig") --[[@as MobEntity]]
local waypoint = pig.pos + Vec3(0, 0, 10)
local ok, reason = pig:startPathTo(waypoint, { targetRadius = 1, speed = 1.2 })
if not ok then
error("The pig cannot get there: " .. reason)
end
while pig.busy do
if not pig.task.reachable then
print("It will not make it all the way.")
end
sleep(20)
end
if pig.task.state == "finished" then
print("The pig walked " .. math.floor(pig.task.traveled) .. " blocks in " .. pig.task.age .. " ticks.")
else
print("The pig gave up: " .. pig.task.reason)
end
Parameters
- target: Vec3 — The position to move toward.
- options?: StartPathToOptions — The options of the task.
Returns
- ok: boolean —
trueif the task started,falseotherwise. - reason: TaskReason? — The reason the task did not start.
startPlaceBlock(pos: Vec3, options?: StartPlaceBlockOptions) → ok: boolean, reason: TaskReason?¶
Starts a task that walks this entity to the given position and places the block it holds there.
The entity walks until the position is within reach and places one item of the stack in its
hand, as a player would. A block with a direction, like a furnace or stairs, faces this entity.
against tells which neighbor the block rests against, which decides between a torch on the
floor and one on a wall, for example.
Unlike a player, this entity needs no neighboring block to place a block against: it can put a stone in mid-air. If it stands where the block goes, it steps aside first.
The task fails with "occupied" if a block was put there in the meantime or another entity is in the way, and with "cannot_place" if the block cannot stay there, like a torch above a hole. Nothing is used up then.
While this task runs, task also carries target, hand, against, reach, searchRange and
speed.
Example: A torch on the wall, from the off hand
local zombie = spell:summon("zombie") --[[@as MobEntity]]
zombie:setEquipment("offhand", Item:new("torch"))
zombie:startPlaceBlock(spell.pos + Vec3(2, 1, 0), { hand = "offhand", against = "east" })
Parameters
- pos: Vec3 — The position to place the block at.
- options?: StartPlaceBlockOptions — The options of the task.
Returns
- ok: boolean —
trueif the task started,falseotherwise. - reason: TaskReason? — The reason the task did not start: "hand_empty", "not_a_block", "occupied", or one of the reasons every task has.
startPutInto(pos: Vec3, options?: StartPutIntoOptions) → ok: boolean, reason: TaskReason?¶
Starts a task that walks this entity to the given container and puts the items it holds into it.
The entity walks until the container is within reach, then moves the items from its hand into
the container, following the rules a hopper follows. The container is found the way a hopper
finds it: a container block, or else a container entity like a chest minecart in that block space.
Many containers, like chests and barrels, take anything from any side. A few decide by the side an
item comes from:
| Container | from above | from a side | from below |
|---|---|---|---|
| furnace, blast furnace, smoker | the item to smelt | fuel | - |
| brewing stand | the ingredient | bottles and blaze powder | - |
| composter | compostable items | - | - |
The side is not a place the entity has to stand at: it only decides which slots the items go to,
so a covered side works as well. Without side, the items go in as if from the side this entity
stands at, and what that side refuses as if from above. So coal goes into a furnace as fuel and
raw iron as the item to smelt. Use side = "up" to smelt logs rather than burn them.
The task finishes when at least one item went in. What does not fit stays in the hand, and
task.moved tells how many went in. The task fails with "container_full" if nothing fit, and with
"target_changed" if the container is gone.
While this task runs, task also carries target, hand, side, count, moved, reach,
searchRange and speed.
Example: Fueling a furnace
local zombie = spell:summon("zombie") --[[@as MobEntity]]
local coal = Item:new("coal")
coal.count = 16
zombie:setEquipment("mainhand", coal)
zombie:startPutInto(spell.pos + Vec3(3, 0, 0))
Parameters
- pos: Vec3 — The position of the container.
- options?: StartPutIntoOptions — The options of the task.
Returns
- ok: boolean —
trueif the task started,falseotherwise. - reason: TaskReason? — The reason the task did not start: "hand_empty", "no_container", or one of the reasons every task has.
startStay(options?: StartStayOptions) → ok: boolean, reason: TaskReason?¶
Starts a task that keeps this entity where it is, or at a given position.
The entity stands still and does not act on its own until the task is stopped or replaced. Without
duration the task runs until then; with one it finishes once the duration has passed. The entity
still falls, burns and takes damage, and it defends itself against nothing: attacking, panicking and
fleeing are all part of the behavior a task suspends.
Setting ai to false also keeps an entity still, but it is a different thing and it is persisted:
such an entity stops moving entirely, does not fall, and executes no tasks until ai is true
again.
With pos or radius, the entity holds its spot: when something moves it further away than
radius, like a hit, a push or a current, it walks back once it has landed, along a path as
startPathTo walks. Its spot is the middle of the block at pos, where it walks first, or else
the place where it stands when the task starts. An entity comes to rest anywhere on the block of
its spot, so with a radius below 1 it goes back when it is a block off. When it finds no way
back, it waits where it is and tries again every two seconds; only on its way to pos at the
start the task fails, with "unreachable", as a path task does.
While this task runs, task also carries duration and remaining, the ticks left, if a
duration was given. An entity that holds its spot also carries pos, its spot, radius,
away, which is true while it is on its way to the spot, searchRange and speed.
Example
Without options, the entity stays where it stands, with no time limit, until the task is stopped or replaced. It doesn't hold its spot: when something moves it, it stays where it ends up.
local villager = spell:summon("villager") --[[@as MobEntity]]
villager:startPathTo(spell.pos + Vec3(8, 0, 0))
villager:awaitTask()
villager:startStay() -- guards the spot instead of wandering off
villager.lookAt = spell.owner
A guard that walks to its post and keeps it, and one that gives way up to 3 blocks:
Parameters
- options?: StartStayOptions — The options of the task.
Returns
- ok: boolean —
trueif the task started,falseotherwise. - reason: TaskReason? — The reason the task did not start: "no_path" if no path leads to
pos.
startTakeFrom(pos: Vec3, options?: StartTakeFromOptions) → ok: boolean, reason: TaskReason?¶
Starts a task that walks this entity to the given container and takes items out of it.
The entity walks until the container is within reach, then takes items into its hand, following
the rules a hopper follows: by default from below, which gives a furnace's output but not its
fuel. item chooses what to take: an item id like "coal", a tag like "#minecraft:logs", or a list
of them. A hand that already holds something only takes more of the same.
The task finishes when at least one item was taken; task.moved tells how many. It fails with
"nothing_to_take" if nothing matched, and with "target_changed" if the container is gone.
While this task runs, task also carries target, hand, side, item, count, moved,
reach, searchRange and speed.
Example: Fetching a new pickaxe
local zombie = spell:summon("zombie") --[[@as MobEntity]]
local chest = spell.pos + Vec3(3, 0, 0)
zombie:startTakeFrom(chest, { item = "#minecraft:pickaxes", count = 1 })
zombie:awaitTask()
print(str(zombie:getEquipment("mainhand")))
Parameters
- pos: Vec3 — The position of the container.
- options?: StartTakeFromOptions — The options of the task.
Returns
- ok: boolean —
trueif the task started,falseotherwise. - reason: TaskReason? — The reason the task did not start: "hand_full", "no_container", or one of the reasons every task has.
stopTask()¶
Stops the task this entity is executing and hands it back to its own behavior.
Does nothing if this entity is executing no task. The stopped task gets the state "interrupted" and the reason "stopped".
Example