Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial docs for Entity Pathfinders and Mob Goal API #289

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions config/sidebar.paper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ const paper: SidebarsConfig = {
"dev/api/scheduler",
"dev/api/plugin-messaging",
"dev/api/plugin-configs",
"dev/api/mob-goals",
"dev/api/entity-pathfinder",
"dev/api/folia-support",
],
},
Expand Down
62 changes: 62 additions & 0 deletions docs/paper/dev/api/entity-pathfinder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
slug: /dev/entity-pathfinder
description: A guide to the Entity Pathfinder API.
---

# Entity Pathfinder API

The Entity Pathfinder API is a way of controlling the movement of entities in Minecraft. It allows you to set a path
for an entity to follow, such as moving to a location, or following a player.

## Accessing the Pathfinder

To access the pathfinder for a Mob, you need to call `getPathfinder()` on the Mob. This will return an instance of `Pathfinder`.

:::important

The pathfinder is only available for Mobs, not for all entities.
olijeffers0n marked this conversation as resolved.
Show resolved Hide resolved

:::

Lets say that we have a `Cow` and we want it to move to a specific `Player`'s location. We can do this by getting the
pathfinder for the cow and then setting the path to the player's location:

```java
Cow cow = ...;
Player player = ...;

Pathfinder pathfinder = cow.getPathfinder();
// moveTo returns a boolean indicating whether the path was set successfully
boolean success = pathfinder.moveTo(player.getLocation());
```

If we want to access the current Path for the cow, we can call `getCurrentPath()` on the pathfinder:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
If we want to access the current Path for the cow, we can call `getCurrentPath()` on the pathfinder:
If we want to access the current path for the cow, we can call `getCurrentPath()` on the pathfinder:


```java
PathResult path = pathfinder.getCurrentPath();

// A PathResult is essentially a wrapper around a List of Locations. These can be accessed with:
List<Location> locations = path.getPoints();
// It is important to note that the list contains points that have already been passed,
// as well as future points. If you want to get the next point, you can use:
Location nextPoint = path.getNextPoint(); // Or locations.get(path.getNextPointIndex())
// Finally, you can access the final destination with:
Location destination = path.getFinalPoint();
```

## Pathfinding Rules

Much of the way that the Pathfinder works is dictated by the limitations of the actual entity pathfinding in Minecraft.
For example, a Polar Bear cannot fly. This means that if you set a path for a Polar Bear to a location that is in the air,
it will not be able to reach it.

There are some attributes that can be set on the pathfinder to change the way that the pathfinder works. These are:
- `setCanOpenDoors(boolean)`: Whether the entity can open doors.
olijeffers0n marked this conversation as resolved.
Show resolved Hide resolved
- `setCanPassDoors(boolean)`: Whether the entity can pass through open doors.
- `setCanFloat(boolean)`: Whether the entity can float in water.
These all have respective getters as well.

## Stopping the Pathfinder

To stop the pathfinder, you can call `stopPathfinding()` on the pathfinder. This will stop the pathfinder and clear the
current path. You can use `hasPath()` to check if the pathfinder is currently running.
126 changes: 126 additions & 0 deletions docs/paper/dev/api/mob-goals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
---
slug: /dev/mob-goals
description: A guide to the PDC API for storing data.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong

---

# Mob Goal API

The Mob Goal API is a way of controlling the behaviour of mobs in Minecraft. It allows you to set a goal for a mob to perform, such as
attacking a player, or moving to a location. It also allows you to create your own custom goals.

## Registering a Goal on an Entity

To register a goal on an entity, you need to create an instance of the goal and then register it with the entity:

```java
Cow cow = ...;
Goal<Cow> goal = new ExampleGoal();

server.getMobGoals().addGoal(cow, 0, goal); // 0 is the priority, lower numbers are higher priority
```

:::tip

You can access the Vanilla goals from the `VanillaGoal` class. These are the goals that are used by Vanilla Minecraft.
They are specific to each mob type, so you can't use a cow goal on a zombie for example.
olijeffers0n marked this conversation as resolved.
Show resolved Hide resolved

:::

## Creating a Custom Goal

To create a custom goal, you need to create a class that implements the `Goal` interface. This interface has several methods:
- `void start()`: Called when the goal is started.
- `void tick()`: Called every tick while the goal is running.
- `void stop()`: Called when the goal is stopped.
- `boolean shouldActivate()`: Called to determine if the goal should start.
- `boolean shouldStayActive()`: Called to determine if the goal should continue running.
- `GoalKey getKey()`: Called to get the key for the goal.
- `EnumSet<GoalType> getTypes()`: Called to get the types of the goal.

:::note[types]

The `getTypes()` method is used to determine what types of goal this is. The types are:
- `GoalType.MOVE`: The goal moves the entity.
- `GoalType.LOOK`: The goal changes the direction the entity is looking.
- `GoalType.JUMP`: The goal makes the entity jump.
- `GoalType.TARGET`: The goal changes the target of the entity.
- `GoalType.UNKNOWN_BEHAVIOR`: The goal does something else. Used for mapping Vanilla goals.

:::

Here is an example of a goal that makes a camel follow a player. This is essentially the same as the `FOLLOW_MOB` in Vanilla,
but it is a good example of how to create a goal.

```java
public class CamelFollowPlayerGoal implements Goal<Camel> {

public static final GoalKey<Camel> KEY = GoalKey.of(Camel.class, new NamespacedKey("testplugin", "camel_follow_player"));

private final Player player;
private final Camel camel;

public CamelFollowPlayerGoal(Player player, Camel camel) {
// The constructor takes the Player to follow and the Camel that is following
this.player = player;
this.camel = camel;
}

@Override
public boolean shouldActivate() {
// This is whether or the goal should start. In this case, we want the goal to always start so we return true.
// You could also return false here if you wanted to only start the goal in certain situations.
return true;
}

@Override
public void start() {
// This is called when the goal starts. In this case, we just send a message to the player.
player.sendMessage(text("I am following you!"));
}

@Override
public void tick() {
// This is called every tick while the goal is running. In this case, we make the camel move towards the player
// using the Pathfinder API. The 5.0 is the speed of the camel.
camel.getPathfinder().moveTo(player, 5.0);
}

@Override
public void stop() {
// This is called when the goal stops. In this case, we just send a message to the player.
player.sendMessage(text("I Stopped following you!"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
player.sendMessage(text("I Stopped following you!"));
player.sendMessage(text("I stopped following you!"));

}

@Override
public @NotNull GoalKey<Camel> getKey() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can remove the NotNull annotations it's better to assume types are not null by default

// This is the key for the goal. It is used to identify the goal and is used to determine if two goals are the same.
// It requires the class of the entity and a NamespacedKey. The NamespacedKey is used to identify the goal.
// You should use the plugin's namespace for the NamespacedKey, not Minecraft or Bukkit.
return KEY;
}

@Override
public @NotNull EnumSet<GoalType> getTypes() {
// This is used to determine what types of goal this is. In this case, we are moving the entity and changing the
// direction it is looking, so we return MOVE and LOOK. Return as many types as you need.
return EnumSet.of(GoalType.MOVE, GoalType.LOOK);
}
}
```

## Stopping a Goal
olijeffers0n marked this conversation as resolved.
Show resolved Hide resolved

To stop a goal, you need to get the goal key and then call `stop()` on the goal:

```java
Cow cow = ...;
// This works because our example has a public static `KEY` field
server.getMobGoals().removeGoal(cow, CamelFollowPlayerGoal.KEY);

// You can also remove Vanilla goals
server.getMobGoals().removeGoal(cow, VanillaGoal.TEMPT);

// You can also remove all goals
server.getMobGoals().removeAllGoals(cow);
server.getMobGoals().removeAllGoals(cow, GoalType.MOVE); // Remove all MOVE goals
```