From 275ef479e8d9514b4110d3e08d84bc52db517ea1 Mon Sep 17 00:00:00 2001 From: arboriginal Date: Tue, 4 Dec 2018 08:03:39 +0100 Subject: [PATCH] Improvements: * Allows to fix the max number of riders (you can have several groups) * Add configurable cooldowns (actions and messages) * Now, items used can be consumed (your choice) * Add the permission playerrider.ride.keepitem * Add the permission playerrider.whip.keepitem * Add an option to prevent the rider to be hit by his victim --- README.md | 7 +- src/config.yml | 80 +++++++-- .../arboriginal/PlayerRider/PRcooldown.java | 74 ++++++++ .../arboriginal/PlayerRider/PlayerRider.java | 161 ++++++++++++++---- src/plugin.yml | 14 +- 5 files changed, 283 insertions(+), 53 deletions(-) create mode 100644 src/me/arboriginal/PlayerRider/PRcooldown.java diff --git a/README.md b/README.md index ef82bca..83ab355 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,9 @@ Simply copy [PlayerRider.jar](https://github.com/arboriginal/PlayerRider/release * **playerrider.reload**: Allows to use `/prider-reload` command. * **playerrider.ride**: Allows to ride a player. +* **playerrider.ride.keepitem**: Allows to ride a player without consuming the item (if activated). * **playerrider.whip**t: Allows to whip he player you are riding. +* **playerrider.whip.keepitem**t: Allows to whip he player you are riding without consuming the item (if activated). * **playerrider.duck**: Allows to be ridden. * **playerrider.eject**: Allows to eject your passenger. @@ -50,10 +52,7 @@ Edit `plugins/PlayerRider/config.yml` then reload the plugin, or the server if y ## Previous version -I did this plugin in 2012, as an easter egg, players on the server I was got tons of fun with it *(we mainly used it on new guys who are not correct)*. So I've decided to extract this part, and shared it as an independent plugin on [Bukkit.org page](http://dev.bukkit.org/projects/playerrider). Then, I've stopped to play at Minecraft, and seen people grabbed the code and make their own "updated" versions *(with minor modifications like change the value of the API...)* without mention the origin, this had discouraged me to make new releases. - -## For now -I've recently started a server with friends, so it gives me motivation to re-use Java and develop new plugins. I've ported this one because it was funny to play with and quick to upgrade. The new version contains (much) more options and a few funny features. +I did this plugin in 2012, as an easter egg, players on the server I was got tons of fun with it *(we mainly used it on new guys who are not correct)*. So I've decided to extract this part, and shared it as an independent plugin on [Bukkit.org page](http://dev.bukkit.org/projects/playerrider). ## TODO Because hitboxes have changed, the player ridden can see his rider's legs. I will work soon to improve this not to reduce the FOV. But the plugin is fully working except this visual effect. diff --git a/src/config.yml b/src/config.yml index 3c65d5a..9a1034a 100644 --- a/src/config.yml +++ b/src/config.yml @@ -13,33 +13,70 @@ # # Prefix used to all plugin messages (at least by default...) # If you want a different prefix for messages (or no prefix), leave it blank « "" ». -# This value replace « {prefix} » in the next parameters. +# This value replace « {prefix} » in the next parameters. prefix: "&8[&7&lPlayerRider&8] " # Message displayed when using the command /prider-reload -reload: "{prefix} &aConfiguration successfully reloaded." +reload: "{prefix}&aConfiguration successfully reloaded." # Error message when the chosen sound « boost_whip_sound » is incorrect. -sndErr: "{prefix} &cChosen sound is invalid and has been disabled." +sndErr: "{prefix}&cChosen sound is invalid and has been disabled." +# Message sent to player trying to ride a player who already have reached his limit. +tooMany: "{prefix}&6{duck}&c cannot wear more players... He needs to eat more spinach!" +# Message when the player should be able to ride but it's fail (maybe another plugin cancel the event) +failed: "{prefix}&cUnable to ride &6{duck}&c, maybe another plugin is preventing it..." # In the both 3 sections: (broadcast, player and duck) # - sub-section "ride" is used when a player riders another -# - sub-section "eject" when he's being ejected +# - sub-section "whip" is used when a player whips another +# - sub-section "eject" when he's being ejected # -# If you don't want one those messages, simply leave it blank « "" ». - +# If you don't want one of those messages, simply leave it blank « "" ». +# You can also adjust the cooldown for messages, see below. broadcast: ride: "&f&l•&r {prefix}&6{player}&3 now rides &6{duck}&3!" + whip: "&f&l•&r {prefix}&6{player}&3 whips &6{duck}&3!" eject: "&f&l•&r {prefix}&6{duck}&3 has ejected &6{player}&3!" player: ride: "{prefix}&bYou are riding &e&l{duck}&b, yeah!!!" + whip: "{prefix}&bYou whiped &e&l{duck}&b!!!" eject: "{prefix}&e&l{duck}&b's got a temper, try to give him a carrot maybe?" duck: ride: "{prefix}&e&l{player}&b likes you so much, he now stands on your shoulder :)" + whip: "{prefix}&e&l{player}&b whipped you, do you like that?" eject: "{prefix}&bYou've just ejected &e&l{player}&b, beware... I'm sure he will try again!" +cooldowns: + ride: + perform: 0 + broadcast: 30 + alertPlayer: 0 + alertDuck: 0 + + whip: + perform: 2 + broadcast: 30 + alertPlayer: 2 + alertDuck: 2 + + eject: + perform: 3 # Not a real cooldown here, but time to wait before being able to eject the rider + broadcast: 30 + alertPlayer: 3 + alertDuck: 3 + +# When an action cannot be done because of the cooldown, the player will receive this message +# Because the chat can be spammed or not read, this will be done as an action bar message. +# Here, you can use {delay} as the delay value in seconds before the player will be able again. +# +# If you don't want one of those messages, simply leave it blank « "" ». +warn_player_when_cooldown: + ride: "&4&lWait &6&l{delay}&4&ls before to ride another player" + whip: "&4&lWait &6&l{delay}&4&ls before to whip again &e&l{duck}" + eject: "&4&lWait &6&l{delay}&4&ls before to eject &e&l{duck}" + ######################################################################################################## -# Settings which change the plugin mechanisms +# Settings which change the plugin mechanisms ######################################################################################################## # Here, you will define which items the player can have in hand to perform the action. @@ -55,6 +92,23 @@ allowed_items: - CARROT - GOLDEN_CARROT +# You decide if items used to perform the action are consumed or not +consume_items: + ride: false + whip: true + +# Define how many riders can sit on the same player's shoulders +# You can set groups you need, and give the appropriate permission to your users. +# Each groups you define will be checked with the permission "playerrider.duck." +max_riders: + # This group is the default one (if you delete it, it will be re-added into your configuration). + # It will be applied to all players who don't have another "playerrider.duck." permission. + # No need to give the permission "playerrider.duck.newbies", it's assumed if the player have "playerrider.duck". + default: 16 + # Here are some examples to illustrate: + # newbies: 1 <--- corresponds to the permission "playerrider.duck.newbies" + # staff: 32 <--- corresponds to the permission "playerrider.duck.staff" + # Max pitch value the riden player must be to be able to eject his rider # (look with F3 in the game and see what pich value is OK for you). eject_maxPitch: -40.0 @@ -64,20 +118,22 @@ eject_when_hurt: true getoff_when_hurt: true # Decide if riders will be protected of suffocation damages when riding a player (true or false). prevent_suffocation: true -# Decide if riders can boost players of not (true or false) +# Decide if a player riden cannot damage his rider (true or false). +prevent_hit_rider: true +# Decide if riders can boost players or not (true or false) boost_allowed: true ##### [ the next parameters are ignored if boost_allowed: false ] ##### -# Value of the speed boost (integer) +# Value of the speed boost (integer), 0 to disable the boost boost_amplifier: 4 -# Duration in server ticks (20 ticks = 1 second) of the speed effect +# Duration in server ticks (20 ticks = 1 second) of the speed effect, 0 to disable the boost boost_duration: 60 -# Max pitch value the rider must be to be able to whip and boost the player (float) +# Max pitch value the rider must be to whip and boost the player (float), check in game with F3. boost_maxPitch: 60.0 # Sound the whip action will play, see https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Effect.html boost_whip_sound: ENTITY_FIREWORK_ROCKET_SHOOT -# Volume of the sound. Set it to zero if you don't want a sound effect +# Volume of the sound. Set it to zero if you don't want a sound effect (float) boost_whip_volume: 1.0 # Pitch of the sound (normal play = 1, value can be from 0.4 to 2 if I remember well) boost_whip_pitch: 1.3 diff --git a/src/me/arboriginal/PlayerRider/PRcooldown.java b/src/me/arboriginal/PlayerRider/PRcooldown.java new file mode 100644 index 0000000..5529efe --- /dev/null +++ b/src/me/arboriginal/PlayerRider/PRcooldown.java @@ -0,0 +1,74 @@ +package me.arboriginal.PlayerRider; + +import java.util.HashMap; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class PRcooldown { + public static HashMap cooldowns; + public static HashMap warnings; + + public PRcooldown() { + cooldowns = new HashMap() { + private static final long serialVersionUID = 1L; + }; + } + + public String id(String key, CommandSender player, CommandSender duck) { + return key + "..." + player.getName() + "." + ((duck == null) ? ".." : duck.getName()); + } + + public boolean isActive(String key, Player player) { + return isActive(key, player, null, false); + } + + public boolean isActive(String key, Player player, boolean warn) { + return isActive(key, player, null, warn); + } + + public boolean isActive(String key, Player player, Player duck) { + return isActive(key, player, duck, false); + } + + public boolean isActive(String key, Player player, Player duck, boolean warn) { + Long now = getCurrentTime(), next = get(key, player, duck); + + if (next > now) { + if (warn) PlayerRider.warn(key, player, duck, (int) Math.max(1, Math.floor((next - now) / 1000))); + + return true; + } + + return false; + } + + public Long getCurrentTime() { + return System.currentTimeMillis(); + } + + public Long get(String key, Player player, Player duck) { + key = id(key, player, duck); + + return cooldowns.containsKey(key) ? cooldowns.get(key) : 0; + } + + public void clear(String key, Player player) { + clear(key, player, null); + } + + public void clear(String key, Player player, Player duck) { + cooldowns.remove(id(key, player, duck)); + } + + public void set(String key, Player player) { + set(key, player, null); + } + + public void set(String key, Player player, Player duck) { + int delay = PlayerRider.config.getInt("cooldowns." + key) * 1000; + + if (delay > 0) { + cooldowns.put(id(key, player, duck), getCurrentTime() + delay); + } + } +} diff --git a/src/me/arboriginal/PlayerRider/PlayerRider.java b/src/me/arboriginal/PlayerRider/PlayerRider.java index 7cbef7a..6e5fd53 100644 --- a/src/me/arboriginal/PlayerRider/PlayerRider.java +++ b/src/me/arboriginal/PlayerRider/PlayerRider.java @@ -2,30 +2,37 @@ import java.util.List; import org.bukkit.ChatColor; +import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; // TODO: Find a way not to hide the duck POV with rider legs (players have new hitbox) public class PlayerRider extends JavaPlugin implements Listener { - protected FileConfiguration config; - public static Sound sound; - public static Float volume; + public static FileConfiguration config; + public static Sound sound; + public static Float volume; + public static PRcooldown cooldown; // ----------------------------------------------------------------------------------------------- // JavaPlugin methods @@ -33,6 +40,7 @@ public class PlayerRider extends JavaPlugin implements Listener { @Override public void onEnable() { + cooldown = new PRcooldown(); reloadConfig(); getServer().getPluginManager().registerEvents(this, this); @@ -86,34 +94,34 @@ public boolean onCommand(CommandSender sender, Command command, String label, St @EventHandler public void onPlayerInteractEntity(PlayerInteractEntityEvent event) { // https://www.spigotmc.org/threads/how-would-i-stop-an-event-from-being-called-twice.135234/#post-1434104 - if (event.getHand() == EquipmentSlot.OFF_HAND) return; - - Entity duck = event.getRightClicked(); - - if (!(duck instanceof Player) || !duck.hasPermission("playerrider.duck")) return; + if (event.getHand() == EquipmentSlot.OFF_HAND || !(event.getRightClicked() instanceof Player)) return; - Player player = event.getPlayer(); + Player player = event.getPlayer(), duck = (Player) event.getRightClicked(); if (player.getPassengers().contains(duck)) { if (player.getLocation().getPitch() < config.getDouble("eject_maxPitch") - && player.hasPermission("playerrider.eject")) { + && player.hasPermission("playerrider.eject") + && !cooldown.isActive("eject.perform", player, true)) { player.eject(); - alert("eject", duck, player); + cooldown.clear("eject.perform", player); } return; } - if (!player.hasPermission("playerrider.ride") || player.isInsideVehicle() - || !config.getStringList("allowed_items.ride") - .contains(player.getInventory().getItemInMainHand().getType().toString())) - return; + if (!playerAllowed(player, "ride") || !duck.hasPermission("playerrider.duck") || player.isInsideVehicle()) return; + + ItemStack item = player.getInventory().getItemInMainHand(); + + if (!config.getStringList("allowed_items.ride").contains(item.getType().toString())) return; List riders = duck.getPassengers(); + int count = 0; while (riders.size() == 1 && riders.get(0) instanceof Player) { - duck = riders.get(0); + duck = (Player) riders.get(0); + count++; if (duck.equals(player)) { return; @@ -122,13 +130,36 @@ public void onPlayerInteractEntity(PlayerInteractEntityEvent event) { riders = duck.getPassengers(); } - if (riders.size() == 0) { - duck.addPassenger(player); + if (riders.size() > 0) return; + + if (!duckAllowed(duck, count + 1)) { + userMessage(player, "tooMany", player, duck); + return; + } + if (duck.addPassenger(player)) { + consume(player, item, "ride"); alert("ride", player, duck); + cooldown.set("ride.perform", player, duck); + cooldown.set("eject.perform", duck); + } + else { + userMessage(player, "failed", player, duck); } } + private boolean duckAllowed(Player duck, int passengersCount) { + ConfigurationSection section = config.getConfigurationSection("max_riders"); + + for (String group : section.getKeys(false)) { + if ((group.equals("default") || duck.hasPermission("playerrider.duck." + group)) + && config.getInt("max_riders." + group) >= passengersCount) + return true; + } + + return false; + } + @EventHandler public void onPlayerInteract(PlayerInteractEvent event) { // https://www.spigotmc.org/threads/how-would-i-stop-an-event-from-being-called-twice.135234/#post-1434104 @@ -138,34 +169,46 @@ public void onPlayerInteract(PlayerInteractEvent event) { Player player = event.getPlayer(); - if (!player.hasPermission("playerrider.whip") + if (!playerAllowed(player, "whip") || player.getLocation().getPitch() < config.getDouble("boost_maxPitch") - || !(player.getVehicle() instanceof Player) - || !config.getStringList("allowed_items.whip") - .contains(player.getInventory().getItemInMainHand().getType().toString())) + || !(player.getVehicle() instanceof Player)) return; + ItemStack item = player.getInventory().getItemInMainHand(); + + if (!config.getStringList("allowed_items.whip").contains(item.getType().toString())) return; + Player duck = (Player) player.getVehicle(); - duck.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, config.getInt("boost_duration"), - config.getInt("boost_amplifier"), false, false, false), true); + if (cooldown.isActive("whip.perform", player, duck, true)) return; + + int amplifier = config.getInt("boost_amplifier"), duration = config.getInt("boost_duration"); + + if (amplifier > 0 && duration > 0) { + if (sound != null) { + player.playSound(player.getLocation(), sound, volume, (float) config.getDouble("boost_whip_pitch")); + duck.playSound(duck.getLocation(), sound, volume, (float) config.getDouble("boost_whip_pitch")); + } - if (sound != null) { - player.playSound(player.getLocation(), sound, volume, (float) config.getDouble("boost_whip_pitch")); - duck.playSound(duck.getLocation(), sound, volume, (float) config.getDouble("boost_whip_pitch")); + duck.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, duration, amplifier, false, false, false), true); } + consume(player, item, "whip"); + alert("whip", player, duck); + cooldown.set("whip.perform", player, duck); } @EventHandler public void onEntityDamage(EntityDamageEvent event) { + if (event.isCancelled()) return; + Entity injured = event.getEntity(); if (!(injured instanceof Player)) return; if (config.getBoolean("prevent_suffocation") && event.getCause() == DamageCause.SUFFOCATION - && event.getEntity().getVehicle() instanceof Player) { + && injured.getVehicle() instanceof Player) { event.setCancelled(true); return; } @@ -181,15 +224,65 @@ public void onEntityDamage(EntityDamageEvent event) { } } + @EventHandler + public void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) { + Entity injured = event.getEntity(); + + if (!(injured instanceof Player)) return; + + if (config.getBoolean("prevent_hit_rider") && event.getDamager() instanceof Player) { + Entity vehicle = injured.getVehicle(); + + if (vehicle != null && event.getDamager().equals(vehicle)) { + event.setCancelled(true); + return; + } + } + } + // ----------------------------------------------------------------------------------------------- // Custom methods // ----------------------------------------------------------------------------------------------- - private void alert(String key, CommandSender player, CommandSender duck) { - userMessage(player, "player." + key, player, duck); - userMessage(duck, "duck." + key, player, duck); + private boolean playerAllowed(Player player, String key) { + return player.hasPermission("playerrider." + key) || player.hasPermission("playerrider." + key + ".keepitem"); + } + + private void consume(Player player, ItemStack item, String key) { + if (config.getBoolean("consume_items." + key) + && !player.hasPermission("playerrider." + key + ".keepitem") + && item.getType() != Material.AIR) + item.setAmount(item.getAmount() - 1); + } + + protected static void warn(String key, Player player, Player duck, int delay) { + String[] parts = key.split("\\."); + + if (parts.length != 2 || !parts[1].equals("perform")) return; + + String message = PlayerRider.config.getString("warn_player_when_cooldown." + parts[0]); + + if (message.isEmpty()) return; + + ((Player) player).spigot().sendMessage(ChatMessageType.ACTION_BAR, + new TextComponent(prepareMessage(message.replace("{delay}", "" + delay), player, duck))); + } + + private void alert(String key, Player player, Player duck) { + if (!cooldown.isActive(key + ".alertPlayer", player, duck)) { + userMessage(player, "player." + key, player, duck); + cooldown.set(key + ".alertPlayer", player, duck); + } - broadcast(key, player, duck); + if (!cooldown.isActive(key + ".alertDuck", player, duck)) { + userMessage(duck, "duck." + key, player, duck); + cooldown.set(key + ".alertDuck", player, duck); + } + + if (!cooldown.isActive(key + ".broadcast", player, duck)) { + broadcast(key, player, duck); + cooldown.set(key + ".broadcast", player, duck); + } } private void userMessage(CommandSender sender, String key) { @@ -212,11 +305,11 @@ private void broadcast(String key, CommandSender player, CommandSender duck) { } } - private String prepareMessage(String message) { + private static String prepareMessage(String message) { return prepareMessage(message, null, null); } - private String prepareMessage(String message, CommandSender player, CommandSender duck) { + private static String prepareMessage(String message, CommandSender player, CommandSender duck) { message = message.replace("{prefix}", config.getString("prefix")); if (player != null) message = message.replace("{player}", player.getName()); diff --git a/src/plugin.yml b/src/plugin.yml index b0fe7d3..1e2d649 100644 --- a/src/plugin.yml +++ b/src/plugin.yml @@ -1,9 +1,9 @@ name: PlayerRider description: Allow you to ride players. -version: 1.13 +version: 1.13.1 author: arboriginal -website: http://dev.bukkit.org/server-mods/playerrider/ +website: https://www.spigotmc.org/resources/playerrider.62799/ dev-url: https://github.com/arboriginal/PlayerRider depend: [ ] @@ -27,11 +27,19 @@ permissions: playerrider.ride: description: Allows to ride a player. default: true - + + playerrider.ride.keepitem: + description: Allows to ride a player without consuming the item. + default: false + playerrider.whip: description: Allows to whip the player you ride. default: true + playerrider.whip.keepitem: + description: Allows to whip the player you ride without consuming the item. + default: true + playerrider.duck: description: Allows to be ridden. default: true