From b92202e86bbb76084c286f8580467e5fb6ab32aa Mon Sep 17 00:00:00 2001 From: Naman Date: Mon, 26 Mar 2018 18:42:26 -0500 Subject: [PATCH] Promote, kick, and admin command bugs fixed. --- .../java/com/massivecraft/factions/P.java | 3 +- .../massivecraft/factions/cmd/CmdAdmin.java | 22 +- .../massivecraft/factions/cmd/CmdBanner.java | 121 ++ .../factions/cmd/CmdCheckpoint.java | 69 + .../factions/cmd/CmdColeader.java | 90 + .../factions/cmd/CmdGetVault.java | 71 + .../massivecraft/factions/cmd/CmdKick.java | 27 +- .../factions/cmd/CmdKillHolograms.java | 35 + .../massivecraft/factions/cmd/CmdNear.java | 53 + .../massivecraft/factions/cmd/CmdRules.java | 90 + .../factions/cmd/CmdSeeChunk.java | 3 +- .../com/massivecraft/factions/cmd/CmdTnt.java | 166 ++ .../factions/cmd/CmdTpBanner.java | 47 + .../factions/cmd/CmdUpgrades.java | 41 + .../factions/cmd/FPromoteCommand.java | 24 +- .../event/FPlayerEnteredFactionEvent.java | 42 + .../factions/event/FPlayerStoppedFlying.java | 28 + .../listeners/EssentialsListener.java | 52 - .../util/Particle/ParticleEffect.java | 1589 +++++++++++++++++ .../util/Particle/ReflectionUtils.java | 605 +++++++ .../massivecraft/factions/zcore/MPlugin.java | 1 - .../zcore/fupgrades/CropUpgrades.java | 73 + .../factions/zcore/fupgrades/EXPUpgrade.java | 48 + .../zcore/fupgrades/FUpgradesGUI.java | 267 +++ .../zcore/fupgrades/SpawnerUpgrades.java | 39 + .../massivecraft/factions/zcore/util/TL.java | 1 + src/main/resources/plugin.yml | 7 +- 27 files changed, 3515 insertions(+), 99 deletions(-) create mode 100644 src/main/java/com/massivecraft/factions/cmd/CmdBanner.java create mode 100644 src/main/java/com/massivecraft/factions/cmd/CmdCheckpoint.java create mode 100644 src/main/java/com/massivecraft/factions/cmd/CmdColeader.java create mode 100644 src/main/java/com/massivecraft/factions/cmd/CmdGetVault.java create mode 100644 src/main/java/com/massivecraft/factions/cmd/CmdKillHolograms.java create mode 100644 src/main/java/com/massivecraft/factions/cmd/CmdNear.java create mode 100644 src/main/java/com/massivecraft/factions/cmd/CmdRules.java create mode 100644 src/main/java/com/massivecraft/factions/cmd/CmdTnt.java create mode 100644 src/main/java/com/massivecraft/factions/cmd/CmdTpBanner.java create mode 100644 src/main/java/com/massivecraft/factions/cmd/CmdUpgrades.java create mode 100644 src/main/java/com/massivecraft/factions/event/FPlayerEnteredFactionEvent.java create mode 100644 src/main/java/com/massivecraft/factions/event/FPlayerStoppedFlying.java delete mode 100644 src/main/java/com/massivecraft/factions/listeners/EssentialsListener.java create mode 100644 src/main/java/com/massivecraft/factions/util/Particle/ParticleEffect.java create mode 100644 src/main/java/com/massivecraft/factions/util/Particle/ReflectionUtils.java create mode 100644 src/main/java/com/massivecraft/factions/zcore/fupgrades/CropUpgrades.java create mode 100644 src/main/java/com/massivecraft/factions/zcore/fupgrades/EXPUpgrade.java create mode 100644 src/main/java/com/massivecraft/factions/zcore/fupgrades/FUpgradesGUI.java create mode 100644 src/main/java/com/massivecraft/factions/zcore/fupgrades/SpawnerUpgrades.java diff --git a/src/main/java/com/massivecraft/factions/P.java b/src/main/java/com/massivecraft/factions/P.java index b11743ca..d8e0abfa 100644 --- a/src/main/java/com/massivecraft/factions/P.java +++ b/src/main/java/com/massivecraft/factions/P.java @@ -34,7 +34,6 @@ import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.RegisteredServiceProvider; -import java.io.IOException; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.*; @@ -141,7 +140,7 @@ public class P extends MPlugin { startAutoLeaveTask(false); //Massive stats - MassiveStats update = new MassiveStats(this, 60); + //MassiveStats update = new MassiveStats(this, 60); mc17 = Bukkit.getServer().getClass().getPackage().getName().contains("1.7"); diff --git a/src/main/java/com/massivecraft/factions/cmd/CmdAdmin.java b/src/main/java/com/massivecraft/factions/cmd/CmdAdmin.java index 212b9c45..6c53d320 100644 --- a/src/main/java/com/massivecraft/factions/cmd/CmdAdmin.java +++ b/src/main/java/com/massivecraft/factions/cmd/CmdAdmin.java @@ -36,10 +36,7 @@ public class CmdAdmin extends FCommand { if (fyou == null) { return; } - if (fme.getRole() == Role.ADMIN){ - fme.msg(TL.COMMAND_ADMIN_NOTADMIN); - return; - } + boolean permAny = Permission.ADMIN_ANY.has(sender, false); Faction targetFaction = fyou.getFaction(); @@ -47,20 +44,17 @@ public class CmdAdmin extends FCommand { msg(TL.COMMAND_ADMIN_NOTMEMBER, fyou.describeTo(fme, true)); return; } - if ((fyou == fme && fme.getRole() == Role.COLEADER) || (fme.getRole() == Role.COLEADER && fyou.getRole() == Role.ADMIN)){ - msg(TL.COMMAND_ADMIN_NOTADMIN); - return; - } + if (fme != null && fme.getRole() != Role.ADMIN && !permAny) { msg(TL.COMMAND_ADMIN_NOTADMIN); return; } - if (fyou == fme && !permAny) { msg(TL.COMMAND_ADMIN_TARGETSELF); return; } + // only perform a FPlayerJoinEvent when newLeader isn't actually in the faction if (fyou.getFaction() != targetFaction) { FPlayerJoinEvent event = new FPlayerJoinEvent(FPlayers.getInstance().getByPlayer(me), targetFaction, FPlayerJoinEvent.PlayerJoinReason.LEADER); @@ -70,23 +64,19 @@ public class CmdAdmin extends FCommand { } } - - FPlayer admin = targetFaction.getFPlayerAdmin(); + // if target player is currently admin, demote and replace him - if (fyou == admin && fyou.getFaction().getSize() == 1){ - msg(TL.COMMAND_ADMIN_NOMEMBERS); - return; - } if (fyou == admin) { targetFaction.promoteNewLeader(); msg(TL.COMMAND_ADMIN_DEMOTES, fyou.describeTo(fme, true)); fyou.msg(TL.COMMAND_ADMIN_DEMOTED, senderIsConsole ? TL.GENERIC_SERVERADMIN.toString() : fme.describeTo(fyou, true)); return; } + // promote target player, and demote existing admin if one exists if (admin != null) { - admin.setRole(Role.COLEADER); + admin.setRole(Role.MODERATOR); } fyou.setRole(Role.ADMIN); msg(TL.COMMAND_ADMIN_PROMOTES, fyou.describeTo(fme, true)); diff --git a/src/main/java/com/massivecraft/factions/cmd/CmdBanner.java b/src/main/java/com/massivecraft/factions/cmd/CmdBanner.java new file mode 100644 index 00000000..3d87f756 --- /dev/null +++ b/src/main/java/com/massivecraft/factions/cmd/CmdBanner.java @@ -0,0 +1,121 @@ +package com.massivecraft.factions.cmd; + +import com.massivecraft.factions.FPlayer; +import com.massivecraft.factions.P; +import com.massivecraft.factions.struct.Permission; +import com.massivecraft.factions.zcore.util.TL; +import net.milkbowl.vault.economy.Economy; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; + +public class CmdBanner extends FCommand { + public CmdBanner() { + super(); + + this.aliases.add("banner"); + this.aliases.add("warbanner"); + + this.permission = Permission.BANNER.node; + this.disableOnLock = false; + + senderMustBePlayer = true; + senderMustBeMember = false; + senderMustBeModerator = false; + senderMustBeColeader = true; + senderMustBeAdmin = false; + } + + @Override + public void perform() { + if (me.getItemInHand().getType() == Material.BANNER) { + if (hasMoney(fme, P.p.getConfig().getInt("fbanners.Banner-Cost"))) { + if (me.getItemInHand().getAmount() != 1) { + me.getItemInHand().setAmount(me.getItemInHand().getAmount() - 1); + } + ItemStack bannerInHand = me.getItemInHand(); + bannerInHand.setAmount(1); + removeFromInventory(me.getInventory(), bannerInHand); + takeMoney(fme, P.p.getConfig().getInt("fbanners.Banner-Cost")); + ItemStack warBanner = P.p.createItem(bannerInHand.getType(), 1, bannerInHand.getDurability(), P.p.getConfig().getString("fbanners.Item.Name"), P.p.getConfig().getStringList("fbanners.Item.Lore")); + me.getInventory().addItem(warBanner); + fme.msg(TL.COMMAND_BANNER_SUCCESS); + + } + } else { + fme.msg(TL.COMMAND_BANNER_WRONGITEM); + } + } + + public boolean hasMoney(FPlayer fme, int amt) { + Economy econ = P.p.getEcon(); + if (econ.getBalance((Player) fme.getPlayer()) >= amt) { + return true; + } else { + fme.msg(TL.COMMAND_BANNER_NOTENOUGHMONEY); + return false; + } + } + + public void takeMoney(FPlayer fme, int amt) { + if (hasMoney(fme, amt)) { + Economy econ = P.p.getEcon(); + econ.withdrawPlayer(fme.getPlayer(), amt); + fme.sendMessage(TL.COMMAND_BANNER_MONEYTAKE.toString().replace("{amount}", amt + "")); + } + } + + public boolean inventoryContains(Inventory inventory, ItemStack item) { + int count = 0; + ItemStack[] items = inventory.getContents(); + for (int i = 0; i < items.length; i++) { + if (items[i] != null && items[i].getType() == item.getType() && items[i].getDurability() == item.getDurability()) { + count += items[i].getAmount(); + } + if (count >= item.getAmount()) { + return true; + } + } + return false; + } + + + public void removeFromInventory(Inventory inventory, ItemStack item) { + int amt = item.getAmount(); + ItemStack[] items = inventory.getContents(); + for (int i = 0; i < items.length; i++) { + if (items[i] != null && items[i].getType() == item.getType() && items[i].getDurability() == item.getDurability()) { + if (items[i].getAmount() > amt) { + items[i].setAmount(items[i].getAmount() - amt); + break; + } else if (items[i].getAmount() == amt) { + items[i] = null; + break; + } else { + amt -= items[i].getAmount(); + items[i] = null; + } + } + } + inventory.setContents(items); + } + + public int getEmptySlots(Player p) { + PlayerInventory inventory = p.getInventory(); + ItemStack[] cont = inventory.getContents(); + int i = 0; + for (ItemStack item : cont) + if (item != null && item.getType() != Material.AIR) { + i++; + } + return 36 - i; + } + + + @Override + public TL getUsageTranslation() { + return TL.COMMAND_BANNER_DESCRIPTION; + } +} diff --git a/src/main/java/com/massivecraft/factions/cmd/CmdCheckpoint.java b/src/main/java/com/massivecraft/factions/cmd/CmdCheckpoint.java new file mode 100644 index 00000000..f2914846 --- /dev/null +++ b/src/main/java/com/massivecraft/factions/cmd/CmdCheckpoint.java @@ -0,0 +1,69 @@ +package com.massivecraft.factions.cmd; + +import com.massivecraft.factions.*; +import com.massivecraft.factions.struct.Permission; +import com.massivecraft.factions.util.WarmUpUtil; +import com.massivecraft.factions.zcore.util.TL; + +public class CmdCheckpoint extends FCommand { + public CmdCheckpoint() { + super(); + this.aliases.add("checkp"); + this.aliases.add("checkpoint"); + this.aliases.add("cpoint"); + + this.optionalArgs.put("set", ""); + + this.permission = Permission.CHECKPOINT.node; + this.disableOnLock = false; + + senderMustBePlayer = true; + senderMustBeMember = true; + senderMustBeModerator = false; + senderMustBeColeader = false; + senderMustBeAdmin = false; + } + + @Override + public void perform() { + if (!P.p.getConfig().getBoolean("checkpoints.Enabled")) { + fme.msg(TL.COMMAND_CHECKPOINT_DISABLED); + return; + } + if (args.size() == 1) { + FLocation myLocation = new FLocation(fme.getPlayer().getLocation()); + Faction myLocFaction = Board.getInstance().getFactionAt(myLocation); + if (myLocFaction == Factions.getInstance().getWilderness() || myLocFaction == fme.getFaction()) { + fme.getFaction().setCheckpoint(fme.getPlayer().getLocation()); + fme.msg(TL.COMMAND_CHECKPOINT_SET); + return; + } + } + if (fme.getFaction().getCheckpoint() == null) { + fme.msg(TL.COMMAND_CHECKPOINT_NOT_SET); + return; + } + FLocation checkLocation = new FLocation(fme.getFaction().getCheckpoint()); + Faction checkfaction = Board.getInstance().getFactionAt(checkLocation); + + if (checkfaction.getId().equals(Factions.getInstance().getWilderness().getId()) || checkfaction.getId().equals(fme.getFaction().getId())) { + fme.msg(TL.COMMAND_CHECKPOINT_GO); + fme.getPlayer().teleport(fme.getFaction().getCheckpoint()); + } else { + fme.msg(TL.COMMAND_CHECKPOINT_CLAIMED); + } + this.doWarmUp(WarmUpUtil.Warmup.WARP, TL.WARMUPS_NOTIFY_TELEPORT, "Checkpoint", new Runnable() { + @Override + public void run() { + // Create a smoke effect + CmdCheckpoint.this.me.teleport(fme.getFaction().getCheckpoint()); + } + }, this.p.getConfig().getLong("warmups.f-checkpoint", 0)); + + } + + @Override + public TL getUsageTranslation() { + return TL.COMMAND_CHECKPOINT_DESCRIPTION; + } +} diff --git a/src/main/java/com/massivecraft/factions/cmd/CmdColeader.java b/src/main/java/com/massivecraft/factions/cmd/CmdColeader.java new file mode 100644 index 00000000..552b4dc7 --- /dev/null +++ b/src/main/java/com/massivecraft/factions/cmd/CmdColeader.java @@ -0,0 +1,90 @@ +package com.massivecraft.factions.cmd; + +import com.massivecraft.factions.Conf; +import com.massivecraft.factions.FPlayer; +import com.massivecraft.factions.Faction; +import com.massivecraft.factions.struct.Permission; +import com.massivecraft.factions.struct.Role; +import com.massivecraft.factions.zcore.util.TL; +import mkremins.fanciful.FancyMessage; +import org.bukkit.ChatColor; + +public class CmdColeader extends FCommand { + public CmdColeader() { + super(); + this.aliases.add("co"); + this.aliases.add("setcoleader"); + this.aliases.add("coleader"); + this.aliases.add("setco"); + + this.optionalArgs.put("player name", "name"); + //this.optionalArgs.put("", ""); + + this.permission = Permission.COLEADER.node; + this.disableOnLock = true; + + senderMustBePlayer = false; + senderMustBeMember = true; + senderMustBeModerator = false; + senderMustBeAdmin = true; + } + + @Override + public void perform() { + FPlayer you = this.argAsBestFPlayerMatch(0); + if (you == null) { + FancyMessage msg = new FancyMessage(TL.COMMAND_COLEADER_CANDIDATES.toString()).color(ChatColor.GOLD); + for (FPlayer player : myFaction.getFPlayersWhereRole(Role.NORMAL)) { + String s = player.getName(); + msg.then(s + " ").color(ChatColor.WHITE).tooltip(TL.COMMAND_MOD_CLICKTOPROMOTE.toString() + s).command("/" + Conf.baseCommandAliases.get(0) + " coleader " + s); + } + for (FPlayer player : myFaction.getFPlayersWhereRole(Role.MODERATOR)) { + String s = player.getName(); + msg.then(s + " ").color(ChatColor.WHITE).tooltip(TL.COMMAND_MOD_CLICKTOPROMOTE.toString() + s).command("/" + Conf.baseCommandAliases.get(0) + " coleader " + s); + } + + sendFancyMessage(msg); + return; + } + + boolean permAny = Permission.COLEADER_ANY.has(sender, false); + Faction targetFaction = you.getFaction(); + + if (targetFaction != myFaction && !permAny) { + msg(TL.COMMAND_MOD_NOTMEMBER, you.describeTo(fme, true)); + return; + } + + if (fme != null && fme.getRole() != Role.ADMIN && !permAny) { + msg(TL.COMMAND_COLEADER_NOTADMIN); + return; + } + + if (you == fme && !permAny) { + msg(TL.COMMAND_COLEADER_SELF); + return; + } + + if (you.getRole() == Role.ADMIN) { + msg(TL.COMMAND_COLEADER_TARGETISADMIN); + return; + } + + if (you.getRole() == Role.COLEADER) { + // Revoke + you.setRole(Role.MODERATOR); + targetFaction.msg(TL.COMMAND_COLEADER_REVOKED, you.describeTo(targetFaction, true)); + msg(TL.COMMAND_COLEADER_REVOKES, you.describeTo(fme, true)); + } else { + // Give + you.setRole(Role.COLEADER); + targetFaction.msg(TL.COMMAND_COLEADER_PROMOTED, you.describeTo(targetFaction, true)); + msg(TL.COMMAND_COLEADER_PROMOTES, you.describeTo(fme, true)); + } + } + + @Override + public TL getUsageTranslation() { + return TL.COMMAND_COLEADER_DESCRIPTION; + } +} diff --git a/src/main/java/com/massivecraft/factions/cmd/CmdGetVault.java b/src/main/java/com/massivecraft/factions/cmd/CmdGetVault.java new file mode 100644 index 00000000..75f443b5 --- /dev/null +++ b/src/main/java/com/massivecraft/factions/cmd/CmdGetVault.java @@ -0,0 +1,71 @@ +package com.massivecraft.factions.cmd; + +import com.massivecraft.factions.P; +import com.massivecraft.factions.struct.Permission; +import com.massivecraft.factions.zcore.util.TL; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +public class CmdGetVault extends FCommand { + public CmdGetVault() { + super(); + + this.aliases.add("getvault"); + + this.permission = Permission.GETVAULT.node; + this.disableOnLock = true; + + senderMustBePlayer = true; + senderMustBeMember = true; + senderMustBeModerator = false; + senderMustBeColeader = false; + senderMustBeAdmin = false; + } + + @Override + public void perform() { + if (!P.p.getConfig().getBoolean("fvault.Enabled")) { + fme.sendMessage("This command is disabled!"); + return; + } + Location vaultLocation = fme.getFaction().getVault(); + ItemStack vault = P.p.createItem(Material.CHEST, 1, (short) 0, P.p.color(P.p.getConfig().getString("fvault.Item.Name")), P.p.colorList(P.p.getConfig().getStringList("fvault.Item.Lore"))); + + if (inventoryContains(me.getInventory(), vault)) { + fme.msg(TL.COMMAND_GETVAULT_ALREADYHAVE); + return; + } + + if (vaultLocation != null) { + fme.msg(TL.COMMAND_GETVAULT_ALREADYSET); + return; + } + + + me.getInventory().addItem(vault); + fme.msg(TL.COMMAND_GETVAULT_RECEIVE); + + } + + public boolean inventoryContains(Inventory inventory, ItemStack item) { + int count = 0; + ItemStack[] items = inventory.getContents(); + for (int i = 0; i < items.length; i++) { + if (items[i] != null && items[i].getType() == item.getType() && items[i].getDurability() == item.getDurability()) { + count += items[i].getAmount(); + } + if (count >= item.getAmount()) { + return true; + } + } + return false; + } + + @Override + public TL getUsageTranslation() { + return TL.COMMAND_GETVAULT_DESCRIPTION; + } + +} diff --git a/src/main/java/com/massivecraft/factions/cmd/CmdKick.java b/src/main/java/com/massivecraft/factions/cmd/CmdKick.java index 73bb031e..dc19f1e9 100644 --- a/src/main/java/com/massivecraft/factions/cmd/CmdKick.java +++ b/src/main/java/com/massivecraft/factions/cmd/CmdKick.java @@ -29,7 +29,6 @@ public class CmdKick extends FCommand { senderMustBePlayer = true; senderMustBeMember = false; senderMustBeModerator = false; - senderMustBeColeader = false; senderMustBeAdmin = false; } @@ -42,11 +41,19 @@ public class CmdKick extends FCommand { String s = player.getName(); msg.then(s + " ").color(ChatColor.WHITE).tooltip(TL.COMMAND_KICK_CLICKTOKICK.toString() + s).command("/" + Conf.baseCommandAliases.get(0) + " kick " + s); } - if (fme.getRole() == Role.ADMIN) { + if (fme.getRole().isAtLeast(Role.COLEADER)) { + // For both coleader and admin, add mods. for (FPlayer player : myFaction.getFPlayersWhereRole(Role.MODERATOR)) { String s = player.getName(); msg.then(s + " ").color(ChatColor.GRAY).tooltip(TL.COMMAND_KICK_CLICKTOKICK.toString() + s).command("/" + Conf.baseCommandAliases.get(0) + " kick " + s); } + if (fme.getRole() == Role.ADMIN) { + // Only add coleader to this for the leader. + for (FPlayer player : myFaction.getFPlayersWhereRole(Role.COLEADER)) { + String s = player.getName(); + msg.then(s + " ").color(ChatColor.RED).tooltip(TL.COMMAND_KICK_CLICKTOKICK.toString() + s).command("/" + Conf.baseCommandAliases.get(0) + " kick " + s); + } + } } sendFancyMessage(msg); @@ -66,11 +73,6 @@ public class CmdKick extends FCommand { return; } - if ((fme.getRole() == Role.MODERATOR || fme.getRole() == Role.COLEADER) && toKick.getRole() == Role.ADMIN){ - msg(TL.COMMAND_KICK_INSUFFICIENTRANK); - return; - } - // players with admin-level "disband" permission can bypass these requirements if (!Permission.KICK_ANY.has(sender)) { @@ -97,6 +99,14 @@ public class CmdKick extends FCommand { } } + Access access = myFaction.getAccess(fme, PermissableAction.KICK); + // This statement allows us to check if they've specifically denied it, or default to + // the old setting of allowing moderators to kick + if (access == Access.DENY || (access == Access.UNDEFINED && !assertMinRole(Role.MODERATOR))) { + fme.msg(TL.GENERIC_NOPERMISSION, "kick"); + return; + } + // if economy is enabled, they're not on the bypass list, and this command has a cost set, make sure they can pay if (!canAffordCommand(Conf.econCostKick, TL.COMMAND_KICK_TOKICK.toString())) { return; @@ -121,7 +131,6 @@ public class CmdKick extends FCommand { } if (Conf.logFactionKick) { - //TODO:TL P.p.log((senderIsConsole ? "A console command" : fme.getName()) + " kicked " + toKick.getName() + " from the faction: " + toKickFaction.getTag()); } @@ -138,4 +147,4 @@ public class CmdKick extends FCommand { return TL.COMMAND_KICK_DESCRIPTION; } -} +} \ No newline at end of file diff --git a/src/main/java/com/massivecraft/factions/cmd/CmdKillHolograms.java b/src/main/java/com/massivecraft/factions/cmd/CmdKillHolograms.java new file mode 100644 index 00000000..0ee95a2b --- /dev/null +++ b/src/main/java/com/massivecraft/factions/cmd/CmdKillHolograms.java @@ -0,0 +1,35 @@ +package com.massivecraft.factions.cmd; + +import com.massivecraft.factions.struct.Permission; +import com.massivecraft.factions.zcore.util.TL; + +public class CmdKillHolograms extends FCommand { + public CmdKillHolograms() { + super(); + + this.aliases.add("killholos"); + + this.requiredArgs.add("radius"); + + this.permission = Permission.KILLHOLOS.node; + this.disableOnLock = true; + + senderMustBePlayer = true; + senderMustBeMember = false; + senderMustBeModerator = false; + senderMustBeColeader = false; + senderMustBeAdmin = false; + } + + @Override + public void perform() { + me.sendMessage("Killing Invisible Armor Stands.."); + me.chat("/minecraft:kill @e[type=ArmorStand,r=" + argAsInt(0) + "]"); + + } + + @Override + public TL getUsageTranslation() { + return TL.COMMAND_KILLHOLOGRAMS_DESCRIPTION; + } +} diff --git a/src/main/java/com/massivecraft/factions/cmd/CmdNear.java b/src/main/java/com/massivecraft/factions/cmd/CmdNear.java new file mode 100644 index 00000000..335b7e20 --- /dev/null +++ b/src/main/java/com/massivecraft/factions/cmd/CmdNear.java @@ -0,0 +1,53 @@ +package com.massivecraft.factions.cmd; + +import com.massivecraft.factions.FPlayer; +import com.massivecraft.factions.FPlayers; +import com.massivecraft.factions.P; +import com.massivecraft.factions.zcore.util.TL; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; + +public class CmdNear extends FCommand { + public CmdNear() { + super(); + + this.aliases.add("near"); + this.aliases.add("nearby"); + + this.disableOnLock = true; + + senderMustBePlayer = true; + senderMustBeMember = true; + senderMustBeModerator = false; + senderMustBeColeader = false; + senderMustBeAdmin = false; + } + + @Override + public void perform() { + if (!P.p.getConfig().getBoolean("fnear.Enabled")) { + fme.msg(TL.COMMAND_NEAR_DISABLED_MSG); + return; + } + + double range = P.p.getConfig().getInt("fnear.Radius"); + String format = TL.COMMAND_NEAR_FORMAT.toString(); + fme.msg(TL.COMMAND_NEAR_USE_MSG); + for (Entity e : me.getNearbyEntities(range, 255, range)) { + if (e instanceof Player) { + Player player = (((Player) e).getPlayer()); + FPlayer fplayer = FPlayers.getInstance().getByPlayer(player); + if (fme.getFaction() == fplayer.getFaction()) { + double distance = me.getLocation().distance(player.getLocation()); + fme.sendMessage(format.replace("{playername}", player.getDisplayName()).replace("{distance}", (int) distance + "")); + } + } + + } + } + + @Override + public TL getUsageTranslation() { + return TL.COMMAND_NEAR_DESCRIPTION; + } +} diff --git a/src/main/java/com/massivecraft/factions/cmd/CmdRules.java b/src/main/java/com/massivecraft/factions/cmd/CmdRules.java new file mode 100644 index 00000000..a9266ad1 --- /dev/null +++ b/src/main/java/com/massivecraft/factions/cmd/CmdRules.java @@ -0,0 +1,90 @@ +package com.massivecraft.factions.cmd; + +import com.massivecraft.factions.P; +import com.massivecraft.factions.struct.Permission; +import com.massivecraft.factions.zcore.util.TL; + +import java.util.HashMap; +import java.util.List; + +public class CmdRules extends FCommand { + public CmdRules() { + super(); + aliases.add("r"); + aliases.add("rule"); + aliases.add("rules"); + + this.optionalArgs.put("add/remove/set/clear", ""); + this.errorOnToManyArgs = false; + + permission = Permission.RULES.node; + + senderMustBePlayer = true; + senderMustBeMember = true; + senderMustBeModerator = false; + senderMustBeColeader = true; + senderMustBeAdmin = false; + } + + @Override + public void perform() { + if (!P.p.getConfig().getBoolean("frules.Enabled")) { + fme.msg(TL.COMMAND_RULES_DISABLED_MSG); + return; + } + if (this.args.size() == 0) { + HashMap rules = fme.getFaction().getRulesMap(); + if (rules.size() == 0) { + List ruleList = P.p.getConfig().getStringList("frules.default-rules"); + fme.sendMessage(P.p.colorList(ruleList)); + + } else { + for (int i = 0; i <= rules.size() - 1; i++) { + fme.sendMessage(P.p.color(rules.get(i))); + } + } + + } + if (this.args.size() == 1) { + if (args.get(0).equalsIgnoreCase("add")) { + fme.msg(TL.COMMAND_RULES_ADD_INVALIDARGS); + } + if (args.get(0).equalsIgnoreCase("set")) { + fme.msg(TL.COMMAND_RULES_SET_INVALIDARGS); + } + if (args.get(0).equalsIgnoreCase("add")) { + fme.msg(TL.COMMAND_RULES_ADD_INVALIDARGS); + } + if (args.get(0).equalsIgnoreCase("clear")) { + fme.getFaction().clearRules(); + fme.msg(TL.COMMAND_RULES_CLEAR_SUCCESS); + } + + } + if (this.args.size() >= 2) { + if (args.get(0).equalsIgnoreCase("add")) { + String message = ""; + StringBuilder string = new StringBuilder(message); + for (int i = 1; i <= args.size() - 1; i++) { + string.append(" " + args.get(i)); + } + fme.getFaction().addRule(string.toString()); + fme.msg(TL.COMMAND_RULES_ADD_SUCCESS); + } + + if (this.args.size() == 2) { + if (args.get(0).equalsIgnoreCase("remove")) { + int index = argAsInt(1); + fme.getFaction().removeRule(index - 1); + fme.msg(TL.COMMAND_RULES_REMOVE_SUCCESS); + } + } + + } + } + + @Override + public TL getUsageTranslation() { + return TL.COMMAND_RULES_DESCRIPTION; + } +} diff --git a/src/main/java/com/massivecraft/factions/cmd/CmdSeeChunk.java b/src/main/java/com/massivecraft/factions/cmd/CmdSeeChunk.java index 4fd10652..66f4e32c 100644 --- a/src/main/java/com/massivecraft/factions/cmd/CmdSeeChunk.java +++ b/src/main/java/com/massivecraft/factions/cmd/CmdSeeChunk.java @@ -1,8 +1,9 @@ package com.massivecraft.factions.cmd; -import com.darkblade12.particleeffect.ParticleEffect; + import com.massivecraft.factions.FLocation; import com.massivecraft.factions.struct.Permission; +import com.massivecraft.factions.util.Particle.ParticleEffect; import com.massivecraft.factions.util.VisualizeUtil; import com.massivecraft.factions.zcore.util.TL; import org.bukkit.Location; diff --git a/src/main/java/com/massivecraft/factions/cmd/CmdTnt.java b/src/main/java/com/massivecraft/factions/cmd/CmdTnt.java new file mode 100644 index 00000000..3609f9ab --- /dev/null +++ b/src/main/java/com/massivecraft/factions/cmd/CmdTnt.java @@ -0,0 +1,166 @@ +package com.massivecraft.factions.cmd; + +import com.massivecraft.factions.P; +import com.massivecraft.factions.struct.Permission; +import com.massivecraft.factions.zcore.util.TL; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; + +public class CmdTnt extends FCommand { + public CmdTnt() { + super(); + this.aliases.add("tnt"); + + this.optionalArgs.put("add/take", ""); + this.optionalArgs.put("amount", "number"); + + this.permission = Permission.TNT.node; + this.disableOnLock = true; + + senderMustBePlayer = true; + senderMustBeMember = false; + senderMustBeModerator = true; + senderMustBeAdmin = false; + } + + @Override + public void perform() { + if (!P.p.getConfig().getBoolean("ftnt.Enabled")) { + fme.msg(TL.COMMAND_TNT_DISABLED_MSG); + return; + } + if (args.size() == 2) { + if (args.get(0).equalsIgnoreCase("add")) { + int testNumber = -1; + try { + testNumber = Integer.parseInt(args.get(1)); + } catch (NumberFormatException e) { + fme.msg(TL.COMMAND_TNT_INVALID_NUM); + return; + } + int amount = Integer.parseInt(args.get(1)); + if (amount < 0) { + fme.msg(TL.COMMAND_TNT_POSITIVE); + return; + } + Inventory inv = me.getInventory(); + int invTnt = 0; + for (int i = 0; i <= inv.getSize(); i++) { + if (inv.getItem(i) == null) { + continue; + } + if (inv.getItem(i).getType() == Material.TNT) { + invTnt += inv.getItem(i).getAmount(); + } + } + if (amount > invTnt) { + fme.msg(TL.COMMAND_TNT_DEPOSIT_NOTENOUGH); + return; + } + ItemStack tnt = new ItemStack(Material.TNT, amount); + removeFromInventory(me.getInventory(), tnt); + me.updateInventory(); + fme.getFaction().addTnt(amount); + fme.msg(TL.COMMAND_TNT_DEPOSIT_SUCCESS); + fme.sendMessage(P.p.color(TL.COMMAND_TNT_AMOUNT.toString().replace("{amount}", fme.getFaction().getTnt() + ""))); + return; + + } + if (args.get(0).equalsIgnoreCase("take")) { + int testNumber = -1; + try { + testNumber = Integer.parseInt(args.get(1)); + } catch (NumberFormatException e) { + fme.msg(TL.COMMAND_TNT_INVALID_NUM); + return; + } + int amount = Integer.parseInt(args.get(1)); + if (amount < 0) { + fme.msg(TL.COMMAND_TNT_POSITIVE); + return; + } + if (fme.getFaction().getTnt() < amount) { + fme.msg(TL.COMMAND_TNT_WIDTHDRAW_NOTENOUGH); + return; + } + int fullStacks = amount / 64; + int remainderAmt = amount % 64; + if ((remainderAmt == 0 && getEmptySlots(me) <= fullStacks)) { + fme.msg(TL.COMMAND_TNT_WIDTHDRAW_NOTENOUGH); + return; + } + if (getEmptySlots(me) + 1 <= fullStacks) { + fme.msg(TL.COMMAND_TNT_WIDTHDRAW_NOTENOUGH); + return; + } + ItemStack tnt64 = new ItemStack(Material.TNT, 64); + for (int i = 0; i <= fullStacks - 1; i++) { + me.getInventory().addItem(tnt64); + } + if (remainderAmt != 0) { + ItemStack tnt = new ItemStack(Material.TNT, remainderAmt); + me.getInventory().addItem(tnt); + } + fme.getFaction().takeTnt(amount); + me.updateInventory(); + fme.msg(TL.COMMAND_TNT_WIDTHDRAW_SUCCESS); + } + } + fme.sendMessage(TL.COMMAND_TNT_AMOUNT.toString().replace("{amount}", fme.getFaction().getTnt() + "")); + } + + + public boolean inventoryContains(Inventory inventory, ItemStack item) { + int count = 0; + ItemStack[] items = inventory.getContents(); + for (int i = 0; i < items.length; i++) { + if (items[i] != null && items[i].getType() == item.getType() && items[i].getDurability() == item.getDurability()) { + count += items[i].getAmount(); + } + if (count >= item.getAmount()) { + return true; + } + } + return false; + } + + + public void removeFromInventory(Inventory inventory, ItemStack item) { + int amt = item.getAmount(); + ItemStack[] items = inventory.getContents(); + for (int i = 0; i < items.length; i++) { + if (items[i] != null && items[i].getType() == item.getType() && items[i].getDurability() == item.getDurability()) { + if (items[i].getAmount() > amt) { + items[i].setAmount(items[i].getAmount() - amt); + break; + } else if (items[i].getAmount() == amt) { + items[i] = null; + break; + } else { + amt -= items[i].getAmount(); + items[i] = null; + } + } + } + inventory.setContents(items); + } + + public int getEmptySlots(Player p) { + PlayerInventory inventory = p.getInventory(); + ItemStack[] cont = inventory.getContents(); + int i = 0; + for (ItemStack item : cont) + if (item != null && item.getType() != Material.AIR) { + i++; + } + return 36 - i; + } + + @Override + public TL getUsageTranslation() { + return TL.COMMAND_TNT_DESCRIPTION; + } +} diff --git a/src/main/java/com/massivecraft/factions/cmd/CmdTpBanner.java b/src/main/java/com/massivecraft/factions/cmd/CmdTpBanner.java new file mode 100644 index 00000000..d3f9cc58 --- /dev/null +++ b/src/main/java/com/massivecraft/factions/cmd/CmdTpBanner.java @@ -0,0 +1,47 @@ +package com.massivecraft.factions.cmd; + +import com.massivecraft.factions.P; +import com.massivecraft.factions.listeners.FactionsPlayerListener; +import com.massivecraft.factions.struct.Permission; +import com.massivecraft.factions.util.WarmUpUtil; +import com.massivecraft.factions.zcore.util.TL; + +public class CmdTpBanner extends FCommand { + public CmdTpBanner() { + super(); + + this.aliases.add("tpbanner"); + + this.permission = Permission.TPBANNER.node; + this.disableOnLock = true; + + senderMustBePlayer = true; + senderMustBeMember = true; + senderMustBeModerator = false; + senderMustBeColeader = false; + senderMustBeAdmin = false; + } + + @Override + public void perform() { + final FactionsPlayerListener fpl = new FactionsPlayerListener(P.p); + + if (fpl.bannerLocations.containsKey(fme.getTag())) { + fme.msg(TL.COMMAND_TPBANNER_SUCCESS); + this.doWarmUp(WarmUpUtil.Warmup.BANNER, TL.WARMUPS_NOTIFY_TELEPORT, "Banner", new Runnable() { + @Override + public void run() { + me.teleport(fpl.bannerLocations.get(fme.getTag())); + } + }, this.p.getConfig().getLong("warmups.f-banner", 0)); + } else { + fme.msg(TL.COMMAND_TPBANNER_NOTSET); + } + + } + + @Override + public TL getUsageTranslation() { + return TL.COMMAND_TPBANNER_DESCRIPTION; + } +} diff --git a/src/main/java/com/massivecraft/factions/cmd/CmdUpgrades.java b/src/main/java/com/massivecraft/factions/cmd/CmdUpgrades.java new file mode 100644 index 00000000..f1a9813c --- /dev/null +++ b/src/main/java/com/massivecraft/factions/cmd/CmdUpgrades.java @@ -0,0 +1,41 @@ +package com.massivecraft.factions.cmd; + +import com.massivecraft.factions.P; +import com.massivecraft.factions.struct.Permission; +import com.massivecraft.factions.zcore.fupgrades.FUpgradesGUI; +import com.massivecraft.factions.zcore.util.TL; + +public class CmdUpgrades extends FCommand { + public CmdUpgrades() { + super(); + this.aliases.add("upgrades"); + this.aliases.add("upgrade"); + + //this.requiredArgs.add(""); + this.optionalArgs.put("mobs/crops/exp", ""); + + this.permission = Permission.UPGRADES.node; + this.disableOnLock = true; + + senderMustBePlayer = true; + senderMustBeMember = true; + senderMustBeModerator = false; + senderMustBeAdmin = false; + } + + @Override + public void perform() { + if (!P.p.getConfig().getBoolean("fupgrades.Enabled")) { + fme.sendMessage("This command is disabled!"); + return; + } + FUpgradesGUI fgui = new FUpgradesGUI(); + fgui.openMainMenu(fme); + } + + @Override + public TL getUsageTranslation() { + return TL.COMMAND_UPGRADES_DESCRIPTION; + } + +} diff --git a/src/main/java/com/massivecraft/factions/cmd/FPromoteCommand.java b/src/main/java/com/massivecraft/factions/cmd/FPromoteCommand.java index 777aff17..cc9eb38f 100644 --- a/src/main/java/com/massivecraft/factions/cmd/FPromoteCommand.java +++ b/src/main/java/com/massivecraft/factions/cmd/FPromoteCommand.java @@ -6,7 +6,6 @@ import com.massivecraft.factions.struct.Role; import com.massivecraft.factions.zcore.fperms.Access; import com.massivecraft.factions.zcore.fperms.PermissableAction; import com.massivecraft.factions.zcore.util.TL; -import org.bukkit.Bukkit; public class FPromoteCommand extends FCommand { @@ -28,30 +27,22 @@ public class FPromoteCommand extends FCommand { @Override public void perform() { - FPlayer target = this.argAsBestFPlayerMatch(0); if (target == null) { msg(TL.GENERIC_NOPLAYERFOUND, this.argAsString(0)); return; } - if (!target.getFaction().equals(myFaction)) { msg(TL.COMMAND_PROMOTE_WRONGFACTION, target.getName()); return; } - Access access = myFaction.getAccess(fme.getRole(), PermissableAction.PROMOTE); - if (fme.getRole() == Role.COLEADER && target.getRole() == Role.ADMIN){ - fme.msg(TL.COMMAND_PROMOTE_COLEADER_ADMIN); - return; - } - // Well this is messy. if (access == null || access == Access.UNDEFINED) { - if (!assertMinRole(Role.COLEADER)) { + if (!assertMinRole(Role.MODERATOR)) { return; } } else if (access == Access.DENY) { @@ -59,22 +50,21 @@ public class FPromoteCommand extends FCommand { return; } - - Role current = target.getRole(); Role promotion = Role.getRelative(current, +relative); + if (promotion == null) { fme.msg(TL.COMMAND_PROMOTE_NOTTHATPLAYER); return; } - - if (fme == target && fme.getRole() == Role.COLEADER){ - fme.msg(TL.COMMAND_PROMOTE_COLEADER_ADMIN); + // Don't allow people to promote people to their same or higher rnak. + if (fme.getRole().value <= promotion.value) { + fme.msg(TL.COMMAND_PROMOTE_NOT_ALLOWED); return; } - String action = relative > 0 ? TL.COMMAND_PROMOTE_PROMOTED.toString() : TL.COMMAND_PROMOTE_DEMOTED.toString(); + String action = relative > 0 ? TL.COMMAND_PROMOTE_PROMOTED.toString() : TL.COMMAND_PROMOTE_DEMOTED.toString(); // Success! target.setRole(promotion); @@ -90,4 +80,4 @@ public class FPromoteCommand extends FCommand { return TL.COMMAND_PROMOTE_DESCRIPTION; } -} +} \ No newline at end of file diff --git a/src/main/java/com/massivecraft/factions/event/FPlayerEnteredFactionEvent.java b/src/main/java/com/massivecraft/factions/event/FPlayerEnteredFactionEvent.java new file mode 100644 index 00000000..d22632c1 --- /dev/null +++ b/src/main/java/com/massivecraft/factions/event/FPlayerEnteredFactionEvent.java @@ -0,0 +1,42 @@ +package com.massivecraft.factions.event; + +import com.massivecraft.factions.FPlayer; +import com.massivecraft.factions.Faction; +import org.bukkit.event.HandlerList; + +public class FPlayerEnteredFactionEvent extends FactionPlayerEvent { + + private static final HandlerList handlers = new HandlerList(); + private FPlayer fPlayer; + private Faction factionTo; + private Faction factionFrom; + + public FPlayerEnteredFactionEvent(Faction factionTo, Faction factionFrom, FPlayer fPlayer) { + super(fPlayer.getFaction(), fPlayer); + this.factionFrom = factionFrom; + this.factionTo = factionTo; + this.fPlayer = fPlayer; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + public HandlerList getHandlers() { + return handlers; + } + + @Override + public FPlayer getfPlayer() { + return fPlayer; + } + + public Faction getFactionTo() { + return factionTo; + } + + public Faction getFactionFrom() { + return factionFrom; + } + +} diff --git a/src/main/java/com/massivecraft/factions/event/FPlayerStoppedFlying.java b/src/main/java/com/massivecraft/factions/event/FPlayerStoppedFlying.java new file mode 100644 index 00000000..f2f283c8 --- /dev/null +++ b/src/main/java/com/massivecraft/factions/event/FPlayerStoppedFlying.java @@ -0,0 +1,28 @@ +package com.massivecraft.factions.event; + +import com.massivecraft.factions.FPlayer; +import org.bukkit.event.HandlerList; + +public class FPlayerStoppedFlying extends FactionPlayerEvent { + private static final HandlerList handlers = new HandlerList(); + private FPlayer fPlayer; + + + public FPlayerStoppedFlying(FPlayer fPlayer) { + super(fPlayer.getFaction(), fPlayer); + this.fPlayer = fPlayer; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public FPlayer getfPlayer() { + return fPlayer; + } + + public HandlerList getHandlers() { + return handlers; + } +} diff --git a/src/main/java/com/massivecraft/factions/listeners/EssentialsListener.java b/src/main/java/com/massivecraft/factions/listeners/EssentialsListener.java deleted file mode 100644 index c7414f8b..00000000 --- a/src/main/java/com/massivecraft/factions/listeners/EssentialsListener.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.massivecraft.factions.listeners; - -import com.earth2me.essentials.IEssentials; -import com.earth2me.essentials.User; -import com.massivecraft.factions.Board; -import com.massivecraft.factions.FLocation; -import com.massivecraft.factions.Faction; -import com.massivecraft.factions.P; -import com.massivecraft.factions.event.FPlayerLeaveEvent; -import org.bukkit.Location; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; - -import java.util.List; -import java.util.UUID; -import java.util.logging.Level; - -public class EssentialsListener implements Listener { - - private final IEssentials ess; - - public EssentialsListener(IEssentials essentials) { - this.ess = essentials; - } - - @EventHandler - public void onLeave(FPlayerLeaveEvent event) throws Exception { - // Get the USER from their UUID. - Faction faction = event.getFaction(); - User user = ess.getUser(UUID.fromString(event.getfPlayer().getId())); - - List homes = user.getHomes(); - if (homes == null || homes.isEmpty()) { - return; - } - - // Not a great way to do this on essential's side. - for (String homeName : user.getHomes()) { - - // This can throw an exception for some reason. - Location loc = user.getHome(homeName); - FLocation floc = new FLocation(loc); - - // We're only going to remove homes in territory that belongs to THEIR faction. - if (Board.getInstance().getFactionAt(floc).equals(faction)) { - user.delHome(homeName); - P.p.log(Level.INFO, "FactionLeaveEvent: Removing home %s, player %s, in territory of %s", - homeName, event.getfPlayer().getName(), faction.getTag()); - } - } - } -} diff --git a/src/main/java/com/massivecraft/factions/util/Particle/ParticleEffect.java b/src/main/java/com/massivecraft/factions/util/Particle/ParticleEffect.java new file mode 100644 index 00000000..3b6d7a08 --- /dev/null +++ b/src/main/java/com/massivecraft/factions/util/Particle/ParticleEffect.java @@ -0,0 +1,1589 @@ +package com.massivecraft.factions.util.Particle; + + +import org.bukkit.Bukkit; +import org.bukkit.Color; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * ParticleEffect Library + *

+ * This library was created by @DarkBlade12 and allows you to display all Minecraft particle effects on a Bukkit server + *

+ * You are welcome to use it, modify it and redistribute it under the following conditions: + *

    + *
  • Don't claim this class as your own + *
  • Don't remove this disclaimer + *
+ *

+ * Special thanks: + *

    + *
  • @microgeek (original idea, names and packet parameters) + *
  • @ShadyPotato (1.8 names, ids and packet parameters) + *
  • @RingOfStorms (particle behavior) + *
  • @Cybermaxke (particle behavior) + *
  • @JamieSinn (hosting a jenkins server and documentation for particleeffect) + *
+ *

+ * It would be nice if you provide credit to me if you use this class in a published project + * + * @author DarkBlade12 + * @version 1.7 + */ +public enum ParticleEffect { + /** + * A particle effect which is displayed by exploding tnt and creepers: + *

    + *
  • It looks like a white cloud + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + EXPLOSION_NORMAL("explode", 0, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by exploding ghast fireballs and wither skulls: + *
    + *
  • It looks like a gray ball which is fading away + *
  • The speed value slightly influences the size of this particle effect + *
+ */ + EXPLOSION_LARGE("largeexplode", 1, -1), + /** + * A particle effect which is displayed by exploding tnt and creepers: + *
    + *
  • It looks like a crowd of gray balls which are fading away + *
  • The speed value has no influence on this particle effect + *
+ */ + EXPLOSION_HUGE("hugeexplosion", 2, -1), + /** + * A particle effect which is displayed by launching fireworks: + *
    + *
  • It looks like a white star which is sparkling + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + FIREWORKS_SPARK("fireworksSpark", 3, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by swimming entities and arrows in water: + *
    + *
  • It looks like a bubble + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + WATER_BUBBLE("bubble", 4, -1, ParticleProperty.DIRECTIONAL, ParticleProperty.REQUIRES_WATER), + /** + * A particle effect which is displayed by swimming entities and shaking wolves: + *
    + *
  • It looks like a blue drop + *
  • The speed value has no influence on this particle effect + *
+ */ + WATER_SPLASH("splash", 5, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed on water when fishing: + *
    + *
  • It looks like a blue droplet + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + WATER_WAKE("wake", 6, 7, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by water: + *
    + *
  • It looks like a tiny blue square + *
  • The speed value has no influence on this particle effect + *
+ */ + SUSPENDED("suspended", 7, -1, ParticleProperty.REQUIRES_WATER), + /** + * A particle effect which is displayed by air when close to bedrock and the in the void: + *
    + *
  • It looks like a tiny gray square + *
  • The speed value has no influence on this particle effect + *
+ */ + SUSPENDED_DEPTH("depthSuspend", 8, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed when landing a critical hit and by arrows: + *
    + *
  • It looks like a light brown cross + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + CRIT("crit", 9, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed when landing a hit with an enchanted weapon: + *
    + *
  • It looks like a cyan star + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + CRIT_MAGIC("magicCrit", 10, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by primed tnt, torches, droppers, dispensers, end portals, brewing stands and monster spawners: + *
    + *
  • It looks like a little gray cloud + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + SMOKE_NORMAL("smoke", 11, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by fire, minecarts with furnace and blazes: + *
    + *
  • It looks like a large gray cloud + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + SMOKE_LARGE("largesmoke", 12, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed when splash potions or bottles o' enchanting hit something: + *
    + *
  • It looks like a white swirl + *
  • The speed value causes the particle to only move upwards when set to 0 + *
  • Only the motion on the y-axis can be controlled, the motion on the x- and z-axis are multiplied by 0.1 when setting the values to 0 + *
+ */ + SPELL("spell", 13, -1), + /** + * A particle effect which is displayed when instant splash potions hit something: + *
    + *
  • It looks like a white cross + *
  • The speed value causes the particle to only move upwards when set to 0 + *
  • Only the motion on the y-axis can be controlled, the motion on the x- and z-axis are multiplied by 0.1 when setting the values to 0 + *
+ */ + SPELL_INSTANT("instantSpell", 14, -1), + /** + * A particle effect which is displayed by entities with active potion effects: + *
    + *
  • It looks like a colored swirl + *
  • The speed value causes the particle to be colored black when set to 0 + *
  • The particle color gets lighter when increasing the speed and darker when decreasing the speed + *
+ */ + SPELL_MOB("mobSpell", 15, -1, ParticleProperty.COLORABLE), + /** + * A particle effect which is displayed by entities with active potion effects applied through a beacon: + *
    + *
  • It looks like a transparent colored swirl + *
  • The speed value causes the particle to be always colored black when set to 0 + *
  • The particle color gets lighter when increasing the speed and darker when decreasing the speed + *
+ */ + SPELL_MOB_AMBIENT("mobSpellAmbient", 16, -1, ParticleProperty.COLORABLE), + /** + * A particle effect which is displayed by witches: + *
    + *
  • It looks like a purple cross + *
  • The speed value causes the particle to only move upwards when set to 0 + *
  • Only the motion on the y-axis can be controlled, the motion on the x- and z-axis are multiplied by 0.1 when setting the values to 0 + *
+ */ + SPELL_WITCH("witchMagic", 17, -1), + /** + * A particle effect which is displayed by blocks beneath a water source: + *
    + *
  • It looks like a blue drip + *
  • The speed value has no influence on this particle effect + *
+ */ + DRIP_WATER("dripWater", 18, -1), + /** + * A particle effect which is displayed by blocks beneath a lava source: + *
    + *
  • It looks like an orange drip + *
  • The speed value has no influence on this particle effect + *
+ */ + DRIP_LAVA("dripLava", 19, -1), + /** + * A particle effect which is displayed when attacking a villager in a village: + *
    + *
  • It looks like a cracked gray heart + *
  • The speed value has no influence on this particle effect + *
+ */ + VILLAGER_ANGRY("angryVillager", 20, -1), + /** + * A particle effect which is displayed when using bone meal and trading with a villager in a village: + *
    + *
  • It looks like a green star + *
  • The speed value has no influence on this particle effect + *
+ */ + VILLAGER_HAPPY("happyVillager", 21, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by mycelium: + *
    + *
  • It looks like a tiny gray square + *
  • The speed value has no influence on this particle effect + *
+ */ + TOWN_AURA("townaura", 22, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by note blocks: + *
    + *
  • It looks like a colored note + *
  • The speed value causes the particle to be colored green when set to 0 + *
+ */ + NOTE("note", 23, -1, ParticleProperty.COLORABLE), + /** + * A particle effect which is displayed by nether portals, endermen, ender pearls, eyes of ender, ender chests and dragon eggs: + *
    + *
  • It looks like a purple cloud + *
  • The speed value influences the spread of this particle effect + *
+ */ + PORTAL("portal", 24, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by enchantment tables which are nearby bookshelves: + *
    + *
  • It looks like a cryptic white letter + *
  • The speed value influences the spread of this particle effect + *
+ */ + ENCHANTMENT_TABLE("enchantmenttable", 25, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by torches, active furnaces, magma cubes and monster spawners: + *
    + *
  • It looks like a tiny flame + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + FLAME("flame", 26, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by lava: + *
    + *
  • It looks like a spark + *
  • The speed value has no influence on this particle effect + *
+ */ + LAVA("lava", 27, -1), + /** + * A particle effect which is currently unused: + *
    + *
  • It looks like a transparent gray square + *
  • The speed value has no influence on this particle effect + *
+ */ + FOOTSTEP("footstep", 28, -1), + /** + * A particle effect which is displayed when a mob dies: + *
    + *
  • It looks like a large white cloud + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + CLOUD("cloud", 29, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by redstone ore, powered redstone, redstone torches and redstone repeaters: + *
    + *
  • It looks like a tiny colored cloud + *
  • The speed value causes the particle to be colored red when set to 0 + *
+ */ + REDSTONE("reddust", 30, -1, ParticleProperty.COLORABLE), + /** + * A particle effect which is displayed when snowballs hit a block: + *
    + *
  • It looks like a little piece with the snowball texture + *
  • The speed value has no influence on this particle effect + *
+ */ + SNOWBALL("snowballpoof", 31, -1), + /** + * A particle effect which is currently unused: + *
    + *
  • It looks like a tiny white cloud + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + SNOW_SHOVEL("snowshovel", 32, -1, ParticleProperty.DIRECTIONAL), + /** + * A particle effect which is displayed by slimes: + *
    + *
  • It looks like a tiny part of the slimeball icon + *
  • The speed value has no influence on this particle effect + *
+ */ + SLIME("slime", 33, -1), + /** + * A particle effect which is displayed when breeding and taming animals: + *
    + *
  • It looks like a red heart + *
  • The speed value has no influence on this particle effect + *
+ */ + HEART("heart", 34, -1), + /** + * A particle effect which is displayed by barriers: + *
    + *
  • It looks like a red box with a slash through it + *
  • The speed value has no influence on this particle effect + *
+ */ + BARRIER("barrier", 35, 8), + /** + * A particle effect which is displayed when breaking a tool or eggs hit a block: + *
    + *
  • It looks like a little piece with an item texture + *
+ */ + ITEM_CRACK("iconcrack", 36, -1, ParticleProperty.DIRECTIONAL, ParticleProperty.REQUIRES_DATA), + /** + * A particle effect which is displayed when breaking blocks or sprinting: + *
    + *
  • It looks like a little piece with a block texture + *
  • The speed value has no influence on this particle effect + *
+ */ + BLOCK_CRACK("blockcrack", 37, -1, ParticleProperty.REQUIRES_DATA), + /** + * A particle effect which is displayed when falling: + *
    + *
  • It looks like a little piece with a block texture + *
+ */ + BLOCK_DUST("blockdust", 38, 7, ParticleProperty.DIRECTIONAL, ParticleProperty.REQUIRES_DATA), + /** + * A particle effect which is displayed when rain hits the ground: + *
    + *
  • It looks like a blue droplet + *
  • The speed value has no influence on this particle effect + *
+ */ + WATER_DROP("droplet", 39, 8), + /** + * A particle effect which is currently unused: + *
    + *
  • It has no visual effect + *
+ */ + ITEM_TAKE("take", 40, 8), + /** + * A particle effect which is displayed by elder guardians: + *
    + *
  • It looks like the shape of the elder guardian + *
  • The speed value has no influence on this particle effect + *
  • The offset values have no influence on this particle effect + *
+ */ + MOB_APPEARANCE("mobappearance", 41, 8); + + private static final Map NAME_MAP = new HashMap(); + private static final Map ID_MAP = new HashMap(); + + // Initialize map for quick name and id lookup + static { + for (ParticleEffect effect : values()) { + NAME_MAP.put(effect.name, effect); + ID_MAP.put(effect.id, effect); + } + } + + private final String name; + private final int id; + private final int requiredVersion; + private final List properties; + + /** + * Construct a new particle effect + * + * @param name Name of this particle effect + * @param id Id of this particle effect + * @param requiredVersion Version which is required (1.x) + * @param properties Properties of this particle effect + */ + private ParticleEffect(String name, int id, int requiredVersion, ParticleProperty... properties) { + this.name = name; + this.id = id; + this.requiredVersion = requiredVersion; + this.properties = Arrays.asList(properties); + } + + /** + * Returns the particle effect with the given name + * + * @param name Name of the particle effect + * @return The particle effect + */ + public static ParticleEffect fromName(String name) { + for (Entry entry : NAME_MAP.entrySet()) { + if (!entry.getKey().equalsIgnoreCase(name)) { + continue; + } + return entry.getValue(); + } + return null; + } + + /** + * Returns the particle effect with the given id + * + * @param id Id of the particle effect + * @return The particle effect + */ + public static ParticleEffect fromId(int id) { + for (Entry entry : ID_MAP.entrySet()) { + if (entry.getKey() != id) { + continue; + } + return entry.getValue(); + } + return null; + } + + /** + * Determine if water is at a certain location + * + * @param location Location to check + * @return Whether water is at this location or not + */ + private static boolean isWater(Location location) { + Material material = location.getBlock().getType(); + return material == Material.WATER || material == Material.STATIONARY_WATER; + } + + /** + * Determine if the distance between @param location and one of the players exceeds 256 + * + * @param location Location to check + * @return Whether the distance exceeds 256 or not + */ + private static boolean isLongDistance(Location location, List players) { + String world = location.getWorld().getName(); + for (Player player : players) { + Location playerLocation = player.getLocation(); + if (!world.equals(playerLocation.getWorld().getName()) || playerLocation.distanceSquared(location) < 65536) { + continue; + } + return true; + } + return false; + } + + /** + * Determine if the data type for a particle effect is correct + * + * @param effect Particle effect + * @param data Particle data + * @return Whether the data type is correct or not + */ + private static boolean isDataCorrect(ParticleEffect effect, ParticleData data) { + return ((effect == BLOCK_CRACK || effect == BLOCK_DUST) && data instanceof BlockData) || (effect == ITEM_CRACK && data instanceof ItemData); + } + + /** + * Determine if the color type for a particle effect is correct + * + * @param effect Particle effect + * @param color Particle color + * @return Whether the color type is correct or not + */ + private static boolean isColorCorrect(ParticleEffect effect, ParticleColor color) { + return ((effect == SPELL_MOB || effect == SPELL_MOB_AMBIENT || effect == REDSTONE) && color instanceof OrdinaryColor) || (effect == NOTE && color instanceof NoteColor); + } + + /** + * Returns the name of this particle effect + * + * @return The name + */ + public String getName() { + return name; + } + + /** + * Returns the id of this particle effect + * + * @return The id + */ + public int getId() { + return id; + } + + /** + * Returns the required version for this particle effect (1.x) + * + * @return The required version + */ + public int getRequiredVersion() { + return requiredVersion; + } + + /** + * Determine if this particle effect has a specific property + * + * @return Whether it has the property or not + */ + public boolean hasProperty(ParticleProperty property) { + return properties.contains(property); + } + + /** + * Determine if this particle effect is supported by your current server version + * + * @return Whether the particle effect is supported or not + */ + public boolean isSupported() { + if (requiredVersion == -1) { + return true; + } + return ParticlePacket.getVersion() >= requiredVersion; + } + + /** + * Displays a particle effect which is only visible for all players within a certain range in the world of @param center + * + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param center Center location of the effect + * @param range Range of the visibility + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect requires additional data + * @throws IllegalArgumentException If the particle effect requires water and none is at the center location + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, double) + */ + public void display(float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, double range) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (hasProperty(ParticleProperty.REQUIRES_DATA)) { + throw new ParticleDataException("This particle effect requires additional data"); + } + if (hasProperty(ParticleProperty.REQUIRES_WATER) && !isWater(center)) { + throw new IllegalArgumentException("There is no water at the center location"); + } + new ParticlePacket(this, offsetX, offsetY, offsetZ, speed, amount, range > 256, null).sendTo(center, range); + } + + /** + * Displays a particle effect which is only visible for the specified players + * + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect requires additional data + * @throws IllegalArgumentException If the particle effect requires water and none is at the center location + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, List) + */ + public void display(float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, List players) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (hasProperty(ParticleProperty.REQUIRES_DATA)) { + throw new ParticleDataException("This particle effect requires additional data"); + } + if (hasProperty(ParticleProperty.REQUIRES_WATER) && !isWater(center)) { + throw new IllegalArgumentException("There is no water at the center location"); + } + new ParticlePacket(this, offsetX, offsetY, offsetZ, speed, amount, isLongDistance(center, players), null).sendTo(center, players); + } + + /** + * Displays a particle effect which is only visible for the specified players + * + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect requires additional data + * @throws IllegalArgumentException If the particle effect requires water and none is at the center location + * @see #display(float, float, float, float, int, Location, List) + */ + public void display(float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, Player... players) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { + display(offsetX, offsetY, offsetZ, speed, amount, center, Arrays.asList(players)); + } + + /** + * Displays a single particle which flies into a determined direction and is only visible for all players within a certain range in the world of @param center + * + * @param direction Direction of the particle + * @param speed Display speed of the particle + * @param center Center location of the effect + * @param range Range of the visibility + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect requires additional data + * @throws IllegalArgumentException If the particle effect is not directional or if it requires water and none is at the center location + * @see ParticlePacket#ParticlePacket(ParticleEffect, Vector, float, boolean, ParticleData) + * @see ParticlePacket#sendTo(Location, double) + */ + public void display(Vector direction, float speed, Location center, double range) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (hasProperty(ParticleProperty.REQUIRES_DATA)) { + throw new ParticleDataException("This particle effect requires additional data"); + } + if (!hasProperty(ParticleProperty.DIRECTIONAL)) { + throw new IllegalArgumentException("This particle effect is not directional"); + } + if (hasProperty(ParticleProperty.REQUIRES_WATER) && !isWater(center)) { + throw new IllegalArgumentException("There is no water at the center location"); + } + new ParticlePacket(this, direction, speed, range > 256, null).sendTo(center, range); + } + + /** + * Displays a single particle which flies into a determined direction and is only visible for the specified players + * + * @param direction Direction of the particle + * @param speed Display speed of the particle + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect requires additional data + * @throws IllegalArgumentException If the particle effect is not directional or if it requires water and none is at the center location + * @see ParticlePacket#ParticlePacket(ParticleEffect, Vector, float, boolean, ParticleData) + * @see ParticlePacket#sendTo(Location, List) + */ + public void display(Vector direction, float speed, Location center, List players) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (hasProperty(ParticleProperty.REQUIRES_DATA)) { + throw new ParticleDataException("This particle effect requires additional data"); + } + if (!hasProperty(ParticleProperty.DIRECTIONAL)) { + throw new IllegalArgumentException("This particle effect is not directional"); + } + if (hasProperty(ParticleProperty.REQUIRES_WATER) && !isWater(center)) { + throw new IllegalArgumentException("There is no water at the center location"); + } + new ParticlePacket(this, direction, speed, isLongDistance(center, players), null).sendTo(center, players); + } + + /** + * Displays a single particle which flies into a determined direction and is only visible for the specified players + * + * @param direction Direction of the particle + * @param speed Display speed of the particle + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect requires additional data + * @throws IllegalArgumentException If the particle effect is not directional or if it requires water and none is at the center location + * @see #display(Vector, float, Location, List) + */ + public void display(Vector direction, float speed, Location center, Player... players) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { + display(direction, speed, center, Arrays.asList(players)); + } + + /** + * Displays a single particle which is colored and only visible for all players within a certain range in the world of @param center + * + * @param color Color of the particle + * @param center Center location of the effect + * @param range Range of the visibility + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleColorException If the particle effect is not colorable or the color type is incorrect + * @see ParticlePacket#ParticlePacket(ParticleEffect, ParticleColor, boolean) + * @see ParticlePacket#sendTo(Location, double) + */ + public void display(ParticleColor color, Location center, double range) throws ParticleVersionException, ParticleColorException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (!hasProperty(ParticleProperty.COLORABLE)) { + throw new ParticleColorException("This particle effect is not colorable"); + } + if (!isColorCorrect(this, color)) { + throw new ParticleColorException("The particle color type is incorrect"); + } + new ParticlePacket(this, color, range > 256).sendTo(center, range); + } + + /** + * Displays a single particle which is colored and only visible for the specified players + * + * @param color Color of the particle + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleColorException If the particle effect is not colorable or the color type is incorrect + * @see ParticlePacket#ParticlePacket(ParticleEffect, ParticleColor, boolean) + * @see ParticlePacket#sendTo(Location, List) + */ + public void display(ParticleColor color, Location center, List players) throws ParticleVersionException, ParticleColorException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (!hasProperty(ParticleProperty.COLORABLE)) { + throw new ParticleColorException("This particle effect is not colorable"); + } + if (!isColorCorrect(this, color)) { + throw new ParticleColorException("The particle color type is incorrect"); + } + new ParticlePacket(this, color, isLongDistance(center, players)).sendTo(center, players); + } + + /** + * Displays a single particle which is colored and only visible for the specified players + * + * @param color Color of the particle + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleColorException If the particle effect is not colorable or the color type is incorrect + * @see #display(ParticleColor, Location, List) + */ + public void display(ParticleColor color, Location center, Player... players) throws ParticleVersionException, ParticleColorException { + display(color, center, Arrays.asList(players)); + } + + /** + * Displays a particle effect which requires additional data and is only visible for all players within a certain range in the world of @param center + * + * @param data Data of the effect + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param center Center location of the effect + * @param range Range of the visibility + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, double) + */ + public void display(ParticleData data, float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, double range) throws ParticleVersionException, ParticleDataException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (!hasProperty(ParticleProperty.REQUIRES_DATA)) { + throw new ParticleDataException("This particle effect does not require additional data"); + } + if (!isDataCorrect(this, data)) { + throw new ParticleDataException("The particle data type is incorrect"); + } + new ParticlePacket(this, offsetX, offsetY, offsetZ, speed, amount, range > 256, data).sendTo(center, range); + } + + /** + * Displays a particle effect which requires additional data and is only visible for the specified players + * + * @param data Data of the effect + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, List) + */ + public void display(ParticleData data, float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, List players) throws ParticleVersionException, ParticleDataException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (!hasProperty(ParticleProperty.REQUIRES_DATA)) { + throw new ParticleDataException("This particle effect does not require additional data"); + } + if (!isDataCorrect(this, data)) { + throw new ParticleDataException("The particle data type is incorrect"); + } + new ParticlePacket(this, offsetX, offsetY, offsetZ, speed, amount, isLongDistance(center, players), data).sendTo(center, players); + } + + /** + * Displays a particle effect which requires additional data and is only visible for the specified players + * + * @param data Data of the effect + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect + * @see #display(ParticleData, float, float, float, float, int, Location, List) + */ + public void display(ParticleData data, float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, Player... players) throws ParticleVersionException, ParticleDataException { + display(data, offsetX, offsetY, offsetZ, speed, amount, center, Arrays.asList(players)); + } + + /** + * Displays a single particle which requires additional data that flies into a determined direction and is only visible for all players within a certain range in the world of @param center + * + * @param data Data of the effect + * @param direction Direction of the particle + * @param speed Display speed of the particles + * @param center Center location of the effect + * @param range Range of the visibility + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, double) + */ + public void display(ParticleData data, Vector direction, float speed, Location center, double range) throws ParticleVersionException, ParticleDataException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (!hasProperty(ParticleProperty.REQUIRES_DATA)) { + throw new ParticleDataException("This particle effect does not require additional data"); + } + if (!isDataCorrect(this, data)) { + throw new ParticleDataException("The particle data type is incorrect"); + } + new ParticlePacket(this, direction, speed, range > 256, data).sendTo(center, range); + } + + /** + * Displays a single particle which requires additional data that flies into a determined direction and is only visible for the specified players + * + * @param data Data of the effect + * @param direction Direction of the particle + * @param speed Display speed of the particles + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, List) + */ + public void display(ParticleData data, Vector direction, float speed, Location center, List players) throws ParticleVersionException, ParticleDataException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (!hasProperty(ParticleProperty.REQUIRES_DATA)) { + throw new ParticleDataException("This particle effect does not require additional data"); + } + if (!isDataCorrect(this, data)) { + throw new ParticleDataException("The particle data type is incorrect"); + } + new ParticlePacket(this, direction, speed, isLongDistance(center, players), data).sendTo(center, players); + } + + /** + * Displays a single particle which requires additional data that flies into a determined direction and is only visible for the specified players + * + * @param data Data of the effect + * @param direction Direction of the particle + * @param speed Display speed of the particles + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect + * @see #display(ParticleData, Vector, float, Location, List) + */ + public void display(ParticleData data, Vector direction, float speed, Location center, Player... players) throws ParticleVersionException, ParticleDataException { + display(data, direction, speed, center, Arrays.asList(players)); + } + + /** + * Represents the property of a particle effect + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.7 + */ + public static enum ParticleProperty { + /** + * The particle effect requires water to be displayed + */ + REQUIRES_WATER, + /** + * The particle effect requires block or item data to be displayed + */ + REQUIRES_DATA, + /** + * The particle effect uses the offsets as direction values + */ + DIRECTIONAL, + /** + * The particle effect uses the offsets as color values + */ + COLORABLE; + } + + /** + * Represents the particle data for effects like {@link ParticleEffect#ITEM_CRACK}, {@link ParticleEffect#BLOCK_CRACK} and {@link ParticleEffect#BLOCK_DUST} + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.6 + */ + public static abstract class ParticleData { + private final Material material; + private final byte data; + private final int[] packetData; + + /** + * Construct a new particle data + * + * @param material Material of the item/block + * @param data Data value of the item/block + */ + @SuppressWarnings("deprecation") + public ParticleData(Material material, byte data) { + this.material = material; + this.data = data; + this.packetData = new int[]{material.getId(), data}; + } + + /** + * Returns the material of this data + * + * @return The material + */ + public Material getMaterial() { + return material; + } + + /** + * Returns the data value of this data + * + * @return The data value + */ + public byte getData() { + return data; + } + + /** + * Returns the data as an int array for packet construction + * + * @return The data for the packet + */ + public int[] getPacketData() { + return packetData; + } + + /** + * Returns the data as a string for pre 1.8 versions + * + * @return The data string for the packet + */ + public String getPacketDataString() { + return "_" + packetData[0] + "_" + packetData[1]; + } + } + + /** + * Represents the item data for the {@link ParticleEffect#ITEM_CRACK} effect + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.6 + */ + public static final class ItemData extends ParticleData { + /** + * Construct a new item data + * + * @param material Material of the item + * @param data Data value of the item + * @see ParticleData#ParticleData(Material, byte) + */ + public ItemData(Material material, byte data) { + super(material, data); + } + } + + /** + * Represents the block data for the {@link ParticleEffect#BLOCK_CRACK} and {@link ParticleEffect#BLOCK_DUST} effects + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.6 + */ + public static final class BlockData extends ParticleData { + /** + * Construct a new block data + * + * @param material Material of the block + * @param data Data value of the block + * @throws IllegalArgumentException If the material is not a block + * @see ParticleData#ParticleData(Material, byte) + */ + public BlockData(Material material, byte data) throws IllegalArgumentException { + super(material, data); + if (!material.isBlock()) { + throw new IllegalArgumentException("The material is not a block"); + } + } + } + + /** + * Represents the color for effects like {@link ParticleEffect#SPELL_MOB}, {@link ParticleEffect#SPELL_MOB_AMBIENT}, {@link ParticleEffect#REDSTONE} and {@link ParticleEffect#NOTE} + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.7 + */ + public static abstract class ParticleColor { + /** + * Returns the value for the offsetX field + * + * @return The offsetX value + */ + public abstract float getValueX(); + + /** + * Returns the value for the offsetY field + * + * @return The offsetY value + */ + public abstract float getValueY(); + + /** + * Returns the value for the offsetZ field + * + * @return The offsetZ value + */ + public abstract float getValueZ(); + } + + /** + * Represents the color for effects like {@link ParticleEffect#SPELL_MOB}, {@link ParticleEffect#SPELL_MOB_AMBIENT} and {@link ParticleEffect#NOTE} + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.7 + */ + public static final class OrdinaryColor extends ParticleColor { + private final int red; + private final int green; + private final int blue; + + /** + * Construct a new ordinary color + * + * @param red Red value of the RGB format + * @param green Green value of the RGB format + * @param blue Blue value of the RGB format + * @throws IllegalArgumentException If one of the values is lower than 0 or higher than 255 + */ + public OrdinaryColor(int red, int green, int blue) throws IllegalArgumentException { + if (red < 0) { + throw new IllegalArgumentException("The red value is lower than 0"); + } + if (red > 255) { + throw new IllegalArgumentException("The red value is higher than 255"); + } + this.red = red; + if (green < 0) { + throw new IllegalArgumentException("The green value is lower than 0"); + } + if (green > 255) { + throw new IllegalArgumentException("The green value is higher than 255"); + } + this.green = green; + if (blue < 0) { + throw new IllegalArgumentException("The blue value is lower than 0"); + } + if (blue > 255) { + throw new IllegalArgumentException("The blue value is higher than 255"); + } + this.blue = blue; + } + + /** + * Construct a new ordinary color + * + * @param color Bukkit color + */ + public OrdinaryColor(Color color) { + this(color.getRed(), color.getGreen(), color.getBlue()); + } + + /** + * Returns the red value of the RGB format + * + * @return The red value + */ + public int getRed() { + return red; + } + + /** + * Returns the green value of the RGB format + * + * @return The green value + */ + public int getGreen() { + return green; + } + + /** + * Returns the blue value of the RGB format + * + * @return The blue value + */ + public int getBlue() { + return blue; + } + + /** + * Returns the red value divided by 255 + * + * @return The offsetX value + */ + @Override + public float getValueX() { + return (float) red / 255F; + } + + /** + * Returns the green value divided by 255 + * + * @return The offsetY value + */ + @Override + public float getValueY() { + return (float) green / 255F; + } + + /** + * Returns the blue value divided by 255 + * + * @return The offsetZ value + */ + @Override + public float getValueZ() { + return (float) blue / 255F; + } + } + + /** + * Represents the color for the {@link ParticleEffect#NOTE} effect + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.7 + */ + public static final class NoteColor extends ParticleColor { + private final int note; + + /** + * Construct a new note color + * + * @param note Note id which determines color + * @throws IllegalArgumentException If the note value is lower than 0 or higher than 24 + */ + public NoteColor(int note) throws IllegalArgumentException { + if (note < 0) { + throw new IllegalArgumentException("The note value is lower than 0"); + } + if (note > 24) { + throw new IllegalArgumentException("The note value is higher than 24"); + } + this.note = note; + } + + /** + * Returns the note value divided by 24 + * + * @return The offsetX value + */ + @Override + public float getValueX() { + return (float) note / 24F; + } + + /** + * Returns zero because the offsetY value is unused + * + * @return zero + */ + @Override + public float getValueY() { + return 0; + } + + /** + * Returns zero because the offsetZ value is unused + * + * @return zero + */ + @Override + public float getValueZ() { + return 0; + } + + } + + /** + * Represents a runtime exception that is thrown either if the displayed particle effect requires data and has none or vice-versa or if the data type is incorrect + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.6 + */ + private static final class ParticleDataException extends RuntimeException { + private static final long serialVersionUID = 3203085387160737484L; + + /** + * Construct a new particle data exception + * + * @param message Message that will be logged + */ + public ParticleDataException(String message) { + super(message); + } + } + + /** + * Represents a runtime exception that is thrown either if the displayed particle effect is not colorable or if the particle color type is incorrect + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.7 + */ + private static final class ParticleColorException extends RuntimeException { + private static final long serialVersionUID = 3203085387160737484L; + + /** + * Construct a new particle color exception + * + * @param message Message that will be logged + */ + public ParticleColorException(String message) { + super(message); + } + } + + /** + * Represents a runtime exception that is thrown if the displayed particle effect requires a newer version + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.6 + */ + private static final class ParticleVersionException extends RuntimeException { + private static final long serialVersionUID = 3203085387160737484L; + + /** + * Construct a new particle version exception + * + * @param message Message that will be logged + */ + public ParticleVersionException(String message) { + super(message); + } + } + + /** + * Represents a particle effect packet with all attributes which is used for sending packets to the players + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.5 + */ + public static final class ParticlePacket { + private static int version; + private static Class enumParticle; + private static Constructor packetConstructor; + private static Method getHandle; + private static Field playerConnection; + private static Method sendPacket; + private static boolean initialized; + private final ParticleEffect effect; + private final float offsetY; + private final float offsetZ; + private final float speed; + private final int amount; + private final boolean longDistance; + private final ParticleData data; + private float offsetX; + private Object packet; + + /** + * Construct a new particle packet + * + * @param effect Particle effect + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param longDistance Indicates whether the maximum distance is increased from 256 to 65536 + * @param data Data of the effect + * @throws IllegalArgumentException If the speed or amount is lower than 0 + * @see #initialize() + */ + public ParticlePacket(ParticleEffect effect, float offsetX, float offsetY, float offsetZ, float speed, int amount, boolean longDistance, ParticleData data) throws IllegalArgumentException { + initialize(); + if (speed < 0) { + throw new IllegalArgumentException("The speed is lower than 0"); + } + if (amount < 0) { + throw new IllegalArgumentException("The amount is lower than 0"); + } + this.effect = effect; + this.offsetX = offsetX; + this.offsetY = offsetY; + this.offsetZ = offsetZ; + this.speed = speed; + this.amount = amount; + this.longDistance = longDistance; + this.data = data; + } + + + public ParticlePacket(ParticleEffect effect, Vector direction, float speed, boolean longDistance, ParticleData data) throws IllegalArgumentException { + this(effect, (float) direction.getX(), (float) direction.getY(), (float) direction.getZ(), speed, 0, longDistance, data); + } + + + public ParticlePacket(ParticleEffect effect, ParticleColor color, boolean longDistance) { + this(effect, color.getValueX(), color.getValueY(), color.getValueZ(), 1, 0, longDistance, null); + if (effect == ParticleEffect.REDSTONE && color instanceof OrdinaryColor && ((OrdinaryColor) color).getRed() == 0) { + offsetX = Float.MIN_NORMAL; + } + } + + /** + * Initializes {@link #packetConstructor}, {@link #getHandle}, {@link #playerConnection} and {@link #sendPacket} and sets {@link #initialized} to true if it succeeds + *

+ * Note: These fields only have to be initialized once, so it will return if {@link #initialized} is already set to true + * + * @throws VersionIncompatibleException if your bukkit version is not supported by this library + */ + public static void initialize() throws VersionIncompatibleException { + if (initialized) { + return; + } + try { + version = Integer.parseInt(Character.toString(ReflectionUtils.PackageType.getServerVersion().charAt(3))); + if (version > 7) { + enumParticle = ReflectionUtils.PackageType.MINECRAFT_SERVER.getClass("EnumParticle"); + } + Class packetClass = ReflectionUtils.PackageType.MINECRAFT_SERVER.getClass(version < 7 ? "Packet63WorldParticles" : "PacketPlayOutWorldParticles"); + packetConstructor = ReflectionUtils.getConstructor(packetClass); + getHandle = ReflectionUtils.getMethod("CraftPlayer", ReflectionUtils.PackageType.CRAFTBUKKIT_ENTITY, "getHandle"); + playerConnection = ReflectionUtils.getField("EntityPlayer", ReflectionUtils.PackageType.MINECRAFT_SERVER, false, "playerConnection"); + sendPacket = ReflectionUtils.getMethod(playerConnection.getType(), "sendPacket", ReflectionUtils.PackageType.MINECRAFT_SERVER.getClass("Packet")); + } catch (Exception exception) { + throw new VersionIncompatibleException("Your current bukkit version seems to be incompatible with this library", exception); + } + initialized = true; + } + + /** + * Returns the version of your server (1.x) + * + * @return The version number + */ + public static int getVersion() { + if (!initialized) { + initialize(); + } + return version; + } + + /** + * Determine if {@link #packetConstructor}, {@link #getHandle}, {@link #playerConnection} and {@link #sendPacket} are initialized + * + * @return Whether these fields are initialized or not + * @see #initialize() + */ + public static boolean isInitialized() { + return initialized; + } + + /** + * Initializes {@link #packet} with all set values + * + * @param center Center location of the effect + * @throws PacketInstantiationException If instantion fails due to an unknown error + */ + private void initializePacket(Location center) throws PacketInstantiationException { + if (packet != null) { + return; + } + try { + packet = packetConstructor.newInstance(); + if (version < 8) { + String name = effect.getName(); + if (data != null) { + name += data.getPacketDataString(); + } + ReflectionUtils.setValue(packet, true, "a", name); + } else { + ReflectionUtils.setValue(packet, true, "a", enumParticle.getEnumConstants()[effect.getId()]); + ReflectionUtils.setValue(packet, true, "j", longDistance); + if (data != null) { + int[] packetData = data.getPacketData(); + ReflectionUtils.setValue(packet, true, "k", effect == ParticleEffect.ITEM_CRACK ? packetData : new int[]{packetData[0] | (packetData[1] << 12)}); + } + } + ReflectionUtils.setValue(packet, true, "b", (float) center.getX()); + ReflectionUtils.setValue(packet, true, "c", (float) center.getY()); + ReflectionUtils.setValue(packet, true, "d", (float) center.getZ()); + ReflectionUtils.setValue(packet, true, "e", offsetX); + ReflectionUtils.setValue(packet, true, "f", offsetY); + ReflectionUtils.setValue(packet, true, "g", offsetZ); + ReflectionUtils.setValue(packet, true, "h", speed); + ReflectionUtils.setValue(packet, true, "i", amount); + } catch (Exception exception) { + throw new PacketInstantiationException("Packet instantiation failed", exception); + } + } + + /** + * Sends the packet to a single player and caches it + * + * @param center Center location of the effect + * @param player Receiver of the packet + * @throws PacketInstantiationException If instantion fails due to an unknown error + * @throws PacketSendingException If sending fails due to an unknown error + * @see #initializePacket(Location) + */ + public void sendTo(Location center, Player player) throws PacketInstantiationException, PacketSendingException { + initializePacket(center); + try { + sendPacket.invoke(playerConnection.get(getHandle.invoke(player)), packet); + } catch (Exception exception) { + throw new PacketSendingException("Failed to send the packet to player '" + player.getName() + "'", exception); + } + } + + /** + * Sends the packet to all players in the list + * + * @param center Center location of the effect + * @param players Receivers of the packet + * @throws IllegalArgumentException If the player list is empty + * @see #sendTo(Location center, Player player) + */ + public void sendTo(Location center, List players) throws IllegalArgumentException { + if (players.isEmpty()) { + throw new IllegalArgumentException("The player list is empty"); + } + for (Player player : players) { + sendTo(center, player); + } + } + + /** + * Sends the packet to all players in a certain range + * + * @param center Center location of the effect + * @param range Range in which players will receive the packet (Maximum range for particles is usually 16, but it can differ for some types) + * @throws IllegalArgumentException If the range is lower than 1 + * @see #sendTo(Location center, Player player) + */ + public void sendTo(Location center, double range) throws IllegalArgumentException { + if (range < 1) { + throw new IllegalArgumentException("The range is lower than 1"); + } + String worldName = center.getWorld().getName(); + double squared = range * range; + for (Player player : Bukkit.getOnlinePlayers()) { + if (!player.getWorld().getName().equals(worldName) || player.getLocation().distanceSquared(center) > squared) { + continue; + } + sendTo(center, player); + } + } + + /** + * Represents a runtime exception that is thrown if a bukkit version is not compatible with this library + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.5 + */ + private static final class VersionIncompatibleException extends RuntimeException { + private static final long serialVersionUID = 3203085387160737484L; + + /** + * Construct a new version incompatible exception + * + * @param message Message that will be logged + * @param cause Cause of the exception + */ + public VersionIncompatibleException(String message, Throwable cause) { + super(message, cause); + } + } + + /** + * Represents a runtime exception that is thrown if packet instantiation fails + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.4 + */ + private static final class PacketInstantiationException extends RuntimeException { + private static final long serialVersionUID = 3203085387160737484L; + + /** + * Construct a new packet instantiation exception + * + * @param message Message that will be logged + * @param cause Cause of the exception + */ + public PacketInstantiationException(String message, Throwable cause) { + super(message, cause); + } + } + + /** + * Represents a runtime exception that is thrown if packet sending fails + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.4 + */ + private static final class PacketSendingException extends RuntimeException { + private static final long serialVersionUID = 3203085387160737484L; + + /** + * Construct a new packet sending exception + * + * @param message Message that will be logged + * @param cause Cause of the exception + */ + public PacketSendingException(String message, Throwable cause) { + super(message, cause); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/massivecraft/factions/util/Particle/ReflectionUtils.java b/src/main/java/com/massivecraft/factions/util/Particle/ReflectionUtils.java new file mode 100644 index 00000000..31dc3b7b --- /dev/null +++ b/src/main/java/com/massivecraft/factions/util/Particle/ReflectionUtils.java @@ -0,0 +1,605 @@ +package com.massivecraft.factions.util.Particle; + + +import org.bukkit.Bukkit; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +/** + * com.prosavage.savagecore.particle.ReflectionUtils + *

+ * This class provides useful methods which makes dealing with reflection much easier, especially when working with Bukkit + *

+ * You are welcome to use it, modify it and redistribute it under the following conditions: + *

    + *
  • Don't claim this class as your own + *
  • Don't remove this disclaimer + *
+ *

+ * It would be nice if you provide credit to me if you use this class in a published project + * + * @author Araos + * @version 1.1 + */ +public final class ReflectionUtils { + // Prevent accidental construction + private ReflectionUtils() { + } + + /** + * Returns the constructor of a given class with the given parameter types + * + * @param clazz Target class + * @param parameterTypes Parameter types of the desired constructor + * @return The constructor of the target class with the specified parameter types + * @throws NoSuchMethodException If the desired constructor with the specified parameter types cannot be found + * @see DataType + * @see DataType#getPrimitive(Class[]) + * @see DataType#compare(Class[], Class[]) + */ + public static Constructor getConstructor(Class clazz, Class... parameterTypes) throws NoSuchMethodException { + Class[] primitiveTypes = DataType.getPrimitive(parameterTypes); + for (Constructor constructor : clazz.getConstructors()) { + if (!DataType.compare(DataType.getPrimitive(constructor.getParameterTypes()), primitiveTypes)) { + continue; + } + return constructor; + } + throw new NoSuchMethodException("There is no such constructor in this class with the specified parameter types"); + } + + /** + * Returns the constructor of a desired class with the given parameter types + * + * @param className Name of the desired target class + * @param packageType Package where the desired target class is located + * @param parameterTypes Parameter types of the desired constructor + * @return The constructor of the desired target class with the specified parameter types + * @throws NoSuchMethodException If the desired constructor with the specified parameter types cannot be found + * @throws ClassNotFoundException ClassNotFoundException If the desired target class with the specified name and package cannot be found + * @see #getConstructor(Class, Class...) + */ + public static Constructor getConstructor(String className, PackageType packageType, Class... parameterTypes) throws NoSuchMethodException, ClassNotFoundException { + return getConstructor(packageType.getClass(className), parameterTypes); + } + + /** + * Returns an instance of a class with the given arguments + * + * @param clazz Target class + * @param arguments Arguments which are used to construct an object of the target class + * @return The instance of the target class with the specified arguments + * @throws InstantiationException If you cannot create an instance of the target class due to certain circumstances + * @throws IllegalAccessException If the desired constructor cannot be accessed due to certain circumstances + * @throws IllegalArgumentException If the types of the arguments do not match the parameter types of the constructor (this should not occur since it searches for a constructor with the types of the arguments) + * @throws InvocationTargetException If the desired constructor cannot be invoked + * @throws NoSuchMethodException If the desired constructor with the specified arguments cannot be found + */ + public static Object instantiateObject(Class clazz, Object... arguments) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException { + return getConstructor(clazz, DataType.getPrimitive(arguments)).newInstance(arguments); + } + + /** + * Returns an instance of a desired class with the given arguments + * + * @param className Name of the desired target class + * @param packageType Package where the desired target class is located + * @param arguments Arguments which are used to construct an object of the desired target class + * @return The instance of the desired target class with the specified arguments + * @throws InstantiationException If you cannot create an instance of the desired target class due to certain circumstances + * @throws IllegalAccessException If the desired constructor cannot be accessed due to certain circumstances + * @throws IllegalArgumentException If the types of the arguments do not match the parameter types of the constructor (this should not occur since it searches for a constructor with the types of the arguments) + * @throws InvocationTargetException If the desired constructor cannot be invoked + * @throws NoSuchMethodException If the desired constructor with the specified arguments cannot be found + * @throws ClassNotFoundException If the desired target class with the specified name and package cannot be found + * @see #instantiateObject(Class, Object...) + */ + public static Object instantiateObject(String className, PackageType packageType, Object... arguments) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException { + return instantiateObject(packageType.getClass(className), arguments); + } + + /** + * Returns a method of a class with the given parameter types + * + * @param clazz Target class + * @param methodName Name of the desired method + * @param parameterTypes Parameter types of the desired method + * @return The method of the target class with the specified name and parameter types + * @throws NoSuchMethodException If the desired method of the target class with the specified name and parameter types cannot be found + * @see DataType#getPrimitive(Class[]) + * @see DataType#compare(Class[], Class[]) + */ + public static Method getMethod(Class clazz, String methodName, Class... parameterTypes) throws NoSuchMethodException { + Class[] primitiveTypes = DataType.getPrimitive(parameterTypes); + for (Method method : clazz.getMethods()) { + if (!method.getName().equals(methodName) || !DataType.compare(DataType.getPrimitive(method.getParameterTypes()), primitiveTypes)) { + continue; + } + return method; + } + throw new NoSuchMethodException("There is no such method in this class with the specified name and parameter types"); + } + + /** + * Returns a method of a desired class with the given parameter types + * + * @param className Name of the desired target class + * @param packageType Package where the desired target class is located + * @param methodName Name of the desired method + * @param parameterTypes Parameter types of the desired method + * @return The method of the desired target class with the specified name and parameter types + * @throws NoSuchMethodException If the desired method of the desired target class with the specified name and parameter types cannot be found + * @throws ClassNotFoundException If the desired target class with the specified name and package cannot be found + * @see #getMethod(Class, String, Class...) + */ + public static Method getMethod(String className, PackageType packageType, String methodName, Class... parameterTypes) throws NoSuchMethodException, ClassNotFoundException { + return getMethod(packageType.getClass(className), methodName, parameterTypes); + } + + /** + * Invokes a method on an object with the given arguments + * + * @param instance Target object + * @param methodName Name of the desired method + * @param arguments Arguments which are used to invoke the desired method + * @return The result of invoking the desired method on the target object + * @throws IllegalAccessException If the desired method cannot be accessed due to certain circumstances + * @throws IllegalArgumentException If the types of the arguments do not match the parameter types of the method (this should not occur since it searches for a method with the types of the arguments) + * @throws InvocationTargetException If the desired method cannot be invoked on the target object + * @throws NoSuchMethodException If the desired method of the class of the target object with the specified name and arguments cannot be found + * @see #getMethod(Class, String, Class...) + * @see DataType#getPrimitive(Object[]) + */ + public static Object invokeMethod(Object instance, String methodName, Object... arguments) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException { + return getMethod(instance.getClass(), methodName, DataType.getPrimitive(arguments)).invoke(instance, arguments); + } + + /** + * Invokes a method of the target class on an object with the given arguments + * + * @param instance Target object + * @param clazz Target class + * @param methodName Name of the desired method + * @param arguments Arguments which are used to invoke the desired method + * @return The result of invoking the desired method on the target object + * @throws IllegalAccessException If the desired method cannot be accessed due to certain circumstances + * @throws IllegalArgumentException If the types of the arguments do not match the parameter types of the method (this should not occur since it searches for a method with the types of the arguments) + * @throws InvocationTargetException If the desired method cannot be invoked on the target object + * @throws NoSuchMethodException If the desired method of the target class with the specified name and arguments cannot be found + * @see #getMethod(Class, String, Class...) + * @see DataType#getPrimitive(Object[]) + */ + public static Object invokeMethod(Object instance, Class clazz, String methodName, Object... arguments) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException { + return getMethod(clazz, methodName, DataType.getPrimitive(arguments)).invoke(instance, arguments); + } + + /** + * Invokes a method of a desired class on an object with the given arguments + * + * @param instance Target object + * @param className Name of the desired target class + * @param packageType Package where the desired target class is located + * @param methodName Name of the desired method + * @param arguments Arguments which are used to invoke the desired method + * @return The result of invoking the desired method on the target object + * @throws IllegalAccessException If the desired method cannot be accessed due to certain circumstances + * @throws IllegalArgumentException If the types of the arguments do not match the parameter types of the method (this should not occur since it searches for a method with the types of the arguments) + * @throws InvocationTargetException If the desired method cannot be invoked on the target object + * @throws NoSuchMethodException If the desired method of the desired target class with the specified name and arguments cannot be found + * @throws ClassNotFoundException If the desired target class with the specified name and package cannot be found + * @see #invokeMethod(Object, Class, String, Object...) + */ + public static Object invokeMethod(Object instance, String className, PackageType packageType, String methodName, Object... arguments) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException { + return invokeMethod(instance, packageType.getClass(className), methodName, arguments); + } + + /** + * Returns a field of the target class with the given name + * + * @param clazz Target class + * @param declared Whether the desired field is declared or not + * @param fieldName Name of the desired field + * @return The field of the target class with the specified name + * @throws NoSuchFieldException If the desired field of the given class cannot be found + * @throws SecurityException If the desired field cannot be made accessible + */ + public static Field getField(Class clazz, boolean declared, String fieldName) throws NoSuchFieldException, SecurityException { + Field field = declared ? clazz.getDeclaredField(fieldName) : clazz.getField(fieldName); + field.setAccessible(true); + return field; + } + + /** + * Returns a field of a desired class with the given name + * + * @param className Name of the desired target class + * @param packageType Package where the desired target class is located + * @param declared Whether the desired field is declared or not + * @param fieldName Name of the desired field + * @return The field of the desired target class with the specified name + * @throws NoSuchFieldException If the desired field of the desired class cannot be found + * @throws SecurityException If the desired field cannot be made accessible + * @throws ClassNotFoundException If the desired target class with the specified name and package cannot be found + * @see #getField(Class, boolean, String) + */ + public static Field getField(String className, PackageType packageType, boolean declared, String fieldName) throws NoSuchFieldException, SecurityException, ClassNotFoundException { + return getField(packageType.getClass(className), declared, fieldName); + } + + /** + * Returns the value of a field of the given class of an object + * + * @param instance Target object + * @param clazz Target class + * @param declared Whether the desired field is declared or not + * @param fieldName Name of the desired field + * @return The value of field of the target object + * @throws IllegalArgumentException If the target object does not feature the desired field + * @throws IllegalAccessException If the desired field cannot be accessed + * @throws NoSuchFieldException If the desired field of the target class cannot be found + * @throws SecurityException If the desired field cannot be made accessible + * @see #getField(Class, boolean, String) + */ + public static Object getValue(Object instance, Class clazz, boolean declared, String fieldName) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { + return getField(clazz, declared, fieldName).get(instance); + } + + /** + * Returns the value of a field of a desired class of an object + * + * @param instance Target object + * @param className Name of the desired target class + * @param packageType Package where the desired target class is located + * @param declared Whether the desired field is declared or not + * @param fieldName Name of the desired field + * @return The value of field of the target object + * @throws IllegalArgumentException If the target object does not feature the desired field + * @throws IllegalAccessException If the desired field cannot be accessed + * @throws NoSuchFieldException If the desired field of the desired class cannot be found + * @throws SecurityException If the desired field cannot be made accessible + * @throws ClassNotFoundException If the desired target class with the specified name and package cannot be found + * @see #getValue(Object, Class, boolean, String) + */ + public static Object getValue(Object instance, String className, PackageType packageType, boolean declared, String fieldName) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException, ClassNotFoundException { + return getValue(instance, packageType.getClass(className), declared, fieldName); + } + + /** + * Returns the value of a field with the given name of an object + * + * @param instance Target object + * @param declared Whether the desired field is declared or not + * @param fieldName Name of the desired field + * @return The value of field of the target object + * @throws IllegalArgumentException If the target object does not feature the desired field (should not occur since it searches for a field with the given name in the class of the object) + * @throws IllegalAccessException If the desired field cannot be accessed + * @throws NoSuchFieldException If the desired field of the target object cannot be found + * @throws SecurityException If the desired field cannot be made accessible + * @see #getValue(Object, Class, boolean, String) + */ + public static Object getValue(Object instance, boolean declared, String fieldName) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { + return getValue(instance, instance.getClass(), declared, fieldName); + } + + /** + * Sets the value of a field of the given class of an object + * + * @param instance Target object + * @param clazz Target class + * @param declared Whether the desired field is declared or not + * @param fieldName Name of the desired field + * @param value New value + * @throws IllegalArgumentException If the type of the value does not match the type of the desired field + * @throws IllegalAccessException If the desired field cannot be accessed + * @throws NoSuchFieldException If the desired field of the target class cannot be found + * @throws SecurityException If the desired field cannot be made accessible + * @see #getField(Class, boolean, String) + */ + public static void setValue(Object instance, Class clazz, boolean declared, String fieldName, Object value) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { + getField(clazz, declared, fieldName).set(instance, value); + } + + /** + * Sets the value of a field of a desired class of an object + * + * @param instance Target object + * @param className Name of the desired target class + * @param packageType Package where the desired target class is located + * @param declared Whether the desired field is declared or not + * @param fieldName Name of the desired field + * @param value New value + * @throws IllegalArgumentException If the type of the value does not match the type of the desired field + * @throws IllegalAccessException If the desired field cannot be accessed + * @throws NoSuchFieldException If the desired field of the desired class cannot be found + * @throws SecurityException If the desired field cannot be made accessible + * @throws ClassNotFoundException If the desired target class with the specified name and package cannot be found + * @see #setValue(Object, Class, boolean, String, Object) + */ + public static void setValue(Object instance, String className, PackageType packageType, boolean declared, String fieldName, Object value) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException, ClassNotFoundException { + setValue(instance, packageType.getClass(className), declared, fieldName, value); + } + + /** + * Sets the value of a field with the given name of an object + * + * @param instance Target object + * @param declared Whether the desired field is declared or not + * @param fieldName Name of the desired field + * @param value New value + * @throws IllegalArgumentException If the type of the value does not match the type of the desired field + * @throws IllegalAccessException If the desired field cannot be accessed + * @throws NoSuchFieldException If the desired field of the target object cannot be found + * @throws SecurityException If the desired field cannot be made accessible + * @see #setValue(Object, Class, boolean, String, Object) + */ + public static void setValue(Object instance, boolean declared, String fieldName, Object value) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { + setValue(instance, instance.getClass(), declared, fieldName, value); + } + + /** + * Represents an enumeration of dynamic packages of NMS and CraftBukkit + *

+ * This class is part of the com.prosavage.savagecore.particle.ReflectionUtils and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.0 + */ + public enum PackageType { + MINECRAFT_SERVER("net.minecraft.server." + getServerVersion()), + CRAFTBUKKIT("org.bukkit.craftbukkit." + getServerVersion()), + CRAFTBUKKIT_BLOCK(CRAFTBUKKIT, "block"), + CRAFTBUKKIT_CHUNKIO(CRAFTBUKKIT, "chunkio"), + CRAFTBUKKIT_COMMAND(CRAFTBUKKIT, "command"), + CRAFTBUKKIT_CONVERSATIONS(CRAFTBUKKIT, "conversations"), + CRAFTBUKKIT_ENCHANTMENS(CRAFTBUKKIT, "enchantments"), + CRAFTBUKKIT_ENTITY(CRAFTBUKKIT, "entity"), + CRAFTBUKKIT_EVENT(CRAFTBUKKIT, "event"), + CRAFTBUKKIT_GENERATOR(CRAFTBUKKIT, "generator"), + CRAFTBUKKIT_HELP(CRAFTBUKKIT, "help"), + CRAFTBUKKIT_INVENTORY(CRAFTBUKKIT, "inventory"), + CRAFTBUKKIT_MAP(CRAFTBUKKIT, "map"), + CRAFTBUKKIT_METADATA(CRAFTBUKKIT, "metadata"), + CRAFTBUKKIT_POTION(CRAFTBUKKIT, "potion"), + CRAFTBUKKIT_PROJECTILES(CRAFTBUKKIT, "projectiles"), + CRAFTBUKKIT_SCHEDULER(CRAFTBUKKIT, "scheduler"), + CRAFTBUKKIT_SCOREBOARD(CRAFTBUKKIT, "scoreboard"), + CRAFTBUKKIT_UPDATER(CRAFTBUKKIT, "updater"), + CRAFTBUKKIT_UTIL(CRAFTBUKKIT, "util"); + + private final String path; + + /** + * Construct a new package type + * + * @param path Path of the package + */ + private PackageType(String path) { + this.path = path; + } + + /** + * Construct a new package type + * + * @param parent Parent package of the package + * @param path Path of the package + */ + private PackageType(PackageType parent, String path) { + this(parent + "." + path); + } + + /** + * Returns the version of your server + * + * @return The server version + */ + public static String getServerVersion() { + return Bukkit.getServer().getClass().getPackage().getName().substring(23); + } + + /** + * Returns the path of this package type + * + * @return The path + */ + public String getPath() { + return path; + } + + /** + * Returns the class with the given name + * + * @param className Name of the desired class + * @return The class with the specified name + * @throws ClassNotFoundException If the desired class with the specified name and package cannot be found + */ + public Class getClass(String className) throws ClassNotFoundException { + return Class.forName(this + "." + className); + } + + // Override for convenience + @Override + public String toString() { + return path; + } + } + + /** + * Represents an enumeration of Java data types with corresponding classes + *

+ * This class is part of the com.prosavage.savagecore.particle.ReflectionUtils and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.0 + */ + public enum DataType { + BYTE(byte.class, Byte.class), + SHORT(short.class, Short.class), + INTEGER(int.class, Integer.class), + LONG(long.class, Long.class), + CHARACTER(char.class, Character.class), + FLOAT(float.class, Float.class), + DOUBLE(double.class, Double.class), + BOOLEAN(boolean.class, Boolean.class); + + private static final Map, DataType> CLASS_MAP = new HashMap, DataType>(); + + // Initialize map for quick class lookup + static { + for (DataType type : values()) { + CLASS_MAP.put(type.primitive, type); + CLASS_MAP.put(type.reference, type); + } + } + + private final Class primitive; + private final Class reference; + + /** + * Construct a new data type + * + * @param primitive Primitive class of this data type + * @param reference Reference class of this data type + */ + private DataType(Class primitive, Class reference) { + this.primitive = primitive; + this.reference = reference; + } + + /** + * Returns the data type with the given primitive/reference class + * + * @param clazz Primitive/Reference class of the data type + * @return The data type + */ + public static DataType fromClass(Class clazz) { + return CLASS_MAP.get(clazz); + } + + /** + * Returns the primitive class of the data type with the given reference class + * + * @param clazz Reference class of the data type + * @return The primitive class + */ + public static Class getPrimitive(Class clazz) { + DataType type = fromClass(clazz); + return type == null ? clazz : type.getPrimitive(); + } + + /** + * Returns the reference class of the data type with the given primitive class + * + * @param clazz Primitive class of the data type + * @return The reference class + */ + public static Class getReference(Class clazz) { + DataType type = fromClass(clazz); + return type == null ? clazz : type.getReference(); + } + + /** + * Returns the primitive class array of the given class array + * + * @param classes Given class array + * @return The primitive class array + */ + public static Class[] getPrimitive(Class[] classes) { + int length = classes == null ? 0 : classes.length; + Class[] types = new Class[length]; + for (int index = 0; index < length; index++) { + types[index] = getPrimitive(classes[index]); + } + return types; + } + + /** + * Returns the reference class array of the given class array + * + * @param classes Given class array + * @return The reference class array + */ + public static Class[] getReference(Class[] classes) { + int length = classes == null ? 0 : classes.length; + Class[] types = new Class[length]; + for (int index = 0; index < length; index++) { + types[index] = getReference(classes[index]); + } + return types; + } + + /** + * Returns the primitive class array of the given object array + * + * @param objects Given object array + * @return The primitive class array + */ + public static Class[] getPrimitive(Object[] objects) { + int length = objects == null ? 0 : objects.length; + Class[] types = new Class[length]; + for (int index = 0; index < length; index++) { + types[index] = getPrimitive(objects[index].getClass()); + } + return types; + } + + /** + * Returns the reference class array of the given object array + * + * @param objects Given object array + * @return The reference class array + */ + public static Class[] getReference(Object[] objects) { + int length = objects == null ? 0 : objects.length; + Class[] types = new Class[length]; + for (int index = 0; index < length; index++) { + types[index] = getReference(objects[index].getClass()); + } + return types; + } + + /** + * Compares two class arrays on equivalence + * + * @param primary Primary class array + * @param secondary Class array which is compared to the primary array + * @return Whether these arrays are equal or not + */ + public static boolean compare(Class[] primary, Class[] secondary) { + if (primary == null || secondary == null || primary.length != secondary.length) { + return false; + } + for (int index = 0; index < primary.length; index++) { + Class primaryClass = primary[index]; + Class secondaryClass = secondary[index]; + if (primaryClass.equals(secondaryClass) || primaryClass.isAssignableFrom(secondaryClass)) { + continue; + } + return false; + } + return true; + } + + /** + * Returns the primitive class of this data type + * + * @return The primitive class + */ + public Class getPrimitive() { + return primitive; + } + + /** + * Returns the reference class of this data type + * + * @return The reference class + */ + public Class getReference() { + return reference; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/massivecraft/factions/zcore/MPlugin.java b/src/main/java/com/massivecraft/factions/zcore/MPlugin.java index 9606c7c8..9498df10 100644 --- a/src/main/java/com/massivecraft/factions/zcore/MPlugin.java +++ b/src/main/java/com/massivecraft/factions/zcore/MPlugin.java @@ -13,7 +13,6 @@ import com.massivecraft.factions.zcore.util.Persist; import com.massivecraft.factions.zcore.util.TL; import com.massivecraft.factions.zcore.util.TextUtil; import org.bukkit.Bukkit; -import org.bukkit.Location; import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.java.JavaPlugin; diff --git a/src/main/java/com/massivecraft/factions/zcore/fupgrades/CropUpgrades.java b/src/main/java/com/massivecraft/factions/zcore/fupgrades/CropUpgrades.java new file mode 100644 index 00000000..c1f4311b --- /dev/null +++ b/src/main/java/com/massivecraft/factions/zcore/fupgrades/CropUpgrades.java @@ -0,0 +1,73 @@ +package com.massivecraft.factions.zcore.fupgrades; + +import com.massivecraft.factions.*; +import org.bukkit.CropState; +import org.bukkit.Material; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockGrowEvent; +import org.bukkit.material.Crops; + +import java.util.concurrent.ThreadLocalRandom; + +public class CropUpgrades implements Listener { + @EventHandler + public void onCropGrow(BlockGrowEvent e) { + FLocation floc = new FLocation(e.getBlock().getLocation()); + Faction factionAtLoc = Board.getInstance().getFactionAt(floc); + if (factionAtLoc != Factions.getInstance().getWilderness()) { + int level = factionAtLoc.getUpgrade("Crop"); + if (level != 0) { + if (level == 1) { + int chance = P.p.getConfig().getInt("fupgrades.MainMenu.Crops.Crop-Boost.level-1"); + int randomNum = ThreadLocalRandom.current().nextInt(1, 100 + 1); + if (randomNum <= chance) { + growCrop(e); + } + } + if (level == 2) { + int chance = P.p.getConfig().getInt("fupgrades.MainMenu.Crops.Crop-Boost.level-2"); + int randomNum = ThreadLocalRandom.current().nextInt(1, 100 + 1); + if (randomNum <= chance) { + growCrop(e); + } + } + if (level == 3) { + int chance = P.p.getConfig().getInt("fupgrades.MainMenu.Crops.Crop-Boost.level-3"); + int randomNum = ThreadLocalRandom.current().nextInt(1, 100 + 1); + if (randomNum <= chance) { + growCrop(e); + } + } + } + } + } + + + private void growCrop(BlockGrowEvent e) { + + if (e.getBlock().getType().equals(Material.CROPS)) { + e.setCancelled(true); + Crops c = new Crops(CropState.RIPE); + org.bukkit.block.BlockState bs = e.getBlock().getState(); + bs.setData(c); + bs.update(); + } + org.bukkit.block.Block below = e.getBlock().getLocation().subtract(0, 1, 0).getBlock(); + if (below.getType() == Material.SUGAR_CANE_BLOCK) { + + org.bukkit.block.Block above = e.getBlock().getLocation().add(0, 1, 0).getBlock(); + if (above.getType() == Material.AIR) { + above.setType(Material.SUGAR_CANE_BLOCK); + } + + } + if (below.getType() == Material.CACTUS) { + + org.bukkit.block.Block above = e.getBlock().getLocation().add(0, 1, 0).getBlock(); + if (above.getType() == Material.AIR) { + above.setType(Material.CACTUS); + } + } + } +} diff --git a/src/main/java/com/massivecraft/factions/zcore/fupgrades/EXPUpgrade.java b/src/main/java/com/massivecraft/factions/zcore/fupgrades/EXPUpgrade.java new file mode 100644 index 00000000..cc50c7ce --- /dev/null +++ b/src/main/java/com/massivecraft/factions/zcore/fupgrades/EXPUpgrade.java @@ -0,0 +1,48 @@ +package com.massivecraft.factions.zcore.fupgrades; + +import com.massivecraft.factions.*; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDeathEvent; + +public class EXPUpgrade implements Listener { + @EventHandler + public void onDeath(EntityDeathEvent e) { + Location loc = e.getEntity().getLocation(); + Faction wild = Factions.getInstance().getWilderness(); + FLocation floc = new FLocation(loc); + Faction faction = Board.getInstance().getFactionAt(floc); + Entity killer = e.getEntity().getKiller(); + Player player = (Player) killer; + if (player == null) { + return; + } + if (faction != wild) { + int level = faction.getUpgrade("Exp"); + if (level != 0) { + if (level == 1) { + double multiplier = P.p.getConfig().getDouble("fupgrades.MainMenu.EXP.EXP-Boost.level-1"); + spawnMoreExp(e, multiplier); + } + if (level == 2) { + double multiplier = P.p.getConfig().getDouble("fupgrades.MainMenu.EXP.EXP-Boost.level-2"); + spawnMoreExp(e, multiplier); + } + if (level == 3) { + double multiplier = P.p.getConfig().getDouble("fupgrades.MainMenu.EXP.EXP-Boost.level-3"); + spawnMoreExp(e, multiplier); + } + + } + } + } + + public void spawnMoreExp(EntityDeathEvent e, double multiplier) { + double newExp = e.getDroppedExp() * multiplier; + int newExpInt = (int) newExp; + e.setDroppedExp(newExpInt); + } +} diff --git a/src/main/java/com/massivecraft/factions/zcore/fupgrades/FUpgradesGUI.java b/src/main/java/com/massivecraft/factions/zcore/fupgrades/FUpgradesGUI.java new file mode 100644 index 00000000..a9c25eec --- /dev/null +++ b/src/main/java/com/massivecraft/factions/zcore/fupgrades/FUpgradesGUI.java @@ -0,0 +1,267 @@ +package com.massivecraft.factions.zcore.fupgrades; + +import com.massivecraft.factions.FPlayer; +import com.massivecraft.factions.FPlayers; +import com.massivecraft.factions.P; +import com.massivecraft.factions.zcore.util.TL; +import net.milkbowl.vault.economy.Economy; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.List; + + +public class FUpgradesGUI implements Listener { + public void openMainMenu(FPlayer fme) { + Inventory inventory = Bukkit.createInventory(null, 27, P.p.color(P.p.getConfig().getString("fupgrades.MainMenu.Title").replace("{faction}", fme.getFaction().getTag()))); + List dummySlots = P.p.getConfig().getIntegerList("fupgrades.MainMenu.DummyItem.slots"); + Material dummyMaterial = Material.getMaterial(P.p.getConfig().getString("fupgrades.MainMenu.DummyItem.Type")); + int dummyAmount = P.p.getConfig().getInt("fupgrades.MainMenu.DummyItem.Amount"); + short dummyData = Short.parseShort(P.p.getConfig().getInt("fupgrades.MainMenu.DummyItem.Damage") + ""); + ItemStack dummyItem = P.p.createItem(dummyMaterial, + dummyAmount, + dummyData, + P.p.color(P.p.getConfig().getString("fupgrades.MainMenu.DummyItem.Name")), + P.p.colorList(P.p.getConfig().getStringList("fupgrades.MainMenu.DummyItem.Lore"))); + for (int i = 0; i <= dummySlots.size() - 1; i++) { + inventory.setItem(dummySlots.get(i), dummyItem); + } + ItemStack[] items = buildItems(fme); + + List cropSlots = P.p.getConfig().getIntegerList("fupgrades.MainMenu.Crops.CropItem.slots"); + for (int i = 0; i <= cropSlots.size() - 1; i++) { + inventory.setItem(cropSlots.get(i), items[2]); + } + List spawnerSlots = P.p.getConfig().getIntegerList("fupgrades.MainMenu.Spawners.SpawnerItem.slots"); + for (int i = 0; i <= spawnerSlots.size() - 1; i++) { + inventory.setItem(spawnerSlots.get(i), items[0]); + } + List expSlots = P.p.getConfig().getIntegerList("fupgrades.MainMenu.EXP.EXPItem.slots"); + for (int i = 0; i <= expSlots.size() - 1; i++) { + inventory.setItem(expSlots.get(i), items[1]); + } + fme.getPlayer().openInventory(inventory); + } + + @EventHandler + public void onClick(InventoryClickEvent e) { + if (e.getClickedInventory() == null || e.getCurrentItem() == null || e.getCurrentItem().getItemMeta() == null || e.getCursor() == null) { + return; + } + FPlayer fme = FPlayers.getInstance().getByPlayer((Player) e.getWhoClicked()); + if (e.getClickedInventory().getTitle().equalsIgnoreCase(P.p.color(P.p.getConfig().getString("fupgrades.MainMenu.Title").replace("{faction}", fme.getFaction().getTag())))) { + e.setCancelled(true); + ItemStack[] items = buildItems(fme); + ItemStack cropItem = items[2]; + ItemStack expItem = items[0]; + ItemStack spawnerItem = items[1]; + int cropLevel = fme.getFaction().getUpgrade("Crop"); + if (e.getCurrentItem().equals(cropItem)) { + if (cropLevel == 3) { + return; + } + if (cropLevel == 2) { + int cost = P.p.getConfig().getInt("fupgrades.MainMenu.Crops.Cost.level-3"); + + if (hasMoney(fme, cost)) { + fme.getFaction().setUpgrades("Crop", 3); + fme.getPlayer().closeInventory(); + takeMoney(fme, cost); + } + + } + if (cropLevel == 1) { + int cost = P.p.getConfig().getInt("fupgrades.MainMenu.Crops.Cost.level-2"); + if (hasMoney(fme, cost)) { + takeMoney(fme, cost); + fme.getFaction().setUpgrades("Crop", 2); + fme.getPlayer().closeInventory(); + } + + } + if (cropLevel == 0) { + int cost = P.p.getConfig().getInt("fupgrades.MainMenu.Crops.Cost.level-1"); + if (hasMoney(fme, cost)) { + takeMoney(fme, cost); + fme.getFaction().setUpgrades("Crop", 1); + fme.getPlayer().closeInventory(); + } + + } + } + int spawnerLevel = fme.getFaction().getUpgrade("Spawner"); + if (e.getCurrentItem().equals(spawnerItem)) { + if (spawnerLevel == 3) { + return; + } + if (spawnerLevel == 2) { + int cost = P.p.getConfig().getInt("fupgrades.MainMenu.Spawners.Cost.level-3"); + if (!hasMoney(fme, cost)) { + return; + } + takeMoney(fme, cost); + fme.getFaction().setUpgrades("Spawner", 3); + fme.getPlayer().closeInventory(); + } + if (spawnerLevel == 1) { + int cost = P.p.getConfig().getInt("fupgrades.MainMenu.Spawners.Cost.level-2"); + if (!hasMoney(fme, cost)) { + return; + } + takeMoney(fme, cost); + fme.getFaction().setUpgrades("Spawner", 2); + fme.getPlayer().closeInventory(); + } + if (spawnerLevel == 0) { + int cost = P.p.getConfig().getInt("fupgrades.MainMenu.Spawners.Cost.level-1"); + if (!hasMoney(fme, cost)) { + return; + } + takeMoney(fme, cost); + fme.getFaction().setUpgrades("Spawner", 1); + fme.getPlayer().closeInventory(); + } + } + int expLevel = fme.getFaction().getUpgrade("Exp"); + if (e.getCurrentItem().equals(expItem)) { + if (expLevel == 3) { + return; + } + if (expLevel == 2) { + int cost = P.p.getConfig().getInt("fupgrades.MainMenu.EXP.Cost.level-3"); + if (!hasMoney(fme, cost)) { + return; + } + takeMoney(fme, cost); + fme.getFaction().setUpgrades("Exp", 3); + fme.getPlayer().closeInventory(); + } + if (expLevel == 1) { + int cost = P.p.getConfig().getInt("fupgrades.MainMenu.EXP.Cost.level-2"); + if (!hasMoney(fme, cost)) { + return; + } + takeMoney(fme, cost); + fme.getFaction().setUpgrades("Exp", 2); + fme.getPlayer().closeInventory(); + } + if (expLevel == 0) { + int cost = P.p.getConfig().getInt("fupgrades.MainMenu.EXP.Cost.level-1"); + if (!hasMoney(fme, cost)) { + return; + } + takeMoney(fme, cost); + fme.getFaction().setUpgrades("Exp", 1); + fme.getPlayer().closeInventory(); + } + } + } + + + } + + public ItemStack[] buildItems(FPlayer fme) { + Material expMaterial = Material.getMaterial(P.p.getConfig().getString("fupgrades.MainMenu.EXP.EXPItem.Type")); + int expAmt = P.p.getConfig().getInt("fupgrades.MainMenu.EXP.EXPItem.Amount"); + short expData = Short.parseShort(P.p.getConfig().getInt("fupgrades.MainMenu.EXP.EXPItem.Damage") + ""); + String expName = P.p.color(P.p.getConfig().getString("fupgrades.MainMenu.EXP.EXPItem.Name")); + List expLore = P.p.colorList(P.p.getConfig().getStringList("fupgrades.MainMenu.EXP.EXPItem.Lore")); + int expLevel = fme.getFaction().getUpgrade("Exp"); + for (int i = 0; i <= expLore.size() - 1; i++) { + expLore.set(i, expLore.get(i).replace("{level}", expLevel + "")); + } + ItemStack expItem = P.p.createItem(expMaterial, expAmt, expData, expName, expLore); + if (expLevel >= 1) { + ItemMeta itemMeta = expItem.getItemMeta(); + itemMeta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + itemMeta.addEnchant(Enchantment.DURABILITY, 3, true); + expItem.setItemMeta(itemMeta); + } + + Material spawnerMaterial = Material.getMaterial(P.p.getConfig().getString("fupgrades.MainMenu.Spawners.SpawnerItem.Type")); + int spawnerAmt = P.p.getConfig().getInt("fupgrades.MainMenu.Spawners.SpawnerItem.Amount"); + short spawnerData = Short.parseShort(P.p.getConfig().getInt("fupgrades.MainMenu.Spawners.SpawnerItem.Damage") + ""); + String spawnerName = P.p.color(P.p.getConfig().getString("fupgrades.MainMenu.Spawners.SpawnerItem.Name")); + List spawnerLore = P.p.colorList(P.p.getConfig().getStringList("fupgrades.MainMenu.Spawners.SpawnerItem.Lore")); + + List spawnerSlots = P.p.getConfig().getIntegerList("fupgrades.MainMenu.Spawners.SpawnerItem.slots"); + int spawnerLevel = fme.getFaction().getUpgrade("Spawner"); + + for (int i = 0; i <= spawnerLore.size() - 1; i++) { + spawnerLore.set(i, spawnerLore.get(i).replace("{level}", spawnerLevel + "")); + } + if (expLevel == 2) { + expItem.setAmount(2); + } else if (expLevel == 3) { + expItem.setAmount(3); + } + Material cropMaterial = Material.getMaterial(P.p.getConfig().getString("fupgrades.MainMenu.Crops.CropItem.Type")); + int cropAmt = P.p.getConfig().getInt("fupgrades.MainMenu.Crops.CropItem.Amount"); + short cropData = Short.parseShort(P.p.getConfig().getInt("fupgrades.MainMenu.Crops.CropItem.Damage") + ""); + String cropName = P.p.color(P.p.getConfig().getString("fupgrades.MainMenu.Crops.CropItem.Name")); + List cropLore = P.p.colorList(P.p.getConfig().getStringList("fupgrades.MainMenu.Crops.CropItem.Lore")); + int cropLevel = fme.getFaction().getUpgrade("Crop"); + for (int i = 0; i <= cropLore.size() - 1; i++) { + String line = cropLore.get(i); + line = line.replace("{level}", cropLevel + ""); + cropLore.set(i, line); + + } + ItemStack cropItem = P.p.createItem(cropMaterial, cropAmt, cropData, cropName, cropLore); + cropItem.getItemMeta().setLore(cropLore); + if (cropLevel >= 1) { + ItemMeta itemMeta = cropItem.getItemMeta(); + itemMeta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + itemMeta.addEnchant(Enchantment.DURABILITY, 3, true); + cropItem.setItemMeta(itemMeta); + } + if (cropLevel == 2) { + cropItem.setAmount(2); + } else if (cropLevel == 3) { + cropItem.setAmount(3); + } + ItemStack spawnerItem = P.p.createItem(spawnerMaterial, spawnerAmt, spawnerData, spawnerName, spawnerLore); + spawnerItem.getItemMeta().setLore(spawnerLore); + if (spawnerLevel >= 1) { + ItemMeta itemMeta = spawnerItem.getItemMeta(); + itemMeta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + itemMeta.addEnchant(Enchantment.DURABILITY, 3, true); + spawnerItem.setItemMeta(itemMeta); + } + if (spawnerLevel == 2) { + spawnerItem.setAmount(2); + } else if (spawnerLevel == 3) { + spawnerItem.setAmount(3); + } + ItemStack[] items = {expItem, spawnerItem, cropItem}; + return items; + } + + public boolean hasMoney(FPlayer fme, int amt) { + Economy econ = P.p.getEcon(); + if (econ.getBalance((Player) fme.getPlayer()) >= amt) { + return true; + } else { + fme.getPlayer().closeInventory(); + fme.msg(TL.COMMAND_UPGRADES_NOTENOUGHMONEY); + return false; + } + } + + public void takeMoney(FPlayer fme, int amt) { + if (hasMoney(fme, amt)) { + Economy econ = P.p.getEcon(); + econ.withdrawPlayer(fme.getPlayer(), amt); + fme.sendMessage(TL.COMMAND_UPGRADES_MONEYTAKE.toString().replace("{amount}", amt + "")); + } + } +} diff --git a/src/main/java/com/massivecraft/factions/zcore/fupgrades/SpawnerUpgrades.java b/src/main/java/com/massivecraft/factions/zcore/fupgrades/SpawnerUpgrades.java new file mode 100644 index 00000000..b7c44841 --- /dev/null +++ b/src/main/java/com/massivecraft/factions/zcore/fupgrades/SpawnerUpgrades.java @@ -0,0 +1,39 @@ +package com.massivecraft.factions.zcore.fupgrades; + +import com.massivecraft.factions.*; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.SpawnerSpawnEvent; + +public class SpawnerUpgrades implements Listener { + @EventHandler + public void onSpawn(SpawnerSpawnEvent e) { + FLocation floc = new FLocation(e.getLocation()); + Faction factionAtLoc = Board.getInstance().getFactionAt(floc); + if (factionAtLoc != Factions.getInstance().getWilderness()) { + int level = factionAtLoc.getUpgrade("Spawner"); + if (level != 0) { + if (level == 1) { + int rate = P.p.getConfig().getInt("fupgrades.MainMenu.Spawners.Spawner-Boost.level-1"); + lowerSpawnerDelay(e, rate); + } + if (level == 2) { + int rate = P.p.getConfig().getInt("fupgrades.MainMenu.Spawners.Spawner-Boost.level-2"); + lowerSpawnerDelay(e, rate); + } + if (level == 3) { + int rate = P.p.getConfig().getInt("fupgrades.MainMenu.Spawners.Spawner-Boost.level-3"); + lowerSpawnerDelay(e, rate); + } + + } + } + } + + private void lowerSpawnerDelay(SpawnerSpawnEvent e, double multiplier) { + int lowerby = (int) Math.round(e.getSpawner().getDelay() * multiplier); + e.getSpawner().setDelay(e.getSpawner().getDelay() - lowerby); + } + + +} diff --git a/src/main/java/com/massivecraft/factions/zcore/util/TL.java b/src/main/java/com/massivecraft/factions/zcore/util/TL.java index cf7f62fc..fdf41fef 100644 --- a/src/main/java/com/massivecraft/factions/zcore/util/TL.java +++ b/src/main/java/com/massivecraft/factions/zcore/util/TL.java @@ -480,6 +480,7 @@ public enum TL { COMMAND_PROMOTE_WRONGFACTION("%1$s is not part of your faction."), COMMAND_NOACCESS("You don't have access to that."), COMMAND_PROMOTE_NOTTHATPLAYER("That player cannot be promoted."), + COMMAND_PROMOTE_NOT_ALLOWED("You cannot promote to the same rank as yourself!"), COMMAND_POWER_TOSHOW("to show player power info"), diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 0bdd37e0..63d1f6a1 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: Factions -version: ${project.version}-SF-1.0.17 +version: ${project.version}-SF-1.0.18 main: com.massivecraft.factions.P authors: [Olof Larsson, Brett Flannigan, drtshock, ProSavage] softdepend: [PlayerVaults, PlaceholderAPI, MVdWPlaceholderAPI, PermissionsEx, Permissions, Essentials, EssentialsChat, HeroChat, iChat, LocalAreaChat, LWC, nChat, ChatManager, CAPI, AuthMe, Vault, Spout, WorldEdit, WorldGuard, AuthDB, CaptureThePoints, CombatTag, dynmap, FactionsTop] @@ -38,6 +38,7 @@ permissions: factions.join.others: true factions.admin.any: true factions.mod.any: true + factions.kit.halfmod: description: Zones, bypassing, kicking, and chatspy children: @@ -61,6 +62,7 @@ permissions: children: factions.admin: true factions.autoclaim: true + factions.promote: true factions.chat: true factions.claim: true factions.claim.line: true @@ -107,6 +109,9 @@ permissions: factions.checkpoint: true factions.rules: true factions.setwarp: true + factions.banner: true + factions.vault: true + factions.getvault: true factions.admin: description: hand over your admin rights factions.admin.any: