From 2f044fe1f8085087f232a2feff5e668d4fe9b87d Mon Sep 17 00:00:00 2001 From: h31ix Date: Fri, 19 Jul 2013 18:02:40 -0400 Subject: Add 1.6 SpreadPlayers command. Fixes BUKKIT-4508 --- .../java/org/bukkit/command/SimpleCommandMap.java | 1 + .../command/defaults/SpreadPlayersCommand.java | 254 +++++++++++++++++++++ 2 files changed, 255 insertions(+) create mode 100644 src/main/java/org/bukkit/command/defaults/SpreadPlayersCommand.java (limited to 'src/main') diff --git a/src/main/java/org/bukkit/command/SimpleCommandMap.java b/src/main/java/org/bukkit/command/SimpleCommandMap.java index 5e1e233d..f567dc0e 100644 --- a/src/main/java/org/bukkit/command/SimpleCommandMap.java +++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java @@ -59,6 +59,7 @@ public class SimpleCommandMap implements CommandMap { fallbackCommands.add(new EffectCommand()); fallbackCommands.add(new ScoreboardCommand()); fallbackCommands.add(new PlaySoundCommand()); + fallbackCommands.add(new SpreadPlayersCommand()); } public SimpleCommandMap(final Server server) { diff --git a/src/main/java/org/bukkit/command/defaults/SpreadPlayersCommand.java b/src/main/java/org/bukkit/command/defaults/SpreadPlayersCommand.java new file mode 100644 index 00000000..506c8fe7 --- /dev/null +++ b/src/main/java/org/bukkit/command/defaults/SpreadPlayersCommand.java @@ -0,0 +1,254 @@ +package org.bukkit.command.defaults; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scoreboard.Team; + +public class SpreadPlayersCommand extends VanillaCommand { + private static final Random random = new Random(); + + public SpreadPlayersCommand() { + super("spreadplayers"); + this.description = "Spreads players around a point"; + this.usageMessage = "/spreadplayers "; + this.setPermission("bukkit.command.spreadplayers"); + } + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) { + if (!testPermission(sender)) { + return true; + } + + if (args.length < 6) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + double x = getDouble(sender, args[0]); + double z = getDouble(sender, args[1]); + double distance = getDouble(sender, args[2]); + + if (distance < 0.0D) { + sender.sendMessage(ChatColor.RED + "Distance is too small."); + return false; + } + + double range = getDouble(sender, args[3]); + if (range < distance + 1.0D) { + sender.sendMessage(ChatColor.RED + "Max range is too small."); + return false; + } + + boolean teams = false; + if ("true".equalsIgnoreCase(args[4])) { + teams = true; + } else if (!"false".equalsIgnoreCase(args[4])) { + sender.sendMessage(String.format(ChatColor.RED + "'%s' is not true or false", args[4])); + return false; + } + + List players = Lists.newArrayList(); + World world = null; + + for (int i = 5; i < args.length; i++) { + Player player = Bukkit.getPlayerExact(args[i]); + if (player == null) { + continue; + } + + if (world != null) { + world = player.getWorld(); + } + players.add(player); + } + + if (world == null) { + return true; + } + + double xRangeMin = x - range; + double zRangeMin = z - range; + double xRangeMax = x + range; + double zRangeMax = z + range; + + Location[] locations = getSpreadLocations(world, teams ? getTeams(players) : players.size(), xRangeMin, zRangeMin, xRangeMax, zRangeMax); + int rangeSpread = range(world, distance, xRangeMin, zRangeMin, xRangeMax, zRangeMax, locations); + double distanceSpread = spread(world, players, locations, teams); + + sender.sendMessage(String.format("Succesfully spread %d %s around %s,%s", locations.length, teams ? "teams" : "players", x, z)); + if (locations.length > 1) { + sender.sendMessage(String.format("(Average distance between %s is %s blocks apart after %s iterations)", teams ? "teams" : "players", String.format("%.2f", distanceSpread), rangeSpread)); + } + return true; + } + + private int range(World world, double distance, double xRangeMin, double zRangeMin, double xRangeMax, double zRangeMax, Location[] locations) { + boolean flag = true; + double max = Float.MAX_VALUE; + + int i; + + for (i = 0; i < 10000 && flag; ++i) { + flag = false; + max = Float.MAX_VALUE; + + Location loc1; + int j; + + for (int k = 0; k < locations.length; ++k) { + Location loc2 = locations[k]; + + j = 0; + loc1 = new Location(world, 0, 0, 0); + + for (int l = 0; l < locations.length; ++l) { + if (k != l) { + Location loc3 = locations[l]; + double dis = loc2.distanceSquared(loc3); + + max = Math.min(dis, max); + if (dis < distance) { + ++j; + loc1.add(loc3.getX() - loc2.getX(), 0, 0); + loc1.add(loc3.getZ() - loc2.getZ(), 0, 0); + } + } + } + + if (j > 0) { + loc2.setX(loc2.getX() / j); + loc2.setZ(loc2.getZ() / j); + double d7 = Math.sqrt(loc1.getX() * loc1.getX() + loc1.getZ() * loc1.getZ()); + + if (d7 > 0.0D) { + loc1.setX(loc1.getX() / d7); + loc2.add(-loc1.getX(), 0, -loc1.getZ()); + } else { + double x = xRangeMin >= xRangeMax ? xRangeMin : random.nextDouble() * (xRangeMax - xRangeMin) + xRangeMin; + double z = zRangeMin >= zRangeMax ? zRangeMin : random.nextDouble() * (zRangeMax - zRangeMin) + zRangeMin; + loc2.setX(x); + loc2.setZ(z); + } + + flag = true; + } + + boolean swap = false; + + if (loc2.getX() < xRangeMin) { + loc2.setX(xRangeMin); + swap = true; + } else if (loc2.getX() > xRangeMax) { + loc2.setX(xRangeMax); + swap = true; + } + + if (loc2.getZ() < zRangeMin) { + loc2.setZ(zRangeMin); + swap = true; + } else if (loc2.getZ() > zRangeMax) { + loc2.setZ(zRangeMax); + swap = true; + } + if (swap) { + flag = true; + } + } + + if (!flag) { + Location[] locs = locations; + int i1 = locations.length; + + for (j = 0; j < i1; ++j) { + loc1 = locs[j]; + if (world.getHighestBlockYAt(loc1) == 0) { + double x = xRangeMin >= xRangeMax ? xRangeMin : random.nextDouble() * (xRangeMax - xRangeMin) + xRangeMin; + double z = zRangeMin >= zRangeMax ? zRangeMin : random.nextDouble() * (zRangeMax - zRangeMin) + zRangeMin; + locations[i] = (new Location(world, x, 0, z)); + loc1.setX(x); + loc1.setZ(z); + flag = true; + } + } + } + } + + if (i >= 10000) { + return -1; + } else { + return i; + } + } + + private double spread(World world, List list, Location[] locations, boolean teams) { + double distance = 0.0D; + int i = 0; + Map hashmap = Maps.newHashMap(); + + for (int j = 0; j < list.size(); ++j) { + Player player = list.get(j); + Location location; + + if (teams) { + Team team = player.getScoreboard().getPlayerTeam(player); + + if (!hashmap.containsKey(team)) { + hashmap.put(team, locations[i++]); + } + + location = hashmap.get(team); + } else { + location = locations[i++]; + } + + player.teleport(new Location(world, Math.floor(location.getX()) + 0.5D, world.getHighestBlockYAt((int) location.getX(), (int) location.getZ()), Math.floor(location.getZ()) + 0.5D)); + double value = Double.MAX_VALUE; + + for (int k = 0; k < locations.length; ++k) { + if (location != locations[k]) { + double d = location.distanceSquared(locations[k]); + value = Math.min(d, value); + } + } + + distance += value; + } + + distance /= list.size(); + return distance; + } + + private int getTeams(List players) { + Set teams = Sets.newHashSet(); + + for (Player player : players) { + teams.add(player.getScoreboard().getPlayerTeam(player)); + } + + return teams.size(); + } + + private Location[] getSpreadLocations(World world, int size, double xRangeMin, double zRangeMin, double xRangeMax, double zRangeMax) { + Location[] locations = new Location[size]; + + for (int i = 0; i < size; ++i) { + double x = xRangeMin >= xRangeMax ? xRangeMin : random.nextDouble() * (xRangeMax - xRangeMin) + xRangeMin; + double z = zRangeMin >= zRangeMax ? zRangeMin : random.nextDouble() * (zRangeMax - zRangeMin) + zRangeMin; + locations[i] = (new Location(world, x, 0, z)); + } + + return locations; + } +} -- cgit v1.2.3