diff --git a/config/sidebar.paper.ts b/config/sidebar.paper.ts index 756be038..20d740eb 100644 --- a/config/sidebar.paper.ts +++ b/config/sidebar.paper.ts @@ -157,6 +157,8 @@ const paper: SidebarsConfig = { "dev/api/lifecycle", "dev/api/registries", "dev/api/recipes", + "dev/api/mob-goals", + "dev/api/entity-pathfinder", "dev/api/folia-support", "dev/api/roadmap", ], diff --git a/docs/paper/dev/api/entity-pathfinder.md b/docs/paper/dev/api/entity-pathfinder.md new file mode 100644 index 00000000..789b7336 --- /dev/null +++ b/docs/paper/dev/api/entity-pathfinder.md @@ -0,0 +1,63 @@ +--- +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 entities that implement `Mob`. + +::: + +Let's 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: + +```java +PathResult path = pathfinder.getCurrentPath(); + +// A PathResult is essentially a wrapper around a List of Locations. These can be accessed with: +List 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. + +Some attributes can be set on the pathfinder to change the way that the pathfinder works. These are: +- `setCanOpenDoors(boolean)`: Whether the entity can open doors. This is relevant for Zombies breaking down doors, and +Villagers opening doors. +- `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 + +You can call `stopPathfinding()` on the pathfinder to stop the pathfinder. This will stop the pathfinder and clear the +current path. You can use `hasPath()` to check if the pathfinder is running. diff --git a/docs/paper/dev/api/mob-goals.md b/docs/paper/dev/api/mob-goals.md new file mode 100644 index 00000000..ba519028 --- /dev/null +++ b/docs/paper/dev/api/mob-goals.md @@ -0,0 +1,126 @@ +--- +slug: /dev/mob-goals +description: A guide to the mob goal API. +--- + +# Mob Goal API + +The Mob Goal API is a way of controlling the behavior 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 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 `VanillaGoal.BEE_ATTACK` on a Zombie, for example. + +::: + +## 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 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 { + + public static final GoalKey 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!")); + } + + @Override + public GoalKey getKey() { + // 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 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); + } +} +``` + +## Removing a Goal + +To remove a goal, you need to get the goal key and then call the `removeGoal` method: + +```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 +```