diff --git a/build.gradle b/build.gradle index 1dcdd5b1..51acc383 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,11 @@ subprojects { repositories { mavenCentral() + maven { + name = 'citizens-repo' + url = 'https://maven.citizensnpcs.co/repo' + } + maven { name 'spigot-repo' url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' @@ -42,7 +47,6 @@ subprojects { } maven { - name 'clip-repo' url 'https://repo.extendedclip.com/content/repositories/placeholderapi/' } diff --git a/duels-api/src/main/java/me/realized/duels/api/Duels.java b/duels-api/src/main/java/me/realized/duels/api/Duels.java index 66478ed7..f224b155 100644 --- a/duels-api/src/main/java/me/realized/duels/api/Duels.java +++ b/duels-api/src/main/java/me/realized/duels/api/Duels.java @@ -59,7 +59,7 @@ public interface Duels extends Plugin { */ @NotNull DQueueManager getQueueManager(); - + /** * Gets the QueueSignManager singleton used by Duels. diff --git a/duels-plugin/build.gradle b/duels-plugin/build.gradle index a292f1e9..fca9bed9 100644 --- a/duels-plugin/build.gradle +++ b/duels-plugin/build.gradle @@ -14,12 +14,15 @@ processResources { } dependencies { + compileOnly('net.citizensnpcs:citizens-main:2.0.33-SNAPSHOT') { + exclude group: '*', module: '*' + } compileOnly 'org.jetbrains:annotations-java5:22.0.0' compileOnly 'org.projectlombok:lombok:1.18.22' annotationProcessor 'org.projectlombok:lombok:1.18.22' implementation 'org.spigotmc:spigot-api:1.14.4-R0.1-SNAPSHOT' implementation 'com.mojang:authlib:1.5.21' - implementation 'me.clip:placeholderapi:2.11.1' + implementation 'me.clip:placeholderapi:2.11.6' implementation 'com.SirBlobman.combatlogx:CombatLogX-API:10.0.0.0-SNAPSHOT' implementation ('net.essentialsx:EssentialsX:2.19.2') { transitive = false @@ -29,6 +32,7 @@ dependencies { } implementation name: 'Vault-1.6.7' implementation name: 'CombatTagPlus' + implementation name: 'AntiRelog-3.0.11' implementation name: 'PvPManager-3.7.16' implementation name: 'Factions-1.6.9.5-U0.1.14' implementation name: 'MassiveCore' diff --git a/duels-plugin/src/main/java/me/realized/duels/DuelsPlugin.java b/duels-plugin/src/main/java/me/realized/duels/DuelsPlugin.java index a4148dc4..64ea2306 100644 --- a/duels-plugin/src/main/java/me/realized/duels/DuelsPlugin.java +++ b/duels-plugin/src/main/java/me/realized/duels/DuelsPlugin.java @@ -15,6 +15,7 @@ import me.realized.duels.arena.ArenaManagerImpl; import me.realized.duels.betting.BettingManager; import me.realized.duels.command.commands.SpectateCommand; +import me.realized.duels.command.commands.bot.BotDuelCommand; import me.realized.duels.command.commands.duel.DuelCommand; import me.realized.duels.command.commands.duels.DuelsCommand; import me.realized.duels.command.commands.queue.QueueCommand; @@ -228,6 +229,7 @@ public void onDisable() { private boolean load() { registerCommands( new DuelCommand(this), + new BotDuelCommand(this), new QueueCommand(this), new SpectateCommand(this), new DuelsCommand(this) diff --git a/duels-plugin/src/main/java/me/realized/duels/Permissions.java b/duels-plugin/src/main/java/me/realized/duels/Permissions.java index 45eb7bae..e249e921 100644 --- a/duels-plugin/src/main/java/me/realized/duels/Permissions.java +++ b/duels-plugin/src/main/java/me/realized/duels/Permissions.java @@ -3,6 +3,7 @@ public final class Permissions { public static final String DUEL = "duels.duel"; + public static final String DUEL_BOT = "duels.duel.bot"; public static final String STATS = "duels.stats"; public static final String STATS_OTHERS = STATS + ".others"; public static final String TOGGLE = "duels.toggle"; diff --git a/duels-plugin/src/main/java/me/realized/duels/arena/ArenaImpl.java b/duels-plugin/src/main/java/me/realized/duels/arena/ArenaImpl.java index f59a82bd..3ed73e49 100644 --- a/duels-plugin/src/main/java/me/realized/duels/arena/ArenaImpl.java +++ b/duels-plugin/src/main/java/me/realized/duels/arena/ArenaImpl.java @@ -27,7 +27,11 @@ import me.realized.duels.util.inventory.ItemBuilder; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -45,7 +49,7 @@ public class ArenaImpl extends BaseButton implements Arena { private final Map positions = new HashMap<>(); @Getter private MatchImpl match; - @Getter(value = AccessLevel.PACKAGE) + @Getter @Setter(value = AccessLevel.PACKAGE) private Countdown countdown; @Getter @@ -164,6 +168,48 @@ public void endMatch(final UUID winner, final UUID loser, final Reason reason) { final Queue source = match.getSource(); match.setFinished(); + + for(Block block : match.placedBlocks) { + block.setType(Material.AIR); + } + + for(Map.Entry map : match.brokenBlocks.entrySet()) { + map.getKey().getBlock().setBlockData(map.getValue()); + } + + for (Block block : match.liquids) { + Location loc = block.getLocation(); + int radius = 1; + + while (true) { + boolean waterFound = false; + + for (int x = -radius; x <= radius; x++) { + for (int y = -radius; y <= radius; y++) { + for (int z = -radius; z <= radius; z++) { + Block findBlock = loc.clone().add(x, y, z).getBlock(); + String type = findBlock.getType().name().toLowerCase(); + + if (type.contains("water") || type.contains("lava") || type.contains("cobblestone") || type.contains("obsidian")) { + waterFound = true; + findBlock.setType(Material.AIR); + } + } + } + } + + if (!waterFound) { + break; + } + + radius++; + } + } + + if(config.isClearItemsAfterMatch()) { + match.droppedItems.forEach(Entity::remove); + } + match = null; if (source != null) { @@ -192,18 +238,18 @@ boolean isCounting() { @Override public boolean has(@NotNull final Player player) { Objects.requireNonNull(player, "player"); - return isUsed() && !match.getPlayerMap().getOrDefault(player, true); + return isUsed() && !match.getPlayerMap().getOrDefault(player, new MatchImpl.PlayerStatus(true)).isDead; } public void add(final Player player) { if (isUsed()) { - match.getPlayerMap().put(player, false); + match.getPlayerMap().put(player, new MatchImpl.PlayerStatus(false)); } } public void remove(final Player player) { if (isUsed() && match.getPlayerMap().containsKey(player)) { - match.getPlayerMap().put(player, true); + match.getPlayerMap().put(player, new MatchImpl.PlayerStatus(true)); } } diff --git a/duels-plugin/src/main/java/me/realized/duels/arena/ArenaManagerImpl.java b/duels-plugin/src/main/java/me/realized/duels/arena/ArenaManagerImpl.java index 175a6950..bb899ee7 100644 --- a/duels-plugin/src/main/java/me/realized/duels/arena/ArenaManagerImpl.java +++ b/duels-plugin/src/main/java/me/realized/duels/arena/ArenaManagerImpl.java @@ -42,11 +42,15 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityShootBowEvent; import org.bukkit.event.entity.ProjectileLaunchEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.inventory.ItemStack; import org.bukkit.projectiles.ProjectileSource; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -230,19 +234,22 @@ public void clearBinds(final KitImpl kit) { private class ArenaListener implements Listener { - @EventHandler(ignoreCancelled = true) + @EventHandler public void on(final PlayerInteractEvent event) { - if (!event.hasBlock() || !config.isPreventInteract()) { + if(event.getAction() != Action.RIGHT_CLICK_AIR && event.isCancelled() || !config.isPreventInteract()) { return; } - final ArenaImpl arena = get(event.getPlayer()); + Player player = event.getPlayer(); + + final ArenaImpl arena = get(player); if (arena == null || !arena.isCounting()) { return; } event.setCancelled(true); + player.updateInventory(); } @EventHandler(ignoreCancelled = true) @@ -274,7 +281,7 @@ public void on(final ProjectileLaunchEvent event) { final ArenaImpl arena = get((Player) shooter); - if (arena == null || !arena.isCounting()) { + if (arena == null || !arena.isCounting() || event.getEntity().getClass().getName().contains("Potion")) { return; } @@ -283,12 +290,13 @@ public void on(final ProjectileLaunchEvent event) { @EventHandler(ignoreCancelled = true) public void on(final PlayerMoveEvent event) { + final Location to = event.getTo(); + if (!config.isPreventMovement()) { return; } final Location from = event.getFrom(); - final Location to = event.getTo(); if (from.getBlockX() == to.getBlockX() && from.getBlockY() == to.getBlockY() && from.getBlockZ() == to.getBlockZ()) { return; @@ -296,7 +304,15 @@ public void on(final PlayerMoveEvent event) { final ArenaImpl arena = get(event.getPlayer()); - if (arena == null || !arena.isCounting()) { + if (arena == null) { + return; + } + + if(to.getBlockY() < config.getMinY()) { + event.getPlayer().damage(99999); + } + + if(!arena.isCounting()) { return; } diff --git a/duels-plugin/src/main/java/me/realized/duels/arena/Countdown.java b/duels-plugin/src/main/java/me/realized/duels/arena/Countdown.java index 6e87da2b..fe06d472 100644 --- a/duels-plugin/src/main/java/me/realized/duels/arena/Countdown.java +++ b/duels-plugin/src/main/java/me/realized/duels/arena/Countdown.java @@ -33,9 +33,7 @@ class Countdown extends BukkitRunnable { @Override public void run() { - if (finished) { - return; - } + if (finished) return; final String rawMessage = messages.remove(0); final String message = StringUtil.color(rawMessage); @@ -44,7 +42,7 @@ public void run() { arena.getPlayers().forEach(player -> { config.playSound(player, rawMessage); - final Pair info = this.info.get(player.getUniqueId()); + final Pair info = this.info.get(player.getUniqueId()); if (info != null) { player.sendMessage(message diff --git a/duels-plugin/src/main/java/me/realized/duels/arena/MatchImpl.java b/duels-plugin/src/main/java/me/realized/duels/arena/MatchImpl.java index a2c001ad..0d4a8325 100644 --- a/duels-plugin/src/main/java/me/realized/duels/arena/MatchImpl.java +++ b/duels-plugin/src/main/java/me/realized/duels/arena/MatchImpl.java @@ -1,25 +1,38 @@ package me.realized.duels.arena; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; -import java.util.UUID; import java.util.stream.Collectors; import lombok.Getter; import me.realized.duels.api.match.Match; import me.realized.duels.kit.KitImpl; import me.realized.duels.queue.Queue; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Item; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; public class MatchImpl implements Match { + public static class PlayerStatus { + + public PlayerStatus(boolean isDead) { + this.isDead = isDead; + } + + // Player is dead value + public boolean isDead; + // How much damage to your opponent. + public double damageCount; + + public int hits; + + } + @Getter private final ArenaImpl arena; @Getter @@ -35,8 +48,13 @@ public class MatchImpl implements Match { @Getter private boolean finished; + public List droppedItems = new ArrayList<>(); + public List placedBlocks = new ArrayList<>(); + public List liquids = new ArrayList<>(); + public HashMap brokenBlocks = new HashMap<>(); + // Default value for players is false, which is set to true if player is killed in the match. - private final Map players = new HashMap<>(); + private final Map players = new HashMap<>(); MatchImpl(final ArenaImpl arena, final KitImpl kit, final Map> items, final int bet, final Queue source) { this.arena = arena; @@ -47,12 +65,42 @@ public class MatchImpl implements Match { this.source = source; } - Map getPlayerMap() { + Map getPlayerMap() { return players; } Set getAlivePlayers() { - return players.entrySet().stream().filter(entry -> !entry.getValue()).map(Entry::getKey).collect(Collectors.toSet()); + return players.entrySet().stream().filter(entry -> !entry.getValue().isDead).map(Entry::getKey).collect(Collectors.toSet()); + } + + public void addDamageToPlayer(Player player, double damage) { + PlayerStatus status = players.get(player); + status.damageCount += damage; + status.hits++; + } + + public int getHits(Player player) { + return players.get(player).hits; + } + + public Player getWinnerOfDamage() { + Player winner = players.entrySet() + .stream() + .max(Comparator.comparingDouble(entry -> entry.getValue().damageCount)) + .map(Map.Entry::getKey) + .orElse(null); + + return winner; + } + + public Player getLooserOfDamage() { + Player looser = players.entrySet() + .stream() + .min(Comparator.comparingDouble(entry -> entry.getValue().damageCount)) + .map(Map.Entry::getKey) + .orElse(null); + + return looser; } public Set getAllPlayers() { @@ -60,7 +108,7 @@ public Set getAllPlayers() { } public boolean isDead(final Player player) { - return players.getOrDefault(player, true); + return players.getOrDefault(player, new PlayerStatus(true)).isDead; } public boolean isFromQueue() { diff --git a/duels-plugin/src/main/java/me/realized/duels/command/commands/bot/BotDuelCommand.java b/duels-plugin/src/main/java/me/realized/duels/command/commands/bot/BotDuelCommand.java new file mode 100644 index 00000000..e0180621 --- /dev/null +++ b/duels-plugin/src/main/java/me/realized/duels/command/commands/bot/BotDuelCommand.java @@ -0,0 +1,113 @@ +package me.realized.duels.command.commands.bot; + +import me.realized.duels.DuelsPlugin; +import me.realized.duels.Permissions; +import me.realized.duels.command.BaseCommand; +import me.realized.duels.hook.hooks.CombatLogXHook; +import me.realized.duels.hook.hooks.CombatTagPlusHook; +import me.realized.duels.hook.hooks.PvPManagerHook; +import me.realized.duels.hook.hooks.worldguard.WorldGuardHook; +import me.realized.duels.setting.Settings; +import me.realized.duels.util.inventory.InventoryUtil; +import org.bukkit.GameMode; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.Arrays; +import java.util.List; + +public class BotDuelCommand extends BaseCommand { + + private final CombatTagPlusHook combatTagPlus; + private final PvPManagerHook pvpManager; + private final CombatLogXHook combatLogX; + private final WorldGuardHook worldGuard; + + public BotDuelCommand(DuelsPlugin plugin) { + super(plugin, "botduel", "/botduel", "Duel with bots", Permissions.DUEL_BOT, 0, true, + "botfight", "pvpbot", "bot", "duelbot"); + + this.combatTagPlus = hookManager.getHook(CombatTagPlusHook.class); + this.pvpManager = hookManager.getHook(PvPManagerHook.class); + this.combatLogX = hookManager.getHook(CombatLogXHook.class); + this.worldGuard = hookManager.getHook(WorldGuardHook.class); + } + + @Override + protected boolean executeFirst(final CommandSender sender, final String label, final String[] args) { + final Player player = (Player) sender; + + if (userManager.get(player) == null) { + lang.sendMessage(sender, "ERROR.data.load-failure"); + return true; + } + + if (args.length != 0) { + lang.sendMessage(sender, "COMMAND.duel.usage", "command", label); + return true; + } + + if (config.isRequiresClearedInventory() && InventoryUtil.hasItem(player)) { + lang.sendMessage(sender, "ERROR.duel.inventory-not-empty"); + return true; + } + + if (config.isPreventCreativeMode() && player.getGameMode() == GameMode.CREATIVE) { + lang.sendMessage(sender, "ERROR.duel.in-creative-mode"); + return true; + } + + if (config.getBlacklistedWorlds().contains(player.getWorld().getName())) { + lang.sendMessage(sender, "ERROR.duel.in-blacklisted-world"); + return true; + } + + if ((combatTagPlus != null && combatTagPlus.isTagged(player)) + || (pvpManager != null && pvpManager.isTagged(player)) + || (combatLogX != null && combatLogX.isTagged(player))) { + lang.sendMessage(sender, "ERROR.duel.is-tagged"); + return true; + } + + String duelzone = null; + + if (worldGuard != null && config.isDuelzoneEnabled() && (duelzone = worldGuard.findDuelZone(player)) == null) { + lang.sendMessage(sender, "ERROR.duel.not-in-duelzone", "regions", config.getDuelzones()); + return true; + } + + if (arenaManager.isInMatch(player)) { + lang.sendMessage(sender, "ERROR.duel.already-in-match.sender"); + return true; + } + + if (spectateManager.isSpectating(player)) { + lang.sendMessage(sender, "ERROR.spectate.already-spectating.sender"); + return true; + } + + final Settings settings = settingManager.getSafely(player, true); + + settings.setTarget(player); + settings.setBaseLoc(player); + settings.setDuelzone(player, duelzone); + + // Maintain old behavior: If own inventory is disabled, prompt kit selector first instead of request settings GUI. + kitManager.getGui().open(player); + + return true; + } + + @Override + protected void execute(final CommandSender sender, final String label, final String[] args) {} + + @Override + public List onTabComplete(final CommandSender sender, final Command command, final String alias, final String[] args) { + if (args.length == 0) { + return Arrays.asList("botduel"); + } + + return null; + } +} diff --git a/duels-plugin/src/main/java/me/realized/duels/command/commands/duel/DuelCommand.java b/duels-plugin/src/main/java/me/realized/duels/command/commands/duel/DuelCommand.java index 64c6bd0c..ba29af32 100644 --- a/duels-plugin/src/main/java/me/realized/duels/command/commands/duel/DuelCommand.java +++ b/duels-plugin/src/main/java/me/realized/duels/command/commands/duel/DuelCommand.java @@ -113,7 +113,7 @@ protected boolean executeFirst(final CommandSender sender, final String label, f final Player target = Bukkit.getPlayerExact(args[0]); - if (target == null || !player.canSee(target)) { + if (target == null /*|| !player.canSee(target)*/) { lang.sendMessage(sender, "ERROR.player.not-found", "name", args[0]); return true; } diff --git a/duels-plugin/src/main/java/me/realized/duels/command/commands/duel/subcommands/AcceptCommand.java b/duels-plugin/src/main/java/me/realized/duels/command/commands/duel/subcommands/AcceptCommand.java index 8a3a889b..975a8d61 100644 --- a/duels-plugin/src/main/java/me/realized/duels/command/commands/duel/subcommands/AcceptCommand.java +++ b/duels-plugin/src/main/java/me/realized/duels/command/commands/duel/subcommands/AcceptCommand.java @@ -3,6 +3,7 @@ import me.realized.duels.DuelsPlugin; import me.realized.duels.api.event.request.RequestAcceptEvent; import me.realized.duels.command.BaseCommand; +import me.realized.duels.hook.hooks.AntiRelogHook; import me.realized.duels.hook.hooks.CombatLogXHook; import me.realized.duels.hook.hooks.CombatTagPlusHook; import me.realized.duels.hook.hooks.PvPManagerHook; @@ -21,6 +22,7 @@ public class AcceptCommand extends BaseCommand { private final PvPManagerHook pvpManager; private final CombatLogXHook combatLogX; private final WorldGuardHook worldGuard; + private final AntiRelogHook antiRelog; public AcceptCommand(final DuelsPlugin plugin) { super(plugin, "accept", "accept [player]", "Accepts a duel request.", 2, true); @@ -28,6 +30,7 @@ public AcceptCommand(final DuelsPlugin plugin) { this.pvpManager = hookManager.getHook(PvPManagerHook.class); this.combatLogX = plugin.getHookManager().getHook(CombatLogXHook.class); this.worldGuard = hookManager.getHook(WorldGuardHook.class); + this.antiRelog = hookManager.getHook(AntiRelogHook.class); } @Override @@ -44,9 +47,11 @@ protected void execute(final CommandSender sender, final String label, final Str return; } - if ((combatTagPlus != null && combatTagPlus.isTagged(player)) + if ( (combatTagPlus != null && combatTagPlus.isTagged(player)) || (pvpManager != null && pvpManager.isTagged(player)) - || (combatLogX != null && combatLogX.isTagged(player))) { + || (combatLogX != null && combatLogX.isTagged(player)) + || (antiRelog != null && antiRelog.isTagged(player)) + ) { lang.sendMessage(sender, "ERROR.duel.is-tagged"); return; } @@ -70,7 +75,7 @@ protected void execute(final CommandSender sender, final String label, final Str final Player target = Bukkit.getPlayerExact(args[1]); - if (target == null || !player.canSee(target)) { + if (target == null /*|| !player.canSee(target)*/) { lang.sendMessage(sender, "ERROR.player.not-found", "name", args[1]); return; } diff --git a/duels-plugin/src/main/java/me/realized/duels/command/commands/duel/subcommands/DenyCommand.java b/duels-plugin/src/main/java/me/realized/duels/command/commands/duel/subcommands/DenyCommand.java index 0a5b8362..76ab2a93 100644 --- a/duels-plugin/src/main/java/me/realized/duels/command/commands/duel/subcommands/DenyCommand.java +++ b/duels-plugin/src/main/java/me/realized/duels/command/commands/duel/subcommands/DenyCommand.java @@ -19,7 +19,7 @@ protected void execute(final CommandSender sender, final String label, final Str final Player player = (Player) sender; final Player target = Bukkit.getPlayerExact(args[1]); - if (target == null || !player.canSee(target)) { + if (target == null /*|| !player.canSee(target)*/) { lang.sendMessage(sender, "ERROR.player.not-found", "name", args[1]); return; } diff --git a/duels-plugin/src/main/java/me/realized/duels/config/Config.java b/duels-plugin/src/main/java/me/realized/duels/config/Config.java index d9ab207e..be741c59 100644 --- a/duels-plugin/src/main/java/me/realized/duels/config/Config.java +++ b/duels-plugin/src/main/java/me/realized/duels/config/Config.java @@ -26,6 +26,10 @@ public class Config extends AbstractConfiguration { @Getter private boolean ctpPreventTag; @Getter + private boolean arPreventDuel; + @Getter + private boolean arPreventTag; + @Getter private boolean pmPreventDuel; @Getter private boolean pmPreventTag; @@ -98,6 +102,8 @@ public class Config extends AbstractConfiguration { @Getter private boolean startCommandsQueueOnly; @Getter + private int minY; + @Getter private List startCommands; @Getter private boolean endCommandsEnabled; @@ -106,6 +112,12 @@ public class Config extends AbstractConfiguration { @Getter private List endCommands; @Getter + private boolean tieCommandsEnabled; + @Getter + private boolean tieCommandsQueueOnly; + @Getter + private List tieCommands; + @Getter private boolean projectileHitMessageEnabled; @Getter private List projectileHitMessageTypes; @@ -136,6 +148,8 @@ public class Config extends AbstractConfiguration { @Getter private boolean preventItemDrop; @Getter + private boolean clearItemsAfterMatch; + @Getter private boolean preventItemPickup; @Getter private boolean limitTeleportEnabled; @@ -265,6 +279,8 @@ protected void loadValues(FileConfiguration configuration) throws Exception { ctpPreventDuel = configuration.getBoolean("supported-plugins.CombatTagPlus.prevent-duel-if-tagged", true); ctpPreventTag = configuration.getBoolean("supported-plugins.CombatTagPlus.prevent-tag-in-duel", true); + arPreventDuel = configuration.getBoolean("supported-plugins.AntiRelog.prevent-duel-if-tagged", true); + arPreventTag = configuration.getBoolean("supported-plugins.AntiRelog.prevent-tag-in-duel", true); pmPreventDuel = configuration.getBoolean("supported-plugins.PvPManager.prevent-duel-if-tagged", true); pmPreventTag = configuration.getBoolean("supported-plugins.PvPManager.prevent-tag-in-duel", true); clxPreventDuel = configuration.getBoolean("supported-plugins.CombatLogX.prevent-duel-if-tagged", true); @@ -300,12 +316,16 @@ protected void loadValues(FileConfiguration configuration) throws Exception { expiration = Math.max(configuration.getInt("request.expiration", 30), 0); maxDuration = configuration.getInt("duel.match.max-duration", -1); + minY = configuration.getInt("duel.match.min-y", -100); startCommandsEnabled = configuration.getBoolean("duel.match.start-commands.enabled", false); startCommandsQueueOnly = configuration.getBoolean("duel.match.start-commands.queue-matches-only", false); startCommands = configuration.getStringList("duel.match.start-commands.commands"); endCommandsEnabled = configuration.getBoolean("duel.match.end-commands.enabled", false); endCommandsQueueOnly = configuration.getBoolean("duel.match.end-commands.queue-matches-only", false); endCommands = configuration.getStringList("duel.match.end-commands.commands"); + tieCommandsEnabled = configuration.getBoolean("duel.match.tie-commands.enabled", false); + tieCommandsQueueOnly = configuration.getBoolean("duel.match.tie-commands.queue-matches-only", false); + tieCommands = configuration.getStringList("duel.match.tie-commands.commands"); projectileHitMessageEnabled = configuration.getBoolean("duel.projectile-hit-message.enabled", true); projectileHitMessageTypes = configuration.getStringList("duel.projectile-hit-message.types"); preventInventoryOpen = configuration.getBoolean("duel.prevent-inventory-open", true); @@ -321,6 +341,7 @@ protected void loadValues(FileConfiguration configuration) throws Exception { arenaOnlyEndMessage = configuration.getBoolean("duel.arena-only-end-message", false); displayInventories = configuration.getBoolean("duel.display-inventories", true); preventItemDrop = configuration.getBoolean("duel.prevent-item-drop", false); + clearItemsAfterMatch = configuration.getBoolean("duel.clear-items-after-duel", false); preventItemPickup = configuration.getBoolean("duel.prevent-item-pickup", true); limitTeleportEnabled = configuration.getBoolean("duel.limit-teleportation.enabled", true); distanceAllowed = configuration.getDouble("duel.limit-teleportation.distance-allowed", 5.0); diff --git a/duels-plugin/src/main/java/me/realized/duels/duel/DuelManager.java b/duels-plugin/src/main/java/me/realized/duels/duel/DuelManager.java index 378ab220..f4339d63 100644 --- a/duels-plugin/src/main/java/me/realized/duels/duel/DuelManager.java +++ b/duels-plugin/src/main/java/me/realized/duels/duel/DuelManager.java @@ -1,15 +1,8 @@ package me.realized.duels.duel; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; +import java.util.*; + +import com.massivecraft.factions.P; import me.realized.duels.DuelsPlugin; import me.realized.duels.api.event.match.MatchEndEvent.Reason; import me.realized.duels.api.event.match.MatchStartEvent; @@ -54,12 +47,14 @@ import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.Firework; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.inventory.InventoryOpenEvent; @@ -130,13 +125,27 @@ public void handleLoad() { final MatchImpl match = arena.getMatch(); // Only handle undecided matches (size > 1) - if (match == null || match.getDurationInMillis() < (config.getMaxDuration() * 60 * 1000L) || arena.size() <= 1) { + if (match == null + || match.getDurationInMillis() < (config.getMaxDuration() * 60 * 1000L) + || arena.size() <= 1) { continue; } - for (final Player player : match.getAllPlayers()) { + Player winner = match.getWinnerOfDamage(), + looser = match.getLooserOfDamage(); + + if (winner != looser) { + looser.damage(1000); + return; + } + + Set members = match.getAllPlayers(); + + for (final Player player : members) { + handleTie(player, arena, match, true); lang.sendMessage(player, "DUEL.on-end.tie"); + } arena.endMatch(null, null, Reason.MAX_TIME_REACHED); @@ -186,9 +195,9 @@ public void handleUnload() { * Resets the player's inventory and balance in the case of a tie game. * * @param player Player to reset state - * @param arena Arena the match is taking place - * @param match Match the player is in - * @param alive Whether the player was alive in the match when the method was called. + * @param arena Arena the match is taking place + * @param match Match the player is in + * @param alive Whether the player was alive in the match when the method was called. */ private void handleTie(final Player player, final ArenaImpl arena, final MatchImpl match, boolean alive) { arena.remove(player); @@ -202,13 +211,30 @@ private void handleTie(final Player player, final ArenaImpl arena, final MatchIm mcMMO.enableSkills(player); } + if (config.isTieCommandsEnabled() && !(!match.isFromQueue() && config.isTieCommandsQueueOnly())) { + try { + for (final String command : config.getTieCommands()) { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command + .replace("%player%", player.getName()) + .replace("%kit%", match.getKit().getName()).replace("%arena%", arena.getName()) + .replace("%bet_amount%", String.valueOf(match.getBet())) + ); + } + } catch (Exception ex) { + Log.warn(DuelManager.this, "Error while running match tie commands: " + ex.getMessage()); + } + } + final PlayerInfo info = playerManager.get(player); final List items = match.getItems(player); if (alive) { - PlayerUtil.reset(player); playerManager.remove(player); + if (!(match.isOwnInventory() && config.isOwnInventoryDropInventoryItems())) { + PlayerUtil.reset(player); + } + if (info != null) { teleport.tryTeleport(player, info.getLocation()); info.restore(player); @@ -230,10 +256,10 @@ private void handleTie(final Player player, final ArenaImpl arena, final MatchIm /** * Rewards the duel winner with money and items bet on the match. * - * @param player Player determined to be the winner + * @param player Player determined to be the winner * @param opponent Player that opposed the winner - * @param arena Arena the match is taking place - * @param match Match the player is in + * @param arena Arena the match is taking place + * @param match Match the player is in */ private void handleWin(final Player player, final Player opponent, final ArenaImpl arena, final MatchImpl match) { arena.remove(player); @@ -376,8 +402,8 @@ private boolean isBlacklistedWorld(final Player player) { private boolean isTagged(final Player player) { return (combatTagPlus != null && combatTagPlus.isTagged(player)) - || (pvpManager != null && pvpManager.isTagged(player)) - || (combatLogX != null && combatLogX.isTagged(player)); + || (pvpManager != null && pvpManager.isTagged(player)) + || (combatLogX != null && combatLogX.isTagged(player)); } private boolean notInLoc(final Player player, final Location location) { @@ -387,9 +413,9 @@ private boolean notInLoc(final Player player, final Location location) { final Location source = player.getLocation(); return !source.getWorld().equals(location.getWorld()) - || source.getBlockX() != location.getBlockX() - || source.getBlockY() != location.getBlockY() - || source.getBlockZ() != location.getBlockZ(); + || source.getBlockX() != location.getBlockX() + || source.getBlockY() != location.getBlockY() + || source.getBlockZ() != location.getBlockZ(); } private boolean notInDz(final Player player, final String duelzone) { @@ -489,14 +515,14 @@ private void handleStats(final MatchImpl match, final UserData winner, final Use } final String message = lang.getMessage("DUEL.on-end.opponent-defeat", - "winner", winner.getName(), - "loser", loser.getName(), - "health", matchData.getHealth(), - "kit", matchData.getKit(), - "arena", match.getArena().getName(), - "winner_rating", winnerRating, - "loser_rating", loserRating, - "change", change + "winner", winner.getName(), + "loser", loser.getName(), + "health", matchData.getHealth(), + "kit", matchData.getKit(), + "arena", match.getArena().getName(), + "winner_rating", winnerRating, + "loser_rating", loserRating, + "change", change ); if (message == null) { @@ -514,7 +540,7 @@ private void handleStats(final MatchImpl match, final UserData winner, final Use private class DuelListener implements Listener { @EventHandler(priority = EventPriority.HIGHEST) - public void on(final PlayerDeathEvent event) { + public void onDeath(final PlayerDeathEvent event) { final Player player = event.getEntity(); final ArenaImpl arena = arenaManager.get(player); @@ -537,17 +563,19 @@ public void on(final PlayerDeathEvent event) { if (top.getType() == InventoryType.CRAFTING) { top.clear(); } - - if (!(match.isOwnInventory() && config.isOwnInventoryDropInventoryItems())) { + + if (!(match.isOwnInventory() && config.isOwnInventoryDropInventoryItems())) { event.getDrops().clear(); event.setKeepLevel(true); event.setDroppedExp(0); event.setKeepInventory(false); } - + inventoryManager.create(player, true); arena.remove(player); + boolean isDead = player.isDead(); + // Call end task only on the first death if (arena.size() <= 0) { return; @@ -583,15 +611,23 @@ public void on(final PlayerDeathEvent event) { handleStats(match, userDataManager.get(winner), userDataManager.get(player), matchData); plugin.doSyncAfter(() -> handleInventories(match), 1L); plugin.doSyncAfter(() -> { + if (!isDead) { + PlayerInfo info = playerManager.get(player); + if (info != null) { + teleport.tryTeleport(player, info.getLocation()); + info.restore(player); + } + } + handleWin(winner, player, arena, match); if (config.isEndCommandsEnabled() && !(!match.isFromQueue() && config.isEndCommandsQueueOnly())) { try { for (final String command : config.getEndCommands()) { Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command - .replace("%winner%", winner.getName()).replace("%loser%", player.getName()) - .replace("%kit%", kitName).replace("%arena%", arena.getName()) - .replace("%bet_amount%", String.valueOf(match.getBet())) + .replace("%winner%", winner.getName()).replace("%loser%", player.getName()) + .replace("%kit%", kitName).replace("%arena%", arena.getName()) + .replace("%bet_amount%", String.valueOf(match.getBet())) ); } } catch (Exception ex) { @@ -620,6 +656,23 @@ public void on(final EntityDamageEvent event) { event.setCancelled(true); } + @EventHandler(ignoreCancelled = true) + public void on(final EntityDamageByEntityEvent event) { + if (!(event.getEntity() instanceof Player) || (!(event.getDamager() instanceof Player))) { + return; + } + + final Player killer = (Player) event.getDamager(); + + final ArenaImpl arena = arenaManager.get(killer); + + if (arena == null || !arena.isEndGame()) { + return; + } + + event.setCancelled(true); + } + @EventHandler public void on(final PlayerQuitEvent event) { final Player player = event.getPlayer(); @@ -632,13 +685,19 @@ public void on(final PlayerQuitEvent event) { } @EventHandler(ignoreCancelled = true) - public void on(final PlayerDropItemEvent event) { - if (!config.isPreventItemDrop() || !arenaManager.isInMatch(event.getPlayer())) { + public void onDropItem(final PlayerDropItemEvent event) { + Player player = event.getPlayer(); + if (!arenaManager.isInMatch(event.getPlayer())) { return; } - event.setCancelled(true); - lang.sendMessage(event.getPlayer(), "DUEL.prevent.item-drop"); + if (config.isPreventItemDrop()) { + event.setCancelled(true); + lang.sendMessage(event.getPlayer(), "DUEL.prevent.item-drop"); + } else if (config.isClearItemsAfterMatch()) { + arenaManager.get(player).getMatch().droppedItems.add(event.getItemDrop()); + } + } @EventHandler(ignoreCancelled = true) @@ -660,7 +719,7 @@ public void on(final PlayerCommandPreprocessEvent event) { final String command = event.getMessage().substring(1).split(" ")[0].toLowerCase(); if (!arenaManager.isInMatch(event.getPlayer()) - || (config.isBlockAllCommands() ? config.getWhitelistedCommands().contains(command) : !config.getBlacklistedCommands().contains(command))) { + || (config.isBlockAllCommands() ? config.getWhitelistedCommands().contains(command) : !config.getBlacklistedCommands().contains(command))) { return; } @@ -674,9 +733,9 @@ public void on(final PlayerTeleportEvent event) { final Location to = event.getTo(); if (!config.isLimitTeleportEnabled() - || event.getCause() == TeleportCause.ENDER_PEARL - || event.getCause() == TeleportCause.SPECTATE - || !arenaManager.isInMatch(player)) { + || event.getCause() == TeleportCause.ENDER_PEARL + || event.getCause() == TeleportCause.SPECTATE + || !arenaManager.isInMatch(player)) { return; } diff --git a/duels-plugin/src/main/java/me/realized/duels/gui/options/OptionsGui.java b/duels-plugin/src/main/java/me/realized/duels/gui/options/OptionsGui.java index 98f1eeaa..ea80a218 100644 --- a/duels-plugin/src/main/java/me/realized/duels/gui/options/OptionsGui.java +++ b/duels-plugin/src/main/java/me/realized/duels/gui/options/OptionsGui.java @@ -48,7 +48,11 @@ public enum Option { SOUP(Items.MUSHROOM_SOUP, Characteristic.SOUP, "When enabled, players will", "receive the amount of health", "defined in config when", "right-clicking a soup."), SUMO(Material.SLIME_BALL, Characteristic.SUMO, "When enabled, players will ", "lose health only when", "interacting with water or lava."), UHC(Material.GOLDEN_APPLE, Characteristic.UHC, "When enabled, player's health", "will not naturally regenerate."), - COMBO(Material.IRON_SWORD, Characteristic.COMBO, "When enabled, players will", "have no delay between hits."); + COMBO(Material.IRON_SWORD, Characteristic.COMBO, "When enabled, players will", "have no delay between hits."), + HUNGER(Material.COOKED_BEEF, Characteristic.HUNGER, "When enabled, players will", "not hungry."), + PLACE(Material.STONE, Characteristic.PLACE, "When enabled, players can", "be placed blocks in arena."), + BREAK(Material.STONE, Characteristic.BREAK, "When enabled, players can", "be break blocks in arena."), + BOXING(Material.DIAMOND_CHESTPLATE, Characteristic.BOXING, "When enabled, players will", "need give opponent 100 hits."); @Getter private final Material displayed; diff --git a/duels-plugin/src/main/java/me/realized/duels/gui/settings/SettingsBotGui.java b/duels-plugin/src/main/java/me/realized/duels/gui/settings/SettingsBotGui.java new file mode 100644 index 00000000..6332084a --- /dev/null +++ b/duels-plugin/src/main/java/me/realized/duels/gui/settings/SettingsBotGui.java @@ -0,0 +1,54 @@ +package me.realized.duels.gui.settings; + +import me.realized.duels.DuelsPlugin; +import me.realized.duels.config.Config; +import me.realized.duels.gui.BaseButton; +import me.realized.duels.gui.settings.buttons.*; +import me.realized.duels.util.compat.Items; +import me.realized.duels.util.gui.SinglePageGui; +import me.realized.duels.util.inventory.Slots; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; + +public class SettingsBotGui extends SinglePageGui { + + private static final int[][] PATTERNS = { + {13}, + {12, 14}, + {12, 13, 14}, + {12, 13, 14, 22} + }; + + public SettingsBotGui(final DuelsPlugin plugin) { + super(plugin, plugin.getLang().getMessage("GUI.settings.title"), 3); + final Config config = plugin.getConfiguration(); + final ItemStack spacing = Items.from(config.getSettingsFillerType(), config.getSettingsFillerData()); + Slots.run(2, 7, slot -> inventory.setItem(slot, spacing)); + Slots.run(11, 16, slot -> inventory.setItem(slot, spacing)); + Slots.run(20, 25, slot -> inventory.setItem(slot, spacing)); + set(4, new RequestDetailsButton(plugin)); + + final List buttons = new ArrayList<>(); + + if (config.isKitSelectingEnabled()) { + buttons.add(new KitSelectButton(plugin)); + } + + if (config.isArenaSelectingEnabled()) { + buttons.add(new ArenaSelectButton(plugin)); + } + + if (!buttons.isEmpty()) { + final int[] pattern = PATTERNS[buttons.size() - 1]; + + for (int i = 0; i < buttons.size(); i++) { + set(pattern[i], buttons.get(i)); + } + } + + set(0, 2, 3, new BotStartMatchButton(plugin)); + set(7, 9, 3, new CancelButton(plugin)); + } +} diff --git a/duels-plugin/src/main/java/me/realized/duels/gui/settings/buttons/BotStartMatchButton.java b/duels-plugin/src/main/java/me/realized/duels/gui/settings/buttons/BotStartMatchButton.java new file mode 100644 index 00000000..842d8778 --- /dev/null +++ b/duels-plugin/src/main/java/me/realized/duels/gui/settings/buttons/BotStartMatchButton.java @@ -0,0 +1,53 @@ +package me.realized.duels.gui.settings.buttons; + +import me.realized.duels.DuelsPlugin; +import me.realized.duels.gui.BaseButton; +import me.realized.duels.kit.KitImpl; +import me.realized.duels.setting.Settings; +import me.realized.duels.util.compat.Items; +import me.realized.duels.util.inventory.ItemBuilder; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +public class BotStartMatchButton extends BaseButton { + + public BotStartMatchButton(final DuelsPlugin plugin) { + super(plugin, ItemBuilder.of(Items.GREEN_PANE.clone()).name(plugin.getLang().getMessage("GUI.settings.buttons.send.name")).build()); + } + + @Override + public void onClick(final Player player) { + final Settings settings = settingManager.getSafely(player); + + if (settings.getTarget() == null) { + settings.reset(); + player.closeInventory(); + return; + } + + final Player target = Bukkit.getPlayer(settings.getTarget()); + + if (target == null) { + settings.reset(); + player.closeInventory(); + lang.sendMessage(player, "ERROR.player.no-longer-online"); + return; + } + + if (!settings.isOwnInventory() && settings.getKit() == null) { + player.closeInventory(); + + KitImpl kit = settings.getArena().getKits().stream().findAny().orElse(null); + + if (kit == null) { + lang.sendMessage(player, "ERROR.duel.mode-unselected"); + return; + } + + settings.setKit(kit); + } + + player.closeInventory(); + requestManager.send(player, target, settings); + } +} diff --git a/duels-plugin/src/main/java/me/realized/duels/gui/settings/buttons/RequestSendButton.java b/duels-plugin/src/main/java/me/realized/duels/gui/settings/buttons/RequestSendButton.java index f5c3b310..2ac72e6c 100644 --- a/duels-plugin/src/main/java/me/realized/duels/gui/settings/buttons/RequestSendButton.java +++ b/duels-plugin/src/main/java/me/realized/duels/gui/settings/buttons/RequestSendButton.java @@ -2,6 +2,7 @@ import me.realized.duels.DuelsPlugin; import me.realized.duels.gui.BaseButton; +import me.realized.duels.kit.KitImpl; import me.realized.duels.setting.Settings; import me.realized.duels.util.compat.Items; import me.realized.duels.util.inventory.ItemBuilder; @@ -35,8 +36,15 @@ public void onClick(final Player player) { if (!settings.isOwnInventory() && settings.getKit() == null) { player.closeInventory(); - lang.sendMessage(player, "ERROR.duel.mode-unselected"); - return; + + KitImpl kit = settings.getArena().getKits().stream().findAny().orElse(null); + + if (kit == null) { + lang.sendMessage(player, "ERROR.duel.mode-unselected"); + return; + } + + settings.setKit(kit); } player.closeInventory(); diff --git a/duels-plugin/src/main/java/me/realized/duels/hook/HookManager.java b/duels-plugin/src/main/java/me/realized/duels/hook/HookManager.java index 8eaf9805..8d615934 100644 --- a/duels-plugin/src/main/java/me/realized/duels/hook/HookManager.java +++ b/duels-plugin/src/main/java/me/realized/duels/hook/HookManager.java @@ -1,19 +1,7 @@ package me.realized.duels.hook; import me.realized.duels.DuelsPlugin; -import me.realized.duels.hook.hooks.BountyHuntersHook; -import me.realized.duels.hook.hooks.CombatLogXHook; -import me.realized.duels.hook.hooks.CombatTagPlusHook; -import me.realized.duels.hook.hooks.EssentialsHook; -import me.realized.duels.hook.hooks.FactionsHook; -import me.realized.duels.hook.hooks.LeaderHeadsHook; -import me.realized.duels.hook.hooks.MVdWPlaceholderHook; -import me.realized.duels.hook.hooks.McMMOHook; -import me.realized.duels.hook.hooks.MyPetHook; -import me.realized.duels.hook.hooks.PlaceholderHook; -import me.realized.duels.hook.hooks.PvPManagerHook; -import me.realized.duels.hook.hooks.SimpleClansHook; -import me.realized.duels.hook.hooks.VaultHook; +import me.realized.duels.hook.hooks.*; import me.realized.duels.hook.hooks.worldguard.WorldGuardHook; import me.realized.duels.util.hook.AbstractHookManager; @@ -23,6 +11,7 @@ public HookManager(final DuelsPlugin plugin) { super(plugin); register(BountyHuntersHook.NAME, BountyHuntersHook.class); register(CombatLogXHook.NAME, CombatLogXHook.class); + register(AntiRelogHook.NAME, AntiRelogHook.class); register(CombatTagPlusHook.NAME, CombatTagPlusHook.class); register(EssentialsHook.NAME, EssentialsHook.class); register(FactionsHook.NAME, FactionsHook.class); diff --git a/duels-plugin/src/main/java/me/realized/duels/hook/hooks/AntiRelogHook.java b/duels-plugin/src/main/java/me/realized/duels/hook/hooks/AntiRelogHook.java new file mode 100644 index 00000000..a88e5883 --- /dev/null +++ b/duels-plugin/src/main/java/me/realized/duels/hook/hooks/AntiRelogHook.java @@ -0,0 +1,58 @@ +package me.realized.duels.hook.hooks; + +import me.realized.duels.DuelsPlugin; +import me.realized.duels.arena.ArenaManagerImpl; +import me.realized.duels.config.Config; +import me.realized.duels.util.hook.PluginHook; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import ru.leymooo.antirelog.Antirelog; +import ru.leymooo.antirelog.event.PvpPreStartEvent; +import ru.leymooo.antirelog.event.PvpStartedEvent; + +public class AntiRelogHook extends PluginHook { + + public static final String NAME = "AntiRelog"; + + private final Config config; + private final ArenaManagerImpl arenaManager; + + public AntiRelogHook(final DuelsPlugin plugin) { + super(plugin, NAME); + this.config = plugin.getConfiguration(); + this.arenaManager = plugin.getArenaManager(); + + try { + Class.forName("ru.leymooo.antirelog.event.PvpPreStartEvent"); + } catch (ClassNotFoundException ex) { + throw new RuntimeException("This version of " + getName() + " is not supported. Please try upgrading to the latest version."); + } + + Bukkit.getPluginManager().registerEvents(new AntiRelogHook.AntiRelogListener(), plugin); + } + + public boolean isTagged(final Player player) { + return config.isArPreventDuel() && ((Antirelog) getPlugin()).getPvpManager().isInPvP(player); + } + + public class AntiRelogListener implements Listener { + + @EventHandler(ignoreCancelled = true) + public void on(final PvpPreStartEvent event) { + if (!config.isArPreventTag()) { + return; + } + + final Player attacker = event.getAttacker(); + final Player defender = event.getDefender(); + + if (!arenaManager.isInMatch(attacker) && !arenaManager.isInMatch(defender)) { + return; + } + + event.setCancelled(true); + } + } +} diff --git a/duels-plugin/src/main/java/me/realized/duels/hook/hooks/PlaceholderHook.java b/duels-plugin/src/main/java/me/realized/duels/hook/hooks/PlaceholderHook.java index b8a07ce9..61a97d4a 100644 --- a/duels-plugin/src/main/java/me/realized/duels/hook/hooks/PlaceholderHook.java +++ b/duels-plugin/src/main/java/me/realized/duels/hook/hooks/PlaceholderHook.java @@ -2,9 +2,15 @@ import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.realized.duels.DuelsPlugin; +import me.realized.duels.api.arena.ArenaManager; +import me.realized.duels.arena.ArenaImpl; +import me.realized.duels.arena.ArenaManagerImpl; import me.realized.duels.data.UserData; import me.realized.duels.data.UserManagerImpl; +import me.realized.duels.util.StringUtil; import me.realized.duels.util.hook.PluginHook; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.entity.Player; public class PlaceholderHook extends PluginHook { @@ -60,6 +66,50 @@ public String onPlaceholderRequest(final Player player, final String identifier) return String.valueOf(user.getLosses()); case "can_request": return String.valueOf(user.canRequest()); + + case "hits": { + final ArenaManagerImpl arenaManager = plugin.getArenaManager(); + final ArenaImpl arena = arenaManager.get(player); + + // Only activate when winner is undeclared + if (arena == null || !arenaManager.isInMatch(player) || arena.isEndGame()) { + return "-1"; + } + + return String.valueOf(arena.getMatch().getHits(player)); + } + + case "hits_opponent": { + final ArenaManagerImpl arenaManager = plugin.getArenaManager(); + final ArenaImpl arena = arenaManager.get(player); + + // Only activate when winner is undeclared + if (arena == null || !arenaManager.isInMatch(player) || arena.isEndGame()) { + return "-1"; + } + + return String.valueOf(arena.getMatch().getHits(arena.getOpponent(player))); + } + + case "hits_diff": { + final ArenaManagerImpl arenaManager = plugin.getArenaManager(); + final ArenaImpl arena = arenaManager.get(player); + + // Only activate when winner is undeclared + if (arena == null || !arenaManager.isInMatch(player) || arena.isEndGame()) { + return "-1"; + } + + int playerHits = arena.getMatch().getHits(player); + int opponentHits = arena.getMatch().getHits(arena.getOpponent(player)); + + int hitsDiff = playerHits - opponentHits; + + String color = hitsDiff == 0? "&7" : hitsDiff > 0? "&a+" : "&c"; + + return StringUtil.color(color) + hitsDiff; + } + } return null; diff --git a/duels-plugin/src/main/java/me/realized/duels/kit/KitImpl.java b/duels-plugin/src/main/java/me/realized/duels/kit/KitImpl.java index 4ca26535..33dd9c52 100644 --- a/duels-plugin/src/main/java/me/realized/duels/kit/KitImpl.java +++ b/duels-plugin/src/main/java/me/realized/duels/kit/KitImpl.java @@ -1,10 +1,7 @@ package me.realized.duels.kit; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; + import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; @@ -39,8 +36,8 @@ public class KitImpl extends BaseButton implements Kit { @Setter(value = AccessLevel.PACKAGE) private boolean removed; - public KitImpl(final DuelsPlugin plugin, final String name, final ItemStack displayed, final boolean usePermission, final boolean arenaSpecific, - final Set characteristics) { + public KitImpl(final DuelsPlugin plugin, final String name, final ItemStack displayed, final boolean usePermission, + final boolean arenaSpecific, final Set characteristics) { super(plugin, displayed != null ? displayed : ItemBuilder .of(Material.DIAMOND_SWORD) .name("&7&l" + name) @@ -148,10 +145,13 @@ public int hashCode() { } public enum Characteristic { - SOUP, SUMO, UHC, - COMBO; + COMBO, + HUNGER, + PLACE, + BREAK, + BOXING; } } diff --git a/duels-plugin/src/main/java/me/realized/duels/listeners/DamageListener.java b/duels-plugin/src/main/java/me/realized/duels/listeners/DamageListener.java index 4bd61203..b6507b01 100644 --- a/duels-plugin/src/main/java/me/realized/duels/listeners/DamageListener.java +++ b/duels-plugin/src/main/java/me/realized/duels/listeners/DamageListener.java @@ -1,15 +1,23 @@ package me.realized.duels.listeners; import me.realized.duels.DuelsPlugin; +import me.realized.duels.api.event.match.MatchEndEvent; import me.realized.duels.arena.ArenaImpl; import me.realized.duels.arena.ArenaManagerImpl; +import me.realized.duels.kit.KitImpl; +import me.realized.duels.player.PlayerInfo; import me.realized.duels.util.EventUtil; +import me.realized.duels.util.PlayerUtil; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.PlayerDeathEvent; + +import java.util.ArrayList; +import java.util.Arrays; /** * Overrides damage cancellation by other plugins for players in a duel. @@ -27,8 +35,8 @@ public DamageListener(final DuelsPlugin plugin) { } @EventHandler(priority = EventPriority.HIGHEST) - public void on(final EntityDamageByEntityEvent event) { - if (!event.isCancelled() || !(event.getEntity() instanceof Player)) { + public void onDamage(final EntityDamageByEntityEvent event) { + if (!(event.getEntity() instanceof Player)) { return; } @@ -46,6 +54,26 @@ public void on(final EntityDamageByEntityEvent event) { return; } + KitImpl.Characteristic characteristic = arena.getMatch().getKit().getCharacteristics().stream().filter( + c -> c == KitImpl.Characteristic.BOXING).findFirst().orElse(null); + + if(characteristic != null) { + if(arena.getMatch().getHits(damager) >= 99) { + player.getInventory().clear(); + PlayerDeathEvent customEvent = new PlayerDeathEvent(player, + new ArrayList<>(), 0, + "Suck " + damager.getDisplayName() + " on boxing fight!"); + PlayerUtil.reset(player); + Bukkit.getPluginManager().callEvent(customEvent); + return; + } + } + + arena.getMatch().addDamageToPlayer(damager, event.getFinalDamage()); + + if(!event.isCancelled()) return; + event.setCancelled(false); } + } diff --git a/duels-plugin/src/main/java/me/realized/duels/listeners/KitOptionsListener.java b/duels-plugin/src/main/java/me/realized/duels/listeners/KitOptionsListener.java index 4e1e400b..2dcaa003 100644 --- a/duels-plugin/src/main/java/me/realized/duels/listeners/KitOptionsListener.java +++ b/duels-plugin/src/main/java/me/realized/duels/listeners/KitOptionsListener.java @@ -13,16 +13,19 @@ import me.realized.duels.util.compat.Items; import me.realized.duels.util.metadata.MetadataUtil; import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.Event.Result; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; +import org.bukkit.event.block.*; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityRegainHealthEvent; import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason; +import org.bukkit.event.entity.FoodLevelChangeEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.inventory.EquipmentSlot; @@ -62,13 +65,101 @@ public void on(final EntityDamageEvent event) { final Player player = (Player) event.getEntity(); final ArenaImpl arena = arenaManager.get(player); - if (arena == null || !isEnabled(arena, Characteristic.SUMO)) { + if (arena == null || !isEnabled(arena, Characteristic.SUMO) && !isEnabled(arena, Characteristic.BOXING)) { return; } event.setDamage(0); } + @EventHandler + public void on(final FoodLevelChangeEvent event) { + if (!(event.getEntity() instanceof Player)) { + return; + } + + final Player player = (Player) event.getEntity(); + final ArenaImpl arena = arenaManager.get(player); + + if (arena == null || !isEnabled(arena, Characteristic.HUNGER)) { + return; + } + + event.setCancelled(true); + } + + @EventHandler + public void onInteractLiquid(PlayerInteractEvent event) { + if (event.getAction() != Action.RIGHT_CLICK_BLOCK || !event.hasBlock()) { + return; + } + + ArenaImpl arena = arenaManager.get(event.getPlayer()); + + if (arena == null || !isEnabled(arena, Characteristic.PLACE)) { + return; + } + + ItemStack item = event.getItem(); + + if (item == null) { + return; + } + + String itemType = item.getType().name().toLowerCase(); + + if (!itemType.contains("water") && !itemType.contains("lava")) { + return; + } + + arena.getMatch().liquids.add(event.getClickedBlock()); + } + + @EventHandler + public void on(BlockPlaceEvent event) { + final Player player = event.getPlayer(); + final ArenaImpl arena = arenaManager.get(player); + + if (arena == null) { + return; + } + + if(isEnabled(arena, Characteristic.PLACE)) { + Material blockType = event.getBlockReplacedState().getType(); + if(blockType == Material.AIR || blockType == Material.WATER || blockType == Material.LAVA) { + arena.getMatch().placedBlocks.add(event.getBlock()); + return; + } + } + + event.setCancelled(true); + } + + @EventHandler + public void on(BlockBreakEvent event) { + final Player player = event.getPlayer(); + final ArenaImpl arena = arenaManager.get(player); + + if (arena == null || arena.getMatch() == null) { + return; + } + + if(arena.getMatch().placedBlocks.contains(event.getBlock())) { + return; + } + + if(isEnabled(arena, Characteristic.BREAK)) { + if(arena.getMatch().brokenBlocks.containsKey(event.getBlock().getLocation())) { + return; + } + + arena.getMatch().brokenBlocks.put(event.getBlock().getLocation(), event.getBlock().getBlockData().clone()); + return; + } + + event.setCancelled(true); + } + @EventHandler public void on(final PlayerMoveEvent event) { final Player player = event.getPlayer(); @@ -78,6 +169,17 @@ public void on(final PlayerMoveEvent event) { return; } + final Location to = event.getTo(), from = event.getFrom(); + + if ((from.getBlockX() != + to.getBlockX() || from.getBlockY() != to.getBlockY() || from.getBlockZ() != to.getBlockZ()) + && arena.getCountdown() != null) { + from.setPitch(player.getLocation().getPitch()); + from.setYaw(player.getLocation().getYaw()); + event.setTo(from); + return; + } + final Block block = event.getFrom().getBlock(); if (!(block.getType().name().contains("WATER") || block.getType().name().contains("LAVA"))) { diff --git a/duels-plugin/src/main/java/me/realized/duels/player/PlayerInfo.java b/duels-plugin/src/main/java/me/realized/duels/player/PlayerInfo.java index b4e1d88e..ed6faf13 100644 --- a/duels-plugin/src/main/java/me/realized/duels/player/PlayerInfo.java +++ b/duels-plugin/src/main/java/me/realized/duels/player/PlayerInfo.java @@ -43,7 +43,7 @@ public PlayerInfo(final Player player, final boolean excludeInventory) { if (excludeInventory) { return; } - + InventoryUtil.addToMap(player.getInventory(), items); } diff --git a/duels-plugin/src/main/java/me/realized/duels/queue/QueueManager.java b/duels-plugin/src/main/java/me/realized/duels/queue/QueueManager.java index 37fd7518..523e19e9 100644 --- a/duels-plugin/src/main/java/me/realized/duels/queue/QueueManager.java +++ b/duels-plugin/src/main/java/me/realized/duels/queue/QueueManager.java @@ -337,8 +337,9 @@ public boolean queue(final Player player, final Queue queue) { return false; } - lang.sendMessage(player, "ERROR.queue.already-in"); - return false; + //lang.sendMessage(player, "ERROR.queue.already-in"); + found.removePlayer(player); + lang.sendMessage(player, "QUEUE.remove"); } if (spectateManager.isSpectating(player)) { diff --git a/duels-plugin/src/main/java/me/realized/duels/setting/Settings.java b/duels-plugin/src/main/java/me/realized/duels/setting/Settings.java index 6fbaed0e..0c70df44 100644 --- a/duels-plugin/src/main/java/me/realized/duels/setting/Settings.java +++ b/duels-plugin/src/main/java/me/realized/duels/setting/Settings.java @@ -7,15 +7,17 @@ import lombok.Setter; import me.realized.duels.DuelsPlugin; import me.realized.duels.arena.ArenaImpl; +import me.realized.duels.gui.settings.SettingsBotGui; import me.realized.duels.gui.settings.SettingsGui; import me.realized.duels.kit.KitImpl; +import me.realized.duels.util.gui.SinglePageGui; import org.bukkit.Location; import org.bukkit.entity.Player; public class Settings { private final DuelsPlugin plugin; - private final SettingsGui gui; + private final SinglePageGui gui; @Getter private UUID target; @@ -35,15 +37,16 @@ public class Settings { @Getter private Map cache = new HashMap<>(); - public Settings(final DuelsPlugin plugin, final Player player) { + public Settings(final DuelsPlugin plugin, final Player player, boolean bot) { this.plugin = plugin; - this.gui = player != null ? plugin.getGuiListener().addGui(player, new SettingsGui(plugin)) : null; + this.gui = player != null ? plugin.getGuiListener().addGui(player, bot? + new SettingsBotGui(plugin) : new SettingsGui(plugin)) : null; // If kits are disabled, then ownInventory is enabled by default. this.ownInventory = !plugin.getConfiguration().isKitSelectingEnabled(); } public Settings(final DuelsPlugin plugin) { - this(plugin, null); + this(plugin, null, false); } public void reset() { diff --git a/duels-plugin/src/main/java/me/realized/duels/setting/SettingsManager.java b/duels-plugin/src/main/java/me/realized/duels/setting/SettingsManager.java index 2a65a3a7..2c9be311 100644 --- a/duels-plugin/src/main/java/me/realized/duels/setting/SettingsManager.java +++ b/duels-plugin/src/main/java/me/realized/duels/setting/SettingsManager.java @@ -29,9 +29,14 @@ public void handleUnload() { cache.clear(); } + // Only one Settings instance stays in memory while player is online; no need for manual removal of gui from GuiListener + public Settings getSafely(final Player player, boolean bot) { + return cache.computeIfAbsent(player.getUniqueId(), result -> new Settings(plugin, player, bot)); + } + // Only one Settings instance stays in memory while player is online; no need for manual removal of gui from GuiListener public Settings getSafely(final Player player) { - return cache.computeIfAbsent(player.getUniqueId(), result -> new Settings(plugin, player)); + return getSafely(player, false); } @EventHandler diff --git a/duels-plugin/src/main/java/me/realized/duels/spectate/SpectateManagerImpl.java b/duels-plugin/src/main/java/me/realized/duels/spectate/SpectateManagerImpl.java index 3825d0f7..d9239e77 100644 --- a/duels-plugin/src/main/java/me/realized/duels/spectate/SpectateManagerImpl.java +++ b/duels-plugin/src/main/java/me/realized/duels/spectate/SpectateManagerImpl.java @@ -139,7 +139,7 @@ public Result startSpectating(@NotNull final Player player, @NotNull final Playe final MatchImpl match = arena.getMatch(); // Hide from players in match - if (match != null && !essentials.isVanished(player)) { + if (match != null && !(essentials != null && essentials.isVanished(player))) { match.getAllPlayers() .stream() .filter(arenaPlayer -> arenaPlayer.isOnline() && arenaPlayer.canSee(player)) @@ -224,7 +224,7 @@ public void stopSpectating(final Player player, final SpectatorImpl spectator) { final MatchImpl match = spectator.getArena().getMatch(); // Show to players in match - if (match != null && !essentials.isVanished(player)) { + if (match != null && !(essentials != null && essentials.isVanished(player))) { match.getAllPlayers() .stream() .filter(Player::isOnline) diff --git a/duels-plugin/src/main/java/me/realized/duels/util/inventory/InventoryUtil.java b/duels-plugin/src/main/java/me/realized/duels/util/inventory/InventoryUtil.java index 385a2bf0..a7cbc52a 100644 --- a/duels-plugin/src/main/java/me/realized/duels/util/inventory/InventoryUtil.java +++ b/duels-plugin/src/main/java/me/realized/duels/util/inventory/InventoryUtil.java @@ -11,7 +11,6 @@ import org.bukkit.inventory.PlayerInventory; public final class InventoryUtil { - private static final String INVENTORY_IDENTIFIER = "INVENTORY"; private static final String ARMOR_IDENTIFIER = "ARMOR"; diff --git a/duels-plugin/src/main/resources/config.yml b/duels-plugin/src/main/resources/config.yml index 0226a285..919c78a3 100644 --- a/duels-plugin/src/main/resources/config.yml +++ b/duels-plugin/src/main/resources/config.yml @@ -15,6 +15,14 @@ supported-plugins: # default: true prevent-duel-if-tagged: true + # If set to 'true', players will not be combat tagged while in duel. + # default: true + prevent-tag-in-duel: true + AntiRelog: + # If set to 'true', players will not be able to duel while combat tagged. + # default: true + prevent-duel-if-tagged: true + # If set to 'true', players will not be combat tagged while in duel. # default: true prevent-tag-in-duel: true @@ -165,15 +173,18 @@ duel: # default: -1 max-duration: -1 + # If player y value smaller than this value, player has been killed. + min-y: -100 + + # If set to 'true', start commands will run only for matches started through the queue system. + # default: false + queue-matches-only: false + start-commands: # If set to 'true', the commands listed below will run FOR EACH player at the start of a match. # default: false enabled: false - # If set to 'true', start commands will run only for matches started through the queue system. - # default: false - queue-matches-only: false - # Available placeholders: # %player% - Name of the player entering the match commands: @@ -195,6 +206,22 @@ duel: - 'give %winner% diamond 1' - 'give %loser% dirt 1' + tie-commands: + # If set to 'true', the commands listed below will run at the end of a match. + # default: false + enabled: false + + # If set to 'true', end commands will run only for matches started through the queue system. + # default: false + queue-matches-only: false + + # Available placeholders: + # %winner% - Name of the winner of the match + # %loser% - Name of the loser of the match + commands: + - 'give %winner% diamond 1' + - 'give %loser% dirt 1' + projectile-hit-message: # If set to 'true', a message from lang.yml will be sent to the shooter of the projectile. # default: true @@ -259,6 +286,9 @@ duel: # default: false prevent-item-drop: false + # If set to 'true', items drop on duel will be removed from map. + clear-items-after-duel: false + # If set to 'true', players in duel will not be able to pickup items on the ground. # default: false prevent-item-pickup: false @@ -371,7 +401,6 @@ queue: # List of commands to block while in a queue. blacklisted-commands: [] - rating: # If set to 'true', player's rating will change after match based on their opponent. # default: true diff --git a/duels-plugin/src/main/resources/lang.yml b/duels-plugin/src/main/resources/lang.yml index 1576df86..daaf2a10 100644 --- a/duels-plugin/src/main/resources/lang.yml +++ b/duels-plugin/src/main/resources/lang.yml @@ -76,7 +76,6 @@ ERROR: queue: not-found: '{PREFIX} &cNo queue exists with kit %kit% and bet $%bet_amount%.' already-exists: '{PREFIX} &cA queue with kit ''%kit%'' and bet $%bet_amount% already exists.' - already-in: '{PREFIX} &cYou are already in a queue!' not-in-queue: '{PREFIX} &cYou are not in a queue.' not-enough-money: '{PREFIX} &cYou need $%bet_amount% to join that queue.' no-queues-available: '{PREFIX} &cNo queues are available.' diff --git a/duels-plugin/src/main/resources/plugin.yml b/duels-plugin/src/main/resources/plugin.yml index 70064c37..7c4abb5f 100644 --- a/duels-plugin/src/main/resources/plugin.yml +++ b/duels-plugin/src/main/resources/plugin.yml @@ -20,6 +20,9 @@ commands: spectate: description: Spectate a duel. aliases: [spec] + botduel: + description: Spectate a duel. + aliases: [botfight, pvpbot, "bot", "duelbot"] duels: description: Administrative command of Duels. aliases: [ds] diff --git a/libs/antirelog-3.0.11.jar b/libs/antirelog-3.0.11.jar new file mode 100644 index 00000000..246046a1 Binary files /dev/null and b/libs/antirelog-3.0.11.jar differ