#include "stdafx.h"
#include "StrongholdPieces.h"
#include "net.minecraft.world.level.h"
#include "net.minecraft.world.level.tile.h"
#include "net.minecraft.world.level.tile.entity.h"
#include "net.minecraft.world.level.storage.h"
#include "net.minecraft.world.level.levelgen.h"
#include "net.minecraft.world.item.h"
#include "WeighedTreasure.h"
#include "FileHeader.h"
#include "Facing.h"

int StrongholdPieces::totalWeight = 0;
list<StrongholdPieces::PieceWeight *> StrongholdPieces::currentPieces;
StrongholdPieces::EPieceClass StrongholdPieces::imposedPiece;
const bool StrongholdPieces::CHECK_AIR = true;

StrongholdPieces::PieceWeight::PieceWeight(EPieceClass pieceClass, int weight, int maxPlaceCount) : weight(weight)
{
	this->placeCount = 0;	// 4J added initialiser
	this->pieceClass	= pieceClass;
	this->maxPlaceCount = maxPlaceCount;
}

bool StrongholdPieces::PieceWeight::doPlace(int depth)
{
	return maxPlaceCount == 0 || placeCount < maxPlaceCount;
}

bool StrongholdPieces::PieceWeight::isValid()
{
	return maxPlaceCount == 0 || placeCount < maxPlaceCount;
}

void StrongholdPieces::resetPieces()
{
	for( AUTO_VAR(it, currentPieces.begin()); it != currentPieces.end(); it++ )
	{
		delete (*it);
	}
	currentPieces.clear();

    currentPieces.push_back( new PieceWeight(EPieceClass_Straight, 40, 0) );
    currentPieces.push_back( new PieceWeight(EPieceClass_PrisonHall, 5, 5) );
    currentPieces.push_back( new PieceWeight(EPieceClass_LeftTurn, 20, 0) );
    currentPieces.push_back( new PieceWeight(EPieceClass_RightTurn, 20, 0) );
    currentPieces.push_back( new PieceWeight(EPieceClass_RoomCrossing, 10, 6) );
    currentPieces.push_back( new PieceWeight(EPieceClass_StraightStairsDown, 5, 5) );
    currentPieces.push_back( new PieceWeight(EPieceClass_StairsDown, 5, 5) );
    currentPieces.push_back( new PieceWeight(EPieceClass_FiveCrossing, 5, 4) );
	currentPieces.push_back( new PieceWeight(EPieceClass_ChestCorridor, 5, 4) );
    currentPieces.push_back( new PieceWeight_Library(EPieceClass_Library, 10, 2) );
	currentPieces.push_back( new PieceWeight_PortalRoom(EPieceClass_PortalRoom, 20, 1) );

	imposedPiece = EPieceClass_NULL;
}

bool StrongholdPieces::updatePieceWeight()
{
    bool hasAnyPieces = false;
    totalWeight = 0;
	for( AUTO_VAR(it, currentPieces.begin()); it != currentPieces.end(); it++ )
	{
		PieceWeight *piece = *it;
        if (piece->maxPlaceCount > 0 && piece->placeCount < piece->maxPlaceCount)
		{
            hasAnyPieces = true;
        }
        totalWeight += piece->weight;
    }
    return hasAnyPieces;
}

StrongholdPieces::StrongholdPiece *StrongholdPieces::findAndCreatePieceFactory(EPieceClass pieceClass, list<StructurePiece*> *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth)
{
    StrongholdPiece *strongholdPiece = NULL;

    if (pieceClass == EPieceClass_Straight)
	{
        strongholdPiece = Straight::createPiece(pieces, random, footX, footY, footZ, direction, depth);
    }
	else if (pieceClass == EPieceClass_PrisonHall)
	{
        strongholdPiece = PrisonHall::createPiece(pieces, random, footX, footY, footZ, direction, depth);
    }
	else if (pieceClass == EPieceClass_LeftTurn)
	{
        strongholdPiece = LeftTurn::createPiece(pieces, random, footX, footY, footZ, direction, depth);
    }
	else if (pieceClass == EPieceClass_RightTurn)
	{
        strongholdPiece = RightTurn::createPiece(pieces, random, footX, footY, footZ, direction, depth);
    }
	else if (pieceClass == EPieceClass_RoomCrossing)
	{
        strongholdPiece = RoomCrossing::createPiece(pieces, random, footX, footY, footZ, direction, depth);
    }
	else if (pieceClass == EPieceClass_StraightStairsDown)
	{
        strongholdPiece = StraightStairsDown::createPiece(pieces, random, footX, footY, footZ, direction, depth);
    }
	else if (pieceClass == EPieceClass_StairsDown)
	{
        strongholdPiece = StairsDown::createPiece(pieces, random, footX, footY, footZ, direction, depth);
    }
	else if (pieceClass == EPieceClass_FiveCrossing)
	{
        strongholdPiece = FiveCrossing::createPiece(pieces, random, footX, footY, footZ, direction, depth);
    }
	else if (pieceClass == EPieceClass_ChestCorridor)
	{
        strongholdPiece = ChestCorridor::createPiece(pieces, random, footX, footY, footZ, direction, depth);
    }
	else if (pieceClass == EPieceClass_Library)
	{
        strongholdPiece = Library::createPiece(pieces, random, footX, footY, footZ, direction, depth);
    }
		else if (pieceClass == EPieceClass_PortalRoom)
	{
        strongholdPiece = PortalRoom::createPiece(pieces, random, footX, footY, footZ, direction, depth);
    }


    return strongholdPiece;
}

StrongholdPieces::StrongholdPiece *StrongholdPieces::generatePieceFromSmallDoor(StartPiece *startPiece, list<StructurePiece *> *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth)
{
    if (!updatePieceWeight())
	{
        return NULL;
    }

	if (imposedPiece != EPieceClass_NULL)
	{
        StrongholdPiece *strongholdPiece = findAndCreatePieceFactory(imposedPiece, pieces, random, footX, footY, footZ, direction, depth);
        imposedPiece = EPieceClass_NULL;

        if (strongholdPiece != NULL)
		{
            return strongholdPiece;
        }
    }

    int numAttempts = 0;
    while (numAttempts < 5)
	{
        numAttempts++;

        int weightSelection = random->nextInt(totalWeight);
		for( AUTO_VAR(it, currentPieces.begin()); it != currentPieces.end(); it++ )
		{
			PieceWeight *piece = *it;
            weightSelection -= piece->weight;
            if (weightSelection < 0)
			{
                if (!piece->doPlace(depth) || piece == startPiece->previousPiece)
				{
                    break;
                }

                StrongholdPiece *strongholdPiece = findAndCreatePieceFactory(piece->pieceClass, pieces, random, footX, footY, footZ, direction, depth);
                if (strongholdPiece != NULL)
				{
                    piece->placeCount++;
                    startPiece->previousPiece = piece;

                    if (!piece->isValid())
					{
                        currentPieces.remove(piece);
                    }
                    return strongholdPiece;
                }
            }
        }
    }
    {
        BoundingBox *box = FillerCorridor::findPieceBox(pieces, random, footX, footY, footZ, direction);
        if (box != NULL && box->y0 > 1)
		{
            return new FillerCorridor(depth, random, box, direction);
        }
		if(box != NULL) delete box;
    }

    return NULL;
}

StructurePiece *StrongholdPieces::generateAndAddPiece(StartPiece *startPiece, list<StructurePiece *> *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth)
{
    if (depth > MAX_DEPTH)
	{
        return NULL;
    }
    if (abs(footX - startPiece->getBoundingBox()->x0) > 3 * 16 || abs(footZ - startPiece->getBoundingBox()->z0) > 3 * 16)
	{
		// Force attempt at spawning a portal room
		if(startPiece->m_level->getOriginalSaveVersion() >= SAVE_FILE_VERSION_MOVED_STRONGHOLD && !startPiece->m_level->getLevelData()->getHasStrongholdEndPortal())
		{
			for( AUTO_VAR(it, currentPieces.begin()); it != currentPieces.end(); it++ )
			{
				PieceWeight *piece = *it;

				if(piece->pieceClass != EPieceClass_PortalRoom) continue;

#ifndef _CONTENT_PACKAGE
				printf("Portal room forcing attempt\n");
#endif
				StrongholdPiece *strongholdPiece = PortalRoom::createPiece(pieces, random, footX, footY, footZ, direction, depth);
				if (strongholdPiece != NULL)
				{
					piece->placeCount++;
					startPiece->previousPiece = piece;

					if (!piece->isValid())
					{
						currentPieces.remove(piece);
					}
#ifndef _CONTENT_PACKAGE
					printf("Success\n");
#endif
					return strongholdPiece;
				}
			}
		}
        return NULL;
    }

    StructurePiece *newPiece = generatePieceFromSmallDoor(startPiece, pieces, random, footX, footY, footZ, direction, depth + 1);
    if (newPiece != NULL)
	{
		pieces->push_back(newPiece);
        startPiece->pendingChildren.push_back(newPiece);
//            newPiece.addChildren(startPiece, pieces, random, depth + 1);
    }
    return newPiece;
}

StrongholdPieces::StrongholdPiece::StrongholdPiece(int genDepth) : StructurePiece(genDepth)
{
}

void StrongholdPieces::StrongholdPiece::generateSmallDoor(Level *level, Random *random, BoundingBox *chunkBB, StrongholdPieces::StrongholdPiece::SmallDoorType doorType, int footX, int footY, int footZ)
{
    switch (doorType)
	{
        default:
        case OPENING:
            generateBox(level, chunkBB, footX, footY, footZ, footX + SMALL_DOOR_WIDTH - 1, footY + SMALL_DOOR_HEIGHT - 1, footZ, 0, 0, false);
            break;
        case WOOD_DOOR:
            placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX, footY, footZ, chunkBB);
            placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX, footY + 1, footZ, chunkBB);
            placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX, footY + 2, footZ, chunkBB);
            placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX + 1, footY + 2, footZ, chunkBB);
            placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX + 2, footY + 2, footZ, chunkBB);
            placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX + 2, footY + 1, footZ, chunkBB);
            placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX + 2, footY, footZ, chunkBB);
            placeBlock(level, Tile::door_wood_Id, 0, footX + 1, footY, footZ, chunkBB);
            placeBlock(level, Tile::door_wood_Id, DoorTile::UPPER_BIT, footX + 1, footY + 1, footZ, chunkBB);
            break;
        case GRATES:
            placeBlock(level, 0, 0, footX + 1, footY, footZ, chunkBB);
            placeBlock(level, 0, 0, footX + 1, footY + 1, footZ, chunkBB);
            placeBlock(level, Tile::ironFence_Id, 0, footX, footY, footZ, chunkBB);
            placeBlock(level, Tile::ironFence_Id, 0, footX, footY + 1, footZ, chunkBB);
            placeBlock(level, Tile::ironFence_Id, 0, footX, footY + 2, footZ, chunkBB);
            placeBlock(level, Tile::ironFence_Id, 0, footX + 1, footY + 2, footZ, chunkBB);
            placeBlock(level, Tile::ironFence_Id, 0, footX + 2, footY + 2, footZ, chunkBB);
            placeBlock(level, Tile::ironFence_Id, 0, footX + 2, footY + 1, footZ, chunkBB);
            placeBlock(level, Tile::ironFence_Id, 0, footX + 2, footY, footZ, chunkBB);
            break;
        case IRON_DOOR:
            placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX, footY, footZ, chunkBB);
            placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX, footY + 1, footZ, chunkBB);
            placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX, footY + 2, footZ, chunkBB);
            placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX + 1, footY + 2, footZ, chunkBB);
            placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX + 2, footY + 2, footZ, chunkBB);
            placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX + 2, footY + 1, footZ, chunkBB);
            placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX + 2, footY, footZ, chunkBB);
            placeBlock(level, Tile::door_iron_Id, 0, footX + 1, footY, footZ, chunkBB);
            placeBlock(level, Tile::door_iron_Id, DoorTile::UPPER_BIT, footX + 1, footY + 1, footZ, chunkBB);
            placeBlock(level, Tile::button_stone_Id, getOrientationData(Tile::button_stone_Id, 4), footX + 2, footY + 1, footZ + 1, chunkBB);
            placeBlock(level, Tile::button_stone_Id, getOrientationData(Tile::button_stone_Id, 3), footX + 2, footY + 1, footZ - 1, chunkBB);
            break;
        }

}

StrongholdPieces::StrongholdPiece::SmallDoorType StrongholdPieces::StrongholdPiece::randomSmallDoor(Random *random)
{
    int selection = random->nextInt(5);
    switch (selection)
	{
		default:
		case 0:
		case 1:
			return OPENING;
		case 2:
			return WOOD_DOOR;
		case 3:
			return GRATES;
		case 4:
			return IRON_DOOR;
    }
}

StructurePiece *StrongholdPieces::StrongholdPiece::generateSmallDoorChildForward(StartPiece *startPiece, list<StructurePiece *> *pieces, Random *random, int xOff, int yOff)
{
    switch (orientation)
	{
		case Direction::NORTH:
			return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + xOff, boundingBox->y0 + yOff, boundingBox->z0 - 1, orientation, getGenDepth());
		case Direction::SOUTH:
			return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + xOff, boundingBox->y0 + yOff, boundingBox->z1 + 1, orientation, getGenDepth());
		case Direction::WEST:
			return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0 + yOff, boundingBox->z0 + xOff, orientation, getGenDepth());
		case Direction::EAST:
			return generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0 + yOff, boundingBox->z0 + xOff, orientation, getGenDepth());
    }
    return NULL;
}

StructurePiece *StrongholdPieces::StrongholdPiece::generateSmallDoorChildLeft(StartPiece *startPiece, list<StructurePiece *> *pieces, Random *random, int yOff, int zOff)
{
    switch (orientation)
	{
	case Direction::NORTH:
        return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0 + yOff, boundingBox->z0 + zOff, Direction::WEST, getGenDepth());
	case Direction::SOUTH:
        return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0 + yOff, boundingBox->z0 + zOff, Direction::WEST, getGenDepth());
	case Direction::WEST:
        return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + zOff, boundingBox->y0 + yOff, boundingBox->z0 - 1, Direction::NORTH, getGenDepth());
	case Direction::EAST:
        return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + zOff, boundingBox->y0 + yOff, boundingBox->z0 - 1, Direction::NORTH, getGenDepth());
    }
    return NULL;
}

StructurePiece *StrongholdPieces::StrongholdPiece::generateSmallDoorChildRight(StartPiece *startPiece, list<StructurePiece *> *pieces, Random *random, int yOff, int zOff)
{
    switch (orientation)
	{
	case Direction::NORTH:
        return generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0 + yOff, boundingBox->z0 + zOff, Direction::EAST, getGenDepth());
	case Direction::SOUTH:
        return generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0 + yOff, boundingBox->z0 + zOff, Direction::EAST, getGenDepth());
	case Direction::WEST:
        return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + zOff, boundingBox->y0 + yOff, boundingBox->z1 + 1, Direction::SOUTH, getGenDepth());
	case Direction::EAST:
        return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + zOff, boundingBox->y0 + yOff, boundingBox->z1 + 1, Direction::SOUTH, getGenDepth());
    }
    return NULL;
}
	
bool StrongholdPieces::StrongholdPiece::isOkBox(BoundingBox *box, StartPiece *startRoom)
{
	//return box != NULL && box->y0 > LOWEST_Y_POSITION;
	
	bool bIsOk = false;
	
	if(box != NULL)
	{
		if( box->y0 > LOWEST_Y_POSITION ) bIsOk = true;

		if( startRoom != NULL && startRoom->m_level->getOriginalSaveVersion() >= SAVE_FILE_VERSION_MOVED_STRONGHOLD )
		{
			int xzSize = startRoom->m_level->getLevelData()->getXZSize();
			int blockMin = -( (xzSize << 4) / 2) + 1;
			int blockMax = ( (xzSize << 4) / 2 ) - 1;

			if(box->x0 <= blockMin) bIsOk = false;
			if(box->z0 <= blockMin) bIsOk = false;
			if(box->x1 >= blockMax) bIsOk = false;
			if(box->z1 >= blockMax) bIsOk = false;
		}
	}

	return bIsOk;
}

StrongholdPieces::FillerCorridor::FillerCorridor(int genDepth, Random *random, BoundingBox *corridorBox, int direction) : StrongholdPiece(genDepth),
	steps((direction == Direction::NORTH || direction == Direction::SOUTH) ? corridorBox->getZSpan() : corridorBox->getXSpan())
{
    orientation = direction;
    boundingBox = corridorBox;
}

BoundingBox *StrongholdPieces::FillerCorridor::findPieceBox(list<StructurePiece *> *pieces, Random *random, int footX, int footY, int footZ, int direction)
{
    const int maxLength = 3;

    BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, -1, 0, 5, 5, maxLength + 1, direction);

    StructurePiece *collisionPiece = StructurePiece::findCollisionPiece(pieces, box);
	
    if (collisionPiece == NULL)
	{
		delete box;
        // the filler must collide with something in order to be
        // generated
        return NULL;
    }

    if (collisionPiece->getBoundingBox()->y0 == box->y0)
	{
		delete box;
        // attempt to make a smaller piece until it fits
        for (int depth = maxLength; depth >= 1; depth--)
		{
            box = BoundingBox::orientBox(footX, footY, footZ, -1, -1, 0, 5, 5, depth - 1, direction);
            if (!collisionPiece->getBoundingBox()->intersects(box))
			{
				delete box;
                // the corridor has shrunk enough to fit, but make it
                // one step too big to build an entrance into the other
                // block
                return BoundingBox::orientBox(footX, footY, footZ, -1, -1, 0, 5, 5, depth, direction);
            }
			delete box;
        }
    }

    return NULL;
}

bool StrongholdPieces::FillerCorridor::postProcess(Level *level, Random *random, BoundingBox *chunkBB)
{
    if (edgesLiquid(level, chunkBB))
	{
        return false;
    }

    // filler corridor
    for (int i = 0; i < steps; i++)
	{
        // row 0
        placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 0, 0, i, chunkBB);
        placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 1, 0, i, chunkBB);
        placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 2, 0, i, chunkBB);
        placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 3, 0, i, chunkBB);
        placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 4, 0, i, chunkBB);
        // row 1-3
        for (int y = 1; y <= 3; y++)
		{
            placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 0, y, i, chunkBB);
            placeBlock(level, 0, 0, 1, y, i, chunkBB);
            placeBlock(level, 0, 0, 2, y, i, chunkBB);
            placeBlock(level, 0, 0, 3, y, i, chunkBB);
            placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 4, y, i, chunkBB);
        }
        // row 4
        placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 0, 4, i, chunkBB);
        placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 1, 4, i, chunkBB);
        placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 2, 4, i, chunkBB);
        placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 3, 4, i, chunkBB);
        placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 4, 4, i, chunkBB);
    }

    return true;
}

StrongholdPieces::StairsDown::StairsDown(int genDepth, Random *random, int west, int north) : StrongholdPiece(genDepth), isSource(true), entryDoor(OPENING)
{
    orientation = random->nextInt(4);

    switch (orientation)
	{
		case Direction::NORTH:
		case Direction::SOUTH:
			boundingBox = new BoundingBox(west, 64, north, west + width - 1, 64 + height - 1, north + depth - 1);
			break;
		default:
			boundingBox = new BoundingBox(west, 64, north, west + depth - 1, 64 + height - 1, north + width - 1);
			break;
    }
}

StrongholdPieces::StairsDown::StairsDown(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : StrongholdPiece(genDepth), isSource(false), entryDoor(randomSmallDoor(random))
{
    orientation = direction;
    boundingBox = stairsBox;
}

void StrongholdPieces::StairsDown::addChildren(StructurePiece *startPiece, list<StructurePiece *> *pieces, Random *random)
{
	if( isSource )
	{
		imposedPiece = EPieceClass_FiveCrossing;
	}
	generateSmallDoorChildForward((StartPiece *) startPiece, pieces, random, 1, 1);
}

StrongholdPieces::StairsDown *StrongholdPieces::StairsDown::createPiece(list<StructurePiece *> *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth)
{
    BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, 4 - height, 0, width, height, depth, direction);

	StartPiece *startPiece = NULL;	
	if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front());

    if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL)
	{
		delete box;
        return NULL;
    }

    return new StairsDown(genDepth, random, box, direction);
}

bool StrongholdPieces::StairsDown::postProcess(Level *level, Random *random, BoundingBox *chunkBB)
{
	if (edgesLiquid(level, chunkBB))
	{
		return false;
	}

	// bounding walls
	generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector);
	// entry door
	generateSmallDoor(level, random, chunkBB, entryDoor, 1, height - SMALL_DOOR_HEIGHT - 1, 0);
	// exit door
	generateSmallDoor(level, random, chunkBB, OPENING, 1, 1, depth - 1);

	// stair steps
	placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 2, 6, 1, chunkBB);
	placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 1, 5, 1, chunkBB);
	placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::STONE_SLAB, 1, 6, 1, chunkBB);
	placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 1, 5, 2, chunkBB);
	placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 1, 4, 3, chunkBB);
	placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::STONE_SLAB, 1, 5, 3, chunkBB);
	placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 2, 4, 3, chunkBB);
	placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 3, 3, 3, chunkBB);
	placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::STONE_SLAB, 3, 4, 3, chunkBB);
	placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 3, 3, 2, chunkBB);
	placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 3, 2, 1, chunkBB);
	placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::STONE_SLAB, 3, 3, 1, chunkBB);
	placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 2, 2, 1, chunkBB);
	placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 1, 1, 1, chunkBB);
	placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::STONE_SLAB, 1, 2, 1, chunkBB);
	placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 1, 1, 2, chunkBB);
	placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::STONE_SLAB, 1, 1, 3, chunkBB);

	return true;
}

StrongholdPieces::StartPiece::StartPiece(int genDepth, Random *random, int west, int north, Level *level) : StairsDown(0, random, west, north)
{
	// 4J added initialisers
	isLibraryAdded = false;
	previousPiece = NULL;
	portalRoomPiece = NULL;

	m_level = level;
}

TilePos *StrongholdPieces::StartPiece::getLocatorPosition()
{
	if( portalRoomPiece != NULL )
	{
		return portalRoomPiece->getLocatorPosition();
	}
	return StairsDown::getLocatorPosition();
}

StrongholdPieces::Straight::Straight(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : StrongholdPiece(genDepth),
																											entryDoor(randomSmallDoor(random)),
																											leftChild(random->nextInt(2) == 0),
																											rightChild(random->nextInt(2) == 0)
{
    orientation = direction;
    boundingBox = stairsBox;
}

void StrongholdPieces::Straight::addChildren(StructurePiece *startPiece, list<StructurePiece *> *pieces, Random *random)
{
    generateSmallDoorChildForward((StartPiece *) startPiece, pieces, random, 1, 1);
    if (leftChild) generateSmallDoorChildLeft((StartPiece *) startPiece, pieces, random, 1, 2);
    if (rightChild) generateSmallDoorChildRight((StartPiece *) startPiece, pieces, random, 1, 2);
}

StrongholdPieces::Straight *StrongholdPieces::Straight::createPiece(list<StructurePiece *> *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth)
{
    BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, -1, 0, width, height, depth, direction);

	StartPiece *startPiece = NULL;	
	if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front());

    if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL)
	{
		delete box;
        return NULL;
    }

    return new Straight(genDepth, random, box, direction);
}

bool StrongholdPieces::Straight::postProcess(Level *level, Random *random, BoundingBox *chunkBB)
{
    if (edgesLiquid(level, chunkBB))
	{
        return false;
    }

    // bounding walls
    generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector);
    // entry door
    generateSmallDoor(level, random, chunkBB, entryDoor, 1, height - SMALL_DOOR_HEIGHT - 1, 0);
    // exit door
    generateSmallDoor(level, random, chunkBB, OPENING, 1, 1, depth - 1);

    maybeGenerateBlock(level, chunkBB, random, .1f, 1, 2, 1, Tile::torch_Id, 0);
    maybeGenerateBlock(level, chunkBB, random, .1f, 3, 2, 1, Tile::torch_Id, 0);
    maybeGenerateBlock(level, chunkBB, random, .1f, 1, 2, 5, Tile::torch_Id, 0);
    maybeGenerateBlock(level, chunkBB, random, .1f, 3, 2, 5, Tile::torch_Id, 0);

    if (leftChild)
	{
        generateBox(level, chunkBB, 0, 1, 2, 0, 3, 4, 0, 0, false);
    }
    if (rightChild)
	{
        generateBox(level, chunkBB, 4, 1, 2, 4, 3, 4, 0, 0, false);
    }

    return true;
}

WeighedTreasure *StrongholdPieces::ChestCorridor::treasureItems[TREASURE_ITEMS_COUNT] = 
{
    new WeighedTreasure(Item::enderPearl_Id, 0, 1, 1, 10),
    new WeighedTreasure(Item::diamond_Id, 0, 1, 3, 3),
    new WeighedTreasure(Item::ironIngot_Id, 0, 1, 5, 10),
    new WeighedTreasure(Item::goldIngot_Id, 0, 1, 3, 5),
    new WeighedTreasure(Item::redStone_Id, 0, 4, 9, 5),
    new WeighedTreasure(Item::bread_Id, 0, 1, 3, 15),
    new WeighedTreasure(Item::apple_Id, 0, 1, 3, 15),
    new WeighedTreasure(Item::pickAxe_iron_Id, 0, 1, 1, 5),
    new WeighedTreasure(Item::sword_iron_Id, 0, 1, 1, 5),
    new WeighedTreasure(Item::chestplate_iron_Id, 0, 1, 1, 5),
    new WeighedTreasure(Item::helmet_iron_Id, 0, 1, 1, 5),
    new WeighedTreasure(Item::leggings_iron_Id, 0, 1, 1, 5),
    new WeighedTreasure(Item::boots_iron_Id, 0, 1, 1, 5),
    new WeighedTreasure(Item::apple_gold_Id, 0, 1, 1, 1)
};

StrongholdPieces::ChestCorridor::ChestCorridor(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : StrongholdPiece(genDepth), entryDoor(randomSmallDoor(random))
{
	orientation = direction;
	boundingBox = stairsBox;
}

void StrongholdPieces::ChestCorridor::addChildren(StructurePiece *startPiece, list<StructurePiece *> *pieces, Random *random)
{
	generateSmallDoorChildForward((StartPiece *) startPiece, pieces, random, 1, 1);
}

StrongholdPieces::ChestCorridor *StrongholdPieces::ChestCorridor::createPiece(list<StructurePiece *> *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth)
{
    BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, -1, 0, width, height, depth, direction);

	StartPiece *startPiece = NULL;	
	if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front());

    if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL)
	{
		delete box;
        return NULL;
    }

    return new ChestCorridor(genDepth, random, box, direction);
}

bool StrongholdPieces::ChestCorridor::postProcess(Level *level, Random *random, BoundingBox *chunkBB)
{
    if (edgesLiquid(level, chunkBB))
	{
        return false;
    }

    // bounding walls
    generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector);
    // entry door
    generateSmallDoor(level, random, chunkBB, entryDoor, 1, height - SMALL_DOOR_HEIGHT - 1, 0);
    // exit door
    generateSmallDoor(level, random, chunkBB, OPENING, 1, 1, depth - 1);

    // chest placement
    generateBox(level, chunkBB, 3, 1, 2, 3, 1, 4, Tile::stoneBrickSmooth_Id, Tile::stoneBrickSmooth_Id, false);
    placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::SMOOTHBRICK_SLAB, 3, 1, 1, chunkBB);
    placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::SMOOTHBRICK_SLAB, 3, 1, 5, chunkBB);
    placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::SMOOTHBRICK_SLAB, 3, 2, 2, chunkBB);
    placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::SMOOTHBRICK_SLAB, 3, 2, 4, chunkBB);
    for (int z = 2; z <= 4; z++)
	{
        placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::SMOOTHBRICK_SLAB, 2, 1, z, chunkBB);
    }

    if (!hasPlacedChest)
	{
        int y = getWorldY(2);
        int x = getWorldX(3, 3), z = getWorldZ(3, 3);
        if (chunkBB->isInside(x, y, z))
		{
            hasPlacedChest = true;
            createChest(level, chunkBB, random, 3, 2, 3, WeighedTreasure::addToTreasure(WeighedTreasureArray(treasureItems,TREASURE_ITEMS_COUNT), Item::enchantedBook->createForRandomTreasure(random)), 2 + random->nextInt(2));
        }
    }

	return true;
}

StrongholdPieces::StraightStairsDown::StraightStairsDown(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : StrongholdPiece(genDepth), entryDoor(randomSmallDoor(random))
{
    orientation = direction;
    boundingBox = stairsBox;
}

void StrongholdPieces::StraightStairsDown::addChildren(StructurePiece *startPiece, list<StructurePiece *> *pieces, Random *random)
{
	generateSmallDoorChildForward((StartPiece *) startPiece, pieces, random, 1, 1);
}

StrongholdPieces::StraightStairsDown *StrongholdPieces::StraightStairsDown::createPiece(list<StructurePiece *> *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth)
{
    BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, 4 - height, 0, width, height, depth, direction);

	StartPiece *startPiece = NULL;	
	if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front());

    if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL)
	{
		delete box;
        return NULL;
    }

    return new StraightStairsDown(genDepth, random, box, direction);
}

bool StrongholdPieces::StraightStairsDown::postProcess(Level *level, Random *random, BoundingBox *chunkBB)
{
    if (edgesLiquid(level, chunkBB))
	{
        return false;
    }

    // bounding walls
    generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector);
    // entry door
    generateSmallDoor(level, random, chunkBB, entryDoor, 1, height - SMALL_DOOR_HEIGHT - 1, 0);
    // exit door
    generateSmallDoor(level, random, chunkBB, OPENING, 1, 1, depth - 1);

    // stairs
    int orientationData = getOrientationData(Tile::stairs_stone_Id, 2);
    for (int i = 0; i < 6; i++)
	{
        placeBlock(level, Tile::stairs_stone_Id, orientationData, 1, height - 5 - i, 1 + i, chunkBB);
        placeBlock(level, Tile::stairs_stone_Id, orientationData, 2, height - 5 - i, 1 + i, chunkBB);
        placeBlock(level, Tile::stairs_stone_Id, orientationData, 3, height - 5 - i, 1 + i, chunkBB);
        if (i < 5)
		{
            placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 1, height - 6 - i, 1 + i, chunkBB);
            placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 2, height - 6 - i, 1 + i, chunkBB);
            placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 3, height - 6 - i, 1 + i, chunkBB);
        }
    }

    return true;
}

StrongholdPieces::LeftTurn::LeftTurn(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : StrongholdPiece(genDepth), entryDoor(randomSmallDoor(random))
{
    orientation = direction;
    boundingBox = stairsBox;
}

void StrongholdPieces::LeftTurn::addChildren(StructurePiece *startPiece, list<StructurePiece *> *pieces, Random *random)
{
	if (orientation == Direction::NORTH || orientation == Direction::EAST)
	{
		generateSmallDoorChildLeft((StartPiece *) startPiece, pieces, random, 1, 1);
	}
	else
	{
		generateSmallDoorChildRight((StartPiece *) startPiece, pieces, random, 1, 1);
	}
}

StrongholdPieces::LeftTurn *StrongholdPieces::LeftTurn::createPiece(list<StructurePiece *> *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth)
{
    BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, -1, 0, width, height, depth, direction);

	StartPiece *startPiece = NULL;	
	if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front());

    if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL)
	{
		delete box;
        return NULL;
    }

    return new LeftTurn(genDepth, random, box, direction);
}

bool StrongholdPieces::LeftTurn::postProcess(Level *level, Random *random, BoundingBox *chunkBB)
{
    if (edgesLiquid(level, chunkBB))
	{
        return false;
    }

    // bounding walls
    generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector);
    // entry door
    generateSmallDoor(level, random, chunkBB, entryDoor, 1, height - SMALL_DOOR_HEIGHT - 1, 0);
    // exit opening
    if (orientation == Direction::NORTH || orientation == Direction::EAST)
	{
        generateBox(level, chunkBB, 0, 1, 1, 0, 3, 3, 0, 0, false);
    }
	else
	{
        generateBox(level, chunkBB, 4, 1, 1, 4, 3, 3, 0, 0, false);
    }

    return true;
}

StrongholdPieces::RightTurn::RightTurn(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : LeftTurn(genDepth, random, stairsBox, direction)
{
}

void StrongholdPieces::RightTurn::addChildren(StructurePiece *startPiece, list<StructurePiece *> *pieces, Random *random)
{
    if (orientation == Direction::NORTH || orientation == Direction::EAST)
	{
        generateSmallDoorChildRight((StartPiece *) startPiece, pieces, random, 1, 1);
    }
	else
	{
        generateSmallDoorChildLeft((StartPiece *) startPiece, pieces, random, 1, 1);
    }
}

bool StrongholdPieces::RightTurn::postProcess(Level *level, Random *random, BoundingBox *chunkBB)
{
    if (edgesLiquid(level, chunkBB))
	{
        return false;
    }

    // bounding walls
    generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector);
    // entry door
    generateSmallDoor(level, random, chunkBB, entryDoor, 1, height - SMALL_DOOR_HEIGHT - 1, 0);
    // exit opening
    if (orientation == Direction::NORTH || orientation == Direction::EAST)
	{
        generateBox(level, chunkBB, 4, 1, 1, 4, 3, 3, 0, 0, false);
    }
	else
	{
        generateBox(level, chunkBB, 0, 1, 1, 0, 3, 3, 0, 0, false);
    }

    return true;
}


StrongholdPieces::RoomCrossing::RoomCrossing(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : StrongholdPiece(genDepth), entryDoor(randomSmallDoor(random)), type(random->nextInt(5))
{
    orientation = direction;
    boundingBox = stairsBox;       
}

void StrongholdPieces::RoomCrossing::addChildren(StructurePiece *startPiece, list<StructurePiece *> *pieces, Random *random)
{
    generateSmallDoorChildForward((StartPiece*) startPiece, pieces, random, 4, 1);
    generateSmallDoorChildLeft((StartPiece*) startPiece, pieces, random, 1, 4);
    generateSmallDoorChildRight((StartPiece*) startPiece, pieces, random, 1, 4);
}

StrongholdPieces::RoomCrossing *StrongholdPieces::RoomCrossing::createPiece(list<StructurePiece *> *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth)
{
    BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -4, -1, 0, width, height, depth, direction);

	StartPiece *startPiece = NULL;	
	if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front());

    if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL)
	{
		delete box;
        return NULL;
    }

    return new RoomCrossing(genDepth, random, box, direction);
}

WeighedTreasure *StrongholdPieces::RoomCrossing::smallTreasureItems[SMALL_TREASURE_ITEMS_COUNT] = 
{
    new WeighedTreasure(Item::ironIngot_Id, 0, 1, 5, 10),
    new WeighedTreasure(Item::goldIngot_Id, 0, 1, 3, 5),
    new WeighedTreasure(Item::redStone_Id, 0, 4, 9, 5),
    new WeighedTreasure(Item::coal_Id, CoalItem::STONE_COAL, 3, 8, 10),
    new WeighedTreasure(Item::bread_Id, 0, 1, 3, 15),
    new WeighedTreasure(Item::apple_Id, 0, 1, 3, 15),
    new WeighedTreasure(Item::pickAxe_iron_Id, 0, 1, 1, 1),
};

bool StrongholdPieces::RoomCrossing::postProcess(Level *level, Random *random, BoundingBox *chunkBB)
{
    if (edgesLiquid(level, chunkBB))
	{
        return false;
    }

    // bounding walls
    generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector);
    // entry door
    generateSmallDoor(level, random, chunkBB, entryDoor, 4, 1, 0);
    // exit openings
    generateBox(level, chunkBB, 4, 1, depth - 1, 6, 3, depth - 1, 0, 0, false);
    generateBox(level, chunkBB, 0, 1, 4, 0, 3, 6, 0, 0, false);
    generateBox(level, chunkBB, width - 1, 1, 4, width - 1, 3, 6, 0, 0, false);

    switch (type)
	{
		default:
			break;
		case 0:
			// middle torch pillar
			placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 5, 1, 5, chunkBB);
			placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 5, 2, 5, chunkBB);
			placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 5, 3, 5, chunkBB);
			placeBlock(level, Tile::torch_Id, 0, 4, 3, 5, chunkBB);
			placeBlock(level, Tile::torch_Id, 0, 6, 3, 5, chunkBB);
			placeBlock(level, Tile::torch_Id, 0, 5, 3, 4, chunkBB);
			placeBlock(level, Tile::torch_Id, 0, 5, 3, 6, chunkBB);
			placeBlock(level, Tile::stoneSlabHalf_Id, 0, 4, 1, 4, chunkBB);
			placeBlock(level, Tile::stoneSlabHalf_Id, 0, 4, 1, 5, chunkBB);
			placeBlock(level, Tile::stoneSlabHalf_Id, 0, 4, 1, 6, chunkBB);
			placeBlock(level, Tile::stoneSlabHalf_Id, 0, 6, 1, 4, chunkBB);
			placeBlock(level, Tile::stoneSlabHalf_Id, 0, 6, 1, 5, chunkBB);
			placeBlock(level, Tile::stoneSlabHalf_Id, 0, 6, 1, 6, chunkBB);
			placeBlock(level, Tile::stoneSlabHalf_Id, 0, 5, 1, 4, chunkBB);
			placeBlock(level, Tile::stoneSlabHalf_Id, 0, 5, 1, 6, chunkBB);
			break;
		case 1:
			{
				for (int i = 0; i < 5; i++)
				{
					placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 3, 1, 3 + i, chunkBB);
					placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 7, 1, 3 + i, chunkBB);
					placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 3 + i, 1, 3, chunkBB);
					placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 3 + i, 1, 7, chunkBB);
				}
				placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 5, 1, 5, chunkBB);
				placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 5, 2, 5, chunkBB);
				placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 5, 3, 5, chunkBB);
				placeBlock(level, Tile::water_Id, 0, 5, 4, 5, chunkBB);
			}
			break;
		case 2:
			{
			for (int z = 1; z <= 9; z++)
			{
				placeBlock(level, Tile::stoneBrick_Id, 0, 1, 3, z, chunkBB);
				placeBlock(level, Tile::stoneBrick_Id, 0, 9, 3, z, chunkBB);
			}
			for (int x = 1; x <= 9; x++)
			{
				placeBlock(level, Tile::stoneBrick_Id, 0, x, 3, 1, chunkBB);
				placeBlock(level, Tile::stoneBrick_Id, 0, x, 3, 9, chunkBB);
			}
			placeBlock(level, Tile::stoneBrick_Id, 0, 5, 1, 4, chunkBB);
			placeBlock(level, Tile::stoneBrick_Id, 0, 5, 1, 6, chunkBB);
			placeBlock(level, Tile::stoneBrick_Id, 0, 5, 3, 4, chunkBB);
			placeBlock(level, Tile::stoneBrick_Id, 0, 5, 3, 6, chunkBB);
			placeBlock(level, Tile::stoneBrick_Id, 0, 4, 1, 5, chunkBB);
			placeBlock(level, Tile::stoneBrick_Id, 0, 6, 1, 5, chunkBB);
			placeBlock(level, Tile::stoneBrick_Id, 0, 4, 3, 5, chunkBB);
			placeBlock(level, Tile::stoneBrick_Id, 0, 6, 3, 5, chunkBB);
			for (int y = 1; y <= 3; y++)
			{
				placeBlock(level, Tile::stoneBrick_Id, 0, 4, y, 4, chunkBB);
				placeBlock(level, Tile::stoneBrick_Id, 0, 6, y, 4, chunkBB);
				placeBlock(level, Tile::stoneBrick_Id, 0, 4, y, 6, chunkBB);
				placeBlock(level, Tile::stoneBrick_Id, 0, 6, y, 6, chunkBB);
			}
			placeBlock(level, Tile::torch_Id, 0, 5, 3, 5, chunkBB);
			for (int z = 2; z <= 8; z++)
			{
				placeBlock(level, Tile::wood_Id, 0, 2, 3, z, chunkBB);
				placeBlock(level, Tile::wood_Id, 0, 3, 3, z, chunkBB);
				if (z <= 3 || z >= 7)
				{
					placeBlock(level, Tile::wood_Id, 0, 4, 3, z, chunkBB);
					placeBlock(level, Tile::wood_Id, 0, 5, 3, z, chunkBB);
					placeBlock(level, Tile::wood_Id, 0, 6, 3, z, chunkBB);
				}
				placeBlock(level, Tile::wood_Id, 0, 7, 3, z, chunkBB);
				placeBlock(level, Tile::wood_Id, 0, 8, 3, z, chunkBB);
			}
			placeBlock(level, Tile::ladder_Id, getOrientationData(Tile::ladder_Id, Facing::WEST), 9, 1, 3, chunkBB);
			placeBlock(level, Tile::ladder_Id, getOrientationData(Tile::ladder_Id, Facing::WEST), 9, 2, 3, chunkBB);
			placeBlock(level, Tile::ladder_Id, getOrientationData(Tile::ladder_Id, Facing::WEST), 9, 3, 3, chunkBB);

			createChest(level, chunkBB, random, 3, 4, 8, WeighedTreasure::addToTreasure(WeighedTreasureArray(smallTreasureItems,SMALL_TREASURE_ITEMS_COUNT), Item::enchantedBook->createForRandomTreasure(random)), 1 + random->nextInt(4));
			// System.out.println("Created chest at " + getWorldX(3, 8) +
	// "," + getWorldY(4) + "," + getWorldZ(3, 8));

		}
        break;
    }
    return true;
}

StrongholdPieces::PrisonHall::PrisonHall(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : StrongholdPiece(genDepth), entryDoor(randomSmallDoor(random))
{
    orientation = direction;
    boundingBox = stairsBox;
}

void StrongholdPieces::PrisonHall::addChildren(StructurePiece *startPiece, list<StructurePiece *> *pieces, Random *random)
{
	generateSmallDoorChildForward((StartPiece *) startPiece, pieces, random, 1, 1);
}

StrongholdPieces::PrisonHall *StrongholdPieces::PrisonHall::createPiece(list<StructurePiece *> *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth)
{
    BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, -1, 0, width, height, depth, direction);

	StartPiece *startPiece = NULL;	
	if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front());

    if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL)
	{
		delete box;
        return NULL;
    }

    return new PrisonHall(genDepth, random, box, direction);
}

bool StrongholdPieces::PrisonHall::postProcess(Level *level, Random *random, BoundingBox *chunkBB)
{
    if (edgesLiquid(level, chunkBB))
	{
        return false;
    }

    // bounding walls
    generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector);
    // entry door
    generateSmallDoor(level, random, chunkBB, entryDoor, 1, 1, 0);
    // exit openings
    generateBox(level, chunkBB, 1, 1, depth - 1, 3, 3, depth - 1, 0, 0, false);

    // door pillars
    generateBox(level, chunkBB, 4, 1, 1, 4, 3, 1, false, random, (BlockSelector *)smoothStoneSelector);
    generateBox(level, chunkBB, 4, 1, 3, 4, 3, 3, false, random, (BlockSelector *)smoothStoneSelector);
    generateBox(level, chunkBB, 4, 1, 7, 4, 3, 7, false, random, (BlockSelector *)smoothStoneSelector);
    generateBox(level, chunkBB, 4, 1, 9, 4, 3, 9, false, random, (BlockSelector *)smoothStoneSelector);

    // grates
    generateBox(level, chunkBB, 4, 1, 4, 4, 3, 6, Tile::ironFence_Id, Tile::ironFence_Id, false);
    generateBox(level, chunkBB, 5, 1, 5, 7, 3, 5, Tile::ironFence_Id, Tile::ironFence_Id, false);

    // doors
    placeBlock(level, Tile::ironFence_Id, 0, 4, 3, 2, chunkBB);
    placeBlock(level, Tile::ironFence_Id, 0, 4, 3, 8, chunkBB);
    placeBlock(level, Tile::door_iron_Id, getOrientationData(Tile::door_iron_Id, 3), 4, 1, 2, chunkBB);
    placeBlock(level, Tile::door_iron_Id, getOrientationData(Tile::door_iron_Id, 3) + DoorTile::UPPER_BIT, 4, 2, 2, chunkBB);
    placeBlock(level, Tile::door_iron_Id, getOrientationData(Tile::door_iron_Id, 3), 4, 1, 8, chunkBB);
    placeBlock(level, Tile::door_iron_Id, getOrientationData(Tile::door_iron_Id, 3) + DoorTile::UPPER_BIT, 4, 2, 8, chunkBB);

    return true;

}

StrongholdPieces::Library::Library(int genDepth, Random *random, BoundingBox *roomBox, int direction) : StrongholdPiece(genDepth),
																										entryDoor(randomSmallDoor(random)),
																										isTall(roomBox->getYSpan() > height)
{
    orientation = direction;
    boundingBox = roomBox;
}

StrongholdPieces::Library *StrongholdPieces::Library::createPiece(list<StructurePiece *> *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth)
{
    // attempt to make a tall library first
    BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -4, -1, 0, width, tallHeight, depth, direction);

	StartPiece *startPiece = NULL;	
	if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front());

    if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL)
	{
		delete box;
        // make a short library
        box = BoundingBox::orientBox(footX, footY, footZ, -4, -1, 0, width, height, depth, direction);

        if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL)
		{
			delete box;
            return NULL;
        }
    }

    return new Library(genDepth, random, box, direction);
}

WeighedTreasure *StrongholdPieces::Library::libraryTreasureItems[LIBRARY_TREASURE_ITEMS_COUNT] = 
{
    new WeighedTreasure(Item::book_Id, 0, 1, 3, 20),
    new WeighedTreasure(Item::paper_Id, 0, 2, 7, 20),
    new WeighedTreasure(Item::map_Id, 0, 1, 1, 1),
    new WeighedTreasure(Item::compass_Id, 0, 1, 1, 1),
};

bool StrongholdPieces::Library::postProcess(Level *level, Random *random, BoundingBox *chunkBB)
{
    if (edgesLiquid(level, chunkBB))
	{
        return false;
    }

    int currentHeight = tallHeight;
    if (!isTall)
	{
        currentHeight = height;
    }

    // bounding walls
    generateBox(level, chunkBB, 0, 0, 0, width - 1, currentHeight - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector);
    // entry door
    generateSmallDoor(level, random, chunkBB, entryDoor, 4, 1, 0);

    // place sparse cob webs
    generateMaybeBox(level, chunkBB, random, .07f, 2, 1, 1, width - 1 - 2, height - 2, depth - 2, Tile::web_Id, Tile::web_Id, false);

    const int bookLeft = 1;
    const int bookRight = width - 2;

    // place library walls
    for (int d = 1; d <= depth - 2; d++) {
        if (((d - 1) % 4) == 0) {
            generateBox(level, chunkBB, bookLeft, 1, d, bookLeft, 4, d, Tile::wood_Id, Tile::wood_Id, false);
            generateBox(level, chunkBB, bookRight, 1, d, bookRight, 4, d, Tile::wood_Id, Tile::wood_Id, false);

            placeBlock(level, Tile::torch_Id, 0, 2, 3, d, chunkBB);
            placeBlock(level, Tile::torch_Id, 0, width - 3, 3, d, chunkBB);

            if (isTall)
			{
                generateBox(level, chunkBB, bookLeft, 6, d, bookLeft, 9, d, Tile::wood_Id, Tile::wood_Id, false);
                generateBox(level, chunkBB, bookRight, 6, d, bookRight, 9, d, Tile::wood_Id, Tile::wood_Id, false);
            }
        }
		else
		{
            generateBox(level, chunkBB, bookLeft, 1, d, bookLeft, 4, d, Tile::bookshelf_Id, Tile::bookshelf_Id, false);
            generateBox(level, chunkBB, bookRight, 1, d, bookRight, 4, d, Tile::bookshelf_Id, Tile::bookshelf_Id, false);

            if (isTall)
			{
                generateBox(level, chunkBB, bookLeft, 6, d, bookLeft, 9, d, Tile::bookshelf_Id, Tile::bookshelf_Id, false);
                generateBox(level, chunkBB, bookRight, 6, d, bookRight, 9, d, Tile::bookshelf_Id, Tile::bookshelf_Id, false);
            }
        }
    }

    // place book shelves
    for (int d = 3; d < depth - 3; d += 2)
	{
        generateBox(level, chunkBB, 3, 1, d, 4, 3, d, Tile::bookshelf_Id, Tile::bookshelf_Id, false);
        generateBox(level, chunkBB, 6, 1, d, 7, 3, d, Tile::bookshelf_Id, Tile::bookshelf_Id, false);
        generateBox(level, chunkBB, 9, 1, d, 10, 3, d, Tile::bookshelf_Id, Tile::bookshelf_Id, false);
    }

    if (isTall)
	{
        // create balcony
        generateBox(level, chunkBB, 1, 5, 1, 3, 5, depth - 2, Tile::wood_Id, Tile::wood_Id, false);
        generateBox(level, chunkBB, width - 4, 5, 1, width - 2, 5, depth - 2, Tile::wood_Id, Tile::wood_Id, false);
        generateBox(level, chunkBB, 4, 5, 1, width - 5, 5, 2, Tile::wood_Id, Tile::wood_Id, false);
        generateBox(level, chunkBB, 4, 5, depth - 3, width - 5, 5, depth - 2, Tile::wood_Id, Tile::wood_Id, false);

        placeBlock(level, Tile::wood_Id, 0, width - 5, 5, depth - 4, chunkBB);
        placeBlock(level, Tile::wood_Id, 0, width - 6, 5, depth - 4, chunkBB);
        placeBlock(level, Tile::wood_Id, 0, width - 5, 5, depth - 5, chunkBB);

        // balcony fences
        generateBox(level, chunkBB, 3, 6, 2, 3, 6, depth - 3, Tile::fence_Id, Tile::fence_Id, false);
        generateBox(level, chunkBB, width - 4, 6, 2, width - 4, 6, depth - 5, Tile::fence_Id, Tile::fence_Id, false);
        generateBox(level, chunkBB, 4, 6, 2, width - 5, 6, 2, Tile::fence_Id, Tile::fence_Id, false);
        generateBox(level, chunkBB, 4, 6, depth - 3, 8, 6, depth - 3, Tile::fence_Id, Tile::fence_Id, false);
        placeBlock(level, Tile::fence_Id, 0, width - 5, 6, depth - 4, chunkBB);
        placeBlock(level, Tile::fence_Id, 0, width - 6, 6, depth - 4, chunkBB);
        placeBlock(level, Tile::fence_Id, 0, width - 5, 6, depth - 5, chunkBB);

        // ladder
        int orientationData = getOrientationData(Tile::ladder_Id, 3);
        placeBlock(level, Tile::ladder_Id, orientationData, width - 4, 1, depth - 2, chunkBB);
        placeBlock(level, Tile::ladder_Id, orientationData, width - 4, 2, depth - 2, chunkBB);
        placeBlock(level, Tile::ladder_Id, orientationData, width - 4, 3, depth - 2, chunkBB);
        placeBlock(level, Tile::ladder_Id, orientationData, width - 4, 4, depth - 2, chunkBB);
        placeBlock(level, Tile::ladder_Id, orientationData, width - 4, 5, depth - 2, chunkBB);
        placeBlock(level, Tile::ladder_Id, orientationData, width - 4, 6, depth - 2, chunkBB);
        placeBlock(level, Tile::ladder_Id, orientationData, width - 4, 7, depth - 2, chunkBB);

        // chandelier
        int x = width / 2;
        int z = depth / 2;
        placeBlock(level, Tile::fence_Id, 0, x - 1, tallHeight - 2, z, chunkBB);
        placeBlock(level, Tile::fence_Id, 0, x, tallHeight - 2, z, chunkBB);
        placeBlock(level, Tile::fence_Id, 0, x - 1, tallHeight - 3, z, chunkBB);
        placeBlock(level, Tile::fence_Id, 0, x, tallHeight - 3, z, chunkBB);
        placeBlock(level, Tile::fence_Id, 0, x - 1, tallHeight - 4, z, chunkBB);
        placeBlock(level, Tile::fence_Id, 0, x, tallHeight - 4, z, chunkBB);

        placeBlock(level, Tile::fence_Id, 0, x - 2, tallHeight - 4, z, chunkBB);
        placeBlock(level, Tile::fence_Id, 0, x + 1, tallHeight - 4, z, chunkBB);
        placeBlock(level, Tile::fence_Id, 0, x - 1, tallHeight - 4, z - 1, chunkBB);
        placeBlock(level, Tile::fence_Id, 0, x - 1, tallHeight - 4, z + 1, chunkBB);
        placeBlock(level, Tile::fence_Id, 0, x, tallHeight - 4, z - 1, chunkBB);
        placeBlock(level, Tile::fence_Id, 0, x, tallHeight - 4, z + 1, chunkBB);

        placeBlock(level, Tile::torch_Id, 0, x - 2, tallHeight - 3, z, chunkBB);
        placeBlock(level, Tile::torch_Id, 0, x + 1, tallHeight - 3, z, chunkBB);
        placeBlock(level, Tile::torch_Id, 0, x - 1, tallHeight - 3, z - 1, chunkBB);
        placeBlock(level, Tile::torch_Id, 0, x - 1, tallHeight - 3, z + 1, chunkBB);
        placeBlock(level, Tile::torch_Id, 0, x, tallHeight - 3, z - 1, chunkBB);
        placeBlock(level, Tile::torch_Id, 0, x, tallHeight - 3, z + 1, chunkBB);
    }

    // place chests
    createChest(level, chunkBB, random, 3, 3, 5, WeighedTreasure::addToTreasure(WeighedTreasureArray(libraryTreasureItems,LIBRARY_TREASURE_ITEMS_COUNT), Item::enchantedBook->createForRandomTreasure(random, 1, 5, 2)), 1 + random->nextInt(4));
    if (isTall)
	{
        placeBlock(level, 0, 0, width - 2, tallHeight - 2, 1, chunkBB);
        createChest(level, chunkBB, random, width - 2, tallHeight - 3, 1, WeighedTreasure::addToTreasure(WeighedTreasureArray(libraryTreasureItems,LIBRARY_TREASURE_ITEMS_COUNT), Item::enchantedBook->createForRandomTreasure(random, 1, 5, 2)), 1 + random->nextInt(4));
    }

    return true;

}

StrongholdPieces::FiveCrossing::FiveCrossing(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : StrongholdPiece(genDepth), entryDoor(randomSmallDoor(random))
{
    orientation = direction;
    boundingBox = stairsBox;

    leftLow = random->nextBoolean();
    leftHigh = random->nextBoolean();
    rightLow = random->nextBoolean();
    rightHigh = random->nextInt(3) > 0;
}

void StrongholdPieces::FiveCrossing::addChildren(StructurePiece *startPiece, list<StructurePiece *> *pieces, Random *random)
{
    int zOffA = 3;
    int zOffB = 5;
    // compensate for weird negative-facing behaviour
    if (orientation == Direction::WEST || orientation == Direction::NORTH)
	{
        zOffA = depth - 3 - zOffA;
        zOffB = depth - 3 - zOffB;
    } 
	
	generateSmallDoorChildForward((StartPiece *) startPiece, pieces, random, 5, 1);
    if (leftLow) generateSmallDoorChildLeft((StartPiece *) startPiece, pieces, random, zOffA, 1);
    if (leftHigh) generateSmallDoorChildLeft((StartPiece *) startPiece, pieces, random, zOffB, 7);
    if (rightLow) generateSmallDoorChildRight((StartPiece *) startPiece, pieces, random, zOffA, 1);
    if (rightHigh) generateSmallDoorChildRight((StartPiece *) startPiece, pieces, random, zOffB, 7);
}

StrongholdPieces::FiveCrossing *StrongholdPieces::FiveCrossing::createPiece(list<StructurePiece *> *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth)
{
    BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -4, -3, 0, width, height, depth, direction);

	StartPiece *startPiece = NULL;	
	if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front());

    if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL)
	{
		delete box;
        return NULL;
    }

    return new FiveCrossing(genDepth, random, box, direction);
}

bool StrongholdPieces::FiveCrossing::postProcess(Level *level, Random *random, BoundingBox *chunkBB)
{
    if (edgesLiquid(level, chunkBB))
	{
        return false;
    }

    // bounding walls
    generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector);
    // entry door
    generateSmallDoor(level, random, chunkBB, entryDoor, 4, 3, 0);

    // exit openings
    if (leftLow) generateBox(level, chunkBB, 0, 3, 1, 0, 5, 3, 0, 0, false);
    if (rightLow) generateBox(level, chunkBB, 9, 3, 1, 9, 5, 3, 0, 0, false);
    if (leftHigh) generateBox(level, chunkBB, 0, 5, 7, 0, 7, 9, 0, 0, false);
    if (rightHigh) generateBox(level, chunkBB, 9, 5, 7, 9, 7, 9, 0, 0, false);
    generateBox(level, chunkBB, 5, 1, 10, 7, 3, 10, 0, 0, false);

    // main floor
    generateBox(level, chunkBB, 1, 2, 1, 8, 2, 6, false, random, (BlockSelector *)smoothStoneSelector);
    // side walls
    generateBox(level, chunkBB, 4, 1, 5, 4, 4, 9, false, random, (BlockSelector *)smoothStoneSelector);
    generateBox(level, chunkBB, 8, 1, 5, 8, 4, 9, false, random, (BlockSelector *)smoothStoneSelector);
    // upper floor
    generateBox(level, chunkBB, 1, 4, 7, 3, 4, 9, false, random, (BlockSelector *)smoothStoneSelector);

    // left stairs
    generateBox(level, chunkBB, 1, 3, 5, 3, 3, 6, false, random, (BlockSelector *)smoothStoneSelector);
    generateBox(level, chunkBB, 1, 3, 4, 3, 3, 4, Tile::stoneSlabHalf_Id, Tile::stoneSlabHalf_Id, false);
    generateBox(level, chunkBB, 1, 4, 6, 3, 4, 6, Tile::stoneSlabHalf_Id, Tile::stoneSlabHalf_Id, false);

    // lower stairs
    generateBox(level, chunkBB, 5, 1, 7, 7, 1, 8, false, random, (BlockSelector *)smoothStoneSelector);
    generateBox(level, chunkBB, 5, 1, 9, 7, 1, 9, Tile::stoneSlabHalf_Id, Tile::stoneSlabHalf_Id, false);
    generateBox(level, chunkBB, 5, 2, 7, 7, 2, 7, Tile::stoneSlabHalf_Id, Tile::stoneSlabHalf_Id, false);

    // bridge
    generateBox(level, chunkBB, 4, 5, 7, 4, 5, 9, Tile::stoneSlabHalf_Id, Tile::stoneSlabHalf_Id, false);
    generateBox(level, chunkBB, 8, 5, 7, 8, 5, 9, Tile::stoneSlabHalf_Id, Tile::stoneSlabHalf_Id, false);
    generateBox(level, chunkBB, 5, 5, 7, 7, 5, 9, Tile::stoneSlab_Id, Tile::stoneSlab_Id, false);
    placeBlock(level, Tile::torch_Id, 0, 6, 5, 6, chunkBB);

    return true;

}

StrongholdPieces::PortalRoom::PortalRoom(int genDepth, Random *random, BoundingBox *box, int direction) : StrongholdPiece(genDepth)
{
	hasPlacedMobSpawner = false;
    orientation = direction;
    boundingBox = box;
}

void StrongholdPieces::PortalRoom::addChildren(StructurePiece *startPiece, list<StructurePiece *> *pieces, Random *random)
{
    if (startPiece != NULL)
	{
        ((StartPiece *) startPiece)->portalRoomPiece = this;
    }
}

StrongholdPieces::PortalRoom *StrongholdPieces::PortalRoom::createPiece(list<StructurePiece *> *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth)
{
    BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -4, -1, 0, width, height, depth, direction);

	// 4J Added so that we can check that Portals stay within the bounds of the world (which they ALWAYS should anyway)
	StartPiece *startPiece = NULL;	
	if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front());

    if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL)
	{
		delete box;
        return NULL;
    }

    return new PortalRoom(genDepth, random, box, direction);
}

bool StrongholdPieces::PortalRoom::postProcess(Level *level, Random *random, BoundingBox *chunkBB)
{
	// bounding walls
	generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, false, random, (BlockSelector *)smoothStoneSelector);
	// entry door
	generateSmallDoor(level, random, chunkBB, GRATES, 4, 1, 0);

	// inner roof row
	int y = height - 2;
	generateBox(level, chunkBB, 1, y, 1, 1, y, depth - 2, false, random, (BlockSelector *)smoothStoneSelector);
	generateBox(level, chunkBB, width - 2, y, 1, width - 2, y, depth - 2, false, random, (BlockSelector *)smoothStoneSelector);
	generateBox(level, chunkBB, 2, y, 1, width - 3, y, 2, false, random, (BlockSelector *)smoothStoneSelector);
	generateBox(level, chunkBB, 2, y, depth - 2, width - 3, y, depth - 2, false, random, (BlockSelector *)smoothStoneSelector);

	// entrance lava pools
	generateBox(level, chunkBB, 1, 1, 1, 2, 1, 4, false, random, (BlockSelector *)smoothStoneSelector);
	generateBox(level, chunkBB, width - 3, 1, 1, width - 2, 1, 4, false, random, (BlockSelector *)smoothStoneSelector);
	generateBox(level, chunkBB, 1, 1, 1, 1, 1, 3, Tile::lava_Id, Tile::lava_Id, false);
	generateBox(level, chunkBB, width - 2, 1, 1, width - 2, 1, 3, Tile::lava_Id, Tile::lava_Id, false);

	// portal lava pool
	generateBox(level, chunkBB, 3, 1, 8, 7, 1, 12, false, random, (BlockSelector *)smoothStoneSelector);
	generateBox(level, chunkBB, 4, 1, 9, 6, 1, 11, Tile::lava_Id, Tile::lava_Id, false);

	// wall decorations
	for (int z = 3; z < depth - 2; z += 2)
	{
		generateBox(level, chunkBB, 0, 3, z, 0, 4, z, Tile::ironFence_Id, Tile::ironFence_Id, false);
		generateBox(level, chunkBB, width - 1, 3, z, width - 1, 4, z, Tile::ironFence_Id, Tile::ironFence_Id, false);
	}
	for (int x = 2; x < width - 2; x += 2)
	{
		generateBox(level, chunkBB, x, 3, depth - 1, x, 4, depth - 1, Tile::ironFence_Id, Tile::ironFence_Id, false);
	}

	// stair
	int orientationData = getOrientationData(Tile::stairs_stoneBrickSmooth_Id, 3);
	generateBox(level, chunkBB, 4, 1, 5, 6, 1, 7, false, random, (BlockSelector *)smoothStoneSelector);
	generateBox(level, chunkBB, 4, 2, 6, 6, 2, 7, false, random, (BlockSelector *)smoothStoneSelector);
	generateBox(level, chunkBB, 4, 3, 7, 6, 3, 7, false, random, (BlockSelector *)smoothStoneSelector);
	for (int x = 4; x <= 6; x++)
	{
		placeBlock(level, Tile::stairs_stoneBrickSmooth_Id, orientationData, x, 1, 4, chunkBB);
		placeBlock(level, Tile::stairs_stoneBrickSmooth_Id, orientationData, x, 2, 5, chunkBB);
		placeBlock(level, Tile::stairs_stoneBrickSmooth_Id, orientationData, x, 3, 6, chunkBB);
	}

	int north = Direction::NORTH;
	int south = Direction::SOUTH;
	int east = Direction::EAST;
	int west = Direction::WEST;

	switch (orientation)
	{
	case Direction::SOUTH:
			north = Direction::SOUTH;
			south = Direction::NORTH;
			break;
	case Direction::EAST:
			north = Direction::EAST;
			south = Direction::WEST;
			east = Direction::SOUTH;
			west = Direction::NORTH;
			break;
	case Direction::WEST:
			north = Direction::WEST;
			south = Direction::EAST;
			east = Direction::SOUTH;
			west = Direction::NORTH;
			break;
	}

	// 4J-PB - Removed for Christmas update since we don't have The End
	
	// 4J-PB - not going to remove it, so that maps generated will have it in, but it can't be activated
	placeBlock(level, Tile::endPortalFrameTile_Id, north + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 4, 3, 8, chunkBB);
	placeBlock(level, Tile::endPortalFrameTile_Id, north + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 5, 3, 8, chunkBB);
	placeBlock(level, Tile::endPortalFrameTile_Id, north + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 6, 3, 8, chunkBB);
	placeBlock(level, Tile::endPortalFrameTile_Id, south + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 4, 3, 12, chunkBB);
	placeBlock(level, Tile::endPortalFrameTile_Id, south + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 5, 3, 12, chunkBB);
	placeBlock(level, Tile::endPortalFrameTile_Id, south + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 6, 3, 12, chunkBB);
	placeBlock(level, Tile::endPortalFrameTile_Id, east + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 3, 3, 9, chunkBB);
	placeBlock(level, Tile::endPortalFrameTile_Id, east + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 3, 3, 10, chunkBB);
	placeBlock(level, Tile::endPortalFrameTile_Id, east + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 3, 3, 11, chunkBB);
	placeBlock(level, Tile::endPortalFrameTile_Id, west + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 7, 3, 9, chunkBB);
	placeBlock(level, Tile::endPortalFrameTile_Id, west + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 7, 3, 10, chunkBB);
	placeBlock(level, Tile::endPortalFrameTile_Id, west + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 7, 3, 11, chunkBB);
	

	if (!hasPlacedMobSpawner)
	{
		y = getWorldY(3);
		int x = getWorldX(5, 6), z = getWorldZ(5, 6);
		if (chunkBB->isInside(x, y, z))
		{
			// 4J Stu - The mob spawner location is close enough for the map icon display, and this ensures that we only need to set the position once
			app.AddTerrainFeaturePosition(eTerrainFeature_StrongholdEndPortal,x,z);			
			level->getLevelData()->setXStrongholdEndPortal(x);
			level->getLevelData()->setZStrongholdEndPortal(z);
			level->getLevelData()->setHasStrongholdEndPortal();

			hasPlacedMobSpawner = true;
			level->setTile(x, y, z, Tile::mobSpawner_Id);
			shared_ptr<MobSpawnerTileEntity> entity = dynamic_pointer_cast<MobSpawnerTileEntity>(level->getTileEntity(x, y, z));
			if (entity != NULL) entity->setEntityId(L"Silverfish");
		}
	}

	return true;

}

void StrongholdPieces::SmoothStoneSelector::next(Random *random, int worldX, int worldY, int worldZ, bool isEdge)
{
    if (isEdge)
	{
        nextId = Tile::stoneBrickSmooth_Id;

        float selection = random->nextFloat();
        if (selection < 0.2f)
		{
            nextData = SmoothStoneBrickTile::TYPE_CRACKED;
        }
		else if (selection < 0.5f)
		{
            nextData = SmoothStoneBrickTile::TYPE_MOSSY;
        }
		else if (selection < 0.55f)
		{
            nextId = Tile::monsterStoneEgg_Id;
            nextData = StoneMonsterTile::HOST_STONEBRICK;
        }
		else
		{
            nextData = 0;
        }
    }
	else
	{
        nextId = 0;
        nextData = 0;
    }
}

const StrongholdPieces::SmoothStoneSelector *StrongholdPieces::smoothStoneSelector = new SmoothStoneSelector();
