diff --git a/TerminatorPlus-API/src/main/java/net/nuggetmc/tplus/api/agent/legacyagent/LegacyAgent.java b/TerminatorPlus-API/src/main/java/net/nuggetmc/tplus/api/agent/legacyagent/LegacyAgent.java index ddc7f9f..57dc12d 100644 --- a/TerminatorPlus-API/src/main/java/net/nuggetmc/tplus/api/agent/legacyagent/LegacyAgent.java +++ b/TerminatorPlus-API/src/main/java/net/nuggetmc/tplus/api/agent/legacyagent/LegacyAgent.java @@ -48,6 +48,10 @@ public class LegacyAgent extends Agent { private final Set fallDamageCooldown = new HashSet<>(); public boolean offsets = true; private EnumTargetGoal goal; + private BoundingBox region; + private double regionWeightX; + private double regionWeightY; + private double regionWeightZ; public LegacyAgent(BotManager manager, Plugin plugin) { super(manager, plugin); @@ -1346,6 +1350,33 @@ public class LegacyAgent extends Agent { bot.attack(target); } + + public void setRegion(BoundingBox region, double regionWeightX, double regionWeightY, double regionWeightZ) { + this.region = region; + this.regionWeightX = regionWeightX; + this.regionWeightY = regionWeightY; + this.regionWeightZ = regionWeightZ; + } + + public BoundingBox getRegion() { + return region; + } + + public double getRegionWeightX() { + return regionWeightX; + } + + public double getRegionWeightY() { + return regionWeightY; + } + + public double getRegionWeightZ() { + return regionWeightZ; + } + + public EnumTargetGoal getTargetType() { + return goal; + } public void setTargetType(EnumTargetGoal goal) { this.goal = goal; @@ -1446,7 +1477,7 @@ public class LegacyAgent extends Agent { case PLAYER: { //Target a single player. Defaults to NEAREST_VULNERABLE_PLAYER if no player found. if (bot.getTargetPlayer() != null) { Player player = Bukkit.getPlayer(bot.getTargetPlayer()); - if (player != null) { + if (player != null && validateCloserEntity(player, loc, null)) { return player; } } @@ -1460,7 +1491,29 @@ public class LegacyAgent extends Agent { } private boolean validateCloserEntity(LivingEntity entity, Location loc, LivingEntity result) { - return loc.getWorld() == entity.getWorld() && !entity.isDead() && (result == null || loc.distance(entity.getLocation()) < loc.distance(result.getLocation())); + double regionDistEntity = getWeightedRegionDist(entity.getLocation()); + if (regionDistEntity == Double.MAX_VALUE) + return false; + double regionDistResult = result == null ? 0 : getWeightedRegionDist(result.getLocation()); + return loc.getWorld() == entity.getWorld() && !entity.isDead() + && (result == null || (loc.distance(entity.getLocation()) + regionDistEntity) < (loc.distance(result.getLocation())) + regionDistResult); + } + + private double getWeightedRegionDist(Location loc) { + if (region == null) + return 0; + double diffX = Math.min(0, Math.abs(region.getCenterX() - loc.getX()) - region.getWidthX() * 0.5); + double diffY = Math.max(0, Math.abs(region.getCenterY() - loc.getY()) - region.getHeight() * 0.5); + double diffZ = Math.max(0, Math.abs(region.getCenterZ() - loc.getZ()) - region.getWidthZ() * 0.5); + if (regionWeightX == 0 && regionWeightY == 0 && regionWeightZ == 0) { + if (diffX > 0 || diffY > 0 || diffZ > 0) + return Double.MAX_VALUE; + } else { + diffX *= regionWeightX; + diffY *= regionWeightY; + diffZ *= regionWeightZ; + } + return diffX * diffX + diffY * diffY + diffZ * diffZ; } @Override diff --git a/TerminatorPlus-Plugin/src/main/java/net/nuggetmc/tplus/bot/Bot.java b/TerminatorPlus-Plugin/src/main/java/net/nuggetmc/tplus/bot/Bot.java index 60b0301..f1665ac 100644 --- a/TerminatorPlus-Plugin/src/main/java/net/nuggetmc/tplus/bot/Bot.java +++ b/TerminatorPlus-Plugin/src/main/java/net/nuggetmc/tplus/bot/Bot.java @@ -600,7 +600,8 @@ public class Bot extends ServerPlayer implements Terminator { */ private boolean canStandOn(Material mat) { if(mat == Material.END_ROD || mat == Material.FLOWER_POT || mat == Material.REPEATER || mat == Material.COMPARATOR - || mat == Material.SNOW || mat == Material.LADDER || mat == Material.VINE) + || mat == Material.SNOW || mat == Material.LADDER || mat == Material.VINE || mat == Material.SCAFFOLDING + || mat == Material.AZALEA || mat == Material.FLOWERING_AZALEA) return true; if(mat.name().endsWith("_CARPET")) diff --git a/TerminatorPlus-Plugin/src/main/java/net/nuggetmc/tplus/command/commands/BotCommand.java b/TerminatorPlus-Plugin/src/main/java/net/nuggetmc/tplus/command/commands/BotCommand.java index 36b0157..52266b5 100644 --- a/TerminatorPlus-Plugin/src/main/java/net/nuggetmc/tplus/command/commands/BotCommand.java +++ b/TerminatorPlus-Plugin/src/main/java/net/nuggetmc/tplus/command/commands/BotCommand.java @@ -22,6 +22,7 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.util.BoundingBox; import org.bukkit.util.Vector; import java.text.DecimalFormat; @@ -141,7 +142,6 @@ public class BotCommand extends CommandInstance { desc = "Gives all bots an armor set.", autofill = "armorAutofill" ) - @SuppressWarnings("deprecation") public void armor(CommandSender sender, @Arg("armor-tier") String armorTier) { String tier = armorTier.toLowerCase(); @@ -267,17 +267,24 @@ public class BotCommand extends CommandInstance { String extra = ChatColor.GRAY + " [" + ChatColor.YELLOW + "/bot settings" + ChatColor.GRAY + "]"; - if (arg1 == null || ((!arg1.equalsIgnoreCase("setgoal")) && !arg1.equalsIgnoreCase("mobtarget") && !arg1.equalsIgnoreCase("playertarget"))) { + if (arg1 == null || ((!arg1.equalsIgnoreCase("setgoal")) && !arg1.equalsIgnoreCase("mobtarget") && !arg1.equalsIgnoreCase("playertarget") + && !arg1.equalsIgnoreCase("region"))) { sender.sendMessage(ChatUtils.LINE); sender.sendMessage(ChatColor.GOLD + "Bot Settings" + extra); sender.sendMessage(ChatUtils.BULLET_FORMATTED + ChatColor.YELLOW + "setgoal" + ChatUtils.BULLET_FORMATTED + "Set the global bot target selection method."); sender.sendMessage(ChatUtils.BULLET_FORMATTED + ChatColor.YELLOW + "mobtarget" + ChatUtils.BULLET_FORMATTED + "Allow all future bots spawned to be targeted by hostile mobs."); + sender.sendMessage(ChatUtils.BULLET_FORMATTED + ChatColor.YELLOW + "playertarget" + ChatUtils.BULLET_FORMATTED + "Sets a player name for the bots to focus on if the goal is PLAYER."); + sender.sendMessage(ChatUtils.BULLET_FORMATTED + ChatColor.YELLOW + "region" + ChatUtils.BULLET_FORMATTED + "Sets a region for the bots to prioritize entities inside."); sender.sendMessage(ChatUtils.LINE); return; } if (arg1.equalsIgnoreCase("setgoal")) { - EnumTargetGoal goal = EnumTargetGoal.from(arg2 == null ? "" : arg2); + if (arg2 == null) { + sender.sendMessage("The global bot goal is currently " + ChatColor.BLUE + agent.getTargetType() + ChatColor.RESET + "."); + return; + } + EnumTargetGoal goal = EnumTargetGoal.from(arg2); if (goal == null) { sender.sendMessage(ChatUtils.LINE); @@ -290,14 +297,22 @@ public class BotCommand extends CommandInstance { agent.setTargetType(goal); sender.sendMessage("The global bot goal has been set to " + ChatColor.BLUE + goal.name() + ChatColor.RESET + "."); } else if (arg1.equalsIgnoreCase("mobtarget")) { - manager.setMobTarget(!manager.isMobTarget()); - sender.sendMessage("Mob targeting is now " + (manager.isMobTarget() ? ChatColor.GREEN + "enabled" : ChatColor.RED + "disabled") + ChatColor.RESET + ". (for all future bots)"); + if (arg2 == null) { + sender.sendMessage("Mob targeting is currently " + (manager.isMobTarget() ? ChatColor.GREEN + "enabled" : ChatColor.RED + "disabled") + ChatColor.RESET + "."); + return; + } + if (!arg2.equals("true") && !arg2.equals("false")) { + sender.sendMessage(ChatColor.RED + "You must specify true or false!"); + return; + } + manager.setMobTarget(Boolean.parseBoolean(arg2)); + sender.sendMessage("Mob targeting is now " + (manager.isMobTarget() ? ChatColor.GREEN + "enabled" : ChatColor.RED + "disabled") + ChatColor.RESET + ". (for all future bots)"); } else if (arg1.equalsIgnoreCase("playertarget")) { - if (args.size() < 2) { + if (args.size() < 2) { sender.sendMessage(ChatColor.RED + "You must specify a player name!"); return; } - String playerName = args.get(1); + String playerName = arg2; Player player = Bukkit.getPlayer(playerName); if (player == null) { sender.sendMessage(ChatColor.RED + "Could not find player " + ChatColor.YELLOW + playerName + ChatColor.RED + "!"); @@ -307,6 +322,76 @@ public class BotCommand extends CommandInstance { fetch.setTargetPlayer(player.getUniqueId()); } sender.sendMessage("All spawned bots are now set to target " + ChatColor.BLUE + player.getName() + ChatColor.RESET + ". They will target the closest player if they can't be found.\nYou may need to set the goal to PLAYER."); + } else if (arg1.equalsIgnoreCase("region")) { + if (arg2 == null) { + if (agent.getRegion() == null) { + sender.sendMessage("No region has been set."); + return; + } + sender.sendMessage("The current region is " + ChatColor.BLUE + agent.getRegion() + ChatColor.RESET + "."); + if (agent.getRegionWeightX() == 0 && agent.getRegionWeightY() == 0 && agent.getRegionWeightZ() == 0) + sender.sendMessage("Entities out of range will not be targeted."); + else { + sender.sendMessage("The region X weight is " + ChatColor.BLUE + agent.getRegionWeightX() + ChatColor.RESET + "."); + sender.sendMessage("The region Y weight is " + ChatColor.BLUE + agent.getRegionWeightY() + ChatColor.RESET + "."); + sender.sendMessage("The region Z weight is " + ChatColor.BLUE + agent.getRegionWeightZ() + ChatColor.RESET + "."); + } + return; + } + if (arg2.equalsIgnoreCase("clear")) { + agent.setRegion(null, 0, 0, 0); + sender.sendMessage("The region has been cleared."); + return; + } + boolean strict = args.size() == 8 && args.get(7).equalsIgnoreCase("strict"); + if (args.size() != 10 && !strict) { + sender.sendMessage(ChatUtils.LINE); + sender.sendMessage(ChatColor.GOLD + "Bot Region Settings" + extra); + sender.sendMessage(ChatUtils.BULLET_FORMATTED + ChatColor.YELLOW + " " + ChatUtils.BULLET_FORMATTED + + "Sets a region for bots to prioritize entities within."); + sender.sendMessage(ChatUtils.BULLET_FORMATTED + ChatColor.YELLOW + " strict" + ChatUtils.BULLET_FORMATTED + + "Sets a region so that the bots only target entities within the region."); + sender.sendMessage(ChatUtils.BULLET_FORMATTED + ChatColor.YELLOW + "clear" + ChatUtils.BULLET_FORMATTED + + "Clears the region."); + sender.sendMessage("Without strict mode, the entity distance from the region is multiplied by the weight values if outside the region."); + sender.sendMessage("The resulting value is added to the entity distance when selecting an entity."); + sender.sendMessage(ChatUtils.LINE); + return; + } + double x1, y1, z1, x2, y2, z2, wX, wY, wZ; + try { + x1 = Double.parseDouble(args.get(1)); + y1 = Double.parseDouble(args.get(2)); + z1 = Double.parseDouble(args.get(3)); + x2 = Double.parseDouble(args.get(4)); + y2 = Double.parseDouble(args.get(5)); + z2 = Double.parseDouble(args.get(6)); + if (strict) + wX = wY = wZ = 0; + else { + wX = Double.parseDouble(args.get(7)); + wY = Double.parseDouble(args.get(8)); + wZ = Double.parseDouble(args.get(9)); + if (wX <= 0 || wY <= 0 || wZ <= 0) { + sender.sendMessage("The region weights must be positive values!"); + return; + } + } + } catch (NumberFormatException e) { + sender.sendMessage("The region bounds and weights must be valid numbers!"); + sender.sendMessage("Correct syntax: " + ChatColor.YELLOW + "/bot settings region " + + ChatColor.RESET); + return; + } + agent.setRegion(new BoundingBox(x1, y1, z1, x2, y2, z2), wX, wY, wZ); + sender.sendMessage("The region has been set to " + ChatColor.BLUE + agent.getRegion() + ChatColor.RESET + "."); + if (wX == 0 && wY == 0 && wZ == 0) + sender.sendMessage("Entities out of range will not be targeted."); + else { + sender.sendMessage("The region X weight is " + ChatColor.BLUE + agent.getRegionWeightX() + ChatColor.RESET + "."); + sender.sendMessage("The region Y weight is " + ChatColor.BLUE + agent.getRegionWeightY() + ChatColor.RESET + "."); + sender.sendMessage("The region Z weight is " + ChatColor.BLUE + agent.getRegionWeightZ() + ChatColor.RESET + "."); + } } } @@ -325,6 +410,8 @@ public class BotCommand extends CommandInstance { if (args.length == 2) { output.add("setgoal"); output.add("mobtarget"); + output.add("playertarget"); + output.add("region"); } else if (args.length == 3) { if (args[1].equalsIgnoreCase("setgoal")) { Arrays.stream(EnumTargetGoal.values()).forEach(goal -> output.add(goal.name().replace("_", "").toLowerCase())); @@ -333,6 +420,11 @@ public class BotCommand extends CommandInstance { output.add("true"); output.add("false"); } + if (args[1].equalsIgnoreCase("playertarget")) { + for (Player player : Bukkit.getOnlinePlayers()) { + output.add(player.getName()); + } + } } return output;