/**
Copyright (C) <2015> <coolAlias>
This file is part of coolAlias' Zelda Sword Skills Minecraft Mod; as such,
you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package zeldaswordskills.world.gen.structure;
import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks;
import net.minecraft.inventory.IInventory;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.Vec3i;
import net.minecraft.world.World;
import zeldaswordskills.ZSSMain;
import zeldaswordskills.block.BlockSacredFlame;
import zeldaswordskills.block.BlockSecretStone;
import zeldaswordskills.block.ZSSBlocks;
import zeldaswordskills.block.tileentity.TileEntityDungeonCore;
import zeldaswordskills.ref.Config;
import zeldaswordskills.util.BossType;
import zeldaswordskills.util.StructureGenUtils;
import zeldaswordskills.world.gen.DungeonLootLists;
/**
*
* Generates a 'boss' room: larger, more complex, and always locked with a special door.
*
* The room size must be at least 9x9 blocks in area, with a random height from 7-10
*
* Each biome has its own boss room, and each room type will always contain certain items.
*
*/
public class RoomBoss extends RoomBase
{
/** Side of the structure in which the door is located; will not be null after dungeon initialized */
private EnumFacing doorSide = null;
/** The type of dungeon this is, as determined by biome using BossType.getBossType */
private final BossType type;
public RoomBoss(BossType bossType, int chunkX, int chunkZ, Random rand, int size, Block blockRequired) {
super(chunkX, chunkZ, Math.max(size, 9), (rand.nextInt(4) + 7), blockRequired);
isLocked = true;
type = bossType;
}
/** Returns the BossType for this Boss Room */
public BossType getBossType() {
return type;
}
@Override
public boolean generate(ZSSMapGenBase mapGen, World world, Random rand, int x, int y, int z) {
if (initDungeon(world, x, y, z) && canGenerate(world)) {
doStandardRoomGen(world, rand);
return true;
} else {
return false;
}
}
/**
* Performs initial set up and placement of the dungeon
* @return false if anything went awry and generation should be canceled
*/
protected boolean initDungeon(World world, int x, int y, int z) {
bBox.offset(x, y, z);
if (type == null || y < bBox.getYSize() || y > (world.provider.getDimensionId() == -1 ? 96 : 160)) {
return false;
}
// allow all boss types to potentially generate in the Nether (for randomized locations config option)
if (world.provider.getDimensionId() == -1) {
inNether = true;
if (!placeInNether(world)) {
return false;
}
}
switch(type) {
case HELL:
doDefaultAdjustments(world);
if (world.rand.nextFloat() < 0.75F) {
submerged = true;
inLava = true;
}
break;
case OCEAN:
if (!placeInOcean(world, false)) {
return false;
}
break;
case SWAMP:
submerged = true;
doDefaultAdjustments(world);
bBox.offset(0, -1, 0);
break;
default:
doDefaultAdjustments(world);
}
if (submerged) {
--bBox.minY;
}
determineDoorSide(world);
setMetadata(world, new BlockPos(x, bBox.minY, z));
boolean flag = StructureGenUtils.getAverageDistanceToGround(world, bBox, 6) < 4;
return (doorSide != null && flag && (submerged || !isWaterAroundOrUnder(world)));
}
/**
* Makes default adjustments for surrounding materials such as dirt, air, etc.
*/
private void doDefaultAdjustments(World world) {
// adjust up for dirt, grass, and sand
StructureGenUtils.adjustCornersForMaterial(world, bBox, Material.ground, 4, false, true);
StructureGenUtils.adjustCornersForMaterial(world, bBox, Material.grass, 4, false, true);
StructureGenUtils.adjustCornersForMaterial(world, bBox, Material.sand, 4, false, true);
// adjust down for air and water pockets
StructureGenUtils.adjustCornersForMaterial(world, bBox, Material.air, 4, false, false);
if (type != BossType.SWAMP) {
StructureGenUtils.adjustCornersForMaterial(world, bBox, Material.water, 4, false, false);
}
// adjust down one more so door is flush with ground
//bBox.offset(0, -1, 0); TODO add stairs or slabs up instead
}
/**
* Returns true if there are 2 or more blocks of water directly under the structure,
* or if there are 3 or more blocks of water next to any given side
*/
protected boolean isWaterAroundOrUnder(World world) {
if (StructureGenUtils.getNumBlocksOfMaterial(world, bBox, Material.water, -1) > 1) {
return true;
} else if (StructureGenUtils.getNumBlocksOfMaterialInArea(world, Material.water, bBox.minX - 1, bBox.minX, bBox.minY, bBox.minY + 2, bBox.minZ, bBox.maxZ) > 2) {
return true;
} else if (StructureGenUtils.getNumBlocksOfMaterialInArea(world, Material.water, bBox.maxX + 1, bBox.maxX + 2, bBox.minY, bBox.minY + 2, bBox.minZ, bBox.maxZ) > 2) {
return true;
} else if (StructureGenUtils.getNumBlocksOfMaterialInArea(world, Material.water, bBox.minX, bBox.maxX, bBox.minY, bBox.minY + 2, bBox.minZ - 1, bBox.minZ) > 2) {
return true;
} else if (StructureGenUtils.getNumBlocksOfMaterialInArea(world, Material.water, bBox.minX, bBox.maxX, bBox.minY, bBox.minY + 2, bBox.maxZ + 1, bBox.maxZ + 2) > 2) {
return true;
}
return false;
}
@Override
protected void setMetadata(World world, BlockPos pos) {
metadata = type.metadata;
}
/**
* Adds the final touches: chests, dungeon core, pedestal, etc.
*/
protected void decorateDungeon(World world, Random rand) {
int meta = getMetadata();
StructureGenUtils.fillDown(world, bBox, BlockSecretStone.EnumType.byMetadata(meta).getDroppedBlock().getDefaultState());
placeDoor(world);
placeDungeonCore(world);
placePillars(world, meta);
placeCenterPiece(world, rand, meta);
placeChandelier(world);
placeParapet(world, meta);
placeLedge(world, rand, meta);
placeChestOnRoof(world, rand);
placeInvisibleChest(world, rand, rand.nextInt(3)); // very rarely may get 2 invisible chests
placeJars(world, rand, rand.nextInt(5), false);
placeJars(world, rand, rand.nextInt(5) + 3, true);
placeWindows(world);
}
@Override
protected void placeDungeonCore(World world) {
StructureGenUtils.setBlockAtPosition(world, bBox, bBox.getXSize() / 2, 0, bBox.getZSize() / 2, ZSSBlocks.dungeonCore.getStateFromMeta(getMetadata() | 0x8));
int x = StructureGenUtils.getXWithOffset(bBox, bBox.getXSize() / 2, bBox.getZSize() / 2);
int y = StructureGenUtils.getYWithOffset(bBox, 0);
int z = StructureGenUtils.getZWithOffset(bBox, bBox.getXSize() / 2, bBox.getZSize() / 2);
TileEntity te = world.getTileEntity(new BlockPos(x, y, z));
if (te instanceof TileEntityDungeonCore) {
TileEntityDungeonCore core = (TileEntityDungeonCore) te;
core.setRenderState(BlockSecretStone.EnumType.byMetadata(getMetadata()).getDroppedBlock().getDefaultState());
core.setDungeonBoundingBox(bBox);
core.setBossType(type);
core.setDoor(ZSSBlocks.doorBoss, type.ordinal(), doorSide);
}
}
/**
* Determines which side is most suitable for the door
*/
protected void determineDoorSide(World world) {
Vec3i center = bBox.getCenter();
int x = center.getX();
int y = bBox.minY + 1;
int z = center.getZ();
int dx, dz;
doorSide = EnumFacing.Plane.HORIZONTAL.random(world.rand);
for (int i = 0; i < 4; ++i) {
dx = x;
dz = z;
switch(doorSide) {
case SOUTH: dz = bBox.maxZ + 1; break;
case NORTH: dz = bBox.minZ - 1; break;
case EAST: dx = bBox.maxX + 1; break;
case WEST: dx = bBox.maxX - 1; break;
default: ZSSMain.logger.warn(String.format("Invalid boss door side %d at %d/%d/%d", doorSide, x, y, z));
}
Block block1 = world.getBlockState(new BlockPos(dx, y, dz)).getBlock();
Block block2 = world.getBlockState(new BlockPos(dx, y + 1, dz)).getBlock();
if (!block1.isFullBlock() && !block2.isFullBlock()) {
return; // the blocks in front of the door are not full blocks, this is a good side for the door
}
doorSide = doorSide.rotateY();
}
doorSide = null;
}
/**
* Actually places the door; use after determining door side for best results
*/
protected void placeDoor(World world) {
Vec3i center = bBox.getCenter();
int x = center.getX();
int y = bBox.minY + (submerged ? 2 : 1);
int z = center.getZ();
switch(doorSide) {
case SOUTH: z = bBox.maxZ; break;
case NORTH: z = bBox.minZ; break;
case EAST: x = bBox.maxX; break;
case WEST: x = bBox.minX; break;
default: ZSSMain.logger.warn("Placing Boss door with invalid door side");
}
world.setBlockState(new BlockPos(x, y, z), ZSSBlocks.doorBoss.getStateFromMeta(type.ordinal() & ~0x8), 2);
world.setBlockState(new BlockPos(x, y + 1, z), ZSSBlocks.doorBoss.getStateFromMeta(type.ordinal() | 0x8), 2);
}
/**
* Places the center half-slabs and block for either a chest or a pedestal
*/
protected void placeCenterPiece(World world, Random rand, int meta) {
int minX = bBox.getXSize() / 2 - 1;
int minY = 1;
int minZ = bBox.getZSize() / 2 - 1;
if (submerged) {
StructureGenUtils.fillWithBlocks(world, bBox, minX, minX + 3, minY, minY + 1, minZ, minZ + 3, BlockSecretStone.EnumType.byMetadata(meta).getDroppedBlock().getDefaultState());
++minY;
}
if (!inOcean) {
StructureGenUtils.fillWithBlocks(world, bBox, minX, minX + 3, minY, minY + 1, minZ, minZ + 3, BlockSecretStone.EnumType.byMetadata(meta).getSlab());
}
Vec3i center = bBox.getCenter();
world.setBlockState(new BlockPos(center.getX(), bBox.minY + (submerged && !inOcean ? 2 : 1), center.getZ()), (type == BossType.TAIGA ? Blocks.quartz_block.getDefaultState() : BlockSecretStone.EnumType.byMetadata(meta).getDroppedBlock().getDefaultState()), 2);
placeHinderBlock(world);
boolean hasChest = false;
switch(type) {
case DESERT: // fall through
case OCEAN: // fall through
case MOUNTAIN: placeMainChest(world, rand, true); hasChest = true; break;
case FOREST:
if (rand.nextFloat() < Config.getMasterSwordChance()) {
placePedestal(world, minY + 1);
} else { // chance of double chest
placeMainChest(world, rand, true);
hasChest = !(rand.nextFloat() < (2 * Config.getDoubleChestChance()));
}
break;
case HELL: placeFlame(world, BlockSacredFlame.EnumType.DIN); break;
case SWAMP: placeFlame(world, BlockSacredFlame.EnumType.FARORE); break;
case TAIGA: placeFlame(world, BlockSacredFlame.EnumType.NAYRU); break;
default:
}
if (!hasChest) {
placeMainChest(world, rand, false);
}
}
/**
* Places the hanging chandelier only if height is sufficient
*/
protected void placeChandelier(World world) {
if (bBox.getYSize() > 7) {
Vec3i center = bBox.getCenter();
int x = center.getX();
int y = bBox.maxY - 1;
int z = center.getZ();
switch(type) {
case OCEAN:
world.setBlockState(new BlockPos(x + 1, y, z + 1), Blocks.glowstone.getDefaultState(), 2);
world.setBlockState(new BlockPos(x + 1, y, z - 1), Blocks.glowstone.getDefaultState(), 2);
world.setBlockState(new BlockPos(x - 1, y, z + 1), Blocks.glowstone.getDefaultState(), 2);
world.setBlockState(new BlockPos(x - 1, y, z - 1), Blocks.glowstone.getDefaultState(), 2);
world.setBlockState(new BlockPos(x, y, z), Blocks.glowstone.getDefaultState(), 2);
break;
case SWAMP:
world.setBlockState(new BlockPos(bBox.minX + 1, y, bBox.minZ + 1), Blocks.glowstone.getDefaultState(), 2);
world.setBlockState(new BlockPos(bBox.minX + 1, y, bBox.maxZ - 1), Blocks.glowstone.getDefaultState(), 2);
world.setBlockState(new BlockPos(bBox.maxX - 1, y, bBox.minZ + 1), Blocks.glowstone.getDefaultState(), 2);
world.setBlockState(new BlockPos(bBox.maxX - 1, y, bBox.maxZ - 1), Blocks.glowstone.getDefaultState(), 2);
break;
default:
world.setBlockState(new BlockPos(x, y, z), Blocks.oak_fence.getDefaultState(), 2);
world.setBlockState(new BlockPos(x + 1, y, z), Blocks.oak_fence.getDefaultState(), 2);
world.setBlockState(new BlockPos(x + 1, y, z), Blocks.oak_fence.getDefaultState(), 2);
world.setBlockState(new BlockPos(x, y, z + 1), Blocks.oak_fence.getDefaultState(), 2);
world.setBlockState(new BlockPos(x, y, z - 1), Blocks.oak_fence.getDefaultState(), 2);
world.setBlockState(new BlockPos(x + 1, y, z + 1), Blocks.oak_fence.getDefaultState(), 2);
world.setBlockState(new BlockPos(x + 1, y, z - 1), Blocks.oak_fence.getDefaultState(), 2);
world.setBlockState(new BlockPos(x - 1, y, z + 1), Blocks.oak_fence.getDefaultState(), 2);
world.setBlockState(new BlockPos(x - 1, y, z - 1), Blocks.oak_fence.getDefaultState(), 2);
world.setBlockState(new BlockPos(x, y - 1, z), Blocks.oak_fence.getDefaultState(), 2);
world.setBlockState(new BlockPos(x + 1, y - 1, z + 1), Blocks.glowstone.getDefaultState(), 2);
world.setBlockState(new BlockPos(x + 1, y - 1, z - 1), Blocks.glowstone.getDefaultState(), 2);
world.setBlockState(new BlockPos(x - 1, y - 1, z + 1), Blocks.glowstone.getDefaultState(), 2);
world.setBlockState(new BlockPos(x - 1, y - 1, z - 1), Blocks.glowstone.getDefaultState(), 2);
world.setBlockState(new BlockPos(x, y - 2, z), Blocks.glowstone.getDefaultState(), 2);
}
}
}
/**
* Places and generates the contents of this dungeon's main chest
*/
protected void placeMainChest(World world, Random rand, boolean inCenter) {
Vec3i center = bBox.getCenter();
int x = (inCenter ? center.getX() : (rand.nextFloat() < 0.5F ? bBox.minX + 1 : bBox.maxX - 1));
int y = bBox.minY + (inCenter ? 2 : 1) + (submerged && !inOcean ? 1 : 0);
int z = (inCenter ? center.getZ() : (rand.nextFloat() < 0.5F ? bBox.minZ + 1 : bBox.maxZ - 1));
BlockPos pos = new BlockPos(x, y, z);
Block chest = (inCenter ? Blocks.chest : ZSSBlocks.chestLocked);
placeChest(world, pos, chest);
if (inCenter) {
world.setBlockState(pos, chest.getStateFromMeta(doorSide.getIndex()), 2);
} else if (submerged && !inOcean) {
world.setBlockState(pos.down(), BlockSecretStone.EnumType.byMetadata(getMetadata()).getDroppedBlock().getDefaultState(), 2);
}
TileEntity te = world.getTileEntity(pos);
if (te instanceof IInventory) {
DungeonLootLists.generateBossChestContents(world, rand, (IInventory) te, this);
}
}
/**
* Attempts to place a chest (unlocked, but with locked-level loot) in one of the four roof corners
*/
protected void placeChestOnRoof(World world, Random rand) {
if (rand.nextFloat() < 0.1F) {
int x = (rand.nextFloat() < 0.5F ? bBox.minX : bBox.maxX);
int y = bBox.maxY + 1;
int z = (rand.nextFloat() < 0.5F ? bBox.minZ : bBox.maxZ);
placeChestAt(world, new BlockPos(x, y, z), Blocks.chest, rand, true);
}
}
/**
* Attempts to place n invisible chests, either on the roof or in the main room
*/
protected void placeInvisibleChest(World world, Random rand, int n) {
for (int i = 0; i < n; ++i) {
if (rand.nextFloat() < Config.getDoubleChestChance()) {
int x = bBox.minX + rand.nextInt(bBox.getXSize());
int y = (rand.nextFloat() < 0.5F ? bBox.minY + 1 : bBox.maxY + 1);
int z = bBox.minZ + rand.nextInt(bBox.getZSize());
if (bBox.isVecInside(new Vec3i(x, y - 1, z))) { // subtract 1 for bounds check
placeChestAt(world, new BlockPos(x, y, z), ZSSBlocks.chestInvisible, rand, true);
}
}
}
}
/**
* Places the chest block at the given coordinates provided no other block exists,
* and populates the chest with loot (locked level loot if goodLoot is true)
*/
protected void placeChestAt(World world, BlockPos pos, Block chest, Random rand, boolean goodLoot) {
if (world.isAirBlock(pos)) {
placeChest(world, pos, chest);
TileEntity te = world.getTileEntity(pos);
if (te instanceof IInventory) {
DungeonLootLists.generateChestContents(world, rand, (IInventory) te, this, goodLoot);
}
}
}
/**
* Places the designated Sacred Flame on the center block
*/
protected void placeFlame(World world, BlockSacredFlame.EnumType flame) {
Vec3i center = bBox.getCenter();
world.setBlockState(new BlockPos(center.getX(), bBox.minY + (submerged ? 3 : 2), center.getZ()), ZSSBlocks.sacredFlame.getDefaultState().withProperty(BlockSacredFlame.VARIANT, flame), 2);
}
/**
* Places a secret stone block between the chest and door to prevent early looting
*/
protected void placeHinderBlock(World world) {
Vec3i center = bBox.getCenter();
int x = center.getX() + doorSide.getFrontOffsetX();
int y = bBox.minY + (submerged && !inOcean ? 3 : 2);
int z = center.getZ() + doorSide.getFrontOffsetZ();
world.setBlockState(new BlockPos(x, y, z), ZSSBlocks.secretStone.getStateFromMeta(getMetadata()), 2);
}
/**
* Adds n random breakable jars, either on the floor or on the roof
*/
protected void placeJars(World world, Random rand, int n, boolean onRoof) {
for (int i = 0; i < n; ++i) {
int x = bBox.minX + rand.nextInt(bBox.getXSize());
int y = (onRoof ? bBox.maxY : bBox.minY) + 1;
int z = bBox.minZ + rand.nextInt(bBox.getZSize());
BlockPos pos = new BlockPos(x, y, z);
Material m = world.getBlockState(pos).getBlock().getMaterial();
if (m == Material.air || m.isLiquid()) {
world.setBlockState(pos, ZSSBlocks.ceramicJar.getDefaultState());
}
}
}
/**
* Places a one-block wide ledge around the inside wall perimeter at centerY only
* if certain conditions are met (sufficient y size, random)
*/
protected void placeLedge(World world, Random rand, int meta) {
if (type == BossType.OCEAN || type == BossType.MOUNTAIN) {
return;
}
if (bBox.getYSize() > 7 && rand.nextFloat() < (type == BossType.HELL ? 0.75F : 0.5F)) {
int y = bBox.getCenter().getY(); // centerY
Block block = BlockSecretStone.EnumType.byMetadata(meta).getDroppedBlock();
StructureGenUtils.fillWithoutReplace(world, bBox.minX + 1, bBox.minX + 2, y, y + 1, bBox.minZ + 1, bBox.maxZ, block.getDefaultState(), 3);
StructureGenUtils.fillWithoutReplace(world, bBox.maxX - 1, bBox.maxX, y, y + 1, bBox.minZ + 1, bBox.maxZ, block.getDefaultState(), 3);
StructureGenUtils.fillWithoutReplace(world, bBox.minX + 2, bBox.maxX - 1, y, y + 1, bBox.minZ + 1, bBox.minZ + 2, block.getDefaultState(), 3);
StructureGenUtils.fillWithoutReplace(world, bBox.minX + 2, bBox.maxX - 1, y, y + 1, bBox.maxZ - 1, bBox.maxZ, block.getDefaultState(), 3);
}
}
/**
* Places parapet encircling the room's top
*/
protected void placeParapet(World world, int meta) {
int x1 = bBox.minX - 1;
int x2 = bBox.maxX + 1;
int z1 = bBox.minZ - 1;
int z2 = bBox.maxZ + 1;
int y = bBox.maxY;
Block block = BlockSecretStone.EnumType.byMetadata(meta).getDroppedBlock();
Block stairs = BlockSecretStone.EnumType.byMetadata(meta).getStairBlock();
for (int i = x1; i <= x2; ++i) {
world.setBlockState(new BlockPos(i, y, z1), stairs.getStateFromMeta(6), 3);
world.setBlockState(new BlockPos(i, y + 1, z1), block.getDefaultState(), 2);
world.setBlockState(new BlockPos(i, y, z2), stairs.getStateFromMeta(7), 3);
world.setBlockState(new BlockPos(i, y + 1, z2), block.getDefaultState(), 2);
if (i % 2 == 0) {
world.setBlockState(new BlockPos(i, y + 2, z1), block.getDefaultState(), 2);
world.setBlockState(new BlockPos(i, y + 2, z2), block.getDefaultState(), 2);
}
}
for (int i = z1; i <= z2; ++i) {
world.setBlockState(new BlockPos(x1, y, i), stairs.getStateFromMeta(4), 3);
world.setBlockState(new BlockPos(x1, y + 1, i), block.getDefaultState(), 2);
world.setBlockState(new BlockPos(x2, y, i), stairs.getStateFromMeta(5), 3);
world.setBlockState(new BlockPos(x2, y + 1, i), block.getDefaultState(), 2);
if (i % 2 == 0) {
if (world.getBlockState(new BlockPos(x1 + 1, y + 2, i)).getBlock() != block) {
world.setBlockState(new BlockPos(x1, y + 2, i), block.getDefaultState(), 2);
}
if (world.getBlockState(new BlockPos(x2 - 1, y + 2, i)).getBlock() != block) {
world.setBlockState(new BlockPos(x2, y + 2, i), block.getDefaultState(), 2);
}
}
}
}
/**
* Places pillars in the four corners of the dungeon with chance based on room size
*/
protected void placePillars(World world, int meta) {
if (type == BossType.DESERT || type == BossType.SWAMP || type == BossType.MOUNTAIN) {
return;
}
if (world.rand.nextFloat() < (bBox.getXSize() * 0.06F)) {
Vec3i center = bBox.getCenter();
int offset = (bBox.getXSize() < 11 ? 2 : 3);
int x1 = (bBox.getXSize() < 11 ? center.getX() : bBox.minX) + offset;
int x2 = (bBox.getXSize() < 11 ? center.getX() : bBox.maxX) - offset;
int z1 = (bBox.getZSize() < 11 ? center.getZ() : bBox.minZ) + offset;
int z2 = (bBox.getZSize() < 11 ? center.getZ() : bBox.maxZ) - offset;
IBlockState state = BlockSecretStone.EnumType.byMetadata(meta).getDroppedBlock().getDefaultState();
for (int y = bBox.minY + 1; y < bBox.maxY; ++y) {
world.setBlockState(new BlockPos(x1, y, z1), state, 2);
world.setBlockState(new BlockPos(x1, y, z2), state, 2);
world.setBlockState(new BlockPos(x2, y, z1), state, 2);
world.setBlockState(new BlockPos(x2, y, z2), state, 2);
}
if (bBox.getXSize() > 10 && world.rand.nextFloat() < 0.5F) {
placePillarLandings(world, state, new BlockPos(x1, center.getY(), z1), 0);
placePillarLandings(world, state, new BlockPos(x2, center.getY(), z1), 1);
placePillarLandings(world, state, new BlockPos(x1, center.getY(), z2), 2);
placePillarLandings(world, state, new BlockPos(x2, center.getY(), z2), 3);
}
}
}
/**
* Places one-block landings on two sides of the pillar, depending on facing
* @param corner 0 NW, 1 NE, 2 SW, 3 SE
*/
protected void placePillarLandings(World world, IBlockState state, BlockPos pos, int corner) {
switch(corner % 4) {
case 0:
world.setBlockState(pos.east(), state);
world.setBlockState(pos.south(), state);
break;
case 1:
world.setBlockState(pos.west(), state);
world.setBlockState(pos.south(), state);
break;
case 2:
world.setBlockState(pos.east(), state);
world.setBlockState(pos.north(), state);
break;
case 3:
world.setBlockState(pos.west(), state);
world.setBlockState(pos.north(), state);
}
}
/**
* Places 'windows' at regular intervals in the walls
*/
protected void placeWindows(World world) {
if (Config.areWindowsEnabled() && world.rand.nextFloat() < (bBox.getXSize() * 0.06F)) {
int interval = (bBox.getXSize() % 2 == 1 ? 2 : 3);
int j = bBox.getCenter().getY() + 1;
Block window = (world.rand.nextFloat() < 0.25F ? Blocks.iron_bars : Blocks.air);
boolean hasVines = (type == BossType.FOREST || type == BossType.SWAMP);
for (int i = bBox.minX + 1; i < bBox.maxX; ++i) {
if (i % interval == 0) {
world.setBlockState(new BlockPos(i, j, bBox.minZ), window.getDefaultState(), 2);
world.setBlockState(new BlockPos(i, j, bBox.maxZ), window.getDefaultState(), 2);
if (hasVines) {
if (world.rand.nextFloat() < 0.25F) {
placeVines(world, new BlockPos(i, j - 1, bBox.minZ - 1), 1);
}
if (world.rand.nextFloat() < 0.25F) {
placeVines(world, new BlockPos(i, j - 1, bBox.maxZ + 1), 4);
}
}
}
}
for (int k = bBox.minZ + 1; k < bBox.maxZ; ++k) {
if (k % interval == 0) {
world.setBlockState(new BlockPos(bBox.minX, j, k), window.getDefaultState(), 2);
world.setBlockState(new BlockPos(bBox.maxX, j, k), window.getDefaultState(), 2);
if (hasVines) {
if (world.rand.nextFloat() < 0.25F) {
placeVines(world, new BlockPos(bBox.minX - 1, j - 1, k), 8);
}
if (world.rand.nextFloat() < 0.25F) {
placeVines(world, new BlockPos(bBox.maxX + 1, j - 1, k), 2);
}
}
}
}
}
}
/**
* Places a random number of vines with the given metadata facing starting from y and moving downward
*/
protected void placeVines(World world, BlockPos pos, int meta) {
int n = world.rand.nextInt(5) + 1;
for (int i = 0; i < n; ++i) {
if (!world.isAirBlock(pos.down(i))) {
break;
}
world.setBlockState(pos.down(i), Blocks.vine.getStateFromMeta(meta), 2);
}
}
@Override
protected boolean canReplaceBlockAt(int y, Block block) {
boolean flag1 = (block != null && !block.getMaterial().blocksMovement());
boolean flag2 = (block != null && block.getMaterial() == Material.leaves);
boolean flag3 = (type == BossType.TAIGA && (block == Blocks.log || block == Blocks.log2 || (block != null && block.getMaterial() == Material.ice)));
return (block == null || flag1 || flag2 || flag3 || super.canReplaceBlockAt(y, block));
}
@Override
protected boolean placeInOcean(World world, boolean sink) {
if (type == BossType.OCEAN) {
Vec3i center = bBox.getCenter();
while (bBox.minY > 60 && world.getBlockState(new BlockPos(center.getX(), bBox.minY, center.getZ())).getBlock().getMaterial() == Material.air) {
bBox.offset(0, -1, 0);
}
while (bBox.minY > 16 && world.getBlockState(new BlockPos(center.getX(), bBox.minY, center.getZ())).getBlock().getMaterial() == Material.water) {
bBox.offset(0, -1, 0);
}
if (world.getBlockState(new BlockPos(center.getX(), bBox.minY, center.getZ())).getBlock().getMaterial() != Material.water &&
world.getBlockState(new BlockPos(center.getX(), bBox.maxY, center.getZ())).getBlock().getMaterial() == Material.water) {
inOcean = true;
submerged = true;
StructureGenUtils.adjustCornersForMaterial(world, bBox, Material.water, 6, false, false);
return true;
}
}
return false;
}
}