[TUT] Making a LivingEntity walk to a specific tile.

Discussion in 'Resources' started by Ivan, Apr 3, 2013.

Thread Status:
Not open for further replies.
  1. Offline


    As requested by many people, here is a snippet that will make a LivingEntity travel to a specific tile.
    I highly recommend you to follow this tutorial made by Jacek to know how to create a custom creature. AFTER following that tutorial and understanding how to extend a creature you can continue reading this tutorial.

    First off we need to create a PathfinderGoal which will point the entity navigator to a specific tile. We aren't using just going to use a method that points the navigator to a tile, because it's possible that it would get overriden by another pathfinder goal. By looking at PathfinderGoalRandomStroll.java (the simplest pathfindergoal I found) you can see that a() is used to validate the goal and c() to execute the walking process. After looking at that code we can now create our own goal by extending PathFinderGoal. I will call my goal PathfinderGoalWalktoTile and I'll override the two crucial methods (a and c).
    Show Spoiler

    2. public class PathfinderGoalWalktoTile extends PathfinderGoal{
    6. @Override
    7. public boolean a() {
    9. //Validation
    10. }
    12. @Override
    13. public void c(){
    14. //Navigator execution
    15. }
    19. }

    Now I'm going to copy most of the code from the other pathfindergoal and adjust it to my needs, adding a constructor and renaming some variables.

    Show Spoiler

    2. public class PathfinderGoalWalktoTile extends PathfinderGoal{
    4. float speed;
    5. private EntityCreature entitycreature;
    6. public PathfinderGoalWalktoTile(EntityCreature entitycreature, float speed){
    7. this.speed = speed;
    8. this.entitycreature = entitycreature;
    9. }
    12. @Override
    13. public boolean a() {
    15. if (this.entitycreature.aI() >= 100) {
    16. return false;
    17. } else if (<shoudwalk?>) {
    18. return true;
    19. } else {
    20. return false;
    21. }
    23. @Override
    24. public void c(){
    25. this.entitycreature.getNavigation().a(x, y, z, speed);
    26. }
    30. }

    The x, y and z variables can be replaced with actual numbers if you want, or you could make them dynamic by grabbing them from another class.

    Now that we have our PathfinderGoal we can add this goal to the entity's pathfinder selector using reflection. You will have to get the original pathfinder goals from their NMS code which you can find here. I will use the zombie that Jacek used in his tutorial.

    2. public class BloodMoonEntityZombie extends net.minecraft.server.EntityZombie {
    4. public BloodMoonEntityZombie(World world) {
    5. super(world);
    7. try {
    9. Field gsa = net.minecraft.server.v1_5_R2.PathfinderGoalSelector.class.getDeclaredField("a");
    10. gsa.setAccessible(true);
    13. gsa.set(this.goalSelector, new UnsafeList());
    14. gsa.set(this.targetSelector, new UnsafeList());
    15. } catch (SecurityException e) {
    16. e.printStackTrace();
    17. } catch (NoSuchFieldException e) {
    18. e.printStackTrace();
    19. e.printStackTrace();
    20. e.printStackTrace();
    21. }
    23. this.goalSelector.a(0, new PathfinderGoalFloat(this));
    24. this.goalSelector.a(1, new PathfinderGoalBreakDoor(this));
    25. this.goalSelector.a(1, new PathfinderGoalWalktoTile(this, (float) this.bw));//this.bw can be replaced by a custom speed
    26. this.goalSelector.a(2, new PathfinderGoalMeleeAttack(this, EntityHuman.class, this.bI, false));
    27. this.goalSelector.a(3, new PathfinderGoalMeleeAttack(this, EntityVillager.class, this.bI, true));
    28. this.goalSelector.a(4, new PathfinderGoalMoveTowardsRestriction(this, this.bI));
    29. this.goalSelector.a(5, new PathfinderGoalMoveThroughVillage(this, this.bI, false));
    30. this.goalSelector.a(6, new PathfinderGoalRandomStroll(this, this.bI));
    31. this.goalSelector.a(7, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F));
    32. this.goalSelector.a(7, new PathfinderGoalRandomLookaround(this));
    33. this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, true));
    34. this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget(this, EntityHuman.class, 16.0F, 0, true));
    35. this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget(this, EntityVillager.class, 16.0F, 0, false));
    36. }
    37. }

    If you want to remove a trait of the zombie, like knocking down a door, you can just remove that line from the code. I hope I've helped you. If you find any errors in this code feel free to correct them ;). I'm kinda new at NMS code.
  2. Offline


    Thank-you, very useful!
  3. Offline


    Nice, good work, if you have anymore questions about making plugins, just ask in to Ivan, he knows everything. He didn't showed it yet, but he is the best at it!
    Ivan likes this.
  4. Offline


    First of all:
    Thank you very very much :)
    But why you're setting the PathfinderGoalFloat twice with the same int? Btw what does the int in a(int i, Pathfindergoal)? I can't find any explanation in the nmscode
  5. Offline


    Oops, yeah the float should only be set once. About the meaning of the int, as far as I know it would be the priority of the goal, since it would start with the lowest goal and would go up until the last one.
  6. Offline


    So I just tested my adaptation of this code and my a() is returning true, but c() is not being executed. Any ideas?
  7. Offline


    Post your code in pastebin or here please :D
  8. Offline


    I found the problem actually. Some weird error on my part.
  9. Offline


    Still post ur code to help others (like me)
  10. Offline


  11. Offline


    Ivan What should I put here instead of <statement if he should walk to that specific tile?
    if (this.entitycreature.aH() >= 100) {
    return false;
    } else if (<statement if he should walk to that specific tile>) {
    return true;
    } else {
    return false;
  12. Offline



    also, what is that (aH >= 100) anyways? ;)
  13. Offline


    The boolean should be true when you want him to walk to the specific location. If you want him to always walk to there just return true.
  14. Offline


    the thing is that then there is no delay between the entity spawning and it starting to move, which causes the entity to "jump" a little bit.
    one of the values can be used to have the entity wait a couple ticks before moving.

    However i know close to nothing about this NMS shizzle, so i have no idea how it works in detail.
  15. Offline


    Why does this give me an error, it cannot find PathfinderGoalWalktoTile. Theres a PathfinderGoalActionMove, but i don't know how to actually add coordinates to that, it seems it wants an entity instead, any help Ivan ?
    this.goalSelector.a(1, new PathfinderGoalWalktoTile(this, (float) 5));
  16. Offline


    You need to create that yourself. The tutorial shows you how to do that.
  17. I'm wondering, how do i make the pathfinding only do melee on certain players and not all. I don't think i grasp the pathfinding completely :p
  18. Offline


    Remove the EntityHuman target selector, and it will no longer aggro on players
  19. How would i do that inside an event if some conditions are met?
  20. Offline


    Question; is there a way to wrap this around a EntityHuman? They don't have any navigation variables whatsoever.
  21. Offline


    My zombie won't move at all with only the custom pathfind goal, this is my PathfinderGoalWalktoTile:
    1. public class PathfinderGoalWalktoTile extends PathfinderGoal{
    3. float speed;
    4. private EntityCreature entitycreature;
    5. public PathfinderGoalWalktoTile(EntityCreature entitycreature, float speed){
    6. this.speed = speed;
    7. this.entitycreature = entitycreature;
    8. }
    11. @Override
    12. public boolean a() {
    14. if (this.entitycreature.aH() >= 100) {
    15. return false;
    16. }
    17. else
    18. {
    19. System.out.println("true");
    20. return true;
    21. }
    22. }
    24. @Override
    25. public void c(){
    26. this.entitycreature.getNavigation().a(0.0, 0.0, 0.0, speed);
    27. }
    31. }

    The debug message that returns true, is seen in my console, but C() doesnt activate?
  22. Offline


    I'm having the same issue as Miro, using verbatim code from the tutorial. Any ideas Ivan?
  23. Offline


    I can't access my PC at the moment, I'll look through the code tomorrow.
  24. Offline


    Some additional info. If I make my PathfinderGoal identital to the RandomStroll one, the zombie WILL randomly stroll. The problem is in the logic of a() on my end it would appear. I'm trying to make a creature follow a player, and if he gets too far away, teleport the creature back to the player. If he is close enough, he can stroll randomly.
  25. Offline


    Cant you use an modified version of PathfinderGoalFollowOwner (or whatever the name was)?
  26. Is it possible to make these type of things without using libraries such as ProtocolLib and not making it depend on the version of CraftBukkit such as net.minecraft.server? I love look at these types of things but I just don't like using dependencies.
  27. Offline


    I have more plans, that require I have a pretty decent mastery of this goal system, so I'd like to find out what my problem is with simply moving to a space, but I did try to use the goal you refer to as well and ran into pretty much the same issue.
  28. I was wondering..
    in the code
    1. @Override
    2. public boolean a() {
    4. if (this.entitycreature.aH() >= 100) {
    5. return false;
    6. }
    7. else
    8. {
    9. System.out.println("true");
    10. return true;
    11. }
    12. }

    what does the .aH() method do?
  29. Offline


    I tried looking that up for you, but it went too deep into the obfuscated code. My best guess would be that it is some kind of timeout to prevent the pathfinder from trying to pathfind erm... too much? :p

    I also updated the main post, since with the new updates aH changed into aI.
  30. I have this code now (and the function is called (only once per entity weird enough)) but they aren't actually walking.

    strangely enough .getNavigation() doesn't do anything at all and .getControllerMove() only rotates them a bit in a certain direction (2 sides of a map, only on 1 side they look the correct way, the other side of the map is looking in a completely wrong direction).

    Any ideas what I'm doing wrong? are they relative coordinates perhaps or just exact coordinates?

    1. import org.bukkit.Bukkit;
    2. import org.bukkit.Location;
    4. import net.minecraft.server.v1_6_R2.EntityCreature;
    5. import net.minecraft.server.v1_6_R2.PathfinderGoal;
    7. public class PathfinderGoalWalktoTile extends PathfinderGoal {
    9. private float speed;
    10. private EntityCreature entitycreature;
    12. public PathfinderGoalWalktoTile(EntityCreature entitycreature, float speed){
    13. this.speed = speed;
    14. this.entitycreature = entitycreature;
    15. }
    17. @Override
    18. public boolean a() {
    19. return true;
    20. }
    22. @Override
    23. public void c(){
    24. Bukkit.getLogger().info("method c from PathFinderGoalWalktoTile called!");
    26. Location targetLocation = new Location(Bukkit.getWorld("world"), -1053, 32, 1124);
    28. //this.entitycreature.getControllerMove().a(targetLocation.getX(), targetLocation.getY(), targetLocation.getZ(), (double) this.speed);
    29. this.entitycreature.getNavigation().a(targetLocation.getX(), targetLocation.getY(), targetLocation.getZ(), this.speed);
    30. }
    31. }
Thread Status:
Not open for further replies.

Share This Page