Hi all, In my search of this subforum, I found an awesome resource by Desle on how to make ropes out of leads. Reading the thread, I was inspired by Goblom and others to create throwable ropes that players can use to rappel, grapple, etc. After a bit of messing around, this was the result. Code:java import java.util.ArrayList;import java.util.List; import org.bukkit.Bukkit;import org.bukkit.Location;import org.bukkit.entity.Arrow;import org.bukkit.entity.Entity;import org.bukkit.entity.Item;import org.bukkit.entity.Player;import org.bukkit.scheduler.BukkitTask; import net.minecraft.server.v1_7_R1.EntityBat;import net.minecraft.server.v1_7_R1.PacketPlayOutAttachEntity;import net.minecraft.server.v1_7_R1.PacketPlayOutEntityDestroy;import net.minecraft.server.v1_7_R1.PacketPlayOutSpawnEntityLiving;import net.minecraft.server.v1_7_R1.WorldServer; import org.bukkit.craftbukkit.v1_7_R1.CraftWorld;import org.bukkit.craftbukkit.v1_7_R1.entity.CraftEntity;import org.bukkit.craftbukkit.v1_7_R1.entity.CraftPlayer; /** * Credits to [USER=90856537]Desle[/USER] for the original resource. (Uses entities, not packets) * * @author Goblom * @author Jordan */public class Rope { private static final List<Rope> ropes = new ArrayList<Rope>(); private Location endLocation; private EntityBat batLocation; private Entity holder, hook; private RopeResult result; public static enum RopeResult { MOB, ITEM, GROUND, AIR; } /** * Make a rope with a specified endpoint and holder. * * @param end * The endpoint of the rope. * @param holder * The entity that should hold the rope. */ public Rope(Location end, Entity holder) { this.endLocation = end; this.holder = holder; for (Rope r : new ArrayList<Rope>(ropes)) { if (r.holder != null && r.holder.equals(holder)) { r.holder = null; r.despawn(); } } ropes.add(this); spawn(); } /** * Set the end location of the rope. Will cause issues if the end of the * rope is attached to a mobile creature. * * @param end * The end location. */ public void setEnd(Location end) { this.endLocation = end; spawn(); } /** * Get the end location of this rope. * * @return The end location of the rope. */ public Location getEnd() { return endLocation; } /** * Make the entities and connections necessary for this rope. */ private void makeEnt() { WorldServer world = ((CraftWorld) endLocation.getWorld()).getHandle(); if (batLocation == null) { this.batLocation = new EntityBat(world); batLocation.setInvisible(true); } this.batLocation.setLocation(endLocation.getX(), endLocation.getY(), endLocation.getZ(), 0, 0); batLocation.setLeashHolder(((CraftEntity) holder).getHandle(), true); } /** * Make this Rope appear in the world. Despawns if there is no rope holder. */ public void spawn() { if (holder == null) { despawn(); return; } makeEnt(); PacketPlayOutSpawnEntityLiving bat_end = new PacketPlayOutSpawnEntityLiving(batLocation); PacketPlayOutAttachEntity attach = new PacketPlayOutAttachEntity(1, batLocation, ((CraftPlayer) holder).getHandle()); for (Player player : Bukkit.getOnlinePlayers()) { CraftPlayer cP = (CraftPlayer) player; cP.getHandle().playerConnection.sendPacket(bat_end); cP.getHandle().playerConnection.sendPacket(attach); } } /** * Remove all the ropes/packets that are glued. * * @see despawn() */ public static void removeAll() { for (Rope r : new ArrayList<Rope>(ropes)) { r.despawn(); } } /** * Remove this rope and clean up the packets. */ public void despawn() { holder = null; PacketPlayOutEntityDestroy destroy = new PacketPlayOutEntityDestroy(batLocation.getId()); for (Player player : Bukkit.getOnlinePlayers()) { CraftPlayer cP = (CraftPlayer) player; cP.getHandle().playerConnection.sendPacket(destroy); } ropes.remove(this); } /** * Glue the end of the rope to an entity. * * @param toAttachTo * The entity to glue to. */ public void glueEndTo(Entity toAttachTo) { PacketPlayOutAttachEntity attach = new PacketPlayOutAttachEntity(0, batLocation, ((CraftEntity) toAttachTo).getHandle()); for (Player player : Bukkit.getOnlinePlayers()) { CraftPlayer cP = (CraftPlayer) player; cP.getHandle().playerConnection.sendPacket(attach); } GlueUpdater u = new GlueUpdater(toAttachTo); u.task = Bukkit.getServer().getScheduler().runTaskTimer(PLUGIN, u, 0L, 1L); } private class GlueUpdater implements Runnable { BukkitTask task; Entity glued; public GlueUpdater(Entity glued) { this.glued = glued; } public void run() { if (holder == null || glued == null || glued.isDead() || (glued instanceof Arrow && ((Arrow) glued).isOnGround())) { if (holder == null && glued != null) { glued.remove(); } task.cancel(); result = RopeResult.GROUND; if (glued != null) { for (Entity e : glued.getNearbyEntities(1, 0.5, 1)) if (!e.equals(glued)) { if (e instanceof Item) result = RopeResult.ITEM; else result = RopeResult.MOB; hook = e; glueEndTo(e); return; } } return; } endLocation = glued.getLocation().subtract(0, result == RopeResult.ITEM || (result == RopeResult.MOB && hook instanceof Arrow) ? 0 : 2, 0); if (glued instanceof Arrow) { setEnd(endLocation); } } } /** * Get a rope held by a certain entity. * * @param holder * The entity that might be holding a rope. * @return The rope the holder is holding or null if there is no rope * associated with. */ public static Rope getRope(Entity holder) { for (Rope r : ropes) { if (r.holder != null && r.holder.equals(holder)) { return r; } } return null; } /** * The result of the hook. * * @return The hook result. */ public RopeResult getResult() { return result; } /** * Get the item on the hook end of the rope. * * @return The hook item. */ public Entity getHook() { return hook; } /** * Whether or not the player should be able to pull in the line. * * @return True if the hook landed or hit an entity/item. */ public boolean canPull() { return result != RopeResult.AIR; }} With this, the possibilities are endless. In this example, we will use the ProjectileLaunchEvent to see when a player fires an arrow. If an arrow is fired, we'll glue a rope to the arrow. Code:java @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onShootArrow(ProjectileLaunchEvent e) { if (e.getEntity().getShooter() instanceof Player && e.getEntity() instanceof Arrow) { launchRope(e.getEntity().getShooter(), (Arrow) e.getEntity()); } } private void launchRope(Entity thrower, Arrow glue) { Rope rope = new Rope(glue.getLocation(), thrower); rope.glueEndTo(glue); } Now that we've glued our rope to the arrow, we just need to do something with it. Why not use it as a grapple? Let's listen for the interact event. If a player right clicked with a rope out and bow in hand, we'll launch the player to the location of the arrow! Code:java @EventHandler(priority = EventPriority.LOWEST) private void onClick(PlayerInteractEvent e) { if (e.getAction() == Action.RIGHT_CLICK_AIR || e.getAction() == Action.RIGHT_CLICK_BLOCK && e.getItem().getType() == Material.BOW) { Rope r = Rope.getRope(e.getPlayer()); if (r != null) { e.setCancelled(true); if (r.canPull()) { pullTo(e.getPlayer(), r.getEnd()); r.despawn(); } } } public static void pullTo(Entity e, Location loc) { // This code written by [USER=90696604]SnowGears[/USER] Location l = e.getLocation(); if (l.distanceSquared(loc) < 9) { if (loc.getY() > l.getY()) { e.setVelocity(new Vector(0, 0.25, 0)); return; } Vector v = loc.toVector().subtract(l.toVector()); e.setVelocity(v); return; } l.setY(l.getY() + 0.5); e.teleport(l); double d = loc.distance(l); double g = -0.08; double x = (1.0 + 0.07 * d) * (loc.getX() - l.getX()) / d; double y = (1.0 + 0.03 * d) * (loc.getY() - l.getY()) / d - 0.5 * g * d; double z = (1.0 + 0.07 * d) * (loc.getZ() - l.getZ()) / d; Vector v = e.getVelocity(); v.setX(x); v.setY(y); v.setZ(z); e.setVelocity(v); } Great! Now all arrows we shoot are automatically glued to a rope that can be used to reel the player in. This is only one use for the resource. Feel free to post ideas or any way you used it below!
Ape101 Possibly yes, but you will also need to change the packets as names changed with 1.6.4 --> 1.7
Ape101 All i remember is Packet18SpawnMob, but alas there are 4 different packets being used in this library
Ape101 I've made spreadsheet: https://docs.google.com/spreadsheet...aU1RZUswZ2dqUFRpTTkyUEk1dXc&usp=sharing#gid=0
Oh dayum. Thats a nice spreadsheet. Thanks! Few errors i've got since changing the packages: all of the (CraftWorld) and (CraftEntity) and such can't be resolved as a type? and Code:java Packet29DestroyEntity destroy = new Packet29DestroyEntity(batLocation.getId()); getID() is undefined for EntityBat Hopefully this can be fixed! Really need this to work with 1.6.4 haha EDIT 2: fixed the CraftEntity, but i don't think there is CraftWorld in 1.6.4? that or it's not letting me import it lol. Still can't figure out the getID() the only similar method is getUniqueID()
TeeePeee Not to sure, but when I shoot the arrow, the first one immediately dissapears, then when I shoot the second one its like it hits an invisibly target right in front of me then drops down. After the second arrow is fired, it creates a rope starting right where the second arrow went to me, the arrow doesn't seem to actually be moving, just staying right where the player is. Any ideas? Using your exact code, thought this error was due to the way I implemented it, yet it seems to do this with the example as well.
97waterpolo I've revised the code since posting this (a little bit). Here's the updated one, for sake of ruining the original post format: Code:java import java.util.ArrayList;import java.util.List; import net.minecraft.server.v1_7_R1.EntityBat;import net.minecraft.server.v1_7_R1.PacketPlayOutAttachEntity;import net.minecraft.server.v1_7_R1.PacketPlayOutEntityDestroy;import net.minecraft.server.v1_7_R1.PacketPlayOutSpawnEntityLiving;import net.minecraft.server.v1_7_R1.WorldServer; import org.bukkit.Bukkit;import org.bukkit.Location;import org.bukkit.craftbukkit.v1_7_R1.CraftWorld;import org.bukkit.craftbukkit.v1_7_R1.entity.CraftEntity;import org.bukkit.craftbukkit.v1_7_R1.entity.CraftPlayer;import org.bukkit.entity.Arrow;import org.bukkit.entity.Entity;import org.bukkit.entity.Item;import org.bukkit.entity.Player;import org.bukkit.scheduler.BukkitTask; /*** Credits to [USER=90856537]Desle[/USER] for the original resource. (Uses entities, not packets)** @author Goblom* @author Jordan*/public class Rope { private static final List<Rope> ropes = new ArrayList<Rope>(); private Location endLocation; private EntityBat batLocation; private Entity holder, hook; private RopeResult result; public static enum RopeResult { MOB, ITEM, GROUND, AIR; } /** * Make a rope with a specified endpoint and holder. * * @param end * The endpoint of the rope. * @param holder * The entity that should hold the rope. */ public Rope(Location end, Entity holder) { endLocation = end; this.holder = holder; for (Rope r : new ArrayList<Rope>(ropes)) if (r.holder != null && r.holder.equals(holder)) { r.holder = null; r.despawn(); } ropes.add(this); spawn(); } /** * Set the end location of the rope. Will cause issues if the end of the * rope is attached to a mobile creature. * * @param end * The end location. */ public void setEnd(Location end) { endLocation = end; spawn(); } /** * Get the end location of this rope. * * @return The end location of the rope. */ public Location getEnd() { return endLocation; } /** * Make the entities and connections necessary for this rope. */ private void makeEnt() { WorldServer world = ((CraftWorld) endLocation.getWorld()).getHandle(); if (batLocation == null) { batLocation = new EntityBat(world); batLocation.setInvisible(true); } batLocation.setLocation(endLocation.getX(), endLocation.getY(), endLocation.getZ(), 0, 0); batLocation.setLeashHolder(((CraftEntity) holder).getHandle(), true); } /** * Make this Rope appear in the world. Despawns if there is no rope holder. */ public void spawn() { if (holder == null) { despawn(); return; } makeEnt(); PacketPlayOutSpawnEntityLiving bat_end = new PacketPlayOutSpawnEntityLiving(batLocation); PacketPlayOutAttachEntity attach = new PacketPlayOutAttachEntity(1, batLocation, ((CraftPlayer) holder).getHandle()); for (Player player : Bukkit.getOnlinePlayers()) { CraftPlayer cP = (CraftPlayer) player; cP.getHandle().playerConnection.sendPacket(bat_end); cP.getHandle().playerConnection.sendPacket(attach); } } /** * Remove all the ropes/packets that are glued. * * @see despawn() */ public static void removeAll() { for (Rope r : new ArrayList<Rope>(ropes)) r.despawn(); } /** * Remove this rope and clean up the packets. */ public void despawn() { holder = null; PacketPlayOutEntityDestroy destroy = new PacketPlayOutEntityDestroy(batLocation.getId()); for (Player player : Bukkit.getOnlinePlayers()) { CraftPlayer cP = (CraftPlayer) player; cP.getHandle().playerConnection.sendPacket(destroy); } ropes.remove(this); } /** * Glue the end of the rope to an entity. * * @param toAttachTo * The entity to glue to. */ public void glueEndTo(Entity toAttachTo) { glueEndTo(toAttachTo, 0); } public void glueEndTo(Entity toAttachTo, int count) { PacketPlayOutAttachEntity attach = new PacketPlayOutAttachEntity(0, batLocation, ((CraftEntity) toAttachTo).getHandle()); for (Player player : Bukkit.getOnlinePlayers()) { CraftPlayer cP = (CraftPlayer) player; cP.getHandle().playerConnection.sendPacket(attach); } GlueUpdater u = new GlueUpdater(toAttachTo, count); u.task = Bukkit.getServer().getScheduler().runTaskTimer(PLUGIN, u, 0L, 1L); } private class GlueUpdater implements Runnable { BukkitTask task; Entity glued; int glueCount; public GlueUpdater(Entity glued, int glueCount) { this.glued = glued; this.glueCount = glueCount; } @Override public void run() { if (holder == null || glued == null || glued.isDead() || glued instanceof Arrow && ((Arrow) glued).isOnGround()) { task.cancel(); result = RopeResult.GROUND; if (glued != null && glueCount < 3) for (Entity e : glued.getNearbyEntities(1, 0.5, 1)) if (!e.equals(glued)) { if (e instanceof Item) result = RopeResult.ITEM; else result = RopeResult.MOB; hook = e; glueEndTo(e, glueCount + 1); return; } return; } endLocation = glued.getLocation().subtract(0, result == RopeResult.ITEM || result == RopeResult.MOB && hook instanceof Arrow ? 0 : 2, 0); if (glued instanceof Arrow) setEnd(endLocation); } } /** * Get a rope held by a certain entity. * * @param holder * The entity that might be holding a rope. * @return The rope the holder is holding or null if there is no rope * associated with. */ public static Rope getRope(Entity holder) { for (Rope r : ropes) if (r.holder != null && r.holder.equals(holder)) return r; return null; } /** * The result of the hook. * * @return The hook result. */ public RopeResult getResult() { return result; } /** * Get the item on the hook end of the rope. * * @return The hook item. */ public Entity getHook() { return hook; } /** * Whether or not the player should be able to pull in the line. * * @return True if the hook landed or hit an entity/item. */ public boolean canPull() { return result != RopeResult.AIR; }}
Ape101 There's definitely a CraftWorld class (as evidenced here) so that shouldn't cause a problem. For the Bat ID, I'd have to recommend trying the integer field 'id'.
TeeePeee Getting "Caused by: java.lang.IllegalArgumentException: Plugin cannot be null" Says line 166 of RopePull, which is 'u.task = Bukkit.getServer().getScheduler().runTaskTimer(plugin, u, 0L, 1L);" I have plugin defined in a constructor, any idea why it is saying it is null?
97waterpolo Hard to say without seeing where plugin is initialized and where the rope object is initialized. If the plugin is null, it's simply that. Make sure you define the plugin before the rope.
TeeePeee Code: http://hastebin.com/taqoruleko.avrasm Class I am using the example in. This is your rope class but renamed. http://hastebin.com/yekawifasi.java It shouldn't be null, I have a constructor in the class, for both.
97waterpolo Sorry, only read through the Whip class. In your ropePull class, you made a constructor with the plugin. But when you actually create the RopePull, you use the constructor with location, entity. Change your RopePull(Location, Entity) to RopePull(Location, Entity, Core) and add the plugin initializer there.
TeeePeee Thanks! Will try that when home! TeeePeee There is no ropePull method? Only thing I saw as well as cntrl f found was canPull? Only method I saw, was "public Rope(Location end, Entity holder)" that had location and entity. If so, should I change it to "public Rope(Location end, Entity holder, Plugin core)" EDIT by Moderator: merged posts, please use the edit button instead of double posting.
97waterpolo The class you linked me to here http://hastebin.com/yekawifasi.java is the one I'm referencing. To answer your question, basically, yes. And in it, this.core = core.
TeeePeee Getting an error on "Rope rope = new Rope(glue.getLocation(), thrower);" in the class I am using this resource in. Wants me to add the plugin argument. This is your resource, I edited it so it has your original name. http://hastebin.com/limeregaku.java Any ideas? EDIT: I tried it with it, I added it to it, tried it multiple ways, it still has not worked. I added "Plugin core" to it yet it still does not work :L
97waterpolo I'll try to explain this the best I can to you. You created a constructor for the Rope that accepts only the plugin instance as the parameter. However, when you create the Rope, you don't use that constructor. You use the original constructor that accepts a location and an entity. By doing this, the Rope has no knowledge of the plugin instance. I told you to modify the original constructor so that it also accepts the plugin as a parameter. Here's your version of the file, fixed. As you can see, I removed the constructor with only the plugin and added an initializer to the plugin instance. Now, just change Code:java Rope rope = new Rope(glue.getLocation(), thrower); to Code:java Rope rope = new Rope(glue.getLocation(), thrower, PLUGIN);
TeeePeee Thanks, I see what you are trying to say! Thank you for explaining it! Works, shoots the arrow with the rope atacked, problem is, every other arrow shoots. Every other arrow just dissapears, nothing is shot, no drawback, it seems to dissapear? Could it be due to the player interact event with right click? Did some more testing, not losing the arrow, it makes a ghost arrow, basically doesn't fire the first one, but it does the second. After second is fired, the original arrow re-appears>?
97waterpolo Could be that in your tests, the rope listing got out of sync. The Rope class keeps a static list of each rope that exists in the world. That way, it knows if a player already has a rope out. Either way, try restarting the server and re-testing.
Any chance of someone making a Reflection based one? I hate updating plugins every bukkit version as if people want to stick to the previous version of Bukkit but still want the updated features of the plugin...