001 package org.bukkit.command.defaults; 002 003 import com.google.common.collect.Lists; 004 import com.google.common.collect.Maps; 005 import com.google.common.collect.Sets; 006 import java.util.List; 007 import java.util.Map; 008 import java.util.Random; 009 import java.util.Set; 010 011 import org.bukkit.Bukkit; 012 import org.bukkit.ChatColor; 013 import org.bukkit.Location; 014 import org.bukkit.World; 015 import org.bukkit.command.CommandSender; 016 import org.bukkit.entity.Player; 017 import org.bukkit.scoreboard.Team; 018 019 public class SpreadPlayersCommand extends VanillaCommand { 020 private static final Random random = new Random(); 021 022 public SpreadPlayersCommand() { 023 super("spreadplayers"); 024 this.description = "Spreads players around a point"; 025 this.usageMessage = "/spreadplayers <x> <z> <spreadDistance> <maxRange> <respectTeams true|false> <player ...>"; 026 this.setPermission("bukkit.command.spreadplayers"); 027 } 028 029 @Override 030 public boolean execute(CommandSender sender, String commandLabel, String[] args) { 031 if (!testPermission(sender)) { 032 return true; 033 } 034 035 if (args.length < 6) { 036 sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); 037 return false; 038 } 039 040 final double x = getDouble(sender, args[0], -30000000, 30000000); 041 final double z = getDouble(sender, args[1], -30000000, 30000000); 042 final double distance = getDouble(sender, args[2]); 043 final double range = getDouble(sender, args[3]); 044 045 if (distance < 0.0D) { 046 sender.sendMessage(ChatColor.RED + "Distance is too small."); 047 return false; 048 } 049 050 if (range < distance + 1.0D) { 051 sender.sendMessage(ChatColor.RED + "Max range is too small."); 052 return false; 053 } 054 055 final String respectTeams = args[4]; 056 boolean teams = false; 057 058 if (respectTeams.equalsIgnoreCase("true")) { 059 teams = true; 060 } else if (!respectTeams.equalsIgnoreCase("false")) { 061 sender.sendMessage(String.format(ChatColor.RED + "'%s' is not true or false", args[4])); 062 return false; 063 } 064 065 List<Player> players = Lists.newArrayList(); 066 World world = null; 067 068 for (int i = 5; i < args.length; i++) { 069 Player player = Bukkit.getPlayerExact(args[i]); 070 if (player == null) { 071 continue; 072 } 073 074 if (world == null) { 075 world = player.getWorld(); 076 } 077 players.add(player); 078 } 079 080 if (world == null) { 081 return true; 082 } 083 084 final double xRangeMin = x - range; 085 final double zRangeMin = z - range; 086 final double xRangeMax = x + range; 087 final double zRangeMax = z + range; 088 089 final int spreadSize = teams ? getTeams(players) : players.size(); 090 091 final Location[] locations = getSpreadLocations(world, spreadSize, xRangeMin, zRangeMin, xRangeMax, zRangeMax); 092 final int rangeSpread = range(world, distance, xRangeMin, zRangeMin, xRangeMax, zRangeMax, locations); 093 094 if (rangeSpread == -1) { 095 sender.sendMessage(String.format("Could not spread %d %s around %s,%s (too many players for space - try using spread of at most %s)", spreadSize, teams ? "teams" : "players", x, z)); 096 return false; 097 } 098 099 final double distanceSpread = spread(world, players, locations, teams); 100 101 sender.sendMessage(String.format("Succesfully spread %d %s around %s,%s", locations.length, teams ? "teams" : "players", x, z)); 102 if (locations.length > 1) { 103 sender.sendMessage(String.format("(Average distance between %s is %s blocks apart after %s iterations)", teams ? "teams" : "players", String.format("%.2f", distanceSpread), rangeSpread)); 104 } 105 return true; 106 } 107 108 private int range(World world, double distance, double xRangeMin, double zRangeMin, double xRangeMax, double zRangeMax, Location[] locations) { 109 boolean flag = true; 110 double max; 111 112 int i; 113 114 for (i = 0; i < 10000 && flag; ++i) { 115 flag = false; 116 max = Float.MAX_VALUE; 117 118 Location loc1; 119 int j; 120 121 for (int k = 0; k < locations.length; ++k) { 122 Location loc2 = locations[k]; 123 124 j = 0; 125 loc1 = new Location(world, 0, 0, 0); 126 127 for (int l = 0; l < locations.length; ++l) { 128 if (k != l) { 129 Location loc3 = locations[l]; 130 double dis = loc2.distanceSquared(loc3); 131 132 max = Math.min(dis, max); 133 if (dis < distance) { 134 ++j; 135 loc1.add(loc3.getX() - loc2.getX(), 0, 0); 136 loc1.add(loc3.getZ() - loc2.getZ(), 0, 0); 137 } 138 } 139 } 140 141 if (j > 0) { 142 loc2.setX(loc2.getX() / j); 143 loc2.setZ(loc2.getZ() / j); 144 double d7 = Math.sqrt(loc1.getX() * loc1.getX() + loc1.getZ() * loc1.getZ()); 145 146 if (d7 > 0.0D) { 147 loc1.setX(loc1.getX() / d7); 148 loc2.add(-loc1.getX(), 0, -loc1.getZ()); 149 } else { 150 double x = xRangeMin >= xRangeMax ? xRangeMin : random.nextDouble() * (xRangeMax - xRangeMin) + xRangeMin; 151 double z = zRangeMin >= zRangeMax ? zRangeMin : random.nextDouble() * (zRangeMax - zRangeMin) + zRangeMin; 152 loc2.setX(x); 153 loc2.setZ(z); 154 } 155 156 flag = true; 157 } 158 159 boolean swap = false; 160 161 if (loc2.getX() < xRangeMin) { 162 loc2.setX(xRangeMin); 163 swap = true; 164 } else if (loc2.getX() > xRangeMax) { 165 loc2.setX(xRangeMax); 166 swap = true; 167 } 168 169 if (loc2.getZ() < zRangeMin) { 170 loc2.setZ(zRangeMin); 171 swap = true; 172 } else if (loc2.getZ() > zRangeMax) { 173 loc2.setZ(zRangeMax); 174 swap = true; 175 } 176 if (swap) { 177 flag = true; 178 } 179 } 180 181 if (!flag) { 182 Location[] locs = locations; 183 int i1 = locations.length; 184 185 for (j = 0; j < i1; ++j) { 186 loc1 = locs[j]; 187 if (world.getHighestBlockYAt(loc1) == 0) { 188 double x = xRangeMin >= xRangeMax ? xRangeMin : random.nextDouble() * (xRangeMax - xRangeMin) + xRangeMin; 189 double z = zRangeMin >= zRangeMax ? zRangeMin : random.nextDouble() * (zRangeMax - zRangeMin) + zRangeMin; 190 locations[i] = (new Location(world, x, 0, z)); 191 loc1.setX(x); 192 loc1.setZ(z); 193 flag = true; 194 } 195 } 196 } 197 } 198 199 if (i >= 10000) { 200 return -1; 201 } else { 202 return i; 203 } 204 } 205 206 private double spread(World world, List<Player> list, Location[] locations, boolean teams) { 207 double distance = 0.0D; 208 int i = 0; 209 Map<Team, Location> hashmap = Maps.newHashMap(); 210 211 for (int j = 0; j < list.size(); ++j) { 212 Player player = list.get(j); 213 Location location; 214 215 if (teams) { 216 Team team = player.getScoreboard().getPlayerTeam(player); 217 218 if (!hashmap.containsKey(team)) { 219 hashmap.put(team, locations[i++]); 220 } 221 222 location = hashmap.get(team); 223 } else { 224 location = locations[i++]; 225 } 226 227 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)); 228 double value = Double.MAX_VALUE; 229 230 for (int k = 0; k < locations.length; ++k) { 231 if (location != locations[k]) { 232 double d = location.distanceSquared(locations[k]); 233 value = Math.min(d, value); 234 } 235 } 236 237 distance += value; 238 } 239 240 distance /= list.size(); 241 return distance; 242 } 243 244 private int getTeams(List<Player> players) { 245 Set<Team> teams = Sets.newHashSet(); 246 247 for (Player player : players) { 248 teams.add(player.getScoreboard().getPlayerTeam(player)); 249 } 250 251 return teams.size(); 252 } 253 254 private Location[] getSpreadLocations(World world, int size, double xRangeMin, double zRangeMin, double xRangeMax, double zRangeMax) { 255 Location[] locations = new Location[size]; 256 257 for (int i = 0; i < size; ++i) { 258 double x = xRangeMin >= xRangeMax ? xRangeMin : random.nextDouble() * (xRangeMax - xRangeMin) + xRangeMin; 259 double z = zRangeMin >= zRangeMax ? zRangeMin : random.nextDouble() * (zRangeMax - zRangeMin) + zRangeMin; 260 locations[i] = (new Location(world, x, 0, z)); 261 } 262 263 return locations; 264 } 265 }