package com.rwtema.funkylocomotion.helper;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.Field;
import javax.annotation.Nullable;
import com.mojang.authlib.GameProfile;
import com.rwtema.funkylocomotion.api.FunkyCapabilities;
import com.rwtema.funkylocomotion.api.IMoveCheck;
import com.rwtema.funkylocomotion.api.ISlipperyBlock;
import com.rwtema.funkylocomotion.movepermissions.MoveCheckReflector;
import com.rwtema.funkylocomotion.proxydelegates.ProxyRegistry;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.ChunkCache;
import net.minecraft.world.EnumSkyBlock;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import net.minecraftforge.fml.relauncher.ReflectionHelper;
public class BlockHelper {
private static final MethodHandle methodHandle_Chunk_relightBlock =
MethodHandleUtils.getMethodHandleVirtual(Chunk.class, new String[] { "func_76615_h", "relightBlock" }, int.class, int.class, int.class);
private static final MethodHandle methodHandle_Chunk_propagateSkylightOcclusion =
MethodHandleUtils.getMethodHandleVirtual(Chunk.class, new String[] { "func_76595_e", "propagateSkylightOcclusion" }, int.class, int.class);
private static final Field field_Chunk_precipitationHeightMap = ReflectionHelper.findField(Chunk.class, "field_76638_b", "precipitationHeightMap");
@SuppressWarnings("deprecation")
public static boolean silentSetBlock(Chunk chunk, BlockPos pos, Block block, int meta) {
int dx = pos.getX() & 15;
int dz = pos.getZ() & 15;
int y = pos.getY();
int i1 = dz << 4 | dx;
try
{
int[] precMap = (int[]) field_Chunk_precipitationHeightMap.get(chunk);
if (y >= precMap[i1] - 1) {
precMap[i1] = -999;
}
//if (y >= chunk.precipitationHeightMap[i1] - 1) {
// chunk.precipitationHeightMap[i1] = -999;
//}
}
catch (Exception e)
{
}
IBlockState state1 = chunk.getBlockState(dx, y, dz);
Block block1 = state1.getBlock();
int k1 = block1.getMetaFromState(state1);
if (block1 == block && k1 == meta) {
return false;
} else {
ExtendedBlockStorage extendedblockstorage = chunk.getBlockStorageArray()[y >> 4];
if (extendedblockstorage == Chunk.NULL_BLOCK_STORAGE) {
if (block == Blocks.AIR) {
return false;
}
extendedblockstorage = chunk.getBlockStorageArray()[y >> 4] = new ExtendedBlockStorage(y >> 4 << 4, !chunk.getWorld().provider.hasNoSky());
}
extendedblockstorage.set(dx, y & 15, dz, block.getStateFromMeta(meta));
chunk.setModified(true);
return true;
}
}
public static void silentClear(Chunk chunk, BlockPos pos) {
silentSetBlock(chunk, pos, Blocks.AIR, 0);
}
public static void postUpdateBlock(World world, BlockPos pos) {
int i1 = (pos.getZ() & 15) << 4 | (pos.getX() & 15);
Chunk chunk = world.getChunkFromBlockCoords(pos);
try
{
int[] precMap = (int[]) field_Chunk_precipitationHeightMap.get(chunk);
if (pos.getY() >= precMap[i1] - 1) {
precMap[i1] = -999;
}
//if (pos.getY() >= chunk.precipitationHeightMap[i1] - 1) {
// chunk.precipitationHeightMap[i1] = -999;
//}
}
catch (Exception e)
{
}
int j1 = chunk.getHeightMap()[i1];
boolean flag = pos.getY() >= j1;
IBlockState newState = chunk.getBlockState(pos.getX() & 15, pos.getY(), pos.getZ() & 15);
Block newBlock = newState.getBlock();
int k2 = 255;
if (flag) {
chunk.generateSkylightMap();
} else {
int j2 = newBlock.getLightOpacity(newState, world, pos);
try
{
if (j2 > 0) {
if (pos.getY() >= j1) {
methodHandle_Chunk_relightBlock.invokeExact(chunk, pos.getX() & 15, pos.getY() + 1, pos.getZ() & 15);
//chunk.relightBlock(pos.getX() & 15, pos.getY() + 1, pos.getZ() & 15);
}
} else if (pos.getY() == j1 - 1) {
methodHandle_Chunk_relightBlock.invokeExact(chunk, pos.getX() & 15, pos.getY(), pos.getZ() & 15);
//chunk.relightBlock(pos.getX() & 15, pos.getY(), pos.getZ() & 15);
}
if (j2 != k2 && (j2 < k2 || chunk.getLightFor(EnumSkyBlock.SKY, pos) > 0 || chunk.getLightFor(EnumSkyBlock.BLOCK, pos) > 0)) {
methodHandle_Chunk_propagateSkylightOcclusion.invokeExact(chunk, pos.getX() & 15, pos.getZ() & 15);
//chunk.propagateSkylightOcclusion(pos.getX() & 15, pos.getZ() & 15);
}
}
catch (Throwable t)
{
}
}
world.checkLight(pos);
markBlockForUpdate(world, pos);
if (!world.isRemote) {
// TODO: Is this correct in 1.11?
world.neighborChanged(pos, Blocks.AIR, pos);
world.neighborChanged(pos, newBlock, pos);
// It used to be this in 1.10:
//world.notifyBlockOfStateChange(pos, Blocks.AIR);
//world.notifyBlockOfStateChange(pos, newBlock);
world.notifyNeighborsOfStateChange(pos, newBlock, false);
if (newState.hasComparatorInputOverride()) {
world.updateComparatorOutputLevel(pos, newBlock);
}
}
}
public static boolean canMoveBlock(World world, BlockPos pos, @Nullable GameProfile profile) {
IBlockState state = world.getBlockState(pos);
Block b = state.getBlock();
if (b == Blocks.AIR || b.isAir(state, world, pos))
return false;
IMoveCheck check = ProxyRegistry.getInterface(b, IMoveCheck.class, FunkyCapabilities.MOVE_CHECK);
if (check != null) {
EnumActionResult result = check.canMove(world, pos, profile);
if (result != EnumActionResult.PASS)
return result == EnumActionResult.SUCCESS;
}
if (state.getBlockHardness(world, pos) < 0)
return false;
TileEntity tile = world.getTileEntity(pos);
if (tile != null) {
check = ProxyRegistry.getInterface(tile, IMoveCheck.class, FunkyCapabilities.MOVE_CHECK);
if (check != null) {
EnumActionResult result = check.canMove(world, pos, profile);
if (result != EnumActionResult.PASS)
return result == EnumActionResult.SUCCESS;
} else {
EnumActionResult result = MoveCheckReflector.canMoveClass(tile.getClass(), world, pos, profile);
if (result != EnumActionResult.PASS)
return result == EnumActionResult.SUCCESS;
}
}
return MoveCheckReflector.canMoveClass(b.getClass());
}
public static void breakBlockWithDrop(World world, BlockPos pos) {
IBlockState iblockstate = world.getBlockState(pos);
if (iblockstate.getMaterial().isLiquid()) {
world.setBlockState(pos, Blocks.AIR.getDefaultState(), 3);
} else {
world.destroyBlock(pos, true);
}
}
public static boolean isValid(World world, BlockPos pos) {
return world.isBlockLoaded(pos);
}
public static boolean canStick(World world, BlockPos pos, EnumFacing dir, @Nullable GameProfile profile) {
if (!isValid(world, pos))
return false;
if (!canMoveBlock(world, pos, profile))
return false;
Block b = world.getBlockState(pos).getBlock();
ISlipperyBlock slip = ProxyRegistry.getInterface(b, ISlipperyBlock.class, FunkyCapabilities.SLIPPERY_BLOCK);
return slip == null || slip.canStickTo(world, pos, dir);
}
public static boolean canReplace(World world, BlockPos pos) {
return isValid(world, pos) && (world.isAirBlock(pos) || world.getBlockState(pos).getBlock().isReplaceable(world, pos));
}
public static TileEntity getTile(World world, BlockPos pos) {
return world.getTileEntity(pos);
}
public static void markBlockForUpdate(World world, BlockPos pos) {
IBlockState state = world.getBlockState(pos);
world.notifyBlockUpdate(pos, state, state, 0);
}
@Nullable
public static <T extends TileEntity> T getTileEntitySafely(IBlockAccess world, BlockPos pos, Class<T> tileClass) {
TileEntity te;
if (world instanceof ChunkCache) {
ChunkCache chunkCache = (ChunkCache) world;
te = chunkCache.getTileEntity(pos, Chunk.EnumCreateEntityType.CHECK);
}
else {
te = world.getTileEntity(pos);
}
if (tileClass.isInstance(te)) {
return tileClass.cast(te);
}
else {
return null;
}
}
}