Solved Spawn an NPC Behind a Player

Discussion in 'Plugin Development' started by Xp10d3, Sep 17, 2020.

  1. Offline

    Xp10d3

    Hello! So before you say Citizens API or ProtocolLib, I don't want to use any API's for a few reasons:
    1. I am attempting to improve my meager Java and Bukkit skills, and I don't want to use any API's unless absolutely necessary. For one, they make things more confusing for me; for others, maybe not. However, I have tried using an API in the past and it didn't go well at all. Made things 3x more confusing.
    2. Spawning an NPC is perfectly possible without an API; I've seen a tutorial or two, however they've all had issues. It is possible, but hard to pull off since you have to be really exact.
    3. I just don't like API's :p

    Anyways, onto my "issue". I want to create an Anti-Aura plugin (I created a thread on that, but this is a completely different problem, thus I'm not replying to that thread) that spawns an NPC or armor stand behind the player, then check if the player is hitting that NPC/armor stand using this:
    https://bukkit.org/threads/info-how-to-detect-damage-applied-to-an-npc-created-with-packets.188454/
    That will be an entirely other problem, but I'll deal with that later. Anyways, how would I create an NPC, and then position it perfectly? I know there is this bit:
    Code:java
    1.  
    2. double backstab = player.getLocation().getDirection().angle(player.getLocation().getDirection()) / 180 * Math.PI;
    3. npc.spawn(backstab) // Pseudo code. Spawn the NPC at the position back stab.
    4.  

    Where I can get the position behind the player using the variable backstab, however, I don't know exactly how I can spawn an NPC. If there is a thread about this already, I apologize.

    FYI I'm using 1.8.8.
     
  2. Offline

    gochi9

    Here try this,i used this to push players but i think it might work for your problem

    Code:
            Location loc = player.getEyeLocation();
            Vector vector = entity.getLocation().toVector().subtract(player.getLocation().toVector());
            double force = Math.abs((30 - player.getLocation().distance(entity.getLocation())) * i/15);
            /*Here is a small math problem, we calculate the force to push based on distance
            So: 30 - distance between player and entity, multiply by 0.125
            You can play with these values or set a fixed force.*/
            vector.normalize();
            vector.multiply(-force);
            loc.add(vector);
    
    
     
    Xp10d3 likes this.
  3. Offline

    Xp10d3

    Thank you! So to make sure I understand:
    Code:java
    1.  
    2. Location loc = player.getEyeLocation();
    3. Vector vector = entity.getLocation().toVector().subtract(player.getLocation().toVector());
    4.  

    In this case, player is the assumed player who has KillAura, and entity is the NPC that I will spawn. You get the location the player is looking at, then get the NPC's location, turn it into a vector, then subtract that by the player's location.
    Code:java
    1.  
    2. double force = Math.abs((30 - player.getLocation().distance(entity.getLocation())) * i/15);
    3. vector.normalize();
    4. vector.multiply(-force);
    5. loc.add(vector);
    6.  

    The force that pushes the NPC will be the absolute value of 30 - the distance between the player's location and the entities location, then multiplied by i (I don't know what this is?) divided by 15. Then apply the force to the NPC and add the vector to the location of where the player is looking at. Did I get that right? Thanks :)
     
  4. Offline

    gochi9

    @Xp10d3 I used this to push away entites for a plugin.I think you can modify it to meet your needs
     
  5. Offline

    Xp10d3

    Sorry, I'm a bit confused. I want to spawn an NPC behind a player and have it follow them. How does pushing away entities relate to that...? Sorry if I'm coming across as annoyed; I'm not, just a bit confused.
     
  6. Offline

    gochi9

    Well i said what i used it for and i modified it a little so it would be easier for you to modify it to your needs.However if that's too hard for you then you can use this
    Code:
    Location entityLoc = player.getLocation().add(player.getLocation().getDirection().multiply(-1));
    I think this might work just as great

    EDIT: having it follow the player might include some NMS stuff like modifying the pathfinders so if you that you can do it than you can try
     
    Xp10d3 likes this.
  7. Offline

    Xp10d3

    Ah, I see. No, the code you gave me makes sense; I was just confused on why you sent that :D It was helpful, thank you! I'll take a look at that bit you sent. However, my main issue is spawning the NPC itself. I've taken a look at many threads, but it seems that none of them have been working.
    Ex:
    https://bukkit.org/threads/creating-and-spawning-custom-npcs.90233/
    https://bukkit.org/threads/how-to-spawn-npc-mobs-natively.85339/

    Those two are great examples. I can't find the rest of the threads I've looked at, but it's about 5-8 threads or so I've seen regarding spawning an NPC.
     
  8. Offline

    gochi9

    You most probably must a create a custom EntityPlayer and modify it from there.Also modify the pathfinder goals to only follow the player from behind. You can avoid modfying the pathfinder goals but it would take constantly teleporting the entity behind the player with a runnable which will create lag if you have a lot of players.

    Also i could be wrong,of course,and i would like to be corrected if what i've said is proven to be wrong.Also i would like to know others people approch to this
     
    Xp10d3 likes this.
  9. Offline

    Xp10d3

    I finally found a good thread xD However, it isn't that outdated and should work in 1.8.8, but I keep getting the error, "CraftServer cannot be resolved as a type."
    Here:
    Code:
    MinecraftServer minecraftServer = ((CraftServer) Bukkit.getServer()).getServer();
    
    Same with CraftWorld. It says, "CraftWorld cannot be resolved as a type."
    I am using spigot 1.8.8; latest version on getBukkit.org.
    This is what I have so far:
    Code:
    @EventHandler
        public void checkAura(EntityDamageByEntityEvent event) {
            String skinUuid = "5";
            MinecraftServer minecraftServer = ((CraftServer) Bukkit.getServer()).getServer();
            WorldServer worldServer = ((CraftWorld) this.location.getWorld()).getHandle();
            GameProfile gameProfile = new GameProfile(UUID.fromString(skinUuid), "npc");
            EntityPlayer ep = new EntityPlayer(minecraftServer, worldServer, gameProfile, new PlayerInteractManager(worldServer));
        }
    
    Also, since I'm using Citizens in 90% of all my servers (I manage a Network), I might as well give in and use Citizens. I'd still like to know how to create NPC's natively. I would 100% prefer to not use Citizens as some servers won't use it, especially since Citizens doesn't support 1.9.

    Current class:
    Code:java
    1.  
    2. package endran.voide.reach;
    3.  
    4. import java.text.SimpleDateFormat;
    5. import java.util.Date;
    6. import java.util.Random;
    7. import java.util.UUID;
    8.  
    9. import org.bukkit.BanList.Type;
    10. import org.bukkit.Bukkit;
    11. import org.bukkit.ChatColor;
    12. import org.bukkit.entity.Player;
    13. import org.bukkit.event.EventHandler;
    14. import org.bukkit.event.Listener;
    15. import org.bukkit.event.entity.EntityDamageByEntityEvent;
    16. import org.bukkit.util.Vector;
    17.  
    18. import com.mojang.authlib.GameProfile;
    19.  
    20. import net.minecraft.server.v1_8_R3.EntityPlayer;
    21. import net.minecraft.server.v1_8_R3.MinecraftServer;
    22. import net.minecraft.server.v1_8_R3.PlayerInteractManager;
    23. import net.minecraft.server.v1_8_R3.WorldServer;
    24.  
    25. public class PlayerListeners implements Listener {
    26.  
    27. // Get's the core variable.
    28. private Core core;
    29.  
    30. // Constructor
    31. public PlayerListeners(Core core) {
    32. this.core = core;
    33. }
    34.  
    35. // Get's the date + time to then write into core.otherLog()
    36. Date now = new Date();
    37. SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
    38.  
    39. @EventHandler
    40. public void checkAura(EntityDamageByEntityEvent event) {
    41. String skinUuid = "5";
    42. MinecraftServer minecraftServer = ((CraftServer) Bukkit.getServer()).getServer();
    43. WorldServer worldServer = ((CraftWorld) this.location.getWorld()).getHandle();
    44. GameProfile gameProfile = new GameProfile(UUID.fromString(skinUuid), "npc");
    45. EntityPlayer ep = new EntityPlayer(minecraftServer, worldServer, gameProfile, new PlayerInteractManager(worldServer));
    46. }
    47.  
    48. // Hypixel Hit Reg and anti-reach
    49. @EventHandler
    50. public void onDamage(EntityDamageByEntityEvent event) {
    51. if (core.getConfig().getString("enabled").equalsIgnoreCase("true")) {
    52. /*
    53.   if (event.getCause() != DamageCause.ENTITY_ATTACK || event.getCause() != DamageCause.CONTACT) {
    54.   return;
    55.   }
    56.   */
    57.  
    58. // Get the damager
    59. Player player = (Player) event.getDamager();
    60.  
    61. Player entity = (Player) event.getEntity();
    62.  
    63. // If the damager damages the damaged...
    64. if (((event.getDamager() instanceof Player)) && ((event.getEntity() instanceof Player))) {
    65. int upper = 5;
    66. // Random number
    67. Random random = new Random();
    68. // If num is greater than or equal to 5...
    69. int rNum = random.nextInt(5+(upper));
    70. if (rNum >= 5) {
    71. event.setCancelled(true);
    72. } else {
    73. event.setCancelled(false);
    74. }
    75.  
    76. // Test for reach
    77.  
    78. // Get the distance between the player's location and the victim's location
    79. /*
    80.   Vector from = new Vector(player.getLocation().getX(), 1, player.getLocation().getZ());
    81.   Vector to = new Vector(entity.getLocation().getX(), 1, entity.getLocation().getZ());
    82.   */
    83. Vector from = player.getLocation().toVector().setY(1);
    84. Vector to = entity.getLocation().toVector().setY(1);
    85.  
    86. //Vector vector = to.subtract(from);
    87.  
    88. double distance = from.distance(to) - 0.4;
    89.  
    90. /*
    91.   if (distance >= 3.5) {
    92.   event.setCancelled(true);
    93.   }
    94.   */
    95. if (distance > 3) { // Since MC's default reach is 3, anything above that is considered reach
    96. int checks = core.getConfig().getInt(player.getUniqueId().toString()); // Get the amount of checks a player has in the config file
    97. int stageOneChecks = core.getConfig().getInt("stage-one-checks"); // The amount of checks done in stage one
    98. int stageTwoChecks = core.getConfig().getInt("stage-two-checks"); // The amount of checks done in stage two
    99. int stageThreeChecks = core.getConfig().getInt("stage-three-checks"); // The amount of checks done in stage three
    100. int stageTwoC = stageOneChecks + stageTwoChecks;
    101. int stageThree = stageOneChecks + stageTwoChecks + stageThreeChecks; // Same with stageTwo var
    102. String ban = core.getConfig().getString("ban-stage-one"); // What type of ban to do in stage one
    103. String bantwo = core.getConfig().getString("ban-stage-two"); // Same with ban var
    104. String banthree = core.getConfig().getString("ban-stage-three"); // Same with ban var
    105. //String bumper = StringUtils.repeat("\n", 35); // To create custom ban screen
    106. int min = core.getConfig().getInt("minutes-stage-one"); // Amount of minutes to temp-ban a player
    107. int mintwo = core.getConfig().getInt("minutes-stage-two"); // Same with min var
    108. int minthree = core.getConfig().getInt("minutes-stage-three"); // Same with min var
    109. Date date = new Date(System.currentTimeMillis()+min*60*1000); // Calculate the amount of time to ban a player for
    110. Date datetwo = new Date(System.currentTimeMillis()+mintwo*60*1000); // Same with date var
    111. Date datethree = new Date(System.currentTimeMillis()+minthree*60*1000); // Same with date var
    112. /*
    113.   core.getConfig().set(player.getUniqueId().toString(), checks + 1);
    114.   player.sendMessage(ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + " You seem to have reach. Amount of checks so far: " + checks);
    115.   */
    116. if (!core.getConfig().contains(player.getUniqueId().toString())) { // If the player isn't in the config file...
    117. core.getConfig().set(player.getUniqueId().toString(), 1); // Add the player to the config file, then set the amount of checks to 1.
    118. core.saveConfig(); // Save the config file
    119. } else if (checks == stageOneChecks) { // If the checks are not equal to null and the amount of checks equals stage one checks...
    120. // Send a message to the victim
    121. entity.sendMessage(ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.GREEN + " " + player + " had reach and was banned. Player is at stage one.");
    122. // Check on what type of ban to execute
    123. if (ban.equalsIgnoreCase("ban")) { // If the stage-one ban is equal to ban...
    124. // Ban the player and send a custom screen message. Type.NAME is a regular ban, Type.IP is an IP ban.
    125. player.kickPlayer(ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + " You have been banned for reach! Amount of checks done: " + checks);
    126. Bukkit.getBanList(Type.NAME).addBan(player.getName(), ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + " You have been banned for reach! Amount of checks done: " + checks, null, null);
    127. } else if (ban.equalsIgnoreCase("banip")) { // Same as above
    128. player.kickPlayer(ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + "You have been IP banned for reach! Amount of checks done: " + checks);
    129. Bukkit.getBanList(Type.IP).addBan(player.getAddress().getHostName(), ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + " You have been IP banned for reach! Amount of checks done: " + checks, null, null);
    130. } else if (ban.equalsIgnoreCase("tempban") || ban.equalsIgnoreCase("temp-ban") || ban.equalsIgnoreCase("temp ban")) {
    131. // To temp ban a player, execute a regular ban but for a certain amount of time (specified by the date var)
    132. player.kickPlayer(ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + " You have been temp-banned for reach! Amount of checks done: " + checks + ". You have been banned for " + date + ".");
    133. Bukkit.getBanList(Type.NAME).addBan(player.getName(), ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + " You have been temp-banned for reach! Amount of checks done: " + checks + ". You have been banned for " + date + ".", date, null);
    134. }
    135. } else if (checks == stageTwoC) {
    136. // Same as above except fill in all the stage-two values.
    137. entity.sendMessage(ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.GREEN + " " + player + " had reach and was banned.");
    138. if (bantwo.equalsIgnoreCase("ban")) {
    139. player.kickPlayer(ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + " You have been banned for reach! Amount of checks done: " + checks);
    140. Bukkit.getBanList(Type.NAME).addBan(player.getName(), ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + " You have been banned for reach! Amount of checks done: " + checks, null, null);
    141. } else if (bantwo.equalsIgnoreCase("banip")) {
    142. player.kickPlayer(ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + "You have been IP banned for reach! Amount of checks done: " + checks);
    143. Bukkit.getBanList(Type.IP).addBan(player.getAddress().getHostName(), ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + " You have been IP banned for reach! Amount of checks done: " + checks, null, null);
    144. } else if (bantwo.equalsIgnoreCase("tempban") || ban.equalsIgnoreCase("temp-ban") || ban.equalsIgnoreCase("temp ban")) {
    145. player.kickPlayer(ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + " You have been temp-banned for reach! Amount of checks done: " + checks + ". You have been banned for " + date + ".");
    146. Bukkit.getBanList(Type.NAME).addBan(player.getName(), ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + " You have been temp-banned for reach! Amount of checks done: " + checks + ". You have been banned for " + datetwo + ".", datetwo, null);
    147. }
    148. } else if (checks == stageThree) {
    149. // Same as above except fill in all the stage-three values.
    150. entity.sendMessage(ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.GREEN + " " + player + " had reach and was banned.");
    151. if (banthree.equalsIgnoreCase("ban")) {
    152. player.kickPlayer(ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + " You have been banned for reach! Amount of checks done: " + checks);
    153. Bukkit.getBanList(Type.NAME).addBan(player.getName(), ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + " You have been banned for reach! Amount of checks done: " + checks, null, null);
    154. } else if (banthree.equalsIgnoreCase("banip")) {
    155. player.kickPlayer(ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + "You have been IP banned for reach! Amount of checks done: " + checks);
    156. Bukkit.getBanList(Type.IP).addBan(player.getAddress().getHostName(), ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + " You have been IP banned for reach! Amount of checks done: " + checks, null, null);
    157. } else if (banthree.equalsIgnoreCase("tempban") || ban.equalsIgnoreCase("temp-ban") || ban.equalsIgnoreCase("temp ban")) {
    158. player.kickPlayer(ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + " You have been temp-banned for reach! Amount of checks done: " + checks + ". You have been banned for " + date + ".");
    159. Bukkit.getBanList(Type.NAME).addBan(player.getName(), ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + " You have been temp-banned for reach! Amount of checks done: " + checks + ". You have been banned for " + datethree + ".", datethree, null);
    160. }
    161. } else {
    162. // If the player is in the config file but doesn't have the same amount of checks as required, add one check to the player.
    163. core.getConfig().set(player.getUniqueId().toString(), checks + 1);
    164. player.sendMessage(ChatColor.WHITE + "[" + ChatColor.GOLD + "Voide Reach" + ChatColor.WHITE + "]" + ChatColor.RED + " You seem to have reach. Amount of checks so far: " + checks);
    165. }
    166. }
    167. }
    168. }
    169. }
    170. }
    171.  


    EDIT: Alright, I'm going to use Citizens. I'll mark this as solved for now, but if anyone has any opinions on NPC's let me know.
     
    Last edited: Sep 18, 2020
  10. Offline

    Strahan

    I don't get your reasons for not wanting to use an API. It doesn't make it harder; the whole point is to make it easier. No need to reinvent the wheel. I also think you are seriously underestimating the work you'll need to do to code an NPC that will follow a person but only from behind. If there is an API that can accomplish that, it'd be almost insane not to use it.

    Where you have:
    Code:
    import com.mojang.authlib.GameProfile;
    
    import net.minecraft.server.v1_8_R3.EntityPlayer;
    import net.minecraft.server.v1_8_R3.MinecraftServer;
    import net.minecraft.server.v1_8_R3.PlayerInteractManager;
    import net.minecraft.server.v1_8_R3.WorldServer;
    
    public class PlayerListeners implements Listener {
    Change it to:
    Code:
    import com.mojang.authlib.GameProfile;
    
    import net.minecraft.server.v1_8_R3.EntityPlayer;
    import net.minecraft.server.v1_8_R3.MinecraftServer;
    import net.minecraft.server.v1_8_R3.PlayerInteractManager;
    import net.minecraft.server.v1_8_R3.WorldServer;
    import org.bukkit.craftbukkit.v1_8_R3.CraftServer;
    
    public class PlayerListeners implements Listener {
     
    Xp10d3 likes this.
  11. Offline

    Xp10d3

    Thank you! That did fix everything. And I prefer to not use an API because it is:
    1. More convenient
    2. Most plugins that use an API don't support every damn version that is required :p (ex. Citizens 2 doesn't support 1.9, and I happen to use that version sometimes)
    3. It usually requires a lot more work to use it and learn another API in addition to Bukkit.
    However, I do see how an API can be helpful. Reinventing the wheel is a horrible idea, but ironically I still don't prefer API's. I'll use Citizens, but might play around with regular NPC's instead of Citizens in the future. Thanks everyone! Marking as solved. (although I do need to figure out how to get the location of behind the player... ah, forget it xD)
    EDIT: Oh, I already had this as solved. mb
     

Share This Page