diff --git a/readme.md b/readme.md index fa10bf8..abcc196 100644 --- a/readme.md +++ b/readme.md @@ -7,16 +7,27 @@ Doubleclicking shift you will float (levitation 5 for 1 second), allowing for be You take significantly less damage when falling (5 hearts). You can still die, don't count on your cat-powers catching you every time. **Your superfood is all types of eatable fish.** 3. Fox You do more damage to mobs (25%). **Your superfood is all types of berry.** +4. Dog +You get speed 2 when doing damage. **Your superfood are uncooked meats.** ## **Chat** Chat while you are a animal is very different. When you speak, your words will become furry-ified and every time you talk you'll have animal sounds come out of you. You can disable this via /chatmodoff, and turn it back on via /chatmodon. +If you do not want to see these chat changes, run /chatmodclientoff. This will disable them for you, and you only. + ## **Superfoods** Superfoods are items that when eaten, give you stackable **Speed II** and insane amounts of saturation (9.4 points) and hunger (4 points). ## **Cooldowns & animals** Cooldowns are stored in game. Animal/cooldowns are stored in a json database. +## **Player leashing** +There is player leashing. This is in code, pretty hard. What I'm doing is spawning a invizible zombie (leashing it), and then constantly launching the player towards that zombie. Seems to work pretty well. +That is a concept that was NOT introduced by me, it's a combination of two different projects (one very very NSFW roleplay plugin, and one player prison plugin). Both the players have to be animals, and they also have to have the `animalrp.leash` permission (both the leasher and the leashee). + +## **MINECRAFT SEX PLUGIN??** +Yup. I'm not even joking. This plugin has a /sex command, and it's only available with the permission `animalrp.sex`. It works suprisingly well. + ## **How to add a new animal?** 1. Create a new class in animals/MyCoolAnimal.java diff --git a/src/main/java/lv/pi/animalrp/AnimalRP.java b/src/main/java/lv/pi/animalrp/AnimalRP.java index bff864e..d375bad 100644 --- a/src/main/java/lv/pi/animalrp/AnimalRP.java +++ b/src/main/java/lv/pi/animalrp/AnimalRP.java @@ -59,6 +59,7 @@ import lv.pi.animalrp.commands.ChatModCommand; import lv.pi.animalrp.commands.ClearCooldownCommand; import lv.pi.animalrp.commands.SexCommand; import lv.pi.animalrp.commands.TfCommand; +import lv.pi.animalrp.listeners.LeashPlayers; import lv.pi.animalrp.listeners.PlayerChat; import lv.pi.animalrp.listeners.PlayerLeave; import lv.pi.animalrp.util.Cooldown; @@ -247,6 +248,7 @@ public class AnimalRP extends JavaPlugin { pm.registerEvents(new PlayerChat(), this); pm.registerEvents(new PlayerLeave(), this); + pm.registerEvents(new LeashPlayers(), this); getCommand("tf").setExecutor(new TfCommand()); getCommand("emote").setExecutor(new EmoteCommand()); @@ -268,8 +270,6 @@ public class AnimalRP extends JavaPlugin { new InteractionCommand(Mood.CUTE, "%s kissed you.. 0////0 %s", "You kissed %s.. 0////0 %s")); getCommand("bite") .setExecutor(new InteractionCommand(Mood.ANGRY, "%s bit you!! Σ(っ゚Д゚)っ %s", "You bit %s! (○`д´)ノシ %s")); - getCommand("bzz") - .setExecutor(new InteractionCommand(Mood.ANGRY, "%s bzzs at you!! :3 %s", "You bzzed at %s :3 %s")); getCommand("purr").setExecutor(new InteractionCommand(Mood.CUTE, "You hear the soft sound of %s purring on you... %s", "You jump on %s, and start purring. %s")); getCommand("scratch").setExecutor(new InteractionCommand(Mood.ANGRY, "%s SCRATCHES YOU! Ow! %s", diff --git a/src/main/java/lv/pi/animalrp/commands/SexCommand.java b/src/main/java/lv/pi/animalrp/commands/SexCommand.java index 7068ec0..dfaf020 100644 --- a/src/main/java/lv/pi/animalrp/commands/SexCommand.java +++ b/src/main/java/lv/pi/animalrp/commands/SexCommand.java @@ -45,7 +45,13 @@ public class SexCommand implements CommandExecutor { for (Iterator> iterator = modelset.iterator(); iterator.hasNext();) { Entry value = iterator.next(); Player plr = Bukkit.getPlayer(value.getKey()); - if (plr == null || value.getValue().finished) { + Boolean sexing = true; + + if(value.getValue().pl != null) { + sexing = value.getValue().pl.isOnline(); + } + + if (plr == null || value.getValue().finished || !sexing) { value.getValue().removeModel(); iterator.remove(); } else { @@ -131,8 +137,8 @@ public class SexCommand implements CommandExecutor { Player player = (Player) arg0; - if (!player.isOp()) { - arg0.sendMessage(AnimalRP.mm.deserialize("You are not an OP!")); + if (!player.hasPermission("animalrp.sex")) { + arg0.sendMessage(AnimalRP.mm.deserialize("You do not have permission to use this command!")); return true; } @@ -143,6 +149,35 @@ public class SexCommand implements CommandExecutor { String playerName = arg3[0]; OfflinePlayer of = Bukkit.getOfflinePlayer(playerName); + Player gg = Bukkit.getPlayerExact(playerName); + if(gg != null) { + if(gg.getUniqueId().equals(player.getUniqueId())) { + arg0.sendMessage(AnimalRP.mm.deserialize("You cannot add your own name!")); + return false; + } + Boolean sexingSomeone = SexCommand.models.containsKey(gg.getUniqueId()); + if(sexingSomeone) { + arg0.sendMessage(AnimalRP.mm.deserialize("The player is already busy with someone else!")); + return false; + } + Boolean alreadyInProgress = false; + Set> modelset = models.entrySet(); + + for (Iterator> iterator = modelset.iterator(); iterator.hasNext();) { + Entry value = iterator.next(); + + if(value.getValue().pl != null) { + if(value.getValue().pl.getUniqueId().equals(gg.getUniqueId())) { + alreadyInProgress = true; + } + } + } + + if(alreadyInProgress) { + arg0.sendMessage(AnimalRP.mm.deserialize("The player you're mentioning is already in progress!")); + return false; + } + } if (of == null) { arg0.sendMessage(AnimalRP.mm.deserialize("User has never joined.")); return true; @@ -158,6 +193,16 @@ public class SexCommand implements CommandExecutor { Location loc = player.getLocation().add(0, -1, 0); loc.add(getVector(yaw)); + if (gg != null) { + gg.setGravity(false); + gg.setInvulnerable(true); + gg.teleport(loc); + Location facing = faceLocation(gg, player.getEyeLocation()); + gg.teleport(facing); + SexCommand.models.put(player.getUniqueId(), new SexModel(gg, yaw, player.getLocation())); + return true; + } + ArmorStand as = (ArmorStand) player.getWorld().spawnEntity(loc, EntityType.ARMOR_STAND); ItemStack head = new ItemStack(Material.PLAYER_HEAD); SkullMeta meta = (SkullMeta) head.getItemMeta(); @@ -170,12 +215,12 @@ public class SexCommand implements CommandExecutor { as.setHeadPose(new EulerAngle(0.15, 0, 0)); as.setInvulnerable(true); - as.addEquipmentLock(EquipmentSlot.HEAD, LockType.ADDING); - as.addEquipmentLock(EquipmentSlot.CHEST, LockType.ADDING); - as.addEquipmentLock(EquipmentSlot.FEET, LockType.ADDING); - as.addEquipmentLock(EquipmentSlot.LEGS, LockType.ADDING); - as.addEquipmentLock(EquipmentSlot.OFF_HAND, LockType.ADDING); - as.addEquipmentLock(EquipmentSlot.HAND, LockType.ADDING); + as.addEquipmentLock(EquipmentSlot.HEAD, LockType.REMOVING_OR_CHANGING); + as.addEquipmentLock(EquipmentSlot.CHEST, LockType.REMOVING_OR_CHANGING); + as.addEquipmentLock(EquipmentSlot.FEET, LockType.REMOVING_OR_CHANGING); + as.addEquipmentLock(EquipmentSlot.LEGS, LockType.REMOVING_OR_CHANGING); + as.addEquipmentLock(EquipmentSlot.OFF_HAND, LockType.REMOVING_OR_CHANGING); + as.addEquipmentLock(EquipmentSlot.HAND, LockType.REMOVING_OR_CHANGING); Location facing = faceLocation(as, player.getEyeLocation()); as.teleport(facing); @@ -186,6 +231,8 @@ public class SexCommand implements CommandExecutor { public class SexModel { ArmorStand as; + Player pl; + Yaw yaw; int sexTicks = 0; boolean finished = false; @@ -197,6 +244,12 @@ public class SexCommand implements CommandExecutor { this.playerLocation = playerLocation; } + public SexModel(Player pl, Yaw yaw, Location playerLocation) { + this.pl = pl; + this.yaw = yaw; + this.playerLocation = playerLocation; + } + public void tickSex(Player player) { player.teleport(this.playerLocation); @@ -212,9 +265,15 @@ public class SexCommand implements CommandExecutor { if (this.yaw == Yaw.SOUTH) opposite = Yaw.NORTH; this.yaw = opposite; - Location asl = this.as.getLocation(); - asl.add(SexCommand.getVector(this.yaw, 0.2)); - as.teleport(asl); + if (this.as == null) { + Location pll = this.pl.getLocation(); + pll.add(SexCommand.getVector(this.yaw, 0.2)); + pl.teleport(pll); + } else { + Location asl = this.as.getLocation(); + asl.add(SexCommand.getVector(this.yaw, 0.2)); + as.teleport(asl); + } } if (this.sexTicks == 30) { @@ -230,7 +289,12 @@ public class SexCommand implements CommandExecutor { } public void removeModel() { - this.as.remove(); + if (this.as == null) { + this.pl.setGravity(true); + this.pl.setInvulnerable(false); + } else { + this.as.remove(); + } } } diff --git a/src/main/java/lv/pi/animalrp/listeners/LeashPlayers.java b/src/main/java/lv/pi/animalrp/listeners/LeashPlayers.java new file mode 100644 index 0000000..4d232bd --- /dev/null +++ b/src/main/java/lv/pi/animalrp/listeners/LeashPlayers.java @@ -0,0 +1,166 @@ + +package lv.pi.animalrp.listeners; + +// Code from: +// (warning, NSFW) https://github.com/ApherFox/MC-leash-players-plugin/blob/master/main/Pair.java +// https://github.com/WMGameLive/LeashPlayers/blob/master/src/main/java/wm/vdr/leashplayers/Listeners.java +import org.bukkit.Material; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Zombie; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityCombustEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityUnleashEvent; +import org.bukkit.event.entity.EntityUnleashEvent.UnleashReason; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.util.Vector; + +import lv.pi.animalrp.AnimalRP; +import lv.pi.animalrp.animals.Animal; +import lv.pi.animalrp.util.Mood; + +import java.util.ArrayList; +import java.util.List; + +public class LeashPlayers implements Listener { + List leashed = new ArrayList<>(); + List entityList = new ArrayList<>(); + List distanceUnleash = new ArrayList<>(); + + @EventHandler + public void onUnleash(EntityUnleashEvent e) { + if (e.getReason() == UnleashReason.PLAYER_UNLEASH) + return; + distanceUnleash.add(e.getEntity()); + } + + @EventHandler + public void onLeash(PlayerInteractAtEntityEvent e) { + if (!(e.getRightClicked() instanceof Player)) + return; + + if (!e.getHand().equals(EquipmentSlot.HAND)) + return; + + Player player = e.getPlayer(); + Player target = (Player) e.getRightClicked(); + + if (!player.getInventory().getItemInMainHand().getType().equals(Material.LEAD)) + return; + + if (!player.isOp()) { + if (!player.hasPermission("animalrp.leash")) { + player.sendMessage(AnimalRP.mm.deserialize("You do not have permission to leash a player.")); + return; + } + if (!target.hasPermission("animalrp.leash")) { + player.sendMessage(AnimalRP.mm.deserialize("This player does not have permission to be leashed.")); + return; + } + } + + if (leashed.contains(target)) { + leashed.remove(target); + return; + } + + if (AnimalRP.users.get(player.getUniqueId()) == null) { + player.sendMessage(AnimalRP.mm.deserialize("You aren't a animal, you can't leash others!")); + return; + } + + if (AnimalRP.users.get(target.getUniqueId()) != null) { + Animal targetAnimal = AnimalRP.users.get(target.getUniqueId()); + Animal playerAnimal = AnimalRP.users.get(player.getUniqueId()); + + player.sendMessage( + AnimalRP.mm.deserialize( + String.format("You just leashed <%s>%s!", targetAnimal.color, target.getName()))); + target.sendMessage( + AnimalRP.mm.deserialize(String.format("You just got leashed by <%s>%s! (^▽^)", + playerAnimal.color, player.getName()))); + + player.getWorld().playSound(player.getLocation(), playerAnimal.moodSounds.get(Mood.HAPPY), 1F, 1); + target.getWorld().playSound(target.getLocation(), targetAnimal.moodSounds.get(Mood.CUTE), 1F, 1); + } else { + player.sendMessage(AnimalRP.mm.deserialize("This person isn't a animal.")); + return; + } + LivingEntity zomb = target.getWorld().spawn(target.getLocation(), Zombie.class, zombie -> { + zombie.getEquipment().setItemInMainHand(null); + zombie.getEquipment().setHelmet(null); + zombie.getEquipment().setChestplate(null); + zombie.getEquipment().setLeggings(null); + zombie.getEquipment().setBoots(null); + zombie.setCanPickupItems(false); + zombie.setAdult(); + if (zombie.getVehicle() != null) + zombie.getVehicle().remove(); + zombie.setSilent(true); + zombie.setInvisible(true); + zombie.setCollidable(false); + zombie.setInvulnerable(true); + zombie.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, Integer.MAX_VALUE, 255, false, false)); + zombie.setLeashHolder(player); + }); + + target.setAllowFlight(true); + leashed.add(target); + entityList.add(zomb); + + player.getInventory().getItemInMainHand().setAmount(player.getInventory().getItemInMainHand().getAmount() - 1); + + new BukkitRunnable() { + public void run() { + if (!target.isOnline() || !zomb.isValid() || !zomb.isLeashed() || !leashed.contains(target)) { + leashed.remove(target); + entityList.remove(zomb); + zomb.remove(); + target.setAllowFlight(false); + if (!distanceUnleash.contains(zomb)) + target.getWorld().dropItemNaturally(target.getLocation(), new ItemStack(Material.LEAD)); + else + distanceUnleash.remove(zomb); + cancel(); + } + double distance = zomb.getLocation().distance(target.getLocation()); + if (distance > 10.0D) { + target.teleport(zomb.getLocation().setDirection(target.getLocation().getDirection())); + } else if (distance > 3.0D) { + double x = zomb.getLocation().getX() - target.getLocation().getX(); + double y = zomb.getLocation().getY() - target.getLocation().getY(); + double z = zomb.getLocation().getZ() - target.getLocation().getZ(); + Vector vector = new Vector(x, y, z); + + target.setVelocity(vector.multiply(0.1D)); + } else { + target.teleport(zomb.getLocation().setDirection(target.getLocation().getDirection())); + } + } + }.runTaskTimer(AnimalRP.getProvidingPlugin(AnimalRP.class), 0, 15); + } + + @EventHandler + public void onFlame(EntityCombustEvent e) { + if (!(e.getEntity() instanceof LivingEntity)) + return; + if (entityList.contains((LivingEntity) e.getEntity())) + e.setCancelled(true); + } + + @EventHandler + public void onDamage(EntityDamageByEntityEvent e) { + if (!(e.getDamager() instanceof LivingEntity)) + return; + if (entityList.contains((LivingEntity) e.getDamager())) + e.setCancelled(true); + } +} \ No newline at end of file diff --git a/src/main/java/lv/pi/animalrp/util/Emote.java b/src/main/java/lv/pi/animalrp/util/Emote.java index 1de627b..40dd0a1 100644 --- a/src/main/java/lv/pi/animalrp/util/Emote.java +++ b/src/main/java/lv/pi/animalrp/util/Emote.java @@ -89,7 +89,7 @@ public class Emote { } public void drawEmote(Player player, Emotes emote) { - List locs = getEmoteLocs(player.getLocation(), emotes.get(emote), player.getLocation()); + List locs = getEmoteLocs(player.getLocation().subtract(0, player.isSneaking()?.5:0, 0), emotes.get(emote), player.getLocation()); for (Location loc : locs) { Particle.DustOptions dustOptions = new Particle.DustOptions(Color.WHITE, 0.5f); player.getWorld().spawnParticle(Particle.DUST, loc, 15, 0, 0, 0, dustOptions); diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 83efeb5..f7e30ad 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -34,7 +34,5 @@ commands: usage: Cuddle a person. emote: usage: Emote! - bzz: - usage: Bzz at someone :3 sex: - usage: This command can only be used by Operators. \ No newline at end of file + usage: Dude, no. \ No newline at end of file