|
| 1 | +--- |
| 2 | +slug: /dev/mob-goals |
| 3 | +description: A guide to the PDC API for storing data. |
| 4 | +--- |
| 5 | + |
| 6 | +# Mob Goal API |
| 7 | + |
| 8 | +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 |
| 9 | +attacking a player, or moving to a location. It also allows you to create your own custom goals. |
| 10 | + |
| 11 | +## Registering a Goal on an Entity |
| 12 | + |
| 13 | +To register a goal on an entity, you need to create an instance of the goal and then register it with the entity: |
| 14 | + |
| 15 | +```java |
| 16 | +Cow cow = ...; |
| 17 | +Goal<Cow> goal = new ExampleGoal(); |
| 18 | + |
| 19 | +server.getMobGoals().addGoal(cow, 0, goal); // 0 is the priority, lower numbers are higher priority |
| 20 | +``` |
| 21 | + |
| 22 | +:::tip |
| 23 | + |
| 24 | +You can access the Vanilla goals from the `VanillaGoal` class. These are the goals that are used by Vanilla Minecraft. |
| 25 | +They are specific to each mob type, so you can't use a cow goal on a zombie for example. |
| 26 | + |
| 27 | +::: |
| 28 | + |
| 29 | +## Creating a Custom Goal |
| 30 | + |
| 31 | +To create a custom goal, you need to create a class that implements the `Goal` interface. This interface has several methods: |
| 32 | +- `void start()`: Called when the goal is started. |
| 33 | +- `void tick()`: Called every tick while the goal is running. |
| 34 | +- `void stop()`: Called when the goal is stopped. |
| 35 | +- `boolean shouldActivate()`: Called to determine if the goal should start. |
| 36 | +- `boolean shouldStayActive()`: Called to determine if the goal should continue running. |
| 37 | +- `GoalKey getKey()`: Called to get the key for the goal. |
| 38 | +- `EnumSet<GoalType> getTypes()`: Called to get the types of the goal. |
| 39 | + |
| 40 | +:::note[types] |
| 41 | + |
| 42 | +The `getTypes()` method is used to determine what types of goal this is. The types are: |
| 43 | +- `GoalType.MOVE`: The goal moves the entity. |
| 44 | +- `GoalType.LOOK`: The goal changes the direction the entity is looking. |
| 45 | +- `GoalType.JUMP`: The goal makes the entity jump. |
| 46 | +- `GoalType.TARGET`: The goal changes the target of the entity. |
| 47 | +- `GoalType.UNKNOWN_BEHAVIOR`: The goal does something else. Used for mapping Vanilla goals. |
| 48 | + |
| 49 | +::: |
| 50 | + |
| 51 | +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, |
| 52 | +but it is a good example of how to create a goal. |
| 53 | + |
| 54 | +```java |
| 55 | +public class CamelFollowPlayerGoal implements Goal<Camel> { |
| 56 | + |
| 57 | + public static final GoalKey<Camel> KEY = GoalKey.of(Camel.class, new NamespacedKey("testplugin", "camel_follow_player")); |
| 58 | + |
| 59 | + private final Player player; |
| 60 | + private final Camel camel; |
| 61 | + |
| 62 | + public CamelFollowPlayerGoal(Player player, Camel camel) { |
| 63 | + // The constructor takes the Player to follow and the Camel that is following |
| 64 | + this.player = player; |
| 65 | + this.camel = camel; |
| 66 | + } |
| 67 | + |
| 68 | + @Override |
| 69 | + public boolean shouldActivate() { |
| 70 | + // This is whether or the goal should start. In this case, we want the goal to always start so we return true. |
| 71 | + // You could also return false here if you wanted to only start the goal in certain situations. |
| 72 | + return true; |
| 73 | + } |
| 74 | + |
| 75 | + @Override |
| 76 | + public void start() { |
| 77 | + // This is called when the goal starts. In this case, we just send a message to the player. |
| 78 | + player.sendMessage(text("I am following you!")); |
| 79 | + } |
| 80 | + |
| 81 | + @Override |
| 82 | + public void tick() { |
| 83 | + // This is called every tick while the goal is running. In this case, we make the camel move towards the player |
| 84 | + // using the Pathfinder API. The 5.0 is the speed of the camel. |
| 85 | + camel.getPathfinder().moveTo(player, 5.0); |
| 86 | + } |
| 87 | + |
| 88 | + @Override |
| 89 | + public void stop() { |
| 90 | + // This is called when the goal stops. In this case, we just send a message to the player. |
| 91 | + player.sendMessage(text("I Stopped following you!")); |
| 92 | + } |
| 93 | + |
| 94 | + @Override |
| 95 | + public @NotNull GoalKey<Camel> getKey() { |
| 96 | + // 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. |
| 97 | + // It requires the class of the entity and a NamespacedKey. The NamespacedKey is used to identify the goal. |
| 98 | + // You should use the plugin's namespace for the NamespacedKey, not Minecraft or Bukkit. |
| 99 | + return KEY; |
| 100 | + } |
| 101 | + |
| 102 | + @Override |
| 103 | + public @NotNull EnumSet<GoalType> getTypes() { |
| 104 | + // This is used to determine what types of goal this is. In this case, we are moving the entity and changing the |
| 105 | + // direction it is looking, so we return MOVE and LOOK. Return as many types as you need. |
| 106 | + return EnumSet.of(GoalType.MOVE, GoalType.LOOK); |
| 107 | + } |
| 108 | +} |
| 109 | +``` |
| 110 | + |
| 111 | +## Stopping a Goal |
| 112 | + |
| 113 | +To stop a goal, you need to get the goal key and then call `stop()` on the goal: |
| 114 | + |
| 115 | +```java |
| 116 | +Cow cow = ...; |
| 117 | +// This works because our example has a public static `KEY` field |
| 118 | +server.getMobGoals().removeGoal(cow, CamelFollowPlayerGoal.KEY); |
| 119 | + |
| 120 | +// You can also remove Vanilla goals |
| 121 | +server.getMobGoals().removeGoal(cow, VanillaGoal.TEMPT); |
| 122 | + |
| 123 | +// You can also remove all goals |
| 124 | +server.getMobGoals().removeAllGoals(cow); |
| 125 | +server.getMobGoals().removeAllGoals(cow, GoalType.MOVE); // Remove all MOVE goals |
| 126 | +``` |
0 commit comments