Skip to content

Built in Actions

AzureDoom edited this page Aug 29, 2026 · 1 revision

Built-in Actions

AzureCortex ships a small set of generic, reference Action implementations. They're deliberately minimal building blocks, not fully-tuned creature AI, mods with more sophisticated needs (resin-web awareness, break-to-target GOAP hooks, wall-crawl approach selection, ...) should compose their own actions following the same shape, using Navigation and Pathfinding and Sensing directly.

Every action implements:

public interface Action<E, G> {
    void start(E agent, Blackboard blackboard, CooldownTracker cooldowns);
    ActionOutcome<G> tick(E agent, Blackboard blackboard, CooldownTracker cooldowns);
    void stop(E agent, Blackboard blackboard, CooldownTracker cooldowns, ActionStatus reason);
    boolean isInterruptible();
    default InterruptCategory interruptCategory() { ... }
    int priority();
    default String debugName() { return getClass().getSimpleName(); }
}

See Core Concepts for the Action/tree relationship and interrupt categories, and GOAP Planning for ActionOutcome/PlanFailureReason.

Movement actions (action.movement)

IdleAction

Stops the navigator and reports running() forever until preempted. Every tree needs some lowest-priority branch that's always eligible so the tree never lands on "nothing selected", this is that branch, at priority 0. It does nothing else; build a richer idle action yourself if you want idle agents to look around or play an animation.

WanderAction

Periodically picks a random nearby point and walks toward it; restarts on arrival or after a max duration.

new WanderAction<MyEntity, MyGoal>(
    1.0D,   // speed
    10.0D,  // radius, how far to pick a destination
    100     // maxDurationTicks, give up and re-pick even if not arrived
);

Mod-specific bias (e.g. "prefer dark areas") isn't built in, subclass or wrap this, overriding destination-picking, following the same shape.

MoveToDestinationAction

Drives the agent toward whatever BlockPos is stored under CommonBlackboardKeys.DESTINATION, using a supplied Pathfinder.

new MoveToDestinationAction<MyEntity, MyGoal>(
    AStarPathfinder.INSTANCE,  // or CrawlTraversalEvaluator.INSTANCE
    1.15D,  // speed
    1,      // arrivalRadius
    10,     // repathIntervalTicks
    60      // stuckTimeoutTicks
);

Repaths on a cooldown, reports Blocked/Failed with a PlanFailureReason when the navigator stalls, clears the destination on arrival. When CortexConfig.enableIncrementalPathfinding is on (the default), a repath starts an IncrementalPathSession instead of a synchronous findPath call, see Navigation and Pathfinding.

SwimAction

For mobs caught in water/lava: swims toward TARGET (if pursueTarget) or DESTINATION, and, if idly bobbing with neither for a while (past a grace period), beelines for the nearest shore. Succeeds immediately once the mob is no longer in water/lava, or if it dies. Meant to sit at a low priority, underneath normal combat/pursuit actions, so it only actually drives movement when nothing else has taken over.

new SwimAction<MyEntity, MyGoal>(priority, /* pursueTarget */ true);

Combat actions (action.combat)

TimedAttackAction

A generic melee attack: waits windupTicks (facing the target throughout), then attempts a single MeleeHitResolver.tryStrike, then sets its cooldown.

new TimedAttackAction<MyEntity, MyGoal>(
    "my_melee",  // debugName
    6,           // windupTicks
    2.0D,        // reach
    "my_melee_cooldown",
    20           // cooldownTicks
);

Construct several instances with different windup/reach/cooldown values (and distinct debug names) to represent different attacks, see AttackProfile below for how to offer several to a behavior tree at once.

UseItemAction

Drives the vanilla "hold an item, then release it" lifecycle (startUsingItem/stopUsingItem), the same mechanism vanilla's own ranged-attack and consumable goals use for bows, crossbows, tridents, and food/potions.

Two modes:

  • Auto-completing (eating, drinking), via UseItemAction.autoComplete(...). Starts using the item and waits for vanilla's own use-duration completion; never calls stopUsingItem(), onChargeTick, or onRelease itself.

    UseItemAction.<MyEntity, MyGoal>autoComplete(
        InteractionHand.OFF_HAND,
        agent -> agent.getOffhandItem().is(Items.GOLDEN_APPLE),
        "eat_cooldown", 600,
        priority   // constructor parameter, see below
    );
  • Charge-and-release (bows, crossbows, tridents), construct with minChargeTicks > 0; waits at least that long, then calls stopUsingItem() followed by your onRelease hook once readyToRelease agrees (or once maxChargeTicks is hit regardless). onChargeTick is where you drive continuous per-tick effects like aiming (getLookControl().setLookAt(...)), since this action doesn't do that for you automatically.

    Bows specifically need onRelease to actually fire: for a Mob, stopUsingItem() alone ends the drawing animation but fires nothing (BowItem#releaseUsing only fires for instanceof Player). Wire onRelease to call RangedAttackMob#performRangedAttack(target, power) yourself, mirroring vanilla's own RangedBowAttackGoal.

Priority is a constructor parameter, not a fixed literal, unlike most actions, a charge-and-release bow attack and an emergency heal item both use this action but need very different priority weights, so the value is supplied per instance rather than baked into the class.

MeleeHitResolver

Not an Action itself, the shared "execute" half behind TimedAttackAction and any custom attack: bounding-box reach check, melee line-of-sight check, and damage application, centralized so every new attack doesn't re-implement (and risk drifting on) the same three steps.

boolean landed = MeleeHitResolver.tryStrike(mob, target, reach);

AttackProfile / AttackSelector, choosing between several attacks

AttackProfile is a data-only description of one attack a behavior tree can pick between:

new AttackProfile<MyEntity, MyGoal>(
    "melee",           // name
    meleeAction,        // the Action
    "melee_cooldown",   // cooldownKey that must be ready
    0.0D,               // minRange
    2.0D,               // maxRange
    20                  // priority, tie-break when multiple profiles are eligible
);

AttackSelector.select(mob, target, cooldowns, forceReady, candidates) picks the best-scoring legal profile (in range, off cooldown), highest priority wins, ties keep the first-listed candidate. Line-of-sight/hit resolution isn't checked here (that's MeleeHitResolver's job at execution time), only what a tree needs to know before committing.

Adding a new attack means adding one AttackProfile entry, not editing tree control flow, see HuntTargetNode in Behavior Trees for how this is consumed.

CrawlToDestinationAction

A crawl-aware counterpart to MoveToDestinationAction, added alongside the bundled spider example (see Wall-Climbing Example (Spider)). Where MoveToDestinationAction computes a route with whatever Pathfinder it's given but hands only the final waypoint off to vanilla PathNavigation, CrawlToDestinationAction walks a CrawlTraversalEvaluator path one waypoint at a time itself, toggling CrawlCapability.isWallCrawling on and off per waypoint and driving velocity directly via NavigationQueries.computeWallCrawlVelocity, so a destination on a wall or ceiling is actually reachable, not just computed.

new CrawlToDestinationAction<MyEntity, MyGoal>(
    CrawlTraversalEvaluator.INSTANCE,
    1.1D,  // speed
    1,     // arrivalRadius
    10,    // repathIntervalTicks
    60     // stuckTimeoutTicks
);

Requires the agent to implement CrawlCapability, and, for the velocity this action sets each tick to actually move the agent rather than being overwritten by vanilla's gravity-driven travel(), requires the entity to route its own movement application through CrawlController while isWallCrawling() is true (see Wall-Climbing Example (Spider) for the travel(Vec3) override this implies).

Utility actions (action.utility)

FleeAction

Moves the agent directly away from TARGET, recomputing the flee direction every repathIntervalTicks (not every tick, since pixel-perfect precision isn't needed and constant repathing is wasteful).

new FleeAction<MyEntity, MyGoal>(1.2D, 8.0D, 20); // speed, fleeDistance, repathIntervalTicks

InvestigateLastSeenTargetAction

Walks toward an extrapolated interception point (via TargetPrediction) for a target recently seen but since lost, instead of beelining for its last exact block. Requires LAST_SEEN_POS to be populated, which requires your TargetSensor to have been constructed with a visibilityPredicate (see Sensing).

new InvestigateLastSeenTargetAction<MyEntity, MyGoal>(
    AStarPathfinder.INSTANCE,
    1.1D,   // speed
    2,      // arrivalRadius
    60,     // stuckTimeoutTicks
    100,    // maxSearchAgeTicks
    60,     // maxPredictionStalenessTicks
    0.02D,  // minPredictionSpeed
    2.0D,   // minPredictionDistance
    8.0D    // maxPredictionDistance
);

Computes the search point once, on start(), it isn't recomputed mid-walk, since a fresh sighting means the target was reacquired, at which point your planner's next replan should simply pick a different goal rather than this action retargeting itself. Succeeds on arrival; fails with FAILED_PRECONDITION if there's no usable (or already too stale) sighting to investigate; fails with FAILED_NO_PATH/FAILED_STUCK if the walk itself doesn't pan out. Like MoveToDestinationAction, this is a single one-shot path with no repathing, build your own action around TargetPrediction directly if you want periodic re-scanning while walking.

Validating the search point for a non-ground-only agent

The extrapolated point is validated against a Function<Level, Predicate<BlockPos>>, an optional tenth constructor argument that defaults to TargetPrediction::standableIn (the ordinary ground-mob check) when omitted, so every existing nine-argument call site is unaffected:

new InvestigateLastSeenTargetAction<MyEntity, MyGoal>(
    CrawlTraversalEvaluator.INSTANCE, // or AStarPathfinder.INSTANCE, independently of this parameter
    1.0D, 2, 60, 100, 60, 0.02D, 2.0D, 8.0D,
    SpiderTraversalPredicates::standableForCrawler  // in place of the ground-only default
);

Used exactly this way by the bundled spider example's INVESTIGATE branch (see Wall-Climbing Example (Spider)), note that swapping this predicate is independent of which Pathfinder the action is given: the spider's INVESTIGATE branch keeps the ordinary ground pathfinder and only widens which points count as valid, since a crawler-aware predicate only ever accepts a superset of what the ground-only default would.

Next

Clone this wiki locally