/**
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.init.Blocks;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
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 net.minecraft.world.biome.BiomeGenBase;
import net.minecraftforge.common.ChestGenHooks;
import zeldaswordskills.block.BlockDoorLocked;
import zeldaswordskills.block.BlockHeavy;
import zeldaswordskills.block.BlockPeg;
import zeldaswordskills.block.BlockQuakeStone;
import zeldaswordskills.block.BlockSecretStone;
import zeldaswordskills.block.BlockTime;
import zeldaswordskills.block.ZSSBlocks;
import zeldaswordskills.block.tileentity.TileEntityDungeonCore;
import zeldaswordskills.item.ItemInstrument;
import zeldaswordskills.item.ItemTreasure;
import zeldaswordskills.item.ZSSItems;
import zeldaswordskills.ref.Config;
import zeldaswordskills.util.BossType;
import zeldaswordskills.util.StructureGenUtils;
import zeldaswordskills.util.WorldUtils;
import zeldaswordskills.world.gen.DungeonLootLists;
public class RoomSecret extends RoomBase
{
/** Max height any dungeon can reach */
protected static final int MAX_HEIGHT = 5;
/** Block which will be placed as a door, if any */
private Block door = null;
/** Metadata value for the door variant, if any */
private int doorMeta;
/** Side of the structure that the door is on */
private EnumFacing face = EnumFacing.EAST;
/**
* Creates a new secret room object ready for generation
* @param size minimum size of 3
* @param blockRequired the block that must make up the majority of the structure's volume
*/
public RoomSecret(int chunkX, int chunkZ, int size, Block blockRequired) {
super(chunkX, chunkZ, size, MAX_HEIGHT, blockRequired);
}
@Override
public boolean generate(ZSSMapGenBase mapGen, World world, Random rand, int x, int y, int z) {
if (y < bBox.maxY) {
return false;
}
inNether = (world.provider.getDimensionName().equals("Nether"));
bBox.offset(x, y - bBox.maxY, z);
Vec3i center = bBox.getCenter();
int worldHeight = (inNether ? 128 : world.getHeight(new BlockPos(center.getX(), bBox.minY, center.getZ())).getY());
if (bBox.maxY > worldHeight) {
bBox.offset(0, worldHeight - bBox.maxY - 1, 0);
}
if (!validateTopLayer(world) && !placeInOcean(world, true)) {
return false;
} else if (inNether && submerged && !placeInNether(world)) {
return false;
}
StructureGenUtils.adjustForAir(world, this, bBox);
checkSpecialCases(world, rand);
setMetadata(world, new BlockPos(bBox.getCenter()));
int range = (inOcean ? Config.getMinOceanDistance() : inNether ? Config.getNetherMinDistance() : Config.getMinLandDistance());
if (!mapGen.areStructuresWithinRange(this, range) && isWellHidden(world) && canGenerate(world)) {
doStandardRoomGen(world, rand);
return true;
} else {
return false;
}
}
@Override
protected void setMetadata(World world, BlockPos pos) {
BossType type = (Config.areBossDungeonsRandom() ? null : BossType.getBossType(world, pos));
boolean inWater = inOcean || StructureGenUtils.getNumBlocksOfMaterial(world, bBox, Material.water, 1) > 0;
if (type != null) {
switch(type) {
case HELL: metadata = 2; break; // nether brick
case OCEAN: metadata = (inWater ? 6 : 0); break; // cobblestone
default: metadata = 0;
}
} else if (world.provider.getDimensionName().equals("Nether")) {
metadata = 2; // nether brick
} else {
metadata = (inWater ? 6 : 0);
}
if (door != null) {
metadata |= 8;
}
}
@Override
protected void decorateDungeon(World world, Random rand) {
if (door != null) { placeDoor(world, rand); }
doChestGen(world, rand);
placeDungeonCore(world);
placeJars(world, rand);
}
@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);
if (door != null) {
core.setDoor(door, doorMeta, face);
}
if (!inNether && submerged && !inLava && !inOcean && bBox.getXSize() > 4) {
if (inMountain || world.rand.nextFloat() < Config.getFairySpawnerChance()) {
core.setSpawner();
}
}
}
}
/**
* Makes final checks for submerged, lava, fairy spawners, etc. and adjusts bounding box minY if needed
*/
private void checkSpecialCases(World world, Random rand) {
BiomeGenBase biome = world.getBiomeGenForCoords(new BlockPos(bBox.getCenter()));
boolean flag = (!submerged && bBox.maxY > 64 && biome != null);
if (inNether && !inLava) {
if (rand.nextFloat() < 0.25F) {
submerged = true;
inLava = true;
}
} else if (flag && (biome.biomeName.toLowerCase().contains("hill") || biome.biomeName.toLowerCase().contains("mountain"))) {
submerged = rand.nextFloat() < Config.getFairySpawnerChance();
inMountain = true;
}
if (submerged && bBox.getXSize() > 3) {
--bBox.minY;
}
if (bBox.getXSize() > 5 && rand.nextFloat() < Config.getBarredRoomChance()) {
setDoor(rand);
face = EnumFacing.Plane.HORIZONTAL.random(rand);
}
}
/**
* Called for barred rooms to randomly determine what type of door to use
*/
protected void setDoor(Random rand) {
if (rand.nextInt(16) == 0) {
if (submerged && rand.nextInt(3) == 0) {
door = ZSSBlocks.doorLocked;
} else {
door = ZSSBlocks.timeBlock;
doorMeta = BlockTime.EnumType.TIME.getMetadata();
}
} else if (rand.nextInt(16) == 0) {
door = ZSSBlocks.quakeStone;
doorMeta = rand.nextInt(BlockQuakeStone.EnumType.values().length);
} else if (!submerged) {
if (rand.nextInt(3) == 0) {
if (rand.nextInt(3) == 0) {
door = ZSSBlocks.heavyBlock;
doorMeta = BlockHeavy.EnumType.HEAVY.getMetadata();
} else {
door = ZSSBlocks.pegRusty;
}
} else if (rand.nextInt(3) == 0) {
door = ZSSBlocks.doorLocked;
} else if (rand.nextInt(3) == 0) {
door = ZSSBlocks.heavyBlock;
doorMeta = BlockHeavy.EnumType.LIGHT.getMetadata();
} else {
door = ZSSBlocks.pegWooden;
}
}
}
/**
* Determines location(s) for chest(s), as well as number of items to generate
*/
private void doChestGen(World world, Random rand) {
int rX = bBox.getXSize() - 2;
int rY = (inLava && bBox.getYSize() > 3 ? 2 : 1);
int rZ = bBox.getZSize() - 2;
if (door instanceof BlockPeg) {
switch(face) {
case SOUTH: rX = 1; break;
case NORTH: rX = rZ = 1; break;
case EAST: rZ = 1; break;
case WEST: rX = rZ = 1; break;
default: // UP and DOWN are not possible
}
generateChestContents(world, rand, rX, rY, rZ, true);
} else {
generateChestContents(world, rand, rand.nextInt(rX) + 1, rY, rand.nextInt(rZ) + 1, true);
if (bBox.getXSize() > 5 && rand.nextFloat() < Config.getDoubleChestChance()) {
generateChestContents(world, rand, rand.nextInt(rX) + 1, rY, rand.nextInt(rZ) + 1, false);
}
}
}
/**
* Places a chest and generates items at x/y/z in the structure, returning true if successful
* @param first true for first chest generated
*/
private boolean generateChestContents(World world, Random rand, int x, int y, int z, boolean first) {
int i = StructureGenUtils.getXWithOffset(bBox, x, z);
int j = StructureGenUtils.getYWithOffset(bBox, y);
int k = StructureGenUtils.getZWithOffset(bBox, x, z);
BlockPos pos = new BlockPos(i, j, k);
if (bBox.isVecInside(new Vec3i(i, j, k)) && !StructureGenUtils.isBlockChest(world, pos)) {
Block chestBlock = (rand.nextFloat() < Config.getLockedChestChance() ? ZSSBlocks.chestLocked : Blocks.chest);
if (door != ZSSBlocks.timeBlock && !first && rand.nextFloat() < Config.getLockedChestChance()) {
chestBlock = ZSSBlocks.chestInvisible;
}
placeChest(world, pos, chestBlock);
TileEntity te = world.getTileEntity(pos);
if (te instanceof IInventory) {
IInventory chest = (IInventory) te;
DungeonLootLists.generateChestContents(world, rand, chest, this, chestBlock != Blocks.chest);
if ((first || chestBlock == ZSSBlocks.chestInvisible) && rand.nextFloat() < Config.getHeartPieceChance()) {
WorldUtils.addItemToInventoryAtRandom(rand, new ItemStack(ZSSItems.heartPiece), chest, 3);
}
if (door != null) {
ItemStack loot = ChestGenHooks.getInfo(DungeonLootLists.BOSS_LOOT).getOneItem(rand);
if (rand.nextFloat() < 0.0625F * (1.0F / Math.max(Config.getBarredRoomChance(), 0.1F))) {
if (door == ZSSBlocks.pegWooden) {
loot = new ItemStack(ZSSItems.gauntletsSilver);
} else if (door == ZSSBlocks.heavyBlock && BlockHeavy.EnumType.byMetadata(doorMeta) == BlockHeavy.EnumType.LIGHT) {
loot = new ItemStack(ZSSItems.hammerSkull);
} else if (door == ZSSBlocks.pegRusty) {
loot = new ItemStack(ZSSItems.gauntletsGolden);
} else if (door == ZSSBlocks.heavyBlock && BlockHeavy.EnumType.byMetadata(doorMeta) == BlockHeavy.EnumType.HEAVY) {
loot = new ItemStack(ZSSItems.hammerMegaton);
} else if (door == ZSSBlocks.timeBlock) {
loot = new ItemStack(ZSSItems.treasure, 1, ItemTreasure.Treasures.ZELDAS_LETTER.ordinal());
} else if (door == ZSSBlocks.quakeStone) {
if ((rand.nextInt() & 1) > 0) { // roughly 50% chance
loot = new ItemStack(ZSSItems.instrument, 1, ItemInstrument.Instrument.OCARINA_TIME.ordinal());
} else {
loot = new ItemStack(ZSSItems.magicContainer);
}
}
}
if (loot != null) {
WorldUtils.addItemToInventoryAtRandom(rand, loot, chest, 3);
}
}
}
// this should set the block underneath the chest as stone if it's surrounded by lava
if (world.getBlockState(pos.down()).getBlock().getMaterial() == Material.lava && bBox.getYSize() > 3) {
world.setBlockState(pos.down(), ZSSBlocks.secretStone.getStateFromMeta(getMetadata()), 2);
}
return true;
} else {
return false;
}
}
/**
* Actually places the door; use after determining door side for best results
*/
private void placeDoor(World world, Random rand) {
Vec3i center = bBox.getCenter();
int x = center.getX();
int y = bBox.minY + (submerged ? 2 : 1);
int z = center.getZ();
switch(face) {
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: // UP and DOWN not possible
}
world.setBlockState(new BlockPos(x, y, z), door.getStateFromMeta(doorMeta), 2);
doorMeta = (door instanceof BlockDoorLocked ? (doorMeta | 8) : doorMeta); // upper part of door
world.setBlockState(new BlockPos(x, y + 1, z), (door instanceof BlockPeg ? Blocks.air.getDefaultState() : door.getStateFromMeta(doorMeta)), 2);
}
/**
* Adds some random breakable jars, if possible
*/
private void placeJars(World world, Random rand) {
int size = bBox.getXSize();
if (size > 4) {
int n = rand.nextInt(size - 3);
for (int i = 0; i < n; ++i) {
int x = bBox.minX + rand.nextInt(size);
int y = 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());
}
}
}
}
}