/*
* This file is part of Cubic Chunks Mod, licensed under the MIT License (MIT).
*
* Copyright (c) 2015 contributors
*
* 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 cubicchunks.util;
import net.minecraft.entity.Entity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import java.util.function.Consumer;
import java.util.function.Predicate;
import cubicchunks.world.cube.Cube;
import static cubicchunks.util.Coords.blockToCube;
import static cubicchunks.util.Coords.getCubeXForEntity;
import static cubicchunks.util.Coords.getCubeYForEntity;
import static cubicchunks.util.Coords.getCubeZForEntity;
/**
* Position of a cube.
* <p>
* Tall Worlds uses a column coordinate system (which is really just a cube coordinate system without the y-coordinate),
* a cube coordinate system, and two block coordinate systems, a cube-relative system, and a world absolute system.
* <p>
* It is important that the systems are kept separate. This class should be used whenever a cube coordinate is passed
* along, so that it is clear that cube coordinates are being used, and not block coordinates.
* <p>
* Additionally, I (Nick) like to use xRel, yRel, and zRel for the relative position of a block inside of a cube. In
* world space, I (Nick) refer to the coordinates as xAbs, yAbs, and zAbs.
* <p>
* See {@link AddressTools} for details of hashing the cube coordinates for keys and storage.
* <p>
* This class also contains some helper methods to switch from/to block coordinates.
*/
public class CubePos {
private final int cubeX;
private final int cubeY;
private final int cubeZ;
public CubePos(int cubeX, int cubeY, int cubeZ) {
this.cubeX = cubeX;
this.cubeY = cubeY;
this.cubeZ = cubeZ;
}
public CubePos(long address) {
this.cubeX = AddressTools.getX(address);
this.cubeY = AddressTools.getY(address);
this.cubeZ = AddressTools.getZ(address);
}
/**
* Gets the x position of the cube in the world.
*
* @return The x position.
*/
public int getX() {
return this.cubeX;
}
/**
* Gets the y position of the cube in the world.
*
* @return The y position.
*/
public int getY() {
return this.cubeY;
}
/**
* Gets the z position of the cube in the world.
*
* @return The z position.
*/
public int getZ() {
return this.cubeZ;
}
/**
* Gets the coordinates of the cube as a string.
*
* @return The coordinates, formatted as a string.
*/
@Override
public String toString() {
return String.format("CubePos(%d, %d, %d)", cubeX, cubeY, cubeZ);
}
/**
* Compares the CubeCoordinate against the given object.
*
* @return True if the cube matches the given object, but false if it doesn't match, or is null, or not a
* CubeCoordinate object.
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof CubePos) {
CubePos otherCoords = (CubePos) obj;
return otherCoords.cubeX == cubeX && otherCoords.cubeY == cubeY && otherCoords.cubeZ == cubeZ;
}
return false;
}
/**
* Calculates a 64bit encoding of these coordinates.
*
* @return 64bit encoding of these coordinates.
*/
public long getAddress() {
return AddressTools.getAddress(cubeX, cubeY, cubeZ);
}
/**
* Returns a specification compliant hashCode for this object.
*
* @return A 32bit hashCode for this instance of CubePos.
*/
@Override
public int hashCode() {
return Long.hashCode(this.getAddress());
}
/**
* Gets the absolute position of the cube's center on the x axis.
*
* @return The x center of the cube.
*/
public int getXCenter() {
return cubeX*Cube.SIZE + Cube.SIZE/2;
}
/**
* Gets the absolute position of the cube's center on the y axis.
*
* @return The y center of the cube.
*/
public int getYCenter() {
return cubeY*Cube.SIZE + Cube.SIZE/2;
}
/**
* Gets the absolute position of the cube's center on the z axis.
*
* @return The z center of the cube.
*/
public int getZCenter() {
return cubeZ*Cube.SIZE + Cube.SIZE/2;
}
public int getMinBlockX() {
return Coords.cubeToMinBlock(cubeX);
}
public int getMinBlockY() {
return Coords.cubeToMinBlock(cubeY);
}
public int getMinBlockZ() {
return Coords.cubeToMinBlock(cubeZ);
}
public int getMaxBlockX() {
return Coords.cubeToMaxBlock(this.cubeX);
}
public int getMaxBlockY() {
return Coords.cubeToMaxBlock(this.cubeY);
}
public int getMaxBlockZ() {
return Coords.cubeToMaxBlock(this.cubeZ);
}
public BlockPos getCenterBlockPos() {
return new BlockPos(getXCenter(), getYCenter(), getZCenter());
}
public BlockPos getMinBlockPos() {
return new BlockPos(getMinBlockX(), getMinBlockY(), getMinBlockZ());
}
public BlockPos getMaxBlockPos() {
return new BlockPos(getMaxBlockX(), getMaxBlockY(), getMaxBlockZ());
}
public BlockPos localToBlock(int localX, int localY, int localZ) {
return new BlockPos(getMinBlockX() + localX, getMinBlockY() + localY, getMinBlockZ() + localZ);
}
public CubePos sub(int dx, int dy, int dz) {
return this.add(-dx, -dy, -dz);
}
public CubePos add(int dx, int dy, int dz) {
return new CubePos(getX() + dx, getY() + dy, getZ() + dz);
}
public ChunkPos chunkPos() {
return new ChunkPos(getX(), getZ());
}
public int distSquared(CubePos coords) {
int dx = coords.cubeX - this.cubeX;
int dy = coords.cubeY - this.cubeY;
int dz = coords.cubeZ - this.cubeZ;
return dx*dx + dy*dy + dz*dz;
}
public void forEachWithinRange(int range, Consumer<CubePos> action) {
for (int x = this.cubeX - range; x < this.cubeX + range; x++) {
for (int y = this.cubeY - range; y < this.cubeY + range; y++) {
for (int z = this.cubeZ - range; z < this.cubeZ + range; z++) {
action.accept(new CubePos(x, y, z));
}
}
}
}
/**
* For each x/z coordinate pair in this cube position - goes top-down and calls func for each BlockPos.
* Once func returns false - processing of next block column begins.
*/
public void forEachBlockPosMutableTopDown(Predicate<BlockPos> func) {
BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos();
int baseX = getMinBlockX();
int baseZ = getMinBlockZ();
int blockYMax = getMaxBlockY();
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
blockPos.setPos(baseX + x, blockYMax, baseZ + z);
for (int y = 15; y >= 0; y--) {
boolean cont = func.test(blockPos);
blockPos.move(EnumFacing.DOWN);
if (!cont) {
break;
}
}
}
}
}
public static CubePos fromBlockCoords(int blockX, int blockY, int blockZ) {
return new CubePos(blockToCube(blockX), blockToCube(blockY), blockToCube(blockZ));
}
public static CubePos fromEntity(Entity entity) {
return new CubePos(getCubeXForEntity(entity), getCubeYForEntity(entity), getCubeZForEntity(entity));
}
public static CubePos fromBlockCoords(BlockPos pos) {
return CubePos.fromBlockCoords(pos.getX(), pos.getY(), pos.getZ());
}
}