package zmaster587.advancedRocketry.util;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraftforge.fluids.IFluidBlock;
import zmaster587.advancedRocketry.api.AdvancedRocketryBlocks;
import zmaster587.advancedRocketry.api.atmosphere.IAtmosphereSealHandler;
import zmaster587.libVulpes.util.BlockPosition;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
/**
* Handler for checking if blocks can be used to deal a room.
* <p/>
* Created by Dark(DarkGuardsman, Robert) on 1/6/2016.
*/
public final class SealableBlockHandler implements IAtmosphereSealHandler
{
/** List of blocks not allowed. */
private List<Block> blockBanList = new ArrayList();
/** List of blocks that are allowed regardless of properties. */
private List<Block> blockAllowList = new ArrayList();
/** List of block materials not allowed. */
private List<Material> materialBanList = new ArrayList();
/** List of block materials that are allowed regardless of properties. */
private List<Material> materialAllowList = new ArrayList();
private HashSet<BlockPosition> doorPositions = new HashSet<BlockPosition>();
//TODO add meta support
//TODO add complex logic support threw API interface
//TODO add complex logic handler for integration support
/** INSTANCE */
public static final SealableBlockHandler INSTANCE = new SealableBlockHandler();
private SealableBlockHandler() {}
@Override
public boolean isBlockSealed(World world, int x, int y, int z)
{
return isBlockSealed(world, new BlockPosition(x, y, z));
}
/**
* Checks to see if the block at the location can be sealed
* @param world
* @param pos
* @return
*/
public boolean isBlockSealed(World world, BlockPosition pos)
{
//Ensure we are not checking outside of the map
if(pos.y >= 0 && pos.y <= 256)
{
//Prevents orphan chunk loading - DarkGuardsman
if(world instanceof WorldServer && !((WorldServer) world).theChunkProviderServer.chunkExists(pos.x >> 4, pos.z >> 4))
{
return false;
}
Block block = world.getBlock(pos.x, pos.y, pos.z);
int meta = world.getBlockMetadata(pos.x, pos.y, pos.z);
Material material = block.getMaterial();
//Always allow list
if (blockAllowList.contains(block) || materialAllowList.contains(material))
{
return true;
}
//Always block list
else if (blockBanList.contains(block) || materialBanList.contains(material))
{
return false;
}
else if (material.isLiquid() || !material.isSolid())
{
return false;
}
else if (world.isAirBlock(pos.x, pos.y, pos.z) || block instanceof IFluidBlock)
{
return false;
}
//TODO replace with seal logic handler
else if (block == AdvancedRocketryBlocks.blockAirLock)
{
if(doorPositions.contains(pos))
return true;
doorPositions.add(pos);
boolean doorIsSealed = checkDoorIsSealed(world, pos, meta);
doorPositions.remove(pos);
return doorIsSealed;
}
//TODO add is side solid check, which will require forge direction or side check. Eg more complex logic...
return isFulBlock(world, pos);
}
return false;
}
@Override
public void addUnsealableBlock(Block block)
{
if (!blockBanList.contains(block))
{
blockBanList.add(block);
}
if (blockAllowList.contains(block))
{
blockAllowList.remove(block);
}
}
@Override
public void addSealableBlock(Block block)
{
if (!blockAllowList.contains(block))
{
blockAllowList.add(block);
}
if (blockBanList.contains(block))
{
blockBanList.remove(block);
}
}
/**
* Checks if a block is full sized based off of block bounds. This
* is not a perfect check as mods may have a full size. However,
* have a 3D model that doesn't look a full block in size. There
* is no way around this other than to make a black list check.
*
* @param world - world
* @param pos - location
* @return true if full block
*/
public static boolean isFulBlock(World world, BlockPosition pos)
{
return isFulBlock(world, world.getBlock(pos.x, pos.y, pos.z), pos);
}
/**
* Checks if a block is full sized based off of collision bounds. This
* is not a perfect check as some mods may have blocks that can be walked through.
* But should be air-tight like forcefields
*
* @param block - block to compare
* @return true if full block
*/
public static boolean isFulBlock(World world, Block block, BlockPosition pos)
{
AxisAlignedBB bb = block.getCollisionBoundingBoxFromPool(world, pos.x, pos.y, pos.z);
if(bb == null)
return false;
//size * 100 to correct rounding errors
int minX = (int) ((bb.minX - pos.x) * 100);
int minY = (int) ((bb.minY - pos.y) * 100);
int minZ = (int) ((bb.minZ - pos.z) * 100);
int maxX = (int) ((bb.maxX - pos.x) * 100);
int maxY = (int) ((bb.maxY - pos.y) * 100);
int maxZ = (int) ((bb.maxZ - pos.z) * 100);
return minX == 0 && minY == 0 && minZ == 0 && maxX == 100 && maxY == 100 && maxZ == 100;
}
//TODO unit test, document, cleanup
private boolean checkDoorIsSealed(World world, BlockPosition pos, int meta)
{
//TODO: door corners
return ((meta & 8) == 8
||
((meta & 4) >> 2 == (meta & 1) && checkDoorSeal(world, pos.getPositionAtOffset(0, 0, 1), meta)
&& checkDoorSeal(world, pos.getPositionAtOffset(0, 0, -1), meta))
||
(meta & 4) >> 2 != (meta & 1) && checkDoorSeal(world, pos.getPositionAtOffset(1, 0, 0), meta)
&& checkDoorSeal(world, pos.getPositionAtOffset(-1, 0, 0), meta));
}
//TODO unit test, document, cleanup
private boolean checkDoorSeal(World world, BlockPosition pos, int meta)
{
Block otherBlock = world.getBlock(pos.x, pos.y, pos.z);
int otherMeta = world.getBlockMetadata(pos.x, pos.y, pos.z);
return (otherBlock == AdvancedRocketryBlocks.blockAirLock && (otherMeta & 1) == (meta & 1)) ||
(otherBlock != AdvancedRocketryBlocks.blockAirLock && isBlockSealed(world, pos));
}
/**
* Checks if the block is banned from being a seal
*
* @param block - checked block
* @return true if it is banned, based on ban list only.
*/
public boolean isBlockBanned(Block block)
{
return blockBanList.contains(block);
}
/**
* Checks if the material is banned from being a seal
*
* @param mat - material being checked
* @return true if it is banned, based on ban list only.
*/
public boolean isMaterialBanned(Material mat)
{
return materialBanList.contains(mat);
}
/**
* Loads defaults..
*/
public void loadDefaultData()
{
materialBanList.add(Material.air);
materialBanList.add(Material.cactus);
materialBanList.add(Material.craftedSnow);
materialBanList.add(Material.fire);
materialBanList.add(Material.leaves);
materialBanList.add(Material.portal);
materialBanList.add(Material.vine);
materialBanList.add(Material.plants);
materialBanList.add(Material.coral);
materialBanList.add(Material.web);
materialBanList.add(Material.sponge);
materialBanList.add(Material.sand);
//TODO check each vanilla block
}
}