> For the complete documentation index, see [llms.txt](https://edseries-plugins.gitbook.io/p/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://edseries-plugins.gitbook.io/p/pinnaprison/developers/edlib-api/goals.md).

# Goals

Goals are EdLib's behaviour/animation system. Each [entity](/p/pinnaprison/developers/edlib-api/entities.md) holds a **queue** of goals and runs them one at a time; when a goal finishes, the next one starts automatically. This is how every animated enchant works — a falling block is given an `EdGoalMove` to its impact point, and its *end callback* breaks the blocks.

```java
entity.addGoal(new EdGoalMove(new Vector(10, 64, 5), 0.2));
entity.addGoal(new EdGoalDelay(40));               // wait 2s
entity.addGoal(new EdGoalMove(new Vector(0, 64, 0), 0.2));
```

Goals tick on EdLib's **async** executor, so they're safe to start from the same off-thread code that spawns the entity.

## Callbacks — the important part

Every goal accepts three runnables. The **end callback is where you make things happen** (deal the damage, break the blocks, play the impact FX) the moment the entity arrives:

| Setter                          | Runs                                                                  |
| ------------------------------- | --------------------------------------------------------------------- |
| `setStartRunnable(Runnable)`    | Once, when the goal begins.                                           |
| `setEachTickRunnable(Runnable)` | Every tick while the goal runs (trail particles, etc.).               |
| `setEndRunnable(Runnable)`      | Once, when the goal **completes naturally** (not when force-stopped). |

```java
EdGoalMove fall = new EdGoalMove(impact, 1.4);
fall.setEachTickRunnable(() -> trailParticles(meteor.getPosition()));
fall.setEndRunnable(() -> {
    meteor.remove();
    mines.breakSphere(player, impact, 3, false, true, true); // break + reward
});
entity.addGoal(fall);
```

{% hint style="info" %}
`setEndRunnable` does **not** fire if the goal is cancelled with `clearGoals()` / `forceStop()`. Use it for "I arrived" logic, not for cleanup — schedule cleanup separately (e.g. `getExecutor().asyncLater(entity::remove, 120, "cleanup")`).
{% endhint %}

## Managing an entity's goals

| Method (on `EdEntity`)                      |                                                          |
| ------------------------------------------- | -------------------------------------------------------- |
| `addGoal(EdGoal goal)`                      | Queue a goal (starts immediately if nothing is running). |
| `getCurrentGoal()` / `setCurrentGoal(goal)` | The active goal.                                         |
| `getGoalQueue()`                            | The pending queue.                                       |
| `skipCurrentGoal()`                         | Stop the current goal and advance to the next.           |
| `clearGoals()`                              | Stop everything and empty the queue.                     |

## Built-in goals

### EdGoalMove

Moves in a straight line to a point. `speed` is **blocks per tick**.

```java
EdGoalMove move = new EdGoalMove(new Vector(10, 64, 5), 0.2);
move.setAffectY(false);            // keep current Y (slide along the ground)
move.setSendRotationEachTick(true); // keep facing where it's going
entity.addGoal(move);
```

Options: `setAffectY(boolean)` (default true), `setSendRotation(boolean)` (face the target, default true), `setSendRotationEachTick(boolean)` (default false), `setInvertRotation(boolean)`.

### EdGoalArchMove

Straight line to the target with a sine "hop" added to Y — a gentle arc. `duration` is in **milliseconds**.

```java
entity.addGoal(new EdGoalArchMove(new Vector(5, 64, 5), 0.2, 3000)); // 3s arc
```

### EdGoalParabolicMove

A true parabola to the target with a controllable peak `height`. `duration` is in **milliseconds**. Great for lobbed projectiles.

```java
entity.addGoal(new EdGoalParabolicMove(new Vector(10, 64, 10), 3.0, 1200));
```

### EdGoalOrbit

Circles a center point. `angularSpeed` is radians/tick; `ticksDuration` is the length in ticks (`0` = forever).

```java
EdGoalOrbit orbit = new EdGoalOrbit(center, 5.0, 0.05, true, 200); // 10s, clockwise
orbit.setAffectY(false);
entity.addGoal(orbit);
```

Options: `setAffectY`, `setSendRotation`, `setSendRotationEachTick`, `setInvertRotation`. Helpers: `getCurrentAngle()`, `getCenterPoint()`.

### EdGoalFollowEntity

Trails a target (a player or another EdEntity), keeping `followDistance` away. `speed` is blocks/tick; `duration` is in **ticks** (20 = 1s).

```java
EntityHolder target = new EntityHolder(player);
entity.addGoal(new EdGoalFollowEntity(target, 3.0, 0.2, 200)); // follow 10s
```

### EdGoalDelay

A pause in the sequence. `delayTicks` ticks (20 = 1s).

```java
entity.addGoal(new EdGoalDelay(60)); // wait 3 seconds
```

Helpers: `getProgress()` (0–1), `getRemainingTicks()`, `getRemainingSeconds()`.

## EntityHolder

A small wrapper so a goal can target **either** a Bukkit entity or an EdEntity:

```java
EntityHolder fromPlayer = new EntityHolder(player);   // any Bukkit Entity
EntityHolder fromFake   = new EntityHolder(edEntity); // an EdEntity
Vector pos = fromPlayer.getPosition();
```

## Writing a custom goal

Extend `EdGoal` and implement `shouldExecute()` (return `false` to finish) and `tick()` (runs each tick). Optionally override `start()`.

```java
public class EdGoalShake extends EdGoal {
    private final int durationTicks;
    private int tick = 0;

    public EdGoalShake(int durationTicks) { this.durationTicks = durationTicks; }

    @Override public void start() {
        getEntity().damageEffect();
    }

    @Override public boolean shouldExecute() {
        return tick < durationTicks; // false => goal completes, end callback fires
    }

    @Override public void tick() {
        tick++;
        Vector p = getEntity().getLocVector();
        double j = (Math.random() - 0.5) * 0.2;
        getEntity().shortTp(p.getX() + j, p.getY(), p.getZ() + j);
    }
}
```

`getEntity()` gives you the entity the goal is bound to. `forceStop()` stops it without firing the end callback or advancing the queue; `isRunning()` / `isForceStopped()` report state.

## Next

* [Spawning entities into mines](/p/pinnaprison/developers/edlib-api/mine-entities.md) — put all of this together: drive packet entities over a player's mine and break + reward blocks on impact, exactly like the built-in enchants.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://edseries-plugins.gitbook.io/p/pinnaprison/developers/edlib-api/goals.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
