package com.intellectualcrafters.plot.object;
import com.intellectualcrafters.configuration.ConfigurationSection;
import com.intellectualcrafters.plot.PS;
import com.intellectualcrafters.plot.config.Configuration;
import com.intellectualcrafters.plot.config.ConfigurationNode;
import com.intellectualcrafters.plot.config.Settings;
import com.intellectualcrafters.plot.flag.Flag;
import com.intellectualcrafters.plot.flag.FlagManager;
import com.intellectualcrafters.plot.flag.Flags;
import com.intellectualcrafters.plot.generator.GridPlotWorld;
import com.intellectualcrafters.plot.generator.IndependentPlotGenerator;
import com.intellectualcrafters.plot.util.EconHandler;
import com.intellectualcrafters.plot.util.EventUtil;
import com.intellectualcrafters.plot.util.MainUtil;
import com.intellectualcrafters.plot.util.MathMan;
import com.intellectualcrafters.plot.util.PlotGameMode;
import com.intellectualcrafters.plot.util.StringMan;
import com.intellectualcrafters.plot.util.area.QuadMap;
import com.intellectualcrafters.plot.util.block.GlobalBlockQueue;
import com.intellectualcrafters.plot.util.block.LocalBlockQueue;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nullable;
/**
* @author Jesse Boyd
*/
public abstract class PlotArea {
public final String worldname;
public final String id;
public final PlotManager manager;
public final int worldhash;
private final PlotId min;
private final PlotId max;
protected final ConcurrentHashMap<PlotId, Plot> plots = new ConcurrentHashMap<>();
private final IndependentPlotGenerator generator;
public int MAX_PLOT_MEMBERS = 128;
public boolean AUTO_MERGE = false;
public boolean ALLOW_SIGNS = true;
public boolean MISC_SPAWN_UNOWNED = false;
public boolean MOB_SPAWNING = false;
public boolean MOB_SPAWNER_SPAWNING = false;
public String PLOT_BIOME = "FOREST";
public boolean PLOT_CHAT = false;
public boolean SCHEMATIC_CLAIM_SPECIFY = false;
public boolean SCHEMATIC_ON_CLAIM = false;
public String SCHEMATIC_FILE = "null";
public List<String> SCHEMATICS = null;
public Map<Flag<?>, Object> DEFAULT_FLAGS;
public boolean USE_ECONOMY = false;
public Map<String, Expression<Double>> PRICES = new HashMap<>();
public boolean SPAWN_EGGS = false;
public boolean SPAWN_CUSTOM = true;
public boolean SPAWN_BREEDING = false;
public boolean WORLD_BORDER = false;
public int TYPE = 0;
public int TERRAIN = 0;
public boolean HOME_ALLOW_NONMEMBER = false;
public PlotLoc DEFAULT_HOME;
public int MAX_BUILD_HEIGHT = 256;
public int MIN_BUILD_HEIGHT = 1;
public PlotGameMode GAMEMODE = PlotGameMode.CREATIVE;
private int hash;
private RegionWrapper region;
private ConcurrentHashMap<String, Object> meta;
private QuadMap<PlotCluster> clusters;
public PlotArea(String worldName, String id, IndependentPlotGenerator generator, PlotId min, PlotId max) {
this.worldname = worldName;
this.id = id;
this.manager = generator != null ? generator.getNewPlotManager() : null;
this.generator = generator;
if (min == null || max == null) {
if (min != max) {
throw new IllegalArgumentException("None of the ids can be null for this constructor");
}
this.min = null;
this.max = null;
} else {
this.min = min;
this.max = max;
}
this.worldhash = worldName.hashCode();
}
/**
* Create a new PlotArea object with no functionality/information.
* - Mainly used during startup before worlds are created as a temporary object
* @param world
* @return
*/
public static PlotArea createGeneric(String world) {
return new PlotArea(world, null, null, null, null) {
@Override
public void loadConfiguration(ConfigurationSection config) {}
@Override
public ConfigurationNode[] getSettingNodes() {
return null;
}
};
}
public LocalBlockQueue getQueue(boolean autoQueue) {
return GlobalBlockQueue.IMP.getNewQueue(worldname, autoQueue);
}
/**
* Returns the region for this PlotArea or a RegionWrapper encompassing
* the whole world if none exists.
*
* @return RegionWrapper
*/
public RegionWrapper getRegion() {
this.region = getRegionAbs();
if (this.region == null) {
return new RegionWrapper(Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
}
return this.region;
}
/**
* Returns the region for this PlotArea.
*
* @return RegionWrapper or null if no applicable region
*/
public RegionWrapper getRegionAbs() {
if (this.region == null) {
if (this.min != null) {
Location bot = getPlotManager().getPlotBottomLocAbs(this, this.min);
Location top = getPlotManager().getPlotTopLocAbs(this, this.max);
this.region = new RegionWrapper(bot.getX() - 1, top.getX() + 1, bot.getZ() - 1, top.getZ() + 1);
}
}
return this.region;
}
/**
* Returns the minimum value of a {@link PlotId}.
* @return the minimum value for a {@link PlotId}
*/
public PlotId getMin() {
return this.min == null ? new PlotId(Integer.MIN_VALUE, Integer.MIN_VALUE) : this.min;
}
/**
* Returns the max PlotId.
* @return the maximum value for a {@link PlotId}
*/
public PlotId getMax() {
return this.max == null ? new PlotId(Integer.MAX_VALUE, Integer.MAX_VALUE) : this.max;
}
/**
* Get the implementation independent generator for this area.
*
* @return the {@link IndependentPlotGenerator}
*/
public IndependentPlotGenerator getGenerator() {
return this.generator;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
PlotArea plotarea = (PlotArea) obj;
return this.worldhash == plotarea.worldhash && this.worldname.equals(plotarea.worldname) && StringMan.isEqual(this.id, plotarea.id);
}
public Set<PlotCluster> getClusters() {
return this.clusters == null ? new HashSet<PlotCluster>() : this.clusters.getAll();
}
/**
* Check if a PlotArea is compatible (move/copy etc).
* @param plotArea the {@code PlotArea} to compare
* @return true if both areas are compatible
*/
public boolean isCompatible(PlotArea plotArea) {
ConfigurationSection section = PS.get().worlds.getConfigurationSection("worlds");
for (ConfigurationNode setting : plotArea.getSettingNodes()) {
Object constant = section.get(plotArea.worldname + '.' + setting.getConstant());
if (constant == null || !constant.equals(section.get(this.worldname + '.' + setting.getConstant()))) {
return false;
}
}
return true;
}
/**
* When a world is created, the following method will be called for each.
*
* @param config Configuration Section
*/
public void loadDefaultConfiguration(ConfigurationSection config) {
if ((this.min != null || this.max != null) && !(this instanceof GridPlotWorld)) {
throw new IllegalArgumentException("Must extend GridPlotWorld to provide");
}
if (config.contains("generator.terrain")) {
this.TERRAIN = config.getInt("generator.terrain");
this.TYPE = config.getInt("generator.type");
}
this.MOB_SPAWNING = config.getBoolean("natural_mob_spawning");
this.MISC_SPAWN_UNOWNED = config.getBoolean("misc_spawn_unowned");
this.MOB_SPAWNER_SPAWNING = config.getBoolean("mob_spawner_spawning");
this.AUTO_MERGE = config.getBoolean("plot.auto_merge");
this.MAX_PLOT_MEMBERS = config.getInt("limits.max-members");
this.ALLOW_SIGNS = config.getBoolean("plot.create_signs");
this.PLOT_BIOME = Configuration.BIOME.parseString(config.getString("plot.biome"));
this.SCHEMATIC_ON_CLAIM = config.getBoolean("schematic.on_claim");
this.SCHEMATIC_FILE = config.getString("schematic.file");
this.SCHEMATIC_CLAIM_SPECIFY = config.getBoolean("schematic.specify_on_claim");
this.SCHEMATICS = config.getStringList("schematic.schematics");
this.USE_ECONOMY = config.getBoolean("economy.use") && EconHandler.getEconHandler() != null;
ConfigurationSection priceSection = config.getConfigurationSection("economy.prices");
if (this.USE_ECONOMY) {
this.PRICES = new HashMap<>();
for (String key : priceSection.getKeys(false)) {
this.PRICES.put(key, Expression.doubleExpression(priceSection.getString(key)));
}
}
this.PLOT_CHAT = config.getBoolean("chat.enabled");
this.WORLD_BORDER = config.getBoolean("world.border");
this.MAX_BUILD_HEIGHT = config.getInt("world.max_height");
this.MIN_BUILD_HEIGHT = config.getInt("world.min_height");
switch (config.getString("world.gamemode").toLowerCase()) {
case "survival":
case "s":
case "0":
this.GAMEMODE = PlotGameMode.SURVIVAL;
break;
case "creative":
case "c":
case "1":
this.GAMEMODE = PlotGameMode.CREATIVE;
break;
case "adventure":
case "a":
case "2":
this.GAMEMODE = PlotGameMode.ADVENTURE;
break;
case "spectator":
case "3":
this.GAMEMODE = PlotGameMode.SPECTATOR;
break;
default:
this.GAMEMODE = PlotGameMode.NOT_SET;
break;
}
this.HOME_ALLOW_NONMEMBER = config.getBoolean("home.allow-nonmembers");
String homeDefault = config.getString("home.default");
if ("side".equalsIgnoreCase(homeDefault)) {
this.DEFAULT_HOME = null;
} else if (StringMan.isEqualIgnoreCaseToAny(homeDefault, "center", "middle")) {
this.DEFAULT_HOME = new PlotLoc(Integer.MAX_VALUE, Integer.MAX_VALUE);
} else {
try {
String[] split = homeDefault.split(",");
this.DEFAULT_HOME = new PlotLoc(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
} catch (NumberFormatException ignored) {
this.DEFAULT_HOME = null;
}
}
List<String> flags = config.getStringList("flags.default");
if (flags.isEmpty()) {
flags = config.getStringList("flags");
if (flags.isEmpty()) {
flags = new ArrayList<>();
ConfigurationSection section = config.getConfigurationSection("flags");
Set<String> keys = section.getKeys(false);
for (String key : keys) {
if (!"default".equals(key)) {
flags.add(key + ';' + section.get(key));
}
}
}
}
try {
this.DEFAULT_FLAGS = FlagManager.parseFlags(flags);
} catch (Exception e) {
e.printStackTrace();
PS.debug("&cInvalid default flags for " + this.worldname + ": " + StringMan.join(flags, ","));
this.DEFAULT_FLAGS = new HashMap<>();
}
this.SPAWN_EGGS = config.getBoolean("event.spawn.egg");
this.SPAWN_CUSTOM = config.getBoolean("event.spawn.custom");
this.SPAWN_BREEDING = config.getBoolean("event.spawn.breeding");
loadConfiguration(config);
}
public abstract void loadConfiguration(ConfigurationSection config);
/**
* Saving core PlotArea settings.
*
* @param config Configuration Section
*/
public void saveConfiguration(ConfigurationSection config) {
HashMap<String, Object> options = new HashMap<>();
options.put("natural_mob_spawning", this.MOB_SPAWNING);
options.put("misc_spawn_unowned", this.MISC_SPAWN_UNOWNED);
options.put("mob_spawner_spawning", this.MOB_SPAWNER_SPAWNING);
options.put("plot.auto_merge", this.AUTO_MERGE);
options.put("plot.create_signs", this.ALLOW_SIGNS);
options.put("plot.biome", "FOREST");
options.put("schematic.on_claim", this.SCHEMATIC_ON_CLAIM);
options.put("schematic.file", this.SCHEMATIC_FILE);
options.put("schematic.specify_on_claim", this.SCHEMATIC_CLAIM_SPECIFY);
options.put("schematic.schematics", this.SCHEMATICS);
options.put("economy.use", this.USE_ECONOMY);
options.put("economy.prices.claim", 100);
options.put("economy.prices.merge", 100);
options.put("economy.prices.sell", 100);
options.put("chat.enabled", this.PLOT_CHAT);
options.put("flags.default", null);
options.put("event.spawn.egg", this.SPAWN_EGGS);
options.put("event.spawn.custom", this.SPAWN_CUSTOM);
options.put("event.spawn.breeding", this.SPAWN_BREEDING);
options.put("world.border", this.WORLD_BORDER);
options.put("limits.max-members", this.MAX_PLOT_MEMBERS);
options.put("home.default", "side");
options.put("home.allow-nonmembers", false);
options.put("world.max_height", this.MAX_BUILD_HEIGHT);
options.put("world.min_height", this.MIN_BUILD_HEIGHT);
options.put("world.gamemode", this.GAMEMODE.name().toLowerCase());
if (this.TYPE != 0) {
options.put("generator.terrain", this.TERRAIN);
options.put("generator.type", this.TYPE);
}
ConfigurationNode[] settings = getSettingNodes();
/*
* Saving generator specific settings
*/
for (ConfigurationNode setting : settings) {
options.put(setting.getConstant(), setting.getValue());
}
for (Entry<String, Object> stringObjectEntry : options.entrySet()) {
if (!config.contains(stringObjectEntry.getKey())) {
config.set(stringObjectEntry.getKey(), stringObjectEntry.getValue());
}
}
if (!config.contains("flags")) {
config.set("flags.use", "63,64,68,69,71,77,96,143,167,193,194,195,196,197,77,143,69,70,72,147,148,107,183,184,185,186,187,132");
}
}
@Override
public String toString() {
if (this.id == null) {
return this.worldname;
} else {
return this.worldname + ";" + this.id;
}
}
@Override
public int hashCode() {
if (this.hash != 0) {
return this.hash;
}
return this.hash = toString().hashCode();
}
/**
* Used for the <b>/plot setup</b> command Return null if you do not want to support this feature
*
* @return ConfigurationNode[]
*/
public abstract ConfigurationNode[] getSettingNodes();
/**
* Gets the {@code Plot} at a location.
* @param location the location
* @return the {@code Plot} or null if none exists
*/
public Plot getPlotAbs(Location location) {
PlotId pid = this.manager.getPlotId(this, location.getX(), location.getY(), location.getZ());
if (pid == null) {
return null;
}
return getPlotAbs(pid);
}
/**
* Gets the base plot at a location.
* @param location the location
* @return base Plot
*/
public Plot getPlot(Location location) {
PlotId pid = this.manager.getPlotId(this, location.getX(), location.getY(), location.getZ());
if (pid == null) {
return null;
}
return getPlot(pid);
}
/**
* Get the owned base plot at a location.
* @param location the location
* @return the base plot or null
*/
public Plot getOwnedPlot(Location location) {
PlotId pid = this.manager.getPlotId(this, location.getX(), location.getY(), location.getZ());
if (pid == null) {
return null;
}
Plot plot = this.plots.get(pid);
return plot == null ? null : plot.getBasePlot(false);
}
/**
* Get the owned plot at a location.
* @param location the location
* @return Plot or null
*/
public Plot getOwnedPlotAbs(Location location) {
PlotId pid = this.manager.getPlotId(this, location.getX(), location.getY(), location.getZ());
if (pid == null) {
return null;
}
return this.plots.get(pid);
}
/**
* Get the owned Plot at a PlotId.
* @param id the {@code PlotId}
* @return the plot or null
*/
public Plot getOwnedPlotAbs(PlotId id) {
return this.plots.get(id);
}
public Plot getOwnedPlot(PlotId id) {
Plot plot = this.plots.get(id);
return plot == null ? null : plot.getBasePlot(false);
}
public boolean contains(int x, int z) {
return this.TYPE != 2 || getRegionAbs().isIn(x, z);
}
public boolean contains(PlotId id) {
return this.min == null || (id.x >= this.min.x && id.x <= this.max.x && id.y >= this.min.y && id.y <= this.max.y);
}
public boolean contains(Location location) {
return StringMan.isEqual(location.getWorld(), this.worldname) && (getRegionAbs() == null || this.region
.isIn(location.getX(), location.getZ()));
}
public Set<Plot> getPlotsAbs(final UUID uuid) {
if (uuid == null) {
return Collections.emptySet();
}
final HashSet<Plot> myPlots = new HashSet<>();
foreachPlotAbs(new RunnableVal<Plot>() {
@Override
public void run(Plot value) {
if (uuid.equals(value.owner)) {
myPlots.add(value);
}
}
});
return myPlots;
}
public Set<Plot> getPlots(UUID uuid) {
HashSet<Plot> myplots = new HashSet<>();
for (Plot plot : getPlots()) {
if (plot.isBasePlot()) {
if (plot.isOwner(uuid)) {
myplots.add(plot);
}
}
}
return myplots;
}
public Set<Plot> getPlots(PlotPlayer player) {
return getPlots(player.getUUID());
}
/**
* A collection of the claimed plots in this {@code PlotArea}.
* @return a collection of claimed plots
*/
public Collection<Plot> getPlots() {
return this.plots.values();
}
public Set<Plot> getPlotsAbs(PlotPlayer player) {
return player != null ? getPlotsAbs(player.getUUID()) : new HashSet<Plot>();
}
public int getPlotCount(UUID uuid) {
if (!Settings.Done.COUNTS_TOWARDS_LIMIT) {
int count = 0;
for (Plot plot : getPlotsAbs(uuid)) {
if (!plot.hasFlag(Flags.DONE)) {
count++;
}
}
return count;
}
return getPlotsAbs(uuid).size();
}
public int getPlotCount(PlotPlayer player) {
return player != null ? getPlotCount(player.getUUID()) : 0;
}
public Plot getPlotAbs(PlotId id) {
Plot plot = getOwnedPlotAbs(id);
if (plot == null) {
if (this.min != null && (id.x < this.min.x || id.x > this.max.x || id.y < this.min.y || id.y > this.max.y)) {
return null;
}
return new Plot(this, id);
}
return plot;
}
public Plot getPlot(PlotId id) {
Plot plot = getOwnedPlotAbs(id);
if (plot == null) {
if (this.min != null && (id.x < this.min.x || id.x > this.max.x || id.y < this.min.y || id.y > this.max.y)) {
return null;
}
return new Plot(this, id);
}
return plot.getBasePlot(false);
}
/**
* Retrieves the number of claimed plot in the {@code PlotArea}.
*
* @return the number of claimed plots
*/
public int getPlotCount() {
return this.plots.size();
}
public PlotCluster getCluster(Location location) {
Plot plot = getPlot(location);
if (plot == null) {
return null;
}
return this.clusters != null ? this.clusters.get(plot.getId().x, plot.getId().y) : null;
}
public PlotCluster getFirstIntersectingCluster(PlotId pos1, PlotId pos2) {
if (this.clusters == null) {
return null;
}
for (PlotCluster cluster : this.clusters.getAll()) {
if (cluster.intersects(pos1, pos2)) {
return cluster;
}
}
return null;
}
public PlotCluster getCluster(PlotId id) {
return this.clusters != null ? this.clusters.get(id.x, id.y) : null;
}
public PlotManager getPlotManager() {
return this.manager;
}
/**
* Session only plot metadata (session is until the server stops).
* <br>
* For persistent metadata use the flag system
* @see FlagManager
* @param key
* @param value
*/
public void setMeta(String key, Object value) {
if (this.meta == null) {
this.meta = new ConcurrentHashMap<>();
}
this.meta.put(key, value);
}
public <T> T getMeta(String key, T def) {
Object v = getMeta(key);
return v == null ? def : (T) v;
}
/**
* Get the metadata for a key<br>
* <br>
* For persistent metadata use the flag system
* @param key
* @return
*/
public Object getMeta(String key) {
if (this.meta != null) {
return this.meta.get(key);
}
return null;
}
public Set<Plot> getBasePlots() {
HashSet<Plot> myPlots = new HashSet<>(getPlots());
Iterator<Plot> iterator = myPlots.iterator();
while (iterator.hasNext()) {
if (!iterator.next().isBasePlot()) {
iterator.remove();
}
}
return myPlots;
}
public void foreachPlotAbs(RunnableVal<Plot> run) {
for (Entry<PlotId, Plot> entry : this.plots.entrySet()) {
run.run(entry.getValue());
}
}
public void foreachBasePlot(RunnableVal<Plot> run) {
for (Plot plot : getPlots()) {
if (plot.isBasePlot()) {
run.run(plot);
}
}
}
public Map<PlotId, Plot> getPlotsRaw() {
return this.plots;
}
public Set<Entry<PlotId, Plot>> getPlotEntries() {
return this.plots.entrySet();
}
public boolean addPlot(Plot plot) {
for (PlotPlayer pp : plot.getPlayersInPlot()) {
pp.setMeta("lastplot", plot);
}
return this.plots.put(plot.getId(), plot) == null;
}
public Plot getNextFreePlot(PlotPlayer player, @Nullable PlotId start) {
int plots;
PlotId center;
PlotId min = getMin();
PlotId max = getMax();
if (TYPE == 2) {
center = new PlotId(MathMan.average(min.x, max.x), MathMan.average(min.y, max.y));
plots = Math.max(max.x - min.x, max.y - min.y) + 1;
if (start != null) start = new PlotId(start.x - center.x, start.y - center.y);
} else {
center = new PlotId(0, 0);
plots = Integer.MAX_VALUE;
}
PlotId currentId = new PlotId(0, 0);
for (int i = 0; i < plots; i++) {
if (start == null) {
start = getMeta("lastPlot", new PlotId(0, 0));
} else {
start = start.getNextId(1);
}
currentId = new PlotId(center.x + start.x, center.y + start.y);
Plot plot = getPlotAbs(currentId);
if (plot != null && plot.canClaim(player)) {
setMeta("lastPlot", currentId);
return plot;
}
}
return null;
}
public boolean addPlotIfAbsent(Plot plot) {
if (this.plots.putIfAbsent(plot.getId(), plot) == null) {
for (PlotPlayer pp : plot.getPlayersInPlot()) {
pp.setMeta("lastplot", plot);
}
return true;
}
return false;
}
public boolean addPlotAbs(Plot plot) {
return this.plots.put(plot.getId(), plot) == null;
}
/**
* Check if the plots in a selection are unowned.
* @param pos1 first corner of selection
* @param pos2 second corner of selection
* @return are plots in selection unowned
*/
public boolean isUnowned(PlotId pos1, PlotId pos2) {
int area = (pos2.x - pos1.x + 1) * (pos2.y - pos1.y + 1);
if (area > getPlotCount()) {
for (Plot plot : getPlots()) {
if (plot.getId().x >= pos1.x && plot.getId().x <= pos2.x && plot.getId().y >= pos1.y && plot.getId().y <= pos2.y) {
return false;
}
}
} else {
for (int x = pos1.x; x <= pos2.x; x++) {
for (int y = pos1.y; y <= pos2.y; y++) {
PlotId id = new PlotId(x, y);
if (this.plots.get(id) != null) {
return false;
}
}
}
}
return true;
}
/**
* Get the plot border distance for a world<br>
* @return The border distance or Integer.MAX_VALUE if no border is set
*/
public int getBorder() {
Integer meta = (Integer) getMeta("worldBorder");
if (meta != null) {
int border = meta + 1;
if (border == 0) {
return Integer.MAX_VALUE;
} else {
return border;
}
}
return Integer.MAX_VALUE;
}
/**
* Setup the plot border for a world (usually done when the world is created).
*/
public void setupBorder() {
if (!this.WORLD_BORDER) {
return;
}
Integer meta = (Integer) getMeta("worldBorder");
if (meta == null) {
setMeta("worldBorder", 1);
}
for (Plot plot : getPlots()) {
plot.updateWorldBorder();
}
}
/**
* Delete the metadata for a key.
* - metadata is session only
* - deleting other plugin's metadata may cause issues
* @param key
*/
public void deleteMeta(String key) {
if (this.meta != null) {
this.meta.remove(key);
}
}
public boolean canClaim(PlotPlayer player, PlotId pos1, PlotId pos2) {
if (pos1.x == pos2.x && pos1.y == pos2.y) {
if (getOwnedPlot(pos1) != null) {
return false;
}
Plot plot = getPlotAbs(pos1);
if (plot == null) return false;
return plot.canClaim(player);
}
for (int x = pos1.x; x <= pos2.x; x++) {
for (int y = pos1.y; y <= pos2.y; y++) {
PlotId id = new PlotId(x, y);
Plot plot = getPlotAbs(id);
if (plot == null) return false;
if (!plot.canClaim(player)) {
return false;
}
}
}
return true;
}
public boolean removePlot(PlotId id) {
return this.plots.remove(id) != null;
}
public boolean mergePlots(ArrayList<PlotId> plotIds, boolean removeRoads, boolean updateDatabase) {
if (plotIds.size() < 2) {
return false;
}
PlotId pos1 = plotIds.get(0);
PlotId pos2 = plotIds.get(plotIds.size() - 1);
PlotManager manager = getPlotManager();
boolean result = EventUtil.manager.callMerge(getPlotAbs(pos1), plotIds);
if (!result) {
return false;
}
HashSet<UUID> trusted = new HashSet<>();
HashSet<UUID> members = new HashSet<>();
HashSet<UUID> denied = new HashSet<>();
manager.startPlotMerge(this, plotIds);
for (int x = pos1.x; x <= pos2.x; x++) {
for (int y = pos1.y; y <= pos2.y; y++) {
PlotId id = new PlotId(x, y);
Plot plot = getPlotAbs(id);
trusted.addAll(plot.getTrusted());
members.addAll(plot.getMembers());
denied.addAll(plot.getDenied());
if (removeRoads) {
plot.removeSign();
}
}
}
members.removeAll(trusted);
denied.removeAll(trusted);
denied.removeAll(members);
for (int x = pos1.x; x <= pos2.x; x++) {
for (int y = pos1.y; y <= pos2.y; y++) {
boolean lx = x < pos2.x;
boolean ly = y < pos2.y;
PlotId id = new PlotId(x, y);
Plot plot = getPlotAbs(id);
plot.setTrusted(trusted);
plot.setMembers(members);
plot.setDenied(denied);
Plot plot2;
if (lx) {
if (ly) {
if (!plot.getMerged(1) || !plot.getMerged(2)) {
if (removeRoads) {
plot.removeRoadSouthEast();
}
}
}
if (!plot.getMerged(1)) {
plot2 = plot.getRelative(1, 0);
plot.mergePlot(plot2, removeRoads);
}
}
if (ly) {
if (!plot.getMerged(2)) {
plot2 = plot.getRelative(0, 1);
plot.mergePlot(plot2, removeRoads);
}
}
}
}
manager.finishPlotMerge(this, plotIds);
return true;
}
/**
* Get a set of owned plots within a selection (chooses the best algorithm based on selection size.
* i.e. A selection of billions of plots will work fine
* @param pos1 first corner of selection
* @param pos2 second corner of selection
* @return the plots in the selection which are owned
*/
public HashSet<Plot> getPlotSelectionOwned(PlotId pos1, PlotId pos2) {
int size = (1 + pos2.x - pos1.x) * (1 + pos2.y - pos1.y);
HashSet<Plot> result = new HashSet<>();
if (size < 16 || size < getPlotCount()) {
for (PlotId pid : MainUtil.getPlotSelectionIds(pos1, pos2)) {
Plot plot = getPlotAbs(pid);
if (plot.hasOwner()) {
if (plot.getId().x > pos1.x || plot.getId().y > pos1.y || plot.getId().x < pos2.x || plot.getId().y < pos2.y) {
result.add(plot);
}
}
}
} else {
for (Plot plot : getPlots()) {
if (plot.getId().x > pos1.x || plot.getId().y > pos1.y || plot.getId().x < pos2.x || plot.getId().y < pos2.y) {
result.add(plot);
}
}
}
return result;
}
public void removeCluster(PlotCluster plotCluster) {
if (this.clusters == null) {
throw new IllegalAccessError("Clusters not enabled!");
}
this.clusters.remove(plotCluster);
}
public void addCluster(PlotCluster plotCluster) {
if (this.clusters == null) {
this.clusters = new QuadMap<PlotCluster>(Integer.MAX_VALUE, 0, 0, 64) {
@Override
public RegionWrapper getRegion(PlotCluster value) {
return new RegionWrapper(value.getP1().x, value.getP2().x, value.getP1().y, value.getP2().y);
}
};
}
this.clusters.add(plotCluster);
}
public PlotCluster getCluster(String string) {
for (PlotCluster cluster : getClusters()) {
if (cluster.getName().equalsIgnoreCase(string)) {
return cluster;
}
}
return null;
}
}