package net.tropicraft.world;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.util.LongHashMap;
import net.minecraft.util.MathHelper;
import net.minecraft.world.ChunkCoordIntPair;
import net.minecraft.world.Teleporter;
import net.minecraft.world.WorldServer;
import net.tropicraft.block.tileentity.TileEntityBambooChest;
import net.tropicraft.registry.TCBlockRegistry;
import net.tropicraft.registry.TCItemRegistry;
//import net.tropicraft.block.tileentity.TileEntityBambooChest; TODO
//import net.tropicraft.item.TropicraftItems; TODO
public class TeleporterTropics extends Teleporter {
private static Block PORTAL_WALL_BLOCK;
private static Block PORTAL_BLOCK;
private final WorldServer world;
private final Random random;
/** Stores successful portal placement locations for rapid lookup. */
private final LongHashMap destinationCoordinateCache = new LongHashMap();
/**
* A list of valid keys for the destinationCoordainteCache. These are based on the X & Z of the players initial
* location.
*/
private final List destinationCoordinateKeys = new ArrayList();
public TeleporterTropics(WorldServer world) {
super(world);
PORTAL_BLOCK = TCBlockRegistry.tropicsPortal;
PORTAL_WALL_BLOCK = TCBlockRegistry.tropicsPortalWall;
this.world = world;
this.random = new Random(world.getSeed());
}
@Override
public void placeInPortal(Entity entity, double d, double d2, double d3, float f) {
long startTime = System.currentTimeMillis();
if (!placeInExistingPortal(entity, d, d2, d3, f)) {
makePortal(entity);
placeInExistingPortal(entity, d, d2, d3, f);
}
long finishTime = System.currentTimeMillis();
System.out.printf("It took %f seconds for TeleporterTropics.placeInPortal to complete\n", (finishTime - startTime) / 1000.0F);
}
@Override
public boolean placeInExistingPortal(Entity entity, double d, double d2, double d3, float f)
{
int searchArea = 148;
double closestPortal = -1D;
int foundX = 0;
int foundY = 0;
int foundZ = 0;
int entityX = MathHelper.floor_double(entity.posX);
int entityZ = MathHelper.floor_double(entity.posZ);
boolean notInCache = true;
long j1 = ChunkCoordIntPair.chunkXZ2Int(entityX, entityZ);
if (destinationCoordinateCache.containsItem(j1)) {
// System.out.println("Setting closest portal to 0");
PortalPosition portalposition = (PortalPosition)destinationCoordinateCache.getValueByKey(j1);
closestPortal = 0.0D;
foundX = portalposition.posX;
foundY = portalposition.posY;
foundZ = portalposition.posZ;
portalposition.lastUpdateTime = world.getTotalWorldTime();
notInCache = false;
} else {
for (int x = entityX - searchArea; x <= entityX + searchArea; x ++)
{
double distX = x + 0.5D - entity.posX;
for (int z = entityZ - searchArea; z <= entityZ + searchArea; z ++)
{
double distZ = z + 0.5D - entity.posZ;
for (int y = world.getActualHeight() - 1; y >= 0; y--)
{
if (world.getBlock(x, y, z) == PORTAL_BLOCK)
{
while (world.getBlock(x, y - 1, z) == PORTAL_BLOCK)
{
--y;
}
double distY = y + 0.5D - entity.posY;
double distance = distX * distX + distY * distY + distZ * distZ;
if (closestPortal < 0.0D || distance < closestPortal)
{
closestPortal = distance;
foundX = x;
foundY = y;
foundZ = z;
}
}
}
}
}
}
// System.out.println("Setting closest portal to " + closestPortal);
if (closestPortal >= 0.0D)
{
if (notInCache) {
destinationCoordinateCache.add(j1, new PortalPosition(foundX, foundY, foundZ, world.getTotalWorldTime()));
destinationCoordinateKeys.add(Long.valueOf(j1));
}
int x = foundX;
int y = foundY;
int z = foundZ;
double newLocX = x + 0.5D;
double newLocY = y + 0.5D;
double newLocZ = z + 0.5D;
if (world.getBlock(x - 1, y, z) == PORTAL_BLOCK)
{
newLocX -= 0.5D;
}
if (world.getBlock(x + 1, y, z) == PORTAL_BLOCK)
{
newLocX += 0.5D;
}
if (world.getBlock(x, y, z - 1) == PORTAL_BLOCK)
{
newLocZ -= 0.5D;
}
if (world.getBlock(x, y, z + 1) == PORTAL_BLOCK)
{
newLocZ += 0.5D;
}
entity.setLocationAndAngles(newLocX, newLocY + 2, newLocZ, entity.rotationYaw, 0.0F);
int worldSpawnX = MathHelper.floor_double(newLocX);//TODO + ((new Random()).nextBoolean() ? 3 : -3);
int worldSpawnZ = MathHelper.floor_double(newLocZ);//TODO + ((new Random()).nextBoolean() ? 3 : -3);
int worldSpawnY = world.getHeightValue(worldSpawnX, worldSpawnZ) + 3;
entity.motionX = entity.motionY = entity.motionZ = 0.0D;
// If the player is entering the tropics, spawn an Encyclopedia Tropica
// in the spawn portal chest (if they don't already have one AND one isn't
// already in the chest)
if (entity instanceof EntityPlayer) {
EntityPlayer player = (EntityPlayer) entity;
if (world.provider instanceof WorldProviderTropicraft) {
//TODO improve this logical check to an NBT tag or something?
if (!player.inventory.hasItem(TCItemRegistry.encTropica)) {
// Search for the spawn chest
TileEntityBambooChest chest = null;
int chestX = MathHelper.floor_double(newLocX);
int chestZ = MathHelper.floor_double(newLocZ);
chestSearch:
for (int searchX = -3; searchX < 4; searchX++) {
for (int searchZ = -3; searchZ < 4; searchZ++) {
for (int searchY = -4; searchY < 5; searchY++) {
if (world.getBlock(chestX + searchX, worldSpawnY + searchY, chestZ + searchZ) == TCBlockRegistry.bambooChest) {
chest = (TileEntityBambooChest)world.getTileEntity(chestX + searchX, worldSpawnY + searchY, chestZ + searchZ);
if (chest != null && chest.isUnbreakable()) {
break chestSearch;
}
}
}
}
}
// Make sure chest doesn't have the encyclopedia
if (chest!= null && chest.isUnbreakable()) {
boolean hasEncyclopedia = false;
for (int inv = 0; inv < chest.getSizeInventory(); inv++) {
ItemStack stack = chest.getStackInSlot(inv);
if (stack != null && stack.getItem() == TCItemRegistry.encTropica) {
hasEncyclopedia = true;
}
}
// Give out a new encyclopedia
if (!hasEncyclopedia) {
for (int inv = 0; inv < chest.getSizeInventory(); inv++) {
ItemStack stack = chest.getStackInSlot(inv);
if (stack == null) {
chest.setInventorySlotContents(inv, new ItemStack(TCItemRegistry.encTropica, 1));
break;
}
}
}
}
}
}
}
return true;
}
else
{
return false;
}
}
@Override
public boolean makePortal(Entity entity)
{
int searchArea = 16;
double closestSpot = -1D;
int entityX = MathHelper.floor_double(entity.posX);
int entityY = MathHelper.floor_double(entity.posY);
int entityZ = MathHelper.floor_double(entity.posZ);
int foundX = entityX;
int foundY = entityY;
int foundZ = entityZ;
for (int x = entityX - searchArea; x <= entityX + searchArea; x++) {
double distX = (x + 0.5D) - entity.posX;
nextCoords:
for (int z = entityZ - searchArea; z <= entityZ + searchArea; z++) {
double distZ = (z + 0.5D) - entity.posZ;
// Find topmost solid block at this x,z location
int y = world.getHeight() - 1;
for (; y >= 63 - 1 && (world.getBlock(x, y, z) == Blocks.air ||
!world.getBlock(x, y, z).isOpaqueCube()); y--) {
;
}
// Only generate portal between sea level and sea level + 20
if (y > 63 + 20 || y < 63) {
continue;
}
if (getValidBuildBlocks().contains(world.getBlock(x, y, z))) {
for (int xOffset = -2; xOffset <= 2; xOffset++) {
for (int zOffset = -2; zOffset <= 2; zOffset++) {
int otherY = world.getHeight() - 1;
for (; otherY >= 63 && (world.getBlock(x + xOffset, otherY, z + zOffset) == Blocks.air ||
!world.getBlock(x, y, z).isOpaqueCube()); otherY--) {
;
}
if (Math.abs(y - otherY) >= 3) {
continue nextCoords;
}
if (!getValidBuildBlocks().contains(world.getBlock(x + xOffset, otherY, z + zOffset))) {
continue nextCoords;
}
}
}
double distY = (y + 0.5D) - entity.posY;
double distance = distX * distX + distY * distY + distZ * distZ;
if (closestSpot < 0.0D || distance < closestSpot)
{
closestSpot = distance;
foundX = x;
foundY = y;
foundZ = z;
}
}
}
}
int worldSpawnX = MathHelper.floor_double(foundX);//TODO + ((new Random()).nextBoolean() ? 3 : -3);
int worldSpawnZ = MathHelper.floor_double(foundZ);//TODO + ((new Random()).nextBoolean() ? 3 : -3);
int worldSpawnY = getTerrainHeightAt(worldSpawnX, worldSpawnZ);//world.getHeightValue(worldSpawnX, worldSpawnZ) - 2;
// If we can't find a spot (e.g. we're in the middle of the ocean),
// just put the portal at sea level
if(closestSpot < 0.0D) {
// Perhaps this was the culprit
/* Random r = new Random();
foundX += r.nextInt(16) - 8;
foundZ += r.nextInt(16) - 8;*/
foundY = worldSpawnY - 2;
}
// System.out.printf("Buliding teleporter at x:<%d>, y:<%d>, z:<%d>\n", foundX, foundY, foundZ);
entity.setLocationAndAngles(foundX, foundY + 2, foundZ, entity.rotationYaw, 0.0F);
buildTeleporterAt(worldSpawnX, worldSpawnY + 1, worldSpawnZ, entity);
return true;
}
/**
* Gets the terrain height at the specified coordinates
* @param x The x coordinate
* @param z The z coordinate
* @return The terrain height at the specified coordinates
*/
public int getTerrainHeightAt(int x, int z) {
for(int y = 100; y > 0; y--)
{
Block block = world.getBlock(x, y, z);
if(block == Blocks.dirt || block == Blocks.grass || block == Blocks.sand || block == Blocks.stone ||
block == TCBlockRegistry.tropicsWater || block == TCBlockRegistry.purifiedSand)
{
return y;
}
}
return 0;
}
public void buildTeleporterAt(int x, int y, int z, Entity entity) {
y = y < 9 ? 9 : y;
for (int yOffset = 4; yOffset >= -7; yOffset--) {
for (int zOffset = -2; zOffset <= 2; zOffset++) {
for (int xOffset = -2; xOffset <= 2; xOffset++) {
int blockX = x + xOffset;
int blockY = y + yOffset;
int blockZ = z + zOffset;
if (yOffset == -7) {
// Set bottom of portal to be solid
world.setBlock(blockX, blockY, blockZ, PORTAL_WALL_BLOCK);
} else if (yOffset > 0) {
// Set 4 blocks above portal to air
world.setBlock(blockX, blockY, blockZ, Blocks.air);
} else {
boolean isWall = xOffset == -2 || xOffset == 2 || zOffset == -2 || zOffset == 2;
if (isWall) {
// Set walls around portal
world.setBlock(blockX, blockY, blockZ, PORTAL_WALL_BLOCK);
} else {
// Set inside of portal
int metadata = yOffset <= -5 ? 8 : 0;
world.setBlock(blockX, blockY, blockZ, PORTAL_BLOCK, metadata, 3);
}
}
boolean isCorner = (xOffset == -2 || xOffset == 2) && (zOffset == -2 || zOffset == 2);
if (yOffset == 0 && isCorner) {
world.setBlock(blockX, blockY + 1, blockZ, TCBlockRegistry.tikiTorch, 1, 3);
world.setBlock(blockX, blockY + 2, blockZ, TCBlockRegistry.tikiTorch, 1, 3);
world.setBlock(blockX, blockY + 3, blockZ, TCBlockRegistry.tikiTorch, 0, 3);
}
}
}
}
// Add chest
// Add an unbreakable chest to place encyclopedia in
// NOTE: using instanceof instead of world.getWorldInfo().getDimension()
// because getWorldInfo() may not be set/correct yet
if (world.provider instanceof WorldProviderTropicraft) {
world.setBlock(x + 2, y + 1, z, TCBlockRegistry.bambooChest, 1, 3);
TileEntityBambooChest tile = (TileEntityBambooChest)world.getTileEntity(x + 2, y + 1, z);
if (tile != null) {
tile.setIsUnbreakable(true);
}
}
for (int yOffset = 5; yOffset >= -7; yOffset--) {
for (int zOffset = -2; zOffset <= 2; zOffset++) {
for (int xOffset = -2; xOffset <= 2; xOffset++) {
int blockX = x + xOffset;
int blockY = y + yOffset;
int blockZ = z + zOffset;
world.notifyBlocksOfNeighborChange(blockX, blockY, blockZ, world.getBlock(blockX, blockY, blockZ));
}
}
}
}
/**
* called periodically to remove out-of-date portal locations from the cache list. Argument par1 is a
* WorldServer.getTotalWorldTime() value.
*/
@Override
public void removeStalePortalLocations(long par1)
{
if (par1 % 100L == 0L)
{
Iterator iterator = destinationCoordinateKeys.iterator();
long j = par1 - 600L;
while (iterator.hasNext())
{
Long olong = (Long)iterator.next();
PortalPosition portalposition = (PortalPosition)destinationCoordinateCache.getValueByKey(olong.longValue());
if (portalposition == null || portalposition.lastUpdateTime < j)
{
iterator.remove();
destinationCoordinateCache.remove(olong.longValue());
}
}
}
}
/**
* @return List of valid block ids to build portal on
*/
private List<Block> getValidBuildBlocks() {
return Arrays.asList(Blocks.sand, Blocks.grass, Blocks.dirt, TCBlockRegistry.purifiedSand);
}
}