Skip to content

These docs were made completely by AI, so they might be right, or wrong, you'll need to test them yourself. This was made for a easier understanding of everything. So use at your own risk. If anything is wrong, please don't hurt to make a PR on the page you have a problem with. ON GITHUB

AI & Goals

LCE uses a priority-based goal system for mob AI. Each mob owns two GoalSelector instances, one for general behaviors and one for targeting. Each selector manages a list of Goal objects that compete for execution based on priority and control flag compatibility.

Every AI behavior inherits from Goal and implements its lifecycle methods:

MethodDefaultPurpose
canUse()(pure virtual)Returns true if this goal should start
canContinueToUse()Calls canUse()Returns true if this goal should keep running
canInterrupt()Returns trueWhether higher-priority goals can preempt this one
start()No-opCalled once when the goal begins executing
stop()No-opCalled once when the goal stops executing
tick()No-opCalled every tick while the goal is running
setRequiredControlFlags(flags)(none)Sets the bitmask of control channels this goal uses
getRequiredControlFlags()Returns 0Returns the control flag bitmask
setLevel(level)No-op4J addition: updates level pointer when loading from schematics

Goals declare which “control channels” they need through a bitmask passed to setRequiredControlFlags(). Two goals can run at the same time only if their control flags don’t overlap (bitwise AND is zero). This prevents things like two movement goals from conflicting with each other.

The three control flags are defined in Control.h:

FlagValueWhat it controls
MoveControlFlag1 (bit 0)Walking, pathfinding, speed changes
LookControlFlag2 (bit 1)Head rotation, looking at targets
JumpControlFlag4 (bit 2)Jumping, leaping

The TargetGoal base class defines TargetFlag = 1 as a separate flag for targeting behaviors. Since target goals go in a different GoalSelector than movement goals, the flag value overlapping with MoveControlFlag does not cause conflicts.

Here is what every built-in goal uses:

GoalControl Flags
FloatGoalJump
PanicGoalMove
RandomStrollGoalMove + Look
FleeSunGoalMove
MoveIndoorsGoalMove
MoveThroughVillageGoalMove
MoveTowardsRestrictionGoalMove
MoveTowardsTargetGoalMove
MeleeAttackGoalMove + Look
ArrowAttackGoalMove + Look
LeapAtTargetGoalJump + Move
OzelotAttackGoalMove + Look
SwellGoalMove
AvoidPlayerGoalMove
TemptGoalMove + Look
FollowOwnerGoalMove + Look
FollowParentGoal(none set explicitly)
BreedGoalMove + Look
SitGoalJump + Move
BegGoalLook
EatTileGoalMove + Look + Jump
ControlledByPlayerGoalMove + Look + Jump
LookAtPlayerGoalLook
RandomLookAroundGoal(none set explicitly)
DoorInteractGoal(none set explicitly)
OfferFlowerGoalMove + Look
All targeting goalsTargetFlag (1)

The scheduler that manages and ticks a set of goals.

Goals are wrapped in InternalGoal objects that store:

  • prio: integer priority (lower = higher priority)
  • goal: pointer to the Goal instance
  • canDeletePointer: ownership flag for memory management (4J addition)

Two lists are maintained:

  • goals: all registered goals
  • usingGoals: goals currently executing

Every newGoalRate ticks (default: 3), the selector runs a full evaluation cycle:

  1. For each registered goal:
    • If it’s currently running and either canUseInSystem() fails or canContinueToUse() returns false, call stop() and remove from usingGoals
    • If it’s not running and both canUseInSystem() and canUse() pass, add to start list and usingGoals
  2. Call start() on all newly added goals
  3. Call tick() on all running goals

On non-evaluation ticks, only canContinueToUse() is checked for running goals, stopping any that return false.

canUseInSystem(goal) checks whether a goal can run given the current set of active goals:

  • Against higher-priority (lower number) running goals: the candidate is blocked if their control flags overlap (canCoExist returns false)
  • Against lower-priority (higher number) running goals: the candidate is blocked if the lower-priority goal’s canInterrupt() returns false

canCoExist(goalA, goalB) returns true when (goalA.flags & goalB.flags) == 0.

  • addGoal(priority, goal, canDeletePointer): registers a goal at the given priority. The canDeletePointer flag (default true) controls whether the selector owns the goal memory.
  • setNewGoalRate(rate): changes how often full evaluation happens (default every 3 ticks)
  • setLevel(level): sends a level pointer change to all goals (4J addition for schematic loading)

Abstract base for goals that pick an attack target. Provides shared logic for target validation.

Constructor: TargetGoal(Mob *mob, float within, bool mustSee, bool mustReach = false)

ParameterPurpose
mobThe mob that owns this goal
withinMaximum search/follow distance
mustSeeIf true, target must be visible (line of sight)
mustReachIf true, target must be pathable

Key behavior:

  • canAttack(target, allowInvulnerable): checks that the target is alive, within range, and optionally visible/reachable. For tamed animals, it also checks that the target is not the owner or the owner’s other pet.
  • Maintains a reachCache with three states: EmptyReachCache (0), CanReachCache (1), CantReachCache (2). The cache avoids expensive pathfinding checks every tick.
  • unseenTicks tracks how long since the target was last visible, up to UnseenMemoryTicks (60). After 60 ticks without seeing the target, the goal gives up.
  • On start(), sets the mob’s target via mob->setTarget()
  • On stop(), clears the mob’s target

Makes mobs swim when in water or lava.

ConstructorFloatGoal(Mob *mob)
FlagsJumpControlFlag
canUseReturns true when mob->isInWater() or mob->isInLava()
tickCalls mob->getJumpControl()->jump(). Due to the jump control’s random factor, the mob jumps about 80% of ticks, which creates a bobbing effect.

Flees at increased speed when hurt.

ConstructorPanicGoal(PathfinderMob *mob, float speed)
FlagsMoveControlFlag
canUseReturns true when mob->getLastHurtByMob() != NULL or mob->isOnFire(). Picks a random flee position using RandomPos::getPos(mob, 5, 4).
startBegins pathfinding to the chosen position at speed
canContinueToUseReturns true while the navigation path is not done

Wanders randomly when idle.

ConstructorRandomStrollGoal(PathfinderMob *mob, float speed)
FlagsMoveControlFlag + LookControlFlag
canUseOnly activates with a 1/120 chance when the mob has been idle for less than 5 seconds (noActionTime < 100). Uses RandomPos::getPos(mob, 10, 7) to find a destination. Also supports extra wandering for despawn detection (see entities page).
startBegins pathfinding to the random destination at speed
canContinueToUseReturns true while navigation is not done

Moves to a shaded area when exposed to sunlight.

ConstructorFleeSunGoal(PathfinderMob *mob, float speed)
FlagsMoveControlFlag
canUseOnly when it’s daytime, the mob is on fire, and the mob’s position is under open sky. Tries 10 random positions within range looking for one with a solid block overhead.
startPathfinds to the shaded position at speed
canContinueToUseReturns true while navigation is not done

Moves toward a village door during rain or at night.

ConstructorMoveIndoorsGoal(PathfinderMob *mob)
FlagsMoveControlFlag
canUseActivates at night or during rain with a 1/50 random chance. Finds the nearest village door within range.
startPathfinds to the inside position of the nearest door
canContinueToUseReturns true while navigation is not done

Navigates between village doors during pathfinding.

ConstructorMoveThroughVillageGoal(PathfinderMob *mob, float speed, bool onlyAtNight)
FlagsMoveControlFlag
canUseFinds an unvisited village door and creates a path to it. Keeps a visited list so it doesn’t loop between the same doors.
startBegins pathfinding to the next door at speed
canContinueToUseReturns true while navigation is not done and the mob hasn’t reached the door
stopAdds the current door to the visited list

Returns toward the mob’s home position.

ConstructorMoveTowardsRestrictionGoal(PathfinderMob *mob, float speed)
FlagsMoveControlFlag
canUseReturns true when the mob has a restriction center set and the mob is outside its restriction radius. Uses RandomPos::getPosTowards() biased toward the restriction center.
startPathfinds toward the home position at speed
canContinueToUseReturns true while navigation is not done

Moves toward the current attack target.

ConstructorMoveTowardsTargetGoal(PathfinderMob *mob, float speed, float within)
FlagsMoveControlFlag
canUseReturns true when the mob has a target and is farther than within blocks from it. Uses RandomPos::getPosTowards() biased toward the target.
startPathfinds toward the biased position at speed
canContinueToUseReturns true while navigation is not done

Lets a rider (player with carrot on a stick) steer the mob.

ConstructorControlledByPlayerGoal(Mob *mob, float maxSpeed, float walkSpeed)
FlagsMoveControlFlag + LookControlFlag + JumpControlFlag
ConstantsMIN_BOOST_TIME = 140 ticks (7s), MAX_BOOST_TIME = 700 ticks (35s)
canUseReturns true when the mob has a rider that is a player holding a carrot on a stick
tickSteers the mob based on the rider’s look direction. Handles speed boosting when the player uses the carrot on a stick item. Auto-detects cliffs and obstacles using PathFinder::isFree. Randomly breaks the carrot on a stick when boosting ends.
Other methodsboost() starts a speed boost, isBoosting() checks boost state, canBoost() checks if a boost is available

Pathfinds to target and attacks in melee range.

ConstructorsMeleeAttackGoal(Mob *mob, float speed, bool trackTarget) or MeleeAttackGoal(Mob *mob, eINSTANCEOF attackType, float speed, bool trackTarget)
FlagsMoveControlFlag + LookControlFlag
Parametersspeed: movement speed modifier. trackTarget: if true, constantly recalculates path to moving target. attackType: optional eINSTANCEOF filter so the goal only activates against a specific entity type.
canUseReturns true when the mob has a target (optionally filtered by attackType) and can create a path to it
tickRecalculates the path every 4-11 ticks (random jitter to avoid all mobs pathing on the same frame). Attack cooldown is 20 ticks. Melee hit radius is (bbWidth * 2)^2. Looks at the target while approaching.
stopClears the path and stops navigation

The attackType filter is how the Zombie handles different priorities for players vs villagers. Two MeleeAttackGoal instances at different priorities, each with a different attackType, let the mob prefer one target type over another.

Ranged attack for mobs that shoot projectiles.

ConstructorArrowAttackGoal(Mob *mob, float speed, int projectileType, int attackInterval)
FlagsMoveControlFlag + LookControlFlag
ConstantsArrowType = 1, SnowballType = 2
Parametersspeed: movement speed while approaching. projectileType: determines which projectile to fire. attackInterval: minimum ticks between shots.
canUseReturns true when the mob has a target
tickTracks the target and maintains distance. Attack radius is 10 blocks squared. Requires 20 ticks of continuous line of sight before firing the first shot. After that, fires at attackInterval intervals. Calls fireAtTarget() which creates the appropriate projectile based on projectileType.
stopResets the see time counter

Leaps at the target when within range. Used by wolves.

ConstructorLeapAtTargetGoal(Mob *mob, float yd)
FlagsJumpControlFlag + MoveControlFlag
Parametersyd: the upward velocity component of the leap
canUseReturns true with a 1/5 random chance when the target is 2-4 blocks away and the mob is on the ground
startDirectly sets the mob’s xd, zd, and yd motion values to leap toward the target. The horizontal components are calculated from the direction to the target, clamped to reasonable values.
canContinueToUseReturns false once the mob is back on the ground

Ocelot-specific pounce attack with variable speed.

ConstructorOzelotAttackGoal(Mob *mob)
FlagsMoveControlFlag + LookControlFlag
canUseReturns true when the mob has a target
tickUses three different speeds depending on distance: normal walk speed when far, sprint speed when close, and a slower sneak speed at mid range. Creates a stalking/pounce behavior. Attack cooldown uses the same 20 tick timing as melee.
stopResets the attack timer

Creeper-specific. Starts swelling when near a target, stops when target moves away.

ConstructorSwellGoal(Creeper *creeper)
FlagsMoveControlFlag
canUseReturns true when the creeper has a target within 3 blocks
startStops the creeper’s navigation (it stands still while swelling)
tickIf the target moves beyond 7 blocks or is no longer visible, calls stop(). Otherwise lets the creeper’s swell timer continue.
stopResets the creeper’s swell direction to -1 (deflating)

Finds the nearest entity of a given type within range.

ConstructorNearestAttackableTargetGoal(Mob *mob, const type_info& targetType, float within, int randomInterval, bool mustSee, bool mustReach = false)
FlagsTargetFlag
ParameterstargetType: C++ type_info for the target class. within: search range. randomInterval: if > 0, only runs the search with a 1/randomInterval random chance per check. mustSee/mustReach: passed to TargetGoal base.
canUseIf randomInterval > 0, randomly skips checks. For Player type, uses the optimized getNearestAttackablePlayer(). For other types, searches entities in a box around the mob, sorts them by distance using DistComp, and picks the closest valid one.
startSets the mob’s target to the found entity

The DistComp comparator sorts entities by distance from the mob, closest first. This is how the mob picks the nearest valid target instead of just any random one.

Targets whatever mob last hurt this mob.

ConstructorHurtByTargetGoal(Mob *mob, bool alertSameType)
FlagsTargetFlag
ParametersalertSameType: if true, nearby mobs of the same type will also target the attacker
canUseReturns true when mob->getLastHurtByMob() returns a new attacker (different from the previously stored one). Uses canAttack() to validate the target. Search range is 16 blocks.
startSets the mob’s target. If alertSameType is true, searches nearby mobs of the same class and sets their target to the attacker too.
tickChecks if the stored attacker has changed and updates accordingly

Iron golem targets mobs that attacked a villager.

ConstructorDefendVillageTargetGoal(VillagerGolem *golem)
FlagsTargetFlag
canUseChecks the golem’s village for a recent aggressor. Uses canAttack() to validate the target.
startSets the golem’s target to the village aggressor

Tamed animal targets whatever hurt its owner.

ConstructorOwnerHurtByTargetGoal(TamableAnimal *tameAnimal)
FlagsTargetFlag
canUseReturns true when the owner’s lastHurtByMob is set and is a valid target. Search range is 32 blocks.
startSets the pet’s target to whatever hurt the owner

Tamed animal targets whatever its owner attacked.

ConstructorOwnerHurtTargetGoal(TamableAnimal *tameAnimal)
FlagsTargetFlag
canUseReturns true when the owner’s lastHurtMob is set and is a valid target. Search range is 32 blocks.
startSets the pet’s target to whatever the owner last attacked

Targets random entities, but only if the animal is not tamed.

ConstructorNonTameRandomTargetGoal(TamableAnimal *mob, const type_info& targetType, float within, int randomInterval, bool mustSee)
FlagsTargetFlag
canUseReturns false if the animal is tamed. Otherwise defers to NearestAttackableTargetGoal::canUse().

This is how wild wolves hunt sheep but tamed wolves don’t.

Looks at nearby entities of a given type.

ConstructorsLookAtPlayerGoal(Mob *mob, const type_info& lookAtType, float lookDistance) or LookAtPlayerGoal(Mob *mob, const type_info& lookAtType, float lookDistance, float probability)
FlagsLookControlFlag
ParameterslookAtType: what entity type to look at. lookDistance: max distance to notice. probability: chance per check to activate (default 0.02).
canUseWith probability chance, searches for the nearest entity of lookAtType within lookDistance.
startSets a random look time between 40-80 ticks
tickPoints the mob’s look control at the target entity
canContinueToUseReturns false when look time runs out or the target moves out of range

Villager looks at the player currently trading with them.

ConstructorLookAtTradingPlayerGoal(Villager *villager)
FlagsLookControlFlag (inherited)
canUseReturns true when the villager has an active trading partner

Extends LookAtPlayerGoal and overrides canUse() to check for an active trade.

Randomly looks around when idle.

ConstructorRandomLookAroundGoal(Mob *mob)
Flags(none set)
canUseActivates with a 1/6 random chance
startPicks a random direction to look in and sets a look time of 20-40 ticks
tickPoints the mob’s look control toward the random direction
canContinueToUseReturns false when the look timer runs out

Because this goal has no control flags, it can run alongside basically anything. That’s intentional since looking around shouldn’t block movement.

Generic interaction behavior. Extends LookAtPlayerGoal.

ConstructorsInteractGoal(Mob *mob, const type_info& lookAtType, float lookDistance) or InteractGoal(Mob *mob, const type_info& lookAtType, float lookDistance, float probability)
FlagsLookControlFlag (inherited)

Same as LookAtPlayerGoal but used in contexts where the mob is interacting rather than just looking.

Villager engages in trade interaction.

ConstructorTradeWithPlayerGoal(Villager *mob)
Flags(none set)
canUseReturns true when the villager has an active trading partner
startSets up the trade interaction state
stopCleans up the trade state

Wolf begging behavior when a player holds food.

ConstructorBegGoal(Wolf *wolf, float lookDistance)
FlagsLookControlFlag
canUseSearches for a nearby player within lookDistance who is holding an interesting item. For untamed wolves that’s a bone. For tamed wolves it’s any food item.
startSets a random look time between 40-80 ticks. Calls setDespawnProtected() on the wolf (player interaction prevents despawning).
tickPoints the wolf’s look control at the player
canContinueToUseReturns false when the look timer expires or the player stops holding the item

Tamed animal follows its owner. Teleports if too far.

ConstructorFollowOwnerGoal(TamableAnimal *tamable, float speed, float startDistance, float stopDistance)
FlagsMoveControlFlag + LookControlFlag
ConstantsTeleportDistance = 12
Parametersspeed: movement speed. startDistance: distance to owner before the pet starts following. stopDistance: distance to stop at.
canUseReturns true when the owner exists, the pet is not sitting, and the distance to the owner exceeds startDistance. Won’t activate if the pet is ordered to sit.
startDisables water avoidance on the navigation (so the pet can follow through water) and starts pathfinding to the owner.
tickRe-paths periodically. If the distance exceeds TeleportDistance (12 blocks), attempts to teleport by searching a 5x5 grid around the owner for solid ground with air above.
stopRe-enables water avoidance and stops navigation

Tamed animal sits/stays when ordered.

ConstructorSitGoal(TamableAnimal *mob)
FlagsJumpControlFlag + MoveControlFlag
canUseReturns true when the mob is tamed. Won’t sit if the owner is in danger (within TeleportDistance and the owner was recently hurt).
startStops navigation and puts the mob in the sitting pose
stopRemoves the sitting pose
Other methodswantToSit(bool) toggles the sitting state from external code

By claiming both Jump and Move flags, sitting prevents basically all other movement behaviors from running.

Ocelot-specific: sits on chests, beds, and furnaces.

ConstructorOcelotSitOnTileGoal(Ozelot *ocelot, float speed)
Flags(none set)
ConstantsGIVE_UP_TICKS, SIT_TICKS, SEARCH_RANGE, SIT_CHANCE
canUseSearches for a valid tile (chest, bed, or furnace) within range. The ocelot must be tamed.
tickPathfinds to the tile. Once on it, the ocelot sits down. After SIT_TICKS, the goal ends.

Finds a partner and breeds when in love mode.

ConstructorBreedGoal(Animal *animal, float speed)
FlagsMoveControlFlag + LookControlFlag
canUseReturns true when the animal is in love mode (isInLove()). Searches within 8 blocks for another animal of the same type that canMate() returns true for.
tickPathfinds to the partner and counts up loveTime. After 60 ticks (3 seconds), calls breed().
BreedingAwards 1-7 XP orbs. Sets both parents’ age to 6000 ticks (5 minute breeding cooldown). Creates a baby with age -24000 ticks (20 minutes until adult).
stopResets the love timer

Baby animals follow their parent.

ConstructorFollowParentGoal(Animal *animal, float speed)
Flags(none set explicitly)
canUseOnly works for baby animals (age < 0). Searches for the nearest adult of the same type within 8 blocks.
tickPathfinds to the parent. Re-paths periodically.
canContinueToUseReturns false if the parent moves beyond 16 blocks (gives up) or closer than 3 blocks (close enough).

Follows a player holding a specific item.

ConstructorTemptGoal(PathfinderMob *mob, float speed, int itemId, bool canScare)
FlagsMoveControlFlag + LookControlFlag
Parametersspeed: follow speed. itemId: the item ID that attracts this mob. canScare: if true, the mob stops following when the player moves fast within 6 blocks.
canUseSearches for a player within 10 blocks holding the itemId. Won’t activate during the 100-tick cooldown after the last tempt ended.
startDisables water avoidance. Calls setDespawnProtected().
tickLooks at the player and pathfinds toward them at speed
stopRe-enables water avoidance. Starts the 100-tick calmDown cooldown.
Other methodsisRunning() returns whether the goal is currently active. Used by breeding logic to check if an animal is being led by a player.

Villager-specific breeding/love behavior.

ConstructorMakeLoveGoal(Villager *villager)
Flags(none set)
canUseReturns true when the villager can breed and the village needs more villagers.
tickFinds a partner, pathfinds to them, and after a timer, spawns a baby villager.

Young villagers play/run around.

ConstructorPlayGoal(Villager *mob, float speed)
Flags(none set)
canUseOnly works for young villagers. Finds another young villager to play with.
tickRuns around near the play partner, bouncing between positions. The playTime counter limits how long they play.

Base class for door interaction. Pathfinds to doors.

ConstructorDoorInteractGoal(Mob *mob)
Flags(none set)
canUseSearches nearby blocks for a door. Returns true if a door is found within range along the mob’s path.
tickChecks if the mob has passed through the door by comparing its position to the door’s open direction.

Opens doors and closes them after a delay.

ConstructorOpenDoorGoal(Mob *mob, bool closeDoorAfter)
Flags(inherited from DoorInteractGoal, none)
ParameterscloseDoorAfter: if true, the mob will close the door 20 ticks after passing through
startOpens the door
stopCloses the door (if closeDoorAfter is true)
tickCounts down the forgetTime. After 20 ticks, the door closes.

Breaks down doors (used by zombies).

ConstructorBreakDoorGoal(Mob *mob)
Flags(inherited from DoorInteractGoal, none)
ConstantsDOOR_BREAK_TIME = 240 ticks (12 seconds)
canUseSame as DoorInteractGoal::canUse() but also checks that the difficulty is Hard
tickIncrements breakTime and plays the zombie door-breaking sound. Shows break progress particles. After DOOR_BREAK_TIME ticks, destroys the door block.
stopResets the break progress

Prevents mobs from going through open doors.

ConstructorRestrictOpenDoorGoal(PathfinderMob *mob)
Flags(none set)
canUseReturns true when the mob is near a village door that is currently open
startSets a path restriction to avoid the open door’s position
stopClears the path restriction

Restricts the mob to shaded areas.

ConstructorRestrictSunGoal(PathfinderMob *mob)
Flags(none set)
canUseReturns true when it’s daytime
startEnables sun avoidance on the navigation (setAvoidSun(true))
stopDisables sun avoidance

Sheep-specific: eats grass blocks.

ConstructorEatTileGoal(Mob *mob)
FlagsMoveControlFlag + LookControlFlag + JumpControlFlag
ConstantsEAT_ANIMATION_TICKS = 40 (2 seconds)
canUseRandom chance: 1/1000 for adults, 1/50 for babies. The mob must be standing on grass or tall grass.
startStarts the eating animation. Broadcasts an entity event to clients.
tickCounts down the eatAnimationTick. When it hits 0, eats tall grass (removes the block) or converts a grass block to dirt.
Other methodsgetEatAnimationTick() returns the current animation progress, used by the renderer for the head-dip animation.

Claiming all three control flags (Move + Look + Jump) means the mob completely freezes while eating.

Iron golem offers a flower to a villager child.

ConstructorOfferFlowerGoal(VillagerGolem *golem)
FlagsMoveControlFlag + LookControlFlag
ConstantsOFFER_TICKS = 400 (20 seconds)
canUseActivates during daytime with a 1/8000 chance. Searches for a young villager within 6 blocks.
tickLooks at the villager child. Counts up to OFFER_TICKS.
stopClears the offer state

Villager child takes a flower from an iron golem.

ConstructorTakeFlowerGoal(Villager *villager)
Flags(none set)
canUseReturns true when a nearby iron golem is offering a flower
tickPathfinds to the golem. When close enough, takes the flower.

Runs away from a nearby player or entity.

ConstructorAvoidPlayerGoal(PathfinderMob *mob, const type_info& avoidType, float maxDist, float walkSpeed, float sprintSpeed)
FlagsMoveControlFlag
ParametersavoidType: what entity type to flee from. maxDist: detection range. walkSpeed: speed when the threat is far. sprintSpeed: speed when the threat is within 7 blocks.
canUseSearches for the nearest entity of avoidType within maxDist. For tamed animals, won’t avoid players (they’re family). Uses RandomPos::getPosAvoid() to find a direction away from the threat.
tickAdjusts speed based on distance to the threat. If the entity is within 7 blocks, uses sprintSpeed. Otherwise uses walkSpeed.
stopClears the avoidance path

Despite the name, this works for avoiding any entity type, not just players. Ocelots use it to avoid players, and cats skip it since they’re tamed.

Mobs register goals in their constructor with priorities. A typical zombie looks like:

goalSelector.addGoal(0, FloatGoal)
goalSelector.addGoal(1, BreakDoorGoal)
goalSelector.addGoal(2, MeleeAttackGoal(Player))
goalSelector.addGoal(3, MeleeAttackGoal(Villager))
goalSelector.addGoal(4, MoveTowardsRestrictionGoal)
goalSelector.addGoal(5, MoveThroughVillageGoal)
goalSelector.addGoal(6, RandomStrollGoal)
goalSelector.addGoal(7, LookAtPlayerGoal)
goalSelector.addGoal(7, RandomLookAroundGoal)
targetSelector.addGoal(1, HurtByTargetGoal)
targetSelector.addGoal(2, NearestAttackableTargetGoal(Player))
targetSelector.addGoal(2, NearestAttackableTargetGoal(Villager))

Lower priority numbers take precedence. Goals at the same priority level can coexist if their control flags don’t conflict.

goalSelector.addGoal(0, FloatGoal)
goalSelector.addGoal(1, PanicGoal(0.38))
goalSelector.addGoal(2, BreedGoal(0.2))
goalSelector.addGoal(3, TemptGoal(0.25, wheat, false))
goalSelector.addGoal(4, FollowParentGoal(0.25))
goalSelector.addGoal(5, RandomStrollGoal(0.2))
goalSelector.addGoal(6, LookAtPlayerGoal(Player, 6))
goalSelector.addGoal(7, RandomLookAroundGoal)

No target goals since cows don’t attack anything. Panic is high priority so the cow runs from danger before doing anything else.

goalSelector.addGoal(1, FloatGoal)
goalSelector.addGoal(2, SitGoal)
goalSelector.addGoal(3, LeapAtTargetGoal(0.4))
goalSelector.addGoal(4, MeleeAttackGoal(1.0, true))
goalSelector.addGoal(5, FollowOwnerGoal(1.0, 10, 2))
goalSelector.addGoal(6, BreedGoal(1.0))
goalSelector.addGoal(7, RandomStrollGoal(1.0))
goalSelector.addGoal(8, BegGoal(8))
goalSelector.addGoal(9, LookAtPlayerGoal(Player, 8))
goalSelector.addGoal(9, RandomLookAroundGoal)
targetSelector.addGoal(1, OwnerHurtByTargetGoal)
targetSelector.addGoal(2, OwnerHurtTargetGoal)
targetSelector.addGoal(3, HurtByTargetGoal(true))
targetSelector.addGoal(4, NonTameRandomTargetGoal(Sheep, 200, false))

The wolf shows off multiple target goals in priority order: defend owner first, then retaliate, then hunt sheep (only when wild).

Every mob that uses the new AI system has a set of control objects that goals use to actually make the mob do things. Goals don’t move the mob directly. They tell the control objects what to do, and the control objects handle the actual movement/rotation each tick.

Handles movement toward a target position.

ConstantsMIN_SPEED = 0.0005f, MAX_TURN = 30 degrees
Key methodsetWantedPosition(x, y, z, speed) tells the mob where to go
Tick behaviorCalculates direction to target, smoothly rotates the mob (up to MAX_TURN degrees per tick via rotlerp), applies forward speed. Triggers a jump when the target is above the mob.

Handles head rotation toward a target.

Key methodssetLookAt(entity, yMax, xMax) or setLookAt(x, y, z, yMax, xMax)
ParametersyMax: max Y-axis rotation per tick. xMax: max X-axis (pitch) rotation per tick.
Tick behaviorSmoothly rotates the head toward the target within the rotation limits

Handles jumping.

Key methodjump() sets a flag that makes the mob jump on the next tick
Tick behaviorIf the jump flag is set, calls the mob’s jump method. Clears the flag after.

Caches line-of-sight results for performance.

Key methodcanSee(entity) returns true if the mob has line of sight to the entity
CachingMaintains seen and unseen vectors. Results are cached for the current tick and cleared at the start of each new tick.

Goals should use mob->getSensing()->canSee(target) instead of doing their own line-of-sight calculations. The cache prevents the same raycast from running multiple times per tick when multiple goals check the same target.

MC adds two new AI goal types that LCEMP doesn’t have:

Used by horses when they’re being tamed. Makes the horse run around wildly to try to throw off the player. Once the horse is tamed, this goal stops firing.

A new ranged attack goal that’s separate from the existing ArrowAttackGoal. In LCEMP, ArrowAttackGoal handles both arrow-type and snowball-type ranged attacks. MC splits this out so RangedAttackGoal can be used by the Witch (for potion throwing) and the Skeleton (using the RangedAttackMob interface).

MC also adds OcelotAttackGoal as a separate file pair. In LCEMP, this goal exists but its source may be structured slightly differently.

Since MC adds new mobs (Witch, Wither Boss, Bat, Horse), those mobs each bring their own goal configurations. For example:

  • Witch uses RangedAttackGoal for potion attacks and standard movement/targeting goals
  • Horse uses RunAroundLikeCrazyGoal during taming, plus the usual PanicGoal, BreedGoal, FollowParentGoal, and RandomStrollGoal
  • Wither Boss has its own attack patterns (not simple goal-based AI)
  • Bat has minimal AI since it just flies around randomly