Newer
Older
ultramine_bukkit / src / main / java / org / bukkit / command / defaults / SpreadPlayersCommand.java
@vlad20012 vlad20012 on 24 Feb 2017 7 KB initial
package org.bukkit.command.defaults;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
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;

import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

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 <x> <z> <spreadDistance> <maxRange> <respectTeams true|false> <player ...>";
		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;
		}

		final double x = getDouble(sender, args[0], -30000000, 30000000);
		final double z = getDouble(sender, args[1], -30000000, 30000000);
		final double distance = getDouble(sender, args[2]);
		final double range = getDouble(sender, args[3]);

		if(distance < 0.0D)
		{
			sender.sendMessage(ChatColor.RED + "Distance is too small.");
			return false;
		}

		if(range < distance + 1.0D)
		{
			sender.sendMessage(ChatColor.RED + "Max range is too small.");
			return false;
		}

		final String respectTeams = args[4];
		boolean teams = false;

		if(respectTeams.equalsIgnoreCase("true"))
		{
			teams = true;
		}
		else if(!respectTeams.equalsIgnoreCase("false"))
		{
			sender.sendMessage(String.format(ChatColor.RED + "'%s' is not true or false", args[4]));
			return false;
		}

		List<Player> 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;
		}

		final double xRangeMin = x - range;
		final double zRangeMin = z - range;
		final double xRangeMax = x + range;
		final double zRangeMax = z + range;

		final int spreadSize = teams ? getTeams(players) : players.size();

		final Location[] locations = getSpreadLocations(world, spreadSize, xRangeMin, zRangeMin, xRangeMax, zRangeMax);
		final int rangeSpread = range(world, distance, xRangeMin, zRangeMin, xRangeMax, zRangeMax, locations);

		if(rangeSpread == -1)
		{
			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, "TODO")); // TODO: Add latest argument
			return false;
		}

		final 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;

		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<Player> list, Location[] locations, boolean teams)
	{
		double distance = 0.0D;
		int i = 0;
		Map<Team, Location> 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<Player> players)
	{
		Set<Team> 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;
	}
}