/* * This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT). * * Copyright (c) JCThePants (www.jcwhatever.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.jcwhatever.nucleus.regions; import com.jcwhatever.nucleus.providers.regionselect.IRegionSelection; import com.jcwhatever.nucleus.regions.data.CuboidPoint; import com.jcwhatever.nucleus.regions.data.RegionShape; import com.jcwhatever.nucleus.utils.PreCon; import com.jcwhatever.nucleus.utils.coords.ChunkCoords; import com.jcwhatever.nucleus.utils.coords.IChunkCoords; import com.jcwhatever.nucleus.utils.coords.LocationUtils; import com.jcwhatever.nucleus.utils.coords.SyncLocation; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.World; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.annotation.Nullable; /** * Contains pre-calculated variables regarding a cuboid region * of space as defined by two region locations. */ public class SimpleRegionSelection implements IRegionSelection { private final Object _sync = new Object(); private SyncLocation _p1; private SyncLocation _p2; private SyncLocation _lowerPoint; private SyncLocation _upperPoint; private int _startX; private int _startY; private int _startZ; private int _endX; private int _endY; private int _endZ; private int _xWidth; private int _zWidth; private int _yHeight; private int _xBlockWidth; private int _zBlockWidth; private int _yBlockHeight; private long _volume; private int _chunkX; private int _chunkZ; private int _chunkXWidth; private int _chunkZWidth; private List<IChunkCoords> _chunks; private Location _center; private RegionShape _flatness = RegionShape.CUBOID; /** * Empty Constructor. */ protected SimpleRegionSelection() {} /** * Constructor. * * @param p1 The first point location. * @param p2 The second point location. */ public SimpleRegionSelection(Location p1, Location p2) { PreCon.notNull(p1); PreCon.notNull(p2); setCoords(p1, p2); } @Override public final boolean isDefined() { return _p1 != null && _p2 != null && _p1.getWorldName() != null; } @Override @Nullable public final World getWorld() { synchronized (_sync) { if (isDefined()) { return _p1.getWorld(); } return null; } } @Nullable @Override public String getWorldName() { if (_p1 == null) return null; return _p1.getWorldName(); } @Override public boolean isWorldLoaded() { return isDefined() && _p1.getWorldName() != null && getWorld() != null; } @Override @Nullable public final Location getP1() { if (_p1 == null) return null; synchronized (_sync) { return _p1.getBukkitLocation(); } } @Override @Nullable public Location getP1(Location location) { PreCon.notNull(location); if (_p1 == null) return null; return LocationUtils.copy(_p1, location); } @Override @Nullable public final Location getP2() { if (_p2 == null) return null; synchronized (_sync) { return _p2.getBukkitLocation(); } } @Override @Nullable public Location getP2(Location location) { PreCon.notNull(location); if (_p2 == null) return null; return LocationUtils.copy(_p2, location); } @Override @Nullable public final Location getLowerPoint() { if (_lowerPoint == null) return null; return _lowerPoint.getBukkitLocation(); } @Override @Nullable public Location getLowerPoint(Location location) { PreCon.notNull(location); if (_lowerPoint == null) return null; return LocationUtils.copy(_lowerPoint, location); } @Override @Nullable public final Location getUpperPoint() { if (_upperPoint == null) return null; return _upperPoint.getBukkitLocation(); } @Override public Location getUpperPoint(Location location) { PreCon.notNull(location); if (_upperPoint == null) return null; return LocationUtils.copy(_upperPoint, location); } @Override public final int getXStart() { return _startX; } @Override public final int getYStart() { return _startY; } @Override public final int getZStart() { return _startZ; } @Override public final int getXEnd() { return _endX; } @Override public final int getYEnd() { return _endY; } @Override public final int getZEnd() { return _endZ; } @Override public final int getXWidth() { return _xWidth; } @Override public final int getZWidth() { return _zWidth; } @Override public final int getYHeight() { return _yHeight; } @Override public final int getXBlockWidth() { return _xBlockWidth; } @Override public final int getZBlockWidth() { return _zBlockWidth; } @Override public final int getYBlockHeight() { return _yBlockHeight; } @Override public final long getVolume() { return _volume; } @Override @Nullable public final Location getCenter() { if (_center == null) return null; return LocationUtils.copy(_center); } @Override @Nullable public Location getCenter(Location location) { PreCon.notNull(location); if (_center == null) return null; return LocationUtils.copy(_center, location); } @Override public final int getChunkX() { return _chunkX; } @Override public final int getChunkZ() { return _chunkZ; } @Override public final int getChunkXWidth() { return _chunkXWidth; } @Override public final int getChunkZWidth() { return _chunkZWidth; } @Override public final Collection<IChunkCoords> getChunkCoords() { if (_chunks != null) return new ArrayList<>(_chunks); return getChunkCoords(new ArrayList<IChunkCoords>(1)); } @Override public <T extends Collection<IChunkCoords>> T getChunkCoords(T output) { PreCon.notNull(output); if (getWorld() == null || !isDefined()) return output; if (_chunks != null) { output.addAll(_chunks); return output; } synchronized (_sync) { int startX = _chunkX; int endX = _chunkX + _chunkXWidth - 1; int startZ = _chunkZ; int endZ = _chunkZ + _chunkZWidth - 1; List<IChunkCoords> result = new ArrayList<IChunkCoords>((endX - startX) * (endZ - startZ) + 1); for (int x = startX; x <= endX; x++) { for (int z = startZ; z <= endZ; z++) { result.add(new ChunkCoords(_p1.getWorldName(), x, z)); } } output.addAll(_chunks = result); return output; } } @Override public final RegionShape getShape() { return _flatness; } @Override public final boolean contains(Location loc) { if (!isDefined()) return false; if (loc.getWorld() == null) return false; if (!loc.getWorld().equals(getWorld())) return false; int x = loc.getBlockX(); int y = loc.getBlockY(); int z = loc.getBlockZ(); return contains(x, y, z); } @Override public final boolean contains(int x, int y, int z) { synchronized (_sync) { _sync.notifyAll(); return x >= getXStart() && x <= getXEnd() && y >= getYStart() && y <= getYEnd() && z >= getZStart() && z <= getZEnd(); } } @Override public final boolean contains(Location loc, boolean cx, boolean cy, boolean cz) { if (!isDefined()) return false; synchronized (_sync) { if (!loc.getWorld().equals(getWorld())) return false; if (cx) { int x = loc.getBlockX(); if (x < getXStart() || x > getXEnd()) return false; } if (cy) { int y = loc.getBlockY(); if (y < getYStart() || y > getYEnd()) return false; } if (cz) { int z = loc.getBlockZ(); if (z < getZStart() || z > getZEnd()) return false; } _sync.notifyAll(); return true; } } @Override public final boolean intersects(Chunk chunk) { PreCon.notNull(chunk); return isDefined() && chunk.getWorld().equals(getWorld()) && intersects(chunk.getX(), chunk.getZ()); } @Override public final boolean intersects(int chunkX, int chunkZ) { return getChunkX() <= chunkX && (getChunkX() + getChunkXWidth() - 1) >= chunkX && getChunkZ() <= chunkZ && (getChunkZ() + getChunkZWidth() - 1) >= chunkZ; } @Override public Location getPoint(CuboidPoint point) { PreCon.notNull(point); return point.getLocation(this); } @Nullable @Override public CuboidPoint getPoint(Location location) { PreCon.notNull(location); return CuboidPoint.getCuboidPoint(location, this); } /** * Get the synchronization object. */ protected final Object getSync() { return _sync; } /** * Get a reference to the underlying P1 {@link SyncLocation} * coordinates. */ protected SyncLocation getSyncP1() { return _p1; } /** * Get a reference to the underlying P2 {@link SyncLocation} * coordinates. */ protected SyncLocation getSyncP2() { return _p2; } /** * Set the regions cuboid point coordinates. * * @param p1 The first point location. * @param p2 The second point location. */ protected void setCoords(Location p1, Location p2) { PreCon.notNull(p1); PreCon.notNull(p2); World p1World = p1.getWorld(); World p2World = p2.getWorld(); if (p1World != null && !p1World.equals(p2World) || p2World != null && !p2World.equals(p1World)) { throw new IllegalArgumentException("Both region points must be from the same world."); } double lowerX = Math.min(p1.getX(), p2.getX()); double lowerY = Math.min(p1.getY(), p2.getY()); double lowerZ = Math.min(p1.getZ(), p2.getZ()); double upperX = Math.max(p1.getX(), p2.getX()); double upperY = Math.max(p1.getY(), p2.getY()); double upperZ = Math.max(p1.getZ(), p2.getZ()); String worldName = null; if (p1 instanceof SyncLocation) { worldName = ((SyncLocation) p1).getWorldName(); } else if (p1.getWorld() != null) { worldName = p1.getWorld().getName(); } _p1 = new SyncLocation(worldName, p1.getX(), p1.getY(), p1.getZ(), 0F, 0F); _p2 = new SyncLocation(worldName, p2.getX(), p2.getY(), p2.getZ(), 0F, 0F); _lowerPoint = new SyncLocation(worldName, lowerX, lowerY, lowerZ, 0F, 0F); _upperPoint = new SyncLocation(worldName, upperX, upperY, upperZ, 0F, 0F); updateMath(); } /* * Update region math variables */ protected void updateMath() { if (_p1 == null || _p2 == null) return; synchronized (_sync) { _startX = Math.min(_p1.getBlockX(), _p2.getBlockX()); _startY = Math.min(_p1.getBlockY(), _p2.getBlockY()); _startZ = Math.min(_p1.getBlockZ(), _p2.getBlockZ()); _endX = Math.max(_p1.getBlockX(), _p2.getBlockX()); _endY = Math.max(_p1.getBlockY(), _p2.getBlockY()); _endZ = Math.max(_p1.getBlockZ(), _p2.getBlockZ()); _xWidth = (int)Math.abs(_p1.getX() - _p2.getX()); _zWidth = (int)Math.abs(_p1.getZ() - _p2.getZ()); _yHeight = (int)Math.abs(_p1.getY() - _p2.getY()); _xBlockWidth = _xWidth + 1; _zBlockWidth = _zWidth + 1; _yBlockHeight = _yHeight + 1; _volume = _xWidth * _zWidth * _yHeight; if (getWorld() != null) { double xCenter = (double) _startX + (_xBlockWidth / 2.0D); double yCenter = (double) _startY + (_yBlockHeight / 2.0D); double zCenter = (double) _startZ + (_zBlockWidth / 2.0D); _center = new Location(getWorld(), xCenter, yCenter, zCenter); } _chunkX = Math.min(_p1.getBlockX(), _p2.getBlockX()) == _p1.getBlockX() ? (int)Math.floor(_p1.getBlockX() / 16.0D) : (int)Math.floor(_p2.getBlockX() / 16.0D); _chunkZ = Math.min(_p1.getBlockZ(), _p2.getBlockZ()) == _p1.getBlockZ() ? (int)Math.floor(_p1.getBlockZ() / 16.0D) : (int)Math.floor(_p2.getBlockZ() / 16.0D); int chunkEndX = Math.max(_p1.getBlockX(), _p2.getBlockX()) == _p1.getBlockX() ? (int)Math.floor(_p1.getBlockX() / 16.0D) : (int)Math.floor(_p2.getBlockX() / 16.0D); int chunkEndZ = Math.max(_p1.getBlockZ(), _p2.getBlockZ()) == _p1.getBlockZ() ? (int)Math.floor(_p1.getBlockZ() / 16.0D) : (int)Math.floor(_p2.getBlockZ() / 16.0D); _chunkXWidth = chunkEndX - _chunkX + 1; _chunkZWidth = chunkEndZ - _chunkZ + 1; _chunks = null; _flatness = RegionShape.CUBOID; if (getXBlockWidth() == 1) { // west/east if (getZBlockWidth() == 1) { // north/south _flatness = getYBlockHeight() == 1 ? RegionShape.POINT : RegionShape.VERTICAL_LINE; } else { _flatness = getYBlockHeight() == 1 ? RegionShape.NORTH_SOUTH_LINE : RegionShape.FLAT_WEST_EAST; } } else if (getZBlockWidth() == 1) { // north/south _flatness = getYBlockHeight() == 1 ? RegionShape.WEST_EAST_LINE : RegionShape.FLAT_NORTH_SOUTH; } else if (getYBlockHeight() == 1) { _flatness = RegionShape.FLAT_HORIZONTAL; } } } }