@EventBusSubscriber(modid=SecretRooms5.MODID)
public class SecretBlocks 
{
	
	public static final Block GHOST_BLOCK = new GhostBlock();
	public static final Block FAKE_STAIRS = new SecretStairs();
	public static final Block FAKE_LEVER = new SecretLever();
	public static final Block FAKE_REDSTONE = new SecretRedstone();
	public static final Block ONE_WAY_GLASS = new OneWayGlass();
	public static final Block SECRET_WOODEN_BUTTON = new SecretButton(true);
	public static final Block SECRET_STONE_BUTTON = new SecretButton(false);
	public static final Block TORCH_LEVER = new TorchLever();
	public static final Block SECRET_PRESSURE_PLATE = new SecretPressurePlate();
	public static final Block SECRET_LIGHT_PRESSURE_PLATE = new SecretWeightedPressurePlate("secret_light_pressure_plate", 15);
	public static final Block SECRET_HEAVY_PRESSURE_PLATE = new SecretWeightedPressurePlate("secret_heavy_pressure_plate", 150);
	public static final Block SECRET_PLAYER_PRESSURE_PLATE = new SecretPlayerPressurePlate();
	public static final Block SOLID_AIR = new SolidAir();
	public static final Block SECRET_CHEST = new SecretChest("secret_chest");
	public static final Block SECRET_WOODEN_DOOR = SecretCompatibility.MALISISDOORS ? SecretCompactMalisisDoors.WOODEN_DOOR : new BaseBlockDoor("secret_wooden_door", Material.WOOD);
	public static final Block SECRET_IRON_DOOR = SecretCompatibility.MALISISDOORS ? SecretCompactMalisisDoors.IRON_DOOR : new BaseBlockDoor("secret_iron_door", Material.IRON);
	public static final Block SECRET_WOODEN_TRAPDOOR = SecretCompatibility.MALISISDOORS ? SecretCompactMalisisDoors.WOODEN_TRAPDOOR : new SecretTrapDoor("secret_wooden_trapdoor", Material.WOOD);
	public static final Block SECRET_IRON_TRAPDOOR = SecretCompatibility.MALISISDOORS ? SecretCompactMalisisDoors.IRON_TRAPDOOR : new SecretTrapDoor("secret_iron_trapdoor", Material.IRON);
	public static final Block SECRET_DISPENSER = new SecretDispenser();
	public static final Block SECRET_TRAPPED_CHEST = new SecretTrappedChest();
	public static final Block SECRET_GATE_BLOCK = new SecretGateEmptyBlock();
	public static final Block SECRET_GATE = new SecretGate();
	public static final SecretLightDetector SECRET_LIGHT_DETECTOR = new SecretLightDetector(false);
	public static final SecretLightDetector SECRET_LIGHT_DETECTOR_INVERTED = new SecretLightDetector(true);

	private static IForgeRegistry<Block> blockRegistry;
	
	@SubscribeEvent
	public static void preInit(RegistryEvent.Register<Block> event)
	{
		blockRegistry = event.getRegistry();
		regBlock(GHOST_BLOCK);
		regBlockIgnoreAll(FAKE_STAIRS);
		regBlockIgnoreAll(FAKE_LEVER);
		regBlockIgnoreAll(FAKE_REDSTONE);
		regBlockIgnoreAll(ONE_WAY_GLASS);
		regBlockIgnoreAll(SECRET_WOODEN_BUTTON);
		regBlockIgnoreAll(SECRET_STONE_BUTTON);
		regBlock(TORCH_LEVER, 64, TorchLever.POWERED);
		regBlockIgnoreAll(SECRET_PRESSURE_PLATE);
		regBlockIgnoreAll(SECRET_LIGHT_PRESSURE_PLATE);
		regBlockIgnoreAll(SECRET_HEAVY_PRESSURE_PLATE);
		regBlockIgnoreAll(SECRET_PLAYER_PRESSURE_PLATE);
		regBlock(SOLID_AIR);
		regBlock(SECRET_CHEST);
		regSingleBlockIgnoreAll(SECRET_WOODEN_DOOR);
		regSingleBlockIgnoreAll(SECRET_IRON_DOOR);
		regBlockIgnoreAll(SECRET_WOODEN_TRAPDOOR);
		regBlockIgnoreAll(SECRET_IRON_TRAPDOOR);
		regBlockIgnoreAll(SECRET_DISPENSER);
		regBlock(SECRET_TRAPPED_CHEST);
		regSingleBlock(SECRET_GATE_BLOCK);
		regBlockIgnoreAll(SECRET_GATE);
		regBlockIgnoreAll(SECRET_LIGHT_DETECTOR);
		regSingleBlockIgnoreAll(SECRET_LIGHT_DETECTOR_INVERTED);
	}
	
	private final static ArrayList<Block> BLOCKS_WITH_ITEMS = new ArrayList<Block>();
	private final static HashMap<Block, Integer> BLOCK_STACK_SIZES = new HashMap<>();
	private final static ArrayList<Block> BLOCKS_WITH_CUSTOM_STATE_MAP = new ArrayList<Block>();
	private final static ArrayList<IProperty<?>[]> PROPERTIES_TO_IGNORE_CUSTOM_STATE_MAP = new ArrayList<IProperty<?>[]>();
	
	@SubscribeEvent
	public static void regRenders(ModelRegistryEvent event) {
		for(int i = 0; i < BLOCKS_WITH_CUSTOM_STATE_MAP.size(); i++)
			createStateMappers(BLOCKS_WITH_CUSTOM_STATE_MAP.get(i), PROPERTIES_TO_IGNORE_CUSTOM_STATE_MAP.get(i));
		for(Block b : BLOCKS_WITH_ITEMS)
			regRender(b);
	}

	private static void regBlock(Block block) 
	{
		regBlock(block, 64);
	}

	private static void regBlock(Block block, int stackSize) 
	{
		BLOCKS_WITH_ITEMS.add(block);
		BLOCK_STACK_SIZES.put(block, stackSize);	
		block.setCreativeTab(SecretRooms5.TAB);
		register(block);
	}
	
	private static void regBlockIgnoreAll(Block block)
	{
		regBlockIgnoreAll(block, 64);
	}
	
	private static void regBlockIgnoreAll(Block block, int stackSize)
	{
		regBlock(block, stackSize, block.getDefaultState().getProperties().asMultimap().asMap().keySet().toArray(new IProperty[block.getDefaultState().getProperties().asMultimap().asMap().keySet().size()]));
	}
	
	private static void regSingleBlockIgnoreAll(Block block)
	{
		regSingleBlock(block, block.getDefaultState().getProperties().asMultimap().asMap().keySet().toArray(new IProperty[block.getDefaultState().getProperties().asMultimap().asMap().keySet().size()]));
	}
	
	private static void regBlock(Block block, int stackSize, IProperty<?>... toIgnore)
	{
		BLOCKS_WITH_CUSTOM_STATE_MAP.add(block);
		PROPERTIES_TO_IGNORE_CUSTOM_STATE_MAP.add(toIgnore);
		regBlock(block, stackSize);
	}
	
	@SideOnly(Side.CLIENT)
	private static void createStateMappers(Block block, IProperty<?>[] toIgnore)
	{
		ModelLoader.setCustomStateMapper(block, (new StateMap.Builder().ignore(toIgnore)).build());
	}
	
	private static void regSingleBlock(Block block)
	{
		register(block);
	}
	
	private static void regSingleBlock(Block block,  IProperty<?>... toIgnore)
	{
		BLOCKS_WITH_CUSTOM_STATE_MAP.add(block);
		PROPERTIES_TO_IGNORE_CUSTOM_STATE_MAP.add(toIgnore);
		register(block);
	}

	private static void regRender(Block block) {
		ModelLoader.setCustomModelResourceLocation(Item.getItemFromBlock(block), 0, new ModelResourceLocation(block.getRegistryName(), "inventory"));
	}
	
	private static void register(Block block)
	{
		blockRegistry.register(block);
		if(BLOCKS_WITH_ITEMS.contains(block))
		{
			ItemBlock item = new SecretItemBlock(block);
			item.setRegistryName(block.getRegistryName());
			item.setMaxStackSize(BLOCK_STACK_SIZES.get(block));
			ForgeRegistries.ITEMS.register(item);
		}
	}
} @Mod(modid = SecretRooms5.MODID, name = SecretRooms5.MODNAME, version = SecretRooms5.VERSION, acceptedMinecraftVersions = SecretRooms5.MCVERSION, dependencies = SecretRooms5.DEPENDENCIES,updateJSON = SecretRooms5.UPDATE_URL)
public class SecretRooms5
{
    public static final String MODID = "secretroomsmod";
    public static final String MODNAME = "Secret Rooms 5";
    public static final String VERSION = "5.6.4";
	public static final String MCVERSION = "[1.12.2,1.13]";
	public static final String DEPENDENCIES = "required-after:srm-hooks;required-after:forge@[14.23.0.2502,);";
    public static final String UPDATE_URL = "http://www.wynprice.com/update_jsons/secretroomsmod.json";
    
    @SidedProxy(modId = MODID, clientSide = "com.wynprice.secretroomsmod.proxy.ClientProxy", serverSide = "com.wynprice.secretroomsmod.proxy.ServerProxy")
    public static CommonProxy proxy;
    
    @Instance(MODID)
    public static SecretRooms5 instance;
    public static final CreativeTabs TAB = new CreativeTabs(MODID) {
		
		@Override
		public ItemStack getTabIconItem() {
			return new ItemStack(SecretItems.CAMOUFLAGE_PASTE);
		}
	};
	
	public static final Logger LOGGER = LogManager.getLogger(MODID);
	
    @EventHandler
	public void preInit(FMLPreInitializationEvent event) {
		proxy.preInit(event);
	}
    
    @EventHandler
    public void init(FMLInitializationEvent event) {
    	proxy.init(event);
    }
    
    @EventHandler
    public void postInit(FMLPostInitializationEvent event) {
    	proxy.postInit(event);
    }
    
    @EventHandler
    public void onServerLoaded(FMLServerStartingEvent event) {
    	event.registerServerCommand(new CommandBase() {@Override public int getRequiredPermissionLevel(){return 2;}@Override public String getUsage(ICommandSender sender) {return "Resets the energized blocks";}@Override public String getName() {return "resetenergized";}@Override public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException {EnergizedPasteHandler.getEnergized_map().clear();SecretNetwork.sendToAll(new MessagePacketSyncEnergizedPaste(EnergizedPasteHandler.saveToNBT(), null));}});
    }
} public interface ISecretBlock extends ITileEntityProvider
{
	
	/**
	 * The unlisted property used to store the RenderState
	 */
	IUnlistedProperty<IBlockState> RENDER_PROPERTY = new RenderStateUnlistedProperty();
	
	/**
	 * Used to get the renderState from the World and the position
	 * @param world the world the blocks in
	 * @param pos the position of the block
	 * @return the rendered state of the block, or null if there is none
	 */
	default public IBlockState getState(IBlockAccess world, BlockPos pos)
	{
		return world.getTileEntity(pos) instanceof ISecretTileEntity ? ISecretTileEntity.getMirrorState(world, pos) : Blocks.STONE.getDefaultState();
	}
	
	/**
	 * Gets the world from the TileEntity at the position
	 * @param access The world 
	 * @param pos the position of the tilentity
	 * @return The world, or null if it cant be found
	 */
	default public World getWorld(IBlockAccess access, BlockPos pos) {
		return access.getTileEntity(pos) != null ? access.getTileEntity(pos).getWorld() : null;
	}
	
	/**
	 * Used to force the block render state to the {@link ISecretTileEntity}, if it exists.
	 * @param world the world the SRM blocks in
	 * @param tePos the position of the block 
	 * @param state the state of which to force the block to
	 */
	default public void forceBlockState(World world, BlockPos tePos, IBlockState state)
	{
		if(world.getTileEntity(tePos) instanceof ISecretTileEntity)
			((ISecretTileEntity)world.getTileEntity(tePos)).setMirrorStateForcable(state);
	}
	
	@Override
	default TileEntity createNewTileEntity(World worldIn, int meta) {
		return new TileEntityInfomationHolder();
	}

	/**
	 * Used so individual blocks can change the BlockModel for rendering. Used for things like Glass and Doors to make them different
	 * @param model the original model, will be returned if the Block dosn't need different visuals
	 * @return the model that will be rendered
	 */
	@SideOnly(Side.CLIENT)
	default FakeBlockModel phaseModel(FakeBlockModel model) {
		return model;
	}
	
	/**
	 * Used to get the class from {@link #phaseModel(FakeBlockModel)}
	 * @return the class that {@link #phaseModel(FakeBlockModel)} uses, with the default being {@link FakeBlockModel}
	 */
	@SideOnly(Side.CLIENT)
	default Class<? extends FakeBlockModel> getModelClass() {
		return phaseModel(new FakeBlockModel(Blocks.STONE.getDefaultState())).getClass();
	}
	
	@SideOnly(Side.CLIENT)
	default boolean isRenderTransparent() {
		return getModelClass() != FakeBlockModel.class;
	}
	
	/**
	 * Used so individual blocks can change the BlockModel for rendering, when the Helmet of True Sight is on.
	 * @param model the original model, will be returned if the Block dosn't need different visuals
	 * @return the model that will be rendered
	 */
	@SideOnly(Side.CLIENT)
	default TrueSightModel phaseTrueModel(TrueSightModel model) {
		return model;
	}
	
	/**
	 * Used as an override to SRM blocks. Used to run {@code Block#canCreatureSpawn(IBlockState, IBlockAccess, BlockPos, SpawnPlacementType)} on Mirrored states
	 * @param state The current state
     * @param world The current world
     * @param pos Block position in world
     * @param type The Mob Category Type
     * @return True to allow a mob of the specified category to spawn, false to prevent it.
	 */
	default boolean canCreatureSpawn(IBlockState state, IBlockAccess world, BlockPos pos, SpawnPlacementType type) {
		return getState(world, pos).getBlock().canCreatureSpawn(getState(world, pos), new FakeBlockAccess(world), pos, type);
	}
	
	/**
	 * Used as an override to SRM blocks. Used to run {@link Block#canHarvestBlock(IBlockAccess, BlockPos, EntityPlayer)} on mirrored states
	 * @param world The world
     * @param player The player damaging the block
     * @param pos The block's current position
     * @return True to spawn the drops
     */
	default boolean canHarvestBlock(IBlockAccess world, BlockPos pos, EntityPlayer player) {
		return getState(world, pos).getBlock().canHarvestBlock(new FakeBlockAccess(world), pos, player);
	}
	
	/**
	 * Used as an override to SRM blocks. Used to attempt to get the Mirrored State bounding box
	 * @param state The input state. not needed what so ever. <b> Exists because {@link Block#getBoundingBox(IBlockState, IBlockAccess, BlockPos)} has the {@link IBlockState} as a field. </b>
	 * @param source the world
	 * @param pos the position
	 * @return the bounding box of the Mirrored State, or {@link Block#FULL_BLOCK_AABB} if does there is no tileEntity at the position.
	 */
	default AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos) {
		if(source.getTileEntity(pos) instanceof ISecretTileEntity && ((ISecretTileEntity)source.getTileEntity(pos)).getMirrorStateSafely() != null)
			return ((ISecretTileEntity)source.getTileEntity(pos)).getMirrorStateSafely().getBoundingBox(new FakeBlockAccess(source), pos);
		return Block.FULL_BLOCK_AABB;
	}
	
	/**
	 * Used as an override to SRM blocks. Used to run {@link Block#canBeConnectedTo(IBlockAccess, BlockPos, EnumFacing)} on the MirrorState
     * @param world The current world
     * @param pos The position of the block
     * @param facing The side the connecting block is on
     * @return True to allow another block to connect to this block
     */
	default boolean canBeConnectedTo(IBlockAccess world, BlockPos pos, EnumFacing facing) {
		return getState(world, pos).getBlock().canBeConnectedTo(new FakeBlockAccess(world), pos, facing);
	}
	
	/**
	 * Used as an override to SRM blocks. Used to run {@link Block#getBlockFaceShape(IBlockAccess, IBlockState, BlockPos, EnumFacing)} on Mirror State.
	 * @param worldIn The current world
	 * @param state The {@link IBlockState} of the current position
	 * @param pos The position of the block
	 * @param face The side thats being checked
	 * @return an approximation of the form of the given face.
	 */
	default BlockFaceShape getBlockFaceShape(IBlockAccess worldIn, IBlockState state, BlockPos pos, EnumFacing face) {
		return getState(worldIn, pos).getBlockFaceShape(new FakeBlockAccess(worldIn), pos, face);
	}
	
	/**
	 * Used as an override to SRM blocks. Used to run {@link Block#getBlockHardness(IBlockState, World, BlockPos)} on the Mirrored state
	 * @param worldIn The current world
	 * @param blockState The {@link IBlockState} of the current position
	 * @param pos The position of the block
	 * @return the hardness of the block
	 */
	default float getBlockHardness(IBlockState blockState, World worldIn, BlockPos pos) {
		return ISecretTileEntity.getMirrorState(worldIn, pos).getBlockHardness(worldIn, pos);
	}
	
	/**
	 * Used as an override for SRM blocks. Used to run {@link #canConnectRedstone(IBlockState, IBlockAccess, BlockPos, EnumFacing)} on the mirrored state
     * @param state The current state
     * @param world The current world
     * @param pos Block position in world
     * @param side The side that is trying to make the connection, CAN BE NULL
     * @return True to make the connection
     */
	default public boolean canConnectRedstone(IBlockState state, IBlockAccess world, BlockPos pos, EnumFacing side) {
		return getState(world, pos).getBlock().canConnectRedstone(getState(world, pos), new FakeBlockAccess(world), pos, side);
	}
	
	/**
	 * Used as an override for SRM blocks. Used to run {@link Block#getSlipperiness(IBlockState, IBlockAccess, BlockPos, Entity)} on the mirrored state
     * @param state state of the block
     * @param world the world
     * @param pos the position in the world
     * @param entity the entity in question
     * @return the factor by which the entity's motion should be multiplied
	 */
	default public float getSlipperiness(IBlockState state, IBlockAccess world, BlockPos pos, Entity entity) {
		return getState(world, pos).getBlock().getSlipperiness(getState(world, pos), new FakeBlockAccess(world), pos, entity);
	}
	
	/**
	 * Used as an override for SRM block. Used to run {@link Block#canPlaceTorchOnTop(IBlockState, IBlockAccess, BlockPos)}	on the mirrored state
     * @param state The current state
     * @param world The current world
     * @param pos Block position in world
     * @return True to allow the torch to be placed
     */
	default public boolean canPlaceTorchOnTop(IBlockState state, IBlockAccess world, BlockPos pos) {
		return getState(world, pos).getBlock().canPlaceTorchOnTop(getState(world, pos), new FakeBlockAccess(world), pos);
	}
	
	/**
	 * Used as an override for SRM blocks. Used to run {@link Block#collisionRayTrace(IBlockState, World, BlockPos, Vec3d, Vec3d)} on the mirrored state
	 * @param blockState The current state
	 * @param worldIn the current world
	 * @param pos the current position
	 * @param start the start of the raytrace
	 * @param end the end of the raytrace
	 * @return the raytrace to use
	 */
	default public RayTraceResult collisionRayTrace(IBlockState blockState, World worldIn, BlockPos pos, Vec3d start,
			Vec3d end) {
		return getState(worldIn, pos).collisionRayTrace(worldIn, pos, start, end);
	}
	
	/**
	 * A list of all ISecretTileEntities. Used to get Material and things like that
	 */
	public static final ArrayList<TileEntity> ALL_SECRET_TILE_ENTITIES = new ArrayList<>();
	
	/**
	 * Used as an override to SRM blocks. Used to run {@link Block#isSideSolid(IBlockState, IBlockAccess, BlockPos, EnumFacing)} on the mirrored state
	 * @param base_state The base state, getActualState should be called first
     * @param world The current world
     * @param pos Block position in world
     * @param side The side to check
     * @return True if the mirrored state is solid on the specified side.
	 */
	default boolean isSideSolid(IBlockState base_state, IBlockAccess world, BlockPos pos, EnumFacing side) {
		return getState(world, pos).isSideSolid(new FakeBlockAccess(world), pos, side);
	}
	
	/**
	 * Used as an override for SRM blocks. Used to run {@link Block#addCollisionBoxToList(IBlockState, World, BlockPos, AxisAlignedBB, List, Entity, boolean)} on the mirrored state
	 * @param state The current BlockState
	 * @param worldIn The current World
	 * @param pos The current BlockPos
	 * @param entityBox The colliding Entities bounding box
	 * @param collidingBoxes The list of bounding boxes
	 * @param entityIn The Entity Colliding with the block
	 * @param isActualState Is the {@code state} the actual state
	 */
	default void addCollisionBoxToList(IBlockState state, World worldIn, BlockPos pos, AxisAlignedBB entityBox,
			List<AxisAlignedBB> collidingBoxes, Entity entityIn, boolean isActualState) 
	{
		if(worldIn.getTileEntity(pos) instanceof ISecretTileEntity && ISecretTileEntity.getMirrorState(worldIn, pos) != null)
			ISecretTileEntity.getMirrorState(worldIn, pos).addCollisionBoxToList(worldIn, pos, entityBox, collidingBoxes, entityIn, isActualState);
		else
			Blocks.STONE.addCollisionBoxToList(state, worldIn, pos, entityBox, collidingBoxes, entityIn, isActualState);
	}
	
	/**
	 * Used as an override for SRM blocks. Used to run {@link Block#getSoundType(IBlockState, World, BlockPos, Entity)} on the mirrored state
     * @param state The state
     * @param world The world
     * @param pos The position. Note that the world may not necessarily have {@code state} here!
     * @param entity The entity that is breaking/stepping on/placing/hitting/falling on this block, or null if no entity is in this context
     * @return A SoundType to use
     */
	default SoundType getSoundType(IBlockState state, World world, BlockPos pos, Entity entity) 
	{
		return world.getTileEntity(pos) instanceof ISecretTileEntity && ISecretTileEntity.getMirrorState(world, pos) != null ? 
				ISecretTileEntity.getMirrorState(world, pos).getBlock().getSoundType() : SoundType.STONE;
	}
	
	
	/**
	 * Used to override the opacity of a block. Used to call {@link Block#getLightOpacity(IBlockState)} on the mirrored state
	 * @param state the state
	 * @param world the world
	 * @param pos the position
	 * @return the opacity
	 */
	default int getLightOpacity(IBlockState state, IBlockAccess world, BlockPos pos) {
		return SecretConfig.BLOCK_FUNCTIONALITY.copyLight ? ISecretTileEntity.getMirrorState(world, pos).getLightOpacity(new FakeBlockAccess(world), pos) : 255; //Dont use getState as the tileEntity may be null
	}
	
	/**
	 * Used to override the light of a block. Used to call {@link Block#getLightValue(IBlockState, IBlockAccess, BlockPos)} on the mirrored state
	 * @param state the state
	 * @param world the world
	 * @param pos the position
	 * @return the lightvalue
	 */
	default int getLightValue(IBlockState state, IBlockAccess world, BlockPos pos) {
		return SecretConfig.BLOCK_FUNCTIONALITY.copyLight ? ISecretTileEntity.getMirrorState(world, pos).getLightValue(new FakeBlockAccess(world), pos) : 0; //Dont use getState as the tileEntity may be null
	}
	
	/**
	 * Used as an override of SRM blocks. Runs {@link Block#isOpaqueCube(IBlockState)} on the mirrored state, which it got using the {@link #RENDER_PROPERTY}
	 * @param state the current state
	 * @return if the mirrored state is a opaque cube, or fale if {@code state} dosnt contain the value for {@link #RENDER_PROPERTY}
	 */
	@SideOnly(Side.CLIENT)
	default public boolean isOpaqueCube(IBlockState state)
    {
    	if(state instanceof IExtendedBlockState && ((IExtendedBlockState)state).getValue(RENDER_PROPERTY) != null) {
    		return ((IExtendedBlockState)state).getValue(RENDER_PROPERTY).isOpaqueCube();
    	}
        return false;
    }
	
	/**
	 * Used as an override of SRM blocks. Runs {@link Block#getAmbientOcclusionLightValue(IBlockState)} on the mirrored state, which it got using the {@link #RENDER_PROPERTY}
	 * @param state the current state
	 * @return the mirrored states ambient occulstion value, or 1.0F if {@code state} dosnt contain the value for {@link #RENDER_PROPERTY}
	 */
	@SideOnly(Side.CLIENT)
	default public float getAmbientOcclusionLightValue(IBlockState state)
    {
    	if(state instanceof IExtendedBlockState && ((IExtendedBlockState)state).getValue(RENDER_PROPERTY) != null) {
    		return ((IExtendedBlockState)state).getValue(RENDER_PROPERTY).getAmbientOcclusionLightValue();
    	}
        return 1.0F;
    }
	
	/**
	 * Used as an override for SRM blocks. Runs {@link Block#getPackedLightmapCoords(IBlockState, IBlockAccess, BlockPos)} on mirrored state
	 * @param state the current state
	 * @param source the current world
	 * @param pos the current blockpos
	 * @return the packed light coords on the mirrored state
	 */
	default public int getPackedLightmapCoords(IBlockState state, IBlockAccess source, BlockPos pos) {
    	return getState(source, pos).getBlock().getPackedLightmapCoords(getState(source, pos), new FakeBlockAccess(source), pos);
    }
	
	/**
	 * Used to override the vanilla hit effects of a block. Called from SRM blocks ({@link Block#addHitEffects(IBlockState, World, RayTraceResult, ParticleManager)})
	 * @param state The current BlockState
	 * @param worldObj The current world
	 * @param target The rayTrace used to get the Hit Effects
	 * @param manager The ParticleManager in use
	 * @return true if the blocks position has an SRM block, meaning that the hit effects are overridden
	 */
	@SideOnly(Side.CLIENT)
	default boolean addHitEffects(IBlockState state, World worldObj, RayTraceResult target, ParticleManager manager) 
	{
		if (target.getBlockPos() != null && worldObj.getTileEntity(target.getBlockPos()) instanceof ISecretTileEntity && 
				((ISecretTileEntity)worldObj.getTileEntity(target.getBlockPos())).getMirrorStateSafely() != null)
        {
            int i = target.getBlockPos().getX();
            int j = target.getBlockPos().getY();
            int k = target.getBlockPos().getZ();
            float f = 0.1F;
            AxisAlignedBB axisalignedbb = ((ISecretTileEntity)worldObj.getTileEntity(target.getBlockPos())).getMirrorStateSafely().getBoundingBox(worldObj, target.getBlockPos());
            double d0 = (double)i + new Random().nextDouble() * (axisalignedbb.maxX - axisalignedbb.minX - 0.20000000298023224D) + 0.10000000149011612D + axisalignedbb.minX;
            double d1 = (double)j + new Random().nextDouble() * (axisalignedbb.maxY - axisalignedbb.minY - 0.20000000298023224D) + 0.10000000149011612D + axisalignedbb.minY;
            double d2 = (double)k + new Random().nextDouble() * (axisalignedbb.maxZ - axisalignedbb.minZ - 0.20000000298023224D) + 0.10000000149011612D + axisalignedbb.minZ;
            if (target.sideHit == EnumFacing.DOWN) d1 = (double)j + axisalignedbb.minY - 0.10000000149011612D;
            if (target.sideHit == EnumFacing.UP) d1 = (double)j + axisalignedbb.maxY + 0.10000000149011612D;
            if (target.sideHit == EnumFacing.NORTH) d2 = (double)k + axisalignedbb.minZ - 0.10000000149011612D;
            if (target.sideHit == EnumFacing.SOUTH) d2 = (double)k + axisalignedbb.maxZ + 0.10000000149011612D;
            if (target.sideHit == EnumFacing.WEST) d0 = (double)i + axisalignedbb.minX - 0.10000000149011612D;
            if (target.sideHit == EnumFacing.EAST) d0 = (double)i + axisalignedbb.maxX + 0.10000000149011612D;
            manager.addEffect(((net.minecraft.client.particle.ParticleDigging)new net.minecraft.client.particle.ParticleDigging.Factory()
            		.createParticle(0, worldObj, d0, d1, d2, 0, 0, 0, 
            				Block.getStateId(((ISecretTileEntity)worldObj.getTileEntity(target.getBlockPos())).getMirrorStateSafely())))
            		.setBlockPos(target.getBlockPos()).multiplyVelocity(0.2F).multipleParticleScaleBy(0.6F));
            return true;
        }
		return false;
	}
	
	/**
	 * Used to override the vanilla destroy effects of a block. Called from SRM blocks ({@link Block#addDestroyEffects(World, BlockPos, ParticleManager)})
	 * @param world The current world 
	 * @param pos The current BlockPos
	 * @param manager The particle manager in used
	 * @return true if the blocks position has an SRM block, meaning that the destroy effects are overridden
	 */
	@SideOnly(Side.CLIENT)
	default boolean addDestroyEffects(World world, BlockPos pos, ParticleManager manager) 
	{
		if(ParticleHandler.BLOCKBRAKERENDERMAP.get(pos) != null)
		{
			IBlockState state = ParticleHandler.BLOCKBRAKERENDERMAP.get(pos);
			try
			{
				state = state.getActualState(world, pos);
			}
			catch (Exception e) 
			{
				;
			}
	        int i = 4;

	        for (int j = 0; j < 4; ++j)
	        {
	            for (int k = 0; k < 4; ++k)
	            {
	                for (int l = 0; l < 4; ++l)
	                {
	                    double d0 = ((double)j + 0.5D) / 4.0D;
	                    double d1 = ((double)k + 0.5D) / 4.0D;
	                    double d2 = ((double)l + 0.5D) / 4.0D;
	                    manager.addEffect(((net.minecraft.client.particle.ParticleDigging)new net.minecraft.client.particle.ParticleDigging.Factory()
	                    		.createParticle(0, world, (double)pos.getX() + d0, (double)pos.getY() + d1, (double)pos.getZ() + d2,
	                    				d0 - 0.5D, d1 - 0.5D, d2 - 0.5D, Block.getStateId(state))).setBlockPos(pos));
	                }
	            }
	        }
		}
		return false;
	}
	
	/**
	 * An override for SRM blocks. Used to spawn the correct running particles
     * @param state The BlockState the entity is running on.
     * @param world The world.
     * @param blockpos The position at the entities feet.
     * @param entity The entity running on the block.
     * @return True to prevent vanilla running particles from spawning.
	 */
	default public boolean addRunningEffects(IBlockState state, World world, BlockPos blockpos, Entity entity) {
        IBlockState mirrorState = getState(world, blockpos);
		boolean overriden;
		try {
			overriden = mirrorState.getBlock().addRunningEffects(mirrorState, world, blockpos, entity);
		} catch (Exception e) {
			overriden = false;
		}
        if (!overriden && mirrorState.getRenderType() != EnumBlockRenderType.INVISIBLE) {
        	Random rand = new Random();
            world.spawnParticle(EnumParticleTypes.BLOCK_CRACK, entity.posX + ((double)rand.nextFloat() - 0.5D) * (double)entity.width, entity.getEntityBoundingBox().minY + 0.1D, entity.posZ + ((double)rand.nextFloat() - 0.5D) * (double)entity.width, -entity.motionX * 4.0D, 1.5D, -entity.motionZ * 4.0D, Block.getStateId(mirrorState));
        }
        return true;
	}
	
	/**
	 * An override for SRM blocks. Used to spawn the correct particles when the player lands on the block
	 * @param state The blockstate being landed on
	 * @param world the world
	 * @param blockPosition the position of the block thats being landed on
	 * @param iblockstate the blockstate of the block thats being landed on
	 * @param entity the entity landing on the block
	 * @param numberOfParticles the amount of particles to spawn
	 * @return true to prevent vanilla landing particles from spawning.
	 */
	default public boolean addLandingEffects(IBlockState state, WorldServer world, BlockPos blockPosition,
			IBlockState iblockstate, EntityLivingBase entity, int numberOfParticles) {
		IBlockState mirrorState = getState(world, blockPosition);
		boolean overriden;
		try {
			overriden = mirrorState.getBlock().addLandingEffects(mirrorState, world, blockPosition, iblockstate, entity, numberOfParticles);
		} catch (Exception e) {
			overriden = false;
		}
		if(!overriden) {
			world.spawnParticle(EnumParticleTypes.BLOCK_DUST, entity.posX, entity.posY, entity.posZ, numberOfParticles, 0.0D, 0.0D, 0.0D, 0.15000000596046448D, Block.getStateId(mirrorState));
		}
		return true;
	}
	
	/**
	 * A cache of the mirrored state, stored when {@link #getActualState(IBlockState, IBlockAccess, BlockPos, IBlockState)} is called. 
	 * <br> Used to determine the correct harvest tool
	 */
	ThreadLocal<IBlockState> cachedMirrorState = ThreadLocal.withInitial(() -> Blocks.STONE.getDefaultState()); //Probally shouldnt use a ThreadLocal, but theres no harm in doing so
	
	/**
	 * Called from SRM blocks ({@link Block#getActualState(IBlockState, IBlockAccess, BlockPos)})
	 * Used to remove the {@link IUnlistedProperty} from the blockstate ({@link ISecretBlock#RENDER_PROPERTY}).
	 * Also sets the {@link #cachedMirrorState} to the mirrored state 
	 * @param state The current blockstate. Can be null, not used
	 * @param worldIn The current world. Can be null, not used
	 * @param pos The current BlockPos. Can be null, not used
	 * @param superState The state used from calling {@code super.getActualState(IBlockState, IBlockAccess, BlockPos)} in SRM block classes
	 * @return the IBlockState, with its {@link ISecretBlock#RENDER_PROPERTY} stripped from it
	 */
	default public IBlockState getActualState(IBlockState state, IBlockAccess worldIn, BlockPos pos, IBlockState superState)
	{
		cachedMirrorState.set(getState(worldIn, pos));
		if(superState instanceof IExtendedBlockState)
			return ((IExtendedBlockState)superState).withProperty(RENDER_PROPERTY, null);//Make sure the unlisted property is removed for 
		return superState;
	}
	
	/**
	 * Used as an override to SRM blocks. Returns -1, so all tools can break this block.
	 * @param state the state
	 * @return -1
	 */
	default public int getHarvestLevel(IBlockState state) {
		return -1;
	}
	
	/**
	 * Used as an override to SRM blocks. Used to run {@link Block#getHarvestTool(IBlockState)} on the mirrored state
	 * @param state the state
	 * @return the tool used to harvest the mirrored block
	 */
	default public String getHarvestTool(IBlockState state) {
		return cachedMirrorState.get().getBlock().getHarvestTool(cachedMirrorState.get());
	}
	
	/**
	 * Used as an override to SRM blocks. Used to run {@link Block#canRenderInLayer(IBlockState, BlockRenderLayer)} on the mirrored state
	 * @param state The current IBlockState
	 * @param layer the layer used
	 * @return if the block can render in the mirrored state, or if the layer is {@link BlockRenderLayer#SOLID}, if it does not exist
	 */
	@SideOnly(Side.CLIENT)
	default public boolean canRenderInLayer(IBlockState state, BlockRenderLayer layer) 
	{
		if(SecretBlockModel.instance().SRMBLOCK.get() instanceof IExtendedBlockState)
		{
			IBlockState renderState = ((IExtendedBlockState)SecretBlockModel.instance().SRMBLOCK.get()).getValue(RENDER_PROPERTY);
			if(renderState != null) {
				return renderState.getBlock().canRenderInLayer(renderState, layer);
			}
		}
    	return layer == BlockRenderLayer.SOLID;
	}
	
	/**
	 * Used to get the extended state from a SRM block. Used to put {@link RenderStateUnlistedProperty} into the block.
	 * <br><br>
	 * Also used to set {@link SecretBlockModel#AO} to if the models blockstates model is ambient occlusion
	 * @param state The current state
	 * @param world The current world 
	 * @param pos the current position
	 * @return {@code state}, or the IBlockState with {@link ISecretBlock#RENDER_PROPERTY} set to the mirror state
	 * @deprecated no longer used 
	 */
	@Deprecated
	default public IBlockState getExtendedState(IBlockState state, IBlockAccess world, BlockPos pos) 
	{
//		if(state instanceof IExtendedBlockState && world.getTileEntity(pos) instanceof ISecretTileEntity && ((ISecretTileEntity)world.getTileEntity(pos)).getMirrorState() !=  null)
//		{
//			IBlockState renderState = ((ISecretTileEntity)world.getTileEntity(pos)).getMirrorState();
//			try
//			{
//				renderState = renderState.getActualState(new FakeBlockAccess(world), pos);
//			}
//			catch (Exception e) 
//			{
//				;
//			}
//			IBakedModel model = Minecraft.getMinecraft().getBlockRendererDispatcher().getModelForState(state);
//			state = ((IExtendedBlockState)state).withProperty(RENDER_PROPERTY, renderState);
//			if(model instanceof SecretBlockModel) {
//				((SecretBlockModel)model).AO.set(Minecraft.getMinecraft().getBlockRendererDispatcher().getModelForState(renderState).isAmbientOcclusion());
//				((SecretBlockModel)model).SRMBLOCK.set(state);
//			}
//		}
		return state;
	}
	
} public interface ISecretTileEntity extends ITickable
{
	/**
	 * Used as a map to hold info for when the TileEntity isn't even in place. 
	 */
	static final HashMap<Integer, HashMap<BlockPos, IBlockState>> RENDER_MAP = new HashMap<>();
		
	/**
	 * Used to get the Correct HashMap for the World
	 * @param world the current world
	 * @return The correct map for the worlds dimension, or a new Map if it dosnt exist
	 */
	public static HashMap<BlockPos, IBlockState> getMap(World world)
	{
		if(!RENDER_MAP.containsKey(world.provider.getDimension()))
			RENDER_MAP.put(world.provider.getDimension(), new HashMap<>());
		return RENDER_MAP.get(world.provider.getDimension());
	}
	
	/**
	 * Used to set the mirrored state to the block. Can be used only once. Will be locked after one use
	 * @param mirrorState The state to set the block to
	 */
	public void setMirrorState(IBlockState mirrorState);
	
	/**
	 * Forces the mirror state to a block. Can be used as many times as wanted
	 * @param mirrorState The state to set the SRM to
	 */
	public void setMirrorStateForcable(IBlockState mirrorState);
	
	/**
	 * Gets the mirrored state from the SRM tile entity, in a NoNull way
	 * @return {@link #getMirrorState()}, or if thats null, {@link Blocks#STONE}
	 */
	@Nonnull
	default public IBlockState getMirrorStateSafely() {
		IBlockState mirroredState = getMirrorState();
		return mirroredState == null ? Blocks.STONE.getDefaultState() : mirroredState;
	}
	
	/**
	 * Gets the mirred state from the SRM tileEntity. Can be null if somthing went wrong
	 * @return The mirrored state.
	 */
	@Nullable
	public IBlockState getMirrorState();
	/**
	 * 
	 * Used to get the mirrored state when there is no instance
	 * @param world The current world
	 * @param pos The current position
	 * @return the Mirrored State, or {@link Blocks#STONE} if there is none.
	 */
	public static IBlockState getMirrorState(World world, BlockPos pos)
	{		
		IBlockState blockstate = getMap(world).get(pos) == null && world.getTileEntity(pos) instanceof ISecretTileEntity ? ((ISecretTileEntity)world.getTileEntity(pos)).getMirrorStateSafely() : getMap(world).get(pos);
		return blockstate == null ? Blocks.STONE.getDefaultState() : blockstate;
	}
	
	/**
	 * Used to get the mirrored state when there is no instance, and all you have is the IBlockAccess
	 * @param access The current world
	 * @param pos The current position
	 * @return the current world, or {@link Blocks#STONE} if there is none
	 */
	public static IBlockState getMirrorState(IBlockAccess access, BlockPos pos)
	{
		IBlockState returnState = Blocks.AIR.getDefaultState();
		if(access.getTileEntity(pos) instanceof ISecretTileEntity && ((ISecretTileEntity)access.getTileEntity(pos)).getMirrorStateSafely() != null) {
			return ((ISecretTileEntity)access.getTileEntity(pos)).getMirrorStateSafely();
		} else if(access instanceof World) {
			returnState = getMirrorState((World)access, pos);
		} else if(access.getTileEntity(pos) != null && access.getTileEntity(pos).getWorld() != null) {
			returnState = getMirrorState(access.getTileEntity(pos).getWorld(), pos);
		} else { //Last resort
			for(int dim : RENDER_MAP.keySet()) {
				if(FMLCommonHandler.instance().getMinecraftServerInstance() == null) {
					returnState = getMirrorState(SecretRooms5.proxy.getPlayer().world, pos);
				} else if(FMLCommonHandler.instance().getMinecraftServerInstance().getWorld(dim) == access) {
					returnState = getMirrorState(FMLCommonHandler.instance().getMinecraftServerInstance().getWorld(dim), pos);
				}
			}
		}
		return returnState == null ? Blocks.STONE.getDefaultState() : returnState;
		
	}
	
	/**
	 * Load from NBT. TileEntites have this by default, but this is used so its callable from an instance of {@link ISecretTileEntity}
	 * @param compound The compound to load from
	 */
	public void loadFromNBT(NBTTagCompound compound);
	
	class TileEntityData {
		private IBlockState mirroredState;
		private boolean locked;
		
		public IBlockState getMirroredState() {
			return mirroredState;
		}
		
		public TileEntityData setMirroredState(IBlockState mirroredState) {
			this.mirroredState = mirroredState;
			return this;
		}
		
		public boolean isLocked() {
			return locked;
		}
		
		public TileEntityData setLocked(boolean locked) {
			this.locked = locked;
			return this;
		}
	}
	
	/**
	 * Default readFromNBT performed on tile entities
	 * @param compound The overall {@link NBTTagCompound} being used to read the data
	 * @param tileData gotten from {@link TileEntity#getTileData()}. Usually contained in {@code compound}, however other mods may change that
	 * @return The {@link TileEntityData} containing the infomation for the SRM tileEntity
	 */
	default public TileEntityData readDataFromNBT(NBTTagCompound compound, NBTTagCompound tileData) {
		IBlockState mirrorState = null;
		boolean locked;
		if(!compound.hasKey("SecretRoomsMod", 10)) { //Old method
			locked = tileData.getBoolean("locked");
			Block testBlock = Block.REGISTRY.getObject(new ResourceLocation(tileData.getString("MirrorBlock")));
			if(testBlock != Blocks.AIR) {
				mirrorState = testBlock.getStateFromMeta(tileData.getInteger("MirrorMeta"));
			}
			if(mirrorState != null && mirrorState.getBlock() instanceof ISecretBlock) {
				mirrorState = null;
			}
		} else {
			NBTTagCompound nbt = compound.getCompoundTag("SecretRoomsMod");
			int v = nbt.getInteger("DataVersion");
			if(v == 1) {
				locked = nbt.getBoolean("locked");
				mirrorState = NBTUtil.readBlockState(nbt.getCompoundTag("MirroredState"));
			} else {
				throw new IllegalArgumentException("Secret Tile Entites Dont know how to handle data version " + v);
			}
		}
	
		if(mirrorState == null) {
			SecretRooms5.LOGGER.error("NBT Compound returned a null state. NBTTAG: {}", compound.toString());
			mirrorState = Blocks.STONE.getDefaultState();
		}
		
		if(!ISecretBlock.ALL_SECRET_TILE_ENTITIES.contains(this)) {
			ISecretBlock.ALL_SECRET_TILE_ENTITIES.add((TileEntity) this);
		}
		return new TileEntityData().setMirroredState(mirrorState).setLocked(locked);
	}
	
	/**
	 * Writes the infomation from the tileEntiy to the {@code compound}
	 * @param compound The compound to write to
	 * @param data the data for the tileEntity
	 */
	default public void writeDataToNBT(NBTTagCompound compound, TileEntityData data) {
		NBTTagCompound nbt = new NBTTagCompound();
		if(data.getMirroredState() == null) {
			SecretRooms5.LOGGER.error("Caught null mirrored state in saving file at {}. Saving state as Stone", ((TileEntity)this).getPos());
			data.setMirroredState(Blocks.STONE.getDefaultState());
		}
//		getTileData().setString("MirrorBlock", getMirrorState().getBlock().getRegistryName().toString());
//		getTileData().setInteger("MirrorMeta", getMirrorState().getBlock().getMetaFromState(getMirrorState()));
		
		nbt.setTag("MirroredState", NBTUtil.writeBlockState(new NBTTagCompound(), data.getMirroredState()));	
		nbt.setInteger("DataVersion", 1);
		nbt.setBoolean("locked", data.isLocked());
		compound.setTag("SecretRoomsMod", nbt);
	}


} public class SecretBlockIconProvider implements IBlockIconProvider
{

	private final BlockType type;
	
	public SecretBlockIconProvider(BlockType type) {
		this.type = type;
	}
	
	@Override
	public Icon getIcon(IBlockState state, EnumFacing side) {
		if(state instanceof IExtendedBlockState && state.getBlock() instanceof ISecretBlock && ((IExtendedBlockState)state).getValue(ISecretBlock.RENDER_PROPERTY) != null) {
			return Icon.from(((IExtendedBlockState)state).getValue(ISecretBlock.RENDER_PROPERTY));
		}
		return Icon.from(SecretBlocks.GHOST_BLOCK);
		
	}

	public ThreadLocal<RenderParameters> current_params = ThreadLocal.withInitial(() -> null);
	
	private boolean previousFacingX = false;
	
	@Override
	public Icon getIcon(IBlockAccess world, BlockPos pos, IBlockState state, EnumFacing side) 
	{
		if(world.getBlockState(pos).getBlock() instanceof ISecretBlock) {
			IBlockState mirroredState = ((ISecretTileEntity)world.getTileEntity(pos)).getMirrorStateSafely();

			List<BakedQuad> quadList = Minecraft.getMinecraft().getBlockRendererDispatcher().getModelForState(mirroredState).getQuads(mirroredState, side, MathHelper.getPositionRandom(pos));
			
			Icon icon = quadList.isEmpty() || side == null ? Icon.from(mirroredState) : new Icon(quadList.get(0).getSprite());						
			if(current_params.get() instanceof SecretRenderParameters) {
				icon = new Icon(((SecretRenderParameters)current_params.get()).quadSprite.get());
			}
			
			if(type == BlockType.DOOR) {
				//For some reason, the textureAtlasSprite registed to the quads for the door is missingno. Manually put the UV coords in:
				float u;
				float v;
				
				if(side == null) {
					u = icon.getInterpolatedU(16);
					v = icon.getInterpolatedV(16);
				} else {
					boolean facingX = state.getValue(BlockDoor.FACING).getAxis() == Axis.X != state.getValue(BlockDoor.OPEN);
					
					
					DoorTileEntity te = ((DoorTileEntity)world.getTileEntity(state.getValue(BlockDoor.HALF) == EnumDoorHalf.LOWER? pos : pos.down()));
					if(te.getTimer().elapsedTick() > te.getOpeningTime() && te.isMoving()) {
						facingX = !facingX;
					}
					
					if(facingX) {
						side = side.rotateAround(Axis.Y);
					}
					
					if(side.getAxis() == Axis.X) {
						u = icon.getInterpolatedU(3);
						v = icon.getInterpolatedV(16);
					} else if(side.getAxis() == Axis.Y) {
						u = icon.getInterpolatedU(16);
						v = icon.getInterpolatedV(3);
						
					} else {
						u = icon.getInterpolatedU(16);
						v = icon.getInterpolatedV(16);
					}
					return new Icon("secretroomsmod door", icon.getMinU(), icon.getMinV(), u, v);
				}
			}
			return icon;
		}
		return IBlockIconProvider.super.getIcon(world, pos, state, side);
	}
	
	public static enum BlockType {
		DOOR, TRAPDOOR
	}

} public class SecretRenderParameters extends RenderParameters
{
	
	private final TextureAtlasSprite fallbackSprite;
	
	public SecretRenderParameters(RenderParameters rp, TextureAtlasSprite quadSprite) 
	{
		renderAllFaces.set(rp.renderAllFaces.get());
		useBlockBounds.set(rp.useBlockBounds.get());
		renderBounds.set(rp.renderBounds.get());
		useCustomTexture.set(rp.useCustomTexture.get());
		applyTexture.set(rp.applyTexture.get());
		iconProvider.set(rp.iconProvider.get());
		icon.set(rp.icon.get());
		useWorldSensitiveIcon.set(rp.useWorldSensitiveIcon.get());
		useTexture.set(rp.useTexture.get());
		interpolateUV.set(rp.interpolateUV.get());
		rotateIcon.set(rp.rotateIcon.get());
		calculateAOColor.set(rp.calculateAOColor.get());
		calculateBrightness.set(rp.calculateBrightness.get());
		usePerVertexColor.set(rp.usePerVertexColor.get());
		usePerVertexAlpha.set(rp.usePerVertexAlpha.get());
		usePerVertexBrightness.set(rp.usePerVertexBrightness.get());
		useEnvironmentBrightness.set(rp.useEnvironmentBrightness.get());
		useNormals.set(rp.useNormals.get());
		colorMultiplier.set(rp.colorMultiplier.get());
		colorFactor.set(rp.colorFactor.get());
		brightness.set(rp.brightness.get());
		alpha.set(rp.alpha.get());
		direction.set(rp.direction.get());
		textureSide.set(rp.textureSide.get());
		aoMatrix.set(rp.aoMatrix.get());
		flipU.set(rp.flipU.get());
		flipV.set(rp.flipV.get());
		this.quadSprite.set(quadSprite);
		this.fallbackSprite = quadSprite;
		deductParameters.set(rp.deductParameters.get());
	}
	
	/**
	 * The index of the quad currently being rendered
	 */
	public Parameter<TextureAtlasSprite> quadSprite = new Parameter<>(Icon.missing);

	@Override
	public RenderParameters clone() {
		RenderParameters params = super.clone();
		if(params != null) {
			return new SecretRenderParameters(params, quadSprite.get());
		}
		return null;
	}
	
	@Override
	public void merge(RenderParameters params)
	{
		super.merge(params);
	}
	
	private Parameter<?> getParameter(int index)
	{
		if (index < 0 || index >= listParams.size()) {
			return null;
		}
		return listParams.get(index);
	}
	

	@Override
	protected void buildList() {
		super.buildList();
 		if(quadSprite == null) { //Keeps becoming null. no idea why :/
			quadSprite = new Parameter<TextureAtlasSprite>(Icon.missing);
			quadSprite.set(fallbackSprite);
		}
		listParams.add(quadSprite);
	}
	
} @MalisisRendered(block = SecretTrapDoorRenderer.class, item = SecretMalisisItemRenderer.class)
public class SecretMalisisTrapDoorBlock extends TrapDoor implements ISecretBlock {

	public SecretMalisisTrapDoorBlock(TrapDoorDescriptor desc) {
		super(desc);
		desc.set(this, null);
		getComponents().clear();
		addComponent(new SecretBlockIconProvider(SecretBlockIconProvider.BlockType.TRAPDOOR));
	}
	
	@Override
	public TileEntity createTileEntity(World world, IBlockState state) {
		TrapDoorTileEntity te = new SecretMalisisTileEntityTrapDoor();
		te.setDescriptor(this.getDescriptor());
		if (te.isOpened(state))
			te.setState(DoorState.OPENED);
		return te;
	}
	
	@Override
	public boolean addRunningEffects(IBlockState state, World world, BlockPos pos, Entity entity) {
		return ISecretBlock.super.addRunningEffects(state, world, pos, entity);
	}
	
	@Override
	public boolean addLandingEffects(IBlockState state, WorldServer worldObj, BlockPos blockPosition,
			IBlockState iblockstate, EntityLivingBase entity, int numberOfParticles) {
		return ISecretBlock.super.addLandingEffects(state, worldObj, blockPosition, iblockstate, entity, numberOfParticles);
	}
	
	@Override
	public boolean canConnectRedstone(IBlockState state, IBlockAccess world, BlockPos pos, EnumFacing side) {
		return ISecretBlock.super.canConnectRedstone(state, world, pos, side);
	}
	
	@Override
	public float getSlipperiness(IBlockState state, IBlockAccess world, BlockPos pos, Entity entity) {
		return ISecretBlock.super.getSlipperiness(state, world, pos, entity);
	}
	
	@Override
	public boolean canPlaceTorchOnTop(IBlockState state, IBlockAccess world, BlockPos pos) {
		return ISecretBlock.super.canPlaceTorchOnTop(state, world, pos);
	}
	
	@Override
	public String getHarvestTool(IBlockState state) {
		return ISecretBlock.super.getHarvestTool(state);
	}
	
	@Override
	public int getHarvestLevel(IBlockState state) {
		return ISecretBlock.super.getHarvestLevel(state);
	}
	
	@Override
	public IBlockState getActualState(IBlockState state, IBlockAccess worldIn, BlockPos pos) {
		return ISecretBlock.super.getActualState(state, worldIn, pos, super.getActualState(state, worldIn, pos));
	}
	
	@Override
	public boolean canHarvestBlock(IBlockAccess world, BlockPos pos, EntityPlayer player) {
		return ISecretBlock.super.canHarvestBlock(world, pos, player);
	}
	
	@Override
	public boolean canBeConnectedTo(IBlockAccess world, BlockPos pos, EnumFacing facing) {
		return ISecretBlock.super.canBeConnectedTo(world, pos, facing);
	}
	
	@Override
	public float getBlockHardness(IBlockState blockState, World worldIn, BlockPos pos)  {
		return ISecretBlock.super.getBlockHardness(blockState, worldIn, pos);
	}
	
	@Override
	public SoundType getSoundType(IBlockState state, World world, BlockPos pos, Entity entity) 
	{
		return ISecretBlock.super.getSoundType(state, world, pos, entity);
	}
	
	public boolean isFullCube(IBlockState state)
    {
        return false;
    }
	
    @SideOnly(Side.CLIENT)
	@Override
	public boolean addHitEffects(IBlockState state, World worldObj, RayTraceResult target, ParticleManager manager) 
	{
    	return ISecretBlock.super.addHitEffects(state, worldObj, target, manager);
	}
    
	@SideOnly(Side.CLIENT)
	@Override
	public boolean addDestroyEffects(World world, BlockPos pos, ParticleManager manager) 
	{
		return ISecretBlock.super.addDestroyEffects(world, pos, manager);
	}
	
    public EnumBlockRenderType getRenderType(IBlockState state)
    {
        return EnumBlockRenderType.MODEL;
    }
    
    @SideOnly(Side.CLIENT)
    @Override
    public boolean canRenderInLayer(IBlockState state, BlockRenderLayer layer) 
    {
		return ISecretBlock.super.canRenderInLayer(state, layer);
    }
    
    @Override
	public IBlockState getExtendedState(IBlockState state, IBlockAccess world, BlockPos pos) 
	{
		return super.getExtendedState(ISecretBlock.super.getExtendedState(state, world, pos), world, pos);
	}
    
    @Override
    protected BlockStateContainer createBlockState() {
    	Collection < IProperty<? >> properties = super.createBlockState().getProperties();
    	return new ExtendedBlockState(this, properties.toArray(new IProperty[properties.size()]), new IUnlistedProperty[] {RENDER_PROPERTY});    
    }
		
    @SideOnly(Side.CLIENT)
    @Override
    public boolean isOpaqueCube(IBlockState state)
    {
    	return ISecretBlock.super.isOpaqueCube(state);
    }

    @SideOnly(Side.CLIENT)
    @Override
    public float getAmbientOcclusionLightValue(IBlockState state) {
    	return ISecretBlock.super.getAmbientOcclusionLightValue(state);
    }
    
    @Override
    public int getPackedLightmapCoords(IBlockState state, IBlockAccess source, BlockPos pos) {
    	return ISecretBlock.super.getPackedLightmapCoords(state, source, pos);
    }

} @Syncable("TileEntity")
public class SecretMalisisTileEntityTrapDoor extends TrapDoorTileEntity implements ISecretTileEntity
{
	/**
	 * The mirrored state
	 */
	protected IBlockState mirrorState;
		
	
	/**
	 * If the TileEntity is locked then {@link #setMirrorState(IBlockState)} will not work
	 */
	private boolean locked;
	
	@Override
	public void loadFromNBT(NBTTagCompound compound) {
		readFromNBT(compound);
	}
	
	@Override
	public void readFromNBT(NBTTagCompound compound) {
		super.readFromNBT(compound);
		TileEntityData data = ISecretTileEntity.super.readDataFromNBT(compound, getTileData());
		mirrorState = data.getMirroredState();
		locked = data.isLocked();
	}
	
	@Override
	public void update() {
		super.update();
		if(mirrorState != null)
			ParticleHandler.BLOCKBRAKERENDERMAP.put(pos, mirrorState.getBlock().getStateFromMeta(mirrorState.getBlock().getMetaFromState(mirrorState)));
	}
	
	@Override
	public NBTTagCompound writeToNBT(NBTTagCompound compound) {
		ISecretTileEntity.super.writeDataToNBT(compound, new TileEntityData().setMirroredState(getMirrorStateSafely()).setLocked(locked));
		return super.writeToNBT(compound);
	} 
	
	@Override
	public IBlockState getMirrorState() {
		if(mirrorState == null && ParticleHandler.BLOCKBRAKERENDERMAP.containsKey(pos))
			mirrorState = ParticleHandler.BLOCKBRAKERENDERMAP.get(pos);
		if(mirrorState == null && RENDER_MAP.containsKey(pos))
			mirrorState = ISecretTileEntity.getMap(world).get(pos);
		return mirrorState;
	}
	
	@Override
	public double getMaxRenderDistanceSquared() 
	{
		return Double.MAX_VALUE;
	}
	
	public void setMirrorState(IBlockState mirrorState)
	{
		if(!locked)
			setMirrorStateForcable(mirrorState);
		locked = true;
	}
	
	@Override
	public void setMirrorStateForcable(IBlockState mirrorState)
	{
		if(mirrorState.getBlock() instanceof ISecretBlock)
			mirrorState = Blocks.STONE.getDefaultState();
		ISecretTileEntity.getMap(world).put(this.pos, mirrorState);
		this.mirrorState = mirrorState.getBlock().getStateFromMeta(mirrorState.getBlock().getMetaFromState(mirrorState));
	}
	
	@Override
	public SPacketUpdateTileEntity getUpdatePacket() {
		NBTTagCompound nbt = new NBTTagCompound();
		this.writeToNBT(nbt);
		int metadata = getBlockMetadata();
		return new SPacketUpdateTileEntity(this.pos, metadata, nbt);
	}

	@Override
	public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) {
		this.readFromNBT(pkt.getNbtCompound());
	}

	@Override
	public NBTTagCompound getUpdateTag() {
		NBTTagCompound nbt = new NBTTagCompound();
		this.writeToNBT(nbt);
		return nbt;
	}

	@Override
	public void handleUpdateTag(NBTTagCompound tag) {
		this.readFromNBT(tag);
	}
} public class TrueSightHelmet extends ItemArmor
{
	public TrueSightHelmet(String name, ArmorMaterial materialIn, int renderIndexIn, EntityEquipmentSlot equipmentSlotIn) {
		super(materialIn, renderIndexIn, equipmentSlotIn);
		setUnlocalizedName(name);
		setRegistryName(name);
		this.setMaxStackSize(1);
	}
	
	public static boolean isHelmet(EntityPlayer player) {
		return player.getItemStackFromSlot(EntityEquipmentSlot.HEAD).getItem() instanceof TrueSightHelmet && (player.isCreative() || player.isSpectator() || SecretConfig.GENERAL.survivalModeHelmet);
	}
	
	@SideOnly(Side.CLIENT)
	public static boolean isHelmet() {
		return isHelmet(Minecraft.getMinecraft().player);
	}

} public class FakeBlockModel implements IBakedModel
{
	protected final IBakedModel model;
	
	/**
	 * The actual state of the SRM block in the world
	 */
	protected IBlockState currentRender;
	
	protected IBlockState currentActualState;
	
	public FakeBlockModel(IBlockState overstate) 
	{
		this(Minecraft.getMinecraft().getBlockRendererDispatcher().getModelForState(overstate));
	}
	
	public FakeBlockModel(IBakedModel model) 
	{
		this.model = model;
	}

	@Override
	public List<BakedQuad> getQuads(IBlockState state, EnumFacing side, long rand) 
	{
		return model.getQuads(state, side, rand);
	}
	
	/**
	 * Used to set the current state of the SRM block thats actually in the world
	 * @param currentRender The current SRM block thats in the world
	 * @return this instance
	 */
	public FakeBlockModel setCurrentRender(IBlockState currentRender) {
		this.currentRender = currentRender;
		return this;
	}
	
	/**
	 * Used to set the current actual state of the block being rendered
	 * @param currentActualState The current actual state of the mirroredState
	 * @return this instance
	 */
	public FakeBlockModel setCurrentActualState(IBlockState currentActualState) {
		this.currentActualState = currentActualState;
		return this;
	}
	
	@Override
	public boolean isAmbientOcclusion() {
		return model.isAmbientOcclusion();
	}

	@Override
	public boolean isGui3d() {
		return model.isGui3d();
	}

	@Override
	public boolean isBuiltInRenderer() {
		return model.isBuiltInRenderer();
	}

	@Override
	public TextureAtlasSprite getParticleTexture() {
		return model.getParticleTexture();
	}

	@Override
	public ItemOverrideList getOverrides() {
		return model.getOverrides();
	}
	
	/**
	 * Used to get the model from the IBlockState
	 * @param state the state to get the model from
	 * @return the model used to render {@code state}
	 */
	public static IBakedModel getModel(IBlockState state)
	{
		return Minecraft.getMinecraft().getBlockRendererDispatcher().getModelForState(state);
	}
		
	/**
	 * Gets the model from the {@link ResourceLocation}
	 * @param resourceLocation the location of the model
	 * @return the model, at the {@link ResourceLocation}
	 * @throws RuntimeException if the model can't be loaded
	 */
	public static IBakedModel getModel(ResourceLocation resourceLocation) throws RuntimeException
	{
		IBakedModel bakedModel;
		IModel model;
		try {
	        model = ModelLoaderRegistry.getModel(resourceLocation);
		} catch (Exception e) {
          throw new RuntimeException(e);
		}
		bakedModel = model.bake(TRSRTransformation.identity(), DefaultVertexFormats.BLOCK,
				location -> Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(location.toString()));
	    return bakedModel;
	}
	
}
import java.util.List;
import com.wynprice.secretroomsmod.SecretBlocks;
import com.wynprice.secretroomsmod.SecretRooms5;
import com.wynprice.secretroomsmod.base.interfaces.ISecretBlock;
import com.wynprice.secretroomsmod.base.interfaces.ISecretTileEntity;
import com.wynprice.secretroomsmod.integration.malisisdoors.SecretBlockIconProvider;
import com.wynprice.secretroomsmod.integration.malisisdoors.SecretRenderParameters;
import com.wynprice.secretroomsmod.integration.malisisdoors.registries.blocks.SecretMalisisTrapDoorBlock;
import com.wynprice.secretroomsmod.integration.malisisdoors.registries.tileentities.SecretMalisisTileEntityTrapDoor;
import com.wynprice.secretroomsmod.items.TrueSightHelmet;
import com.wynprice.secretroomsmod.render.fakemodels.FakeBlockModel;
import net.malisis.core.renderer.RenderParameters;
import net.malisis.core.renderer.RenderType;
import net.malisis.core.renderer.element.Face;
import net.malisis.core.renderer.element.Shape;
import net.malisis.core.renderer.icon.Icon;
import net.malisis.core.renderer.icon.provider.IBlockIconProvider;
import net.malisis.core.renderer.icon.provider.IIconProvider;
import net.malisis.core.renderer.icon.provider.IItemIconProvider;
import net.malisis.core.util.EnumFacingUtils;
import net.malisis.doors.block.Door;
import net.malisis.doors.block.TrapDoor;
import net.malisis.doors.renderer.TrapDoorRenderer;
import net.malisis.doors.tileentity.DoorTileEntity;
import net.minecraft.block.BlockDirectional;
import net.minecraft.block.BlockDoor;
import net.minecraft.block.BlockHorizontal;
import net.minecraft.block.BlockTrapDoor;
import net.minecraft.block.BlockDoor.EnumDoorHalf;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.init.Blocks;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.ItemStack;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.EnumFacing.Axis;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.fml.relauncher.ReflectionHelper;
package com.wynprice.secretroomsmod.integration.malisisdoors.malisisrenders;




public class SecretTrapDoorRenderer extends TrapDoorRenderer {
	
	public SecretTrapDoorRenderer() 
	{
		registerFor(SecretMalisisTileEntityTrapDoor.class);
		ensureBlock(SecretMalisisTrapDoorBlock.class, TrapDoor.class);
	}
	
	@Override
	protected void initialize() {
		super.initialize();
		((RenderParameters)ReflectionHelper.getPrivateValue(TrapDoorRenderer.class, this, "rp")).calculateAOColor.set(true);
	}
	
	@Override
	protected Icon getIcon(Face face, RenderParameters params)
	{
		if (params != null && params.icon.get() != null)
			return params.icon.get();
		
		IIconProvider iconProvider = getIconProvider(params);
		if (iconProvider instanceof IItemIconProvider && itemStack != null)
			return ((IItemIconProvider) iconProvider).getIcon(itemStack);
		//SRM START
		if(iconProvider instanceof SecretBlockIconProvider) {
			((SecretBlockIconProvider)iconProvider).current_params.set(params);
		}
		//SRM STOP
		if (iconProvider instanceof IBlockIconProvider && block != null)
		{
			EnumFacing side = params != null ? params.textureSide.get() : EnumFacing.SOUTH;
			if (params != null && shouldRotateIcon(params))
				side = EnumFacingUtils.getRealSide(blockState, side);

			IBlockIconProvider iblockp = (IBlockIconProvider) iconProvider;
			if (renderType == RenderType.BLOCK || renderType == RenderType.TILE_ENTITY)
				return iblockp.getIcon(world, pos, blockState, side);
			else if (renderType == RenderType.ITEM)
				return iblockp.getIcon(itemStack, side);
		}

		return iconProvider != null ? iconProvider.getIcon() : Icon.missing;	
	}
	
	@Override
	public void drawShape(Shape s, RenderParameters params) {
		if (s == null)
			return;

		s.applyMatrix();

		for (Face f : s.getFaces()) {
			
			if(world.getBlockState(pos).getBlock() instanceof ISecretBlock) {
				SecretRenderParameters sParams = new SecretRenderParameters(params, Icon.missing);