package blusunrize.immersiveengineering.common.blocks;
import blusunrize.immersiveengineering.ImmersiveEngineering;
import blusunrize.immersiveengineering.api.Lib;
import blusunrize.immersiveengineering.common.IEContent;
import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IIEMetaBlock;
import com.google.common.collect.Sets;
import net.minecraft.block.Block;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.IProperty;
import net.minecraft.block.properties.PropertyEnum;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.block.statemap.StateMapperBase;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.IStringSerializable;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.Explosion;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.common.property.ExtendedBlockState;
import net.minecraftforge.common.property.IUnlistedProperty;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import java.util.*;
public class BlockIEBase<E extends Enum<E> & BlockIEBase.IBlockEnum> extends Block implements IIEMetaBlock
{
protected static IProperty[] tempProperties;
protected static IUnlistedProperty[] tempUnlistedProperties;
public final String name;
public final PropertyEnum<E> property;
public final IProperty[] additionalProperties;
public final IUnlistedProperty[] additionalUnlistedProperties;
public final E[] enumValues;
boolean[] isMetaHidden;
boolean[] hasFlavour;
protected Set<BlockRenderLayer> renderLayers = Sets.newHashSet(BlockRenderLayer.SOLID);
protected Set<BlockRenderLayer>[] metaRenderLayers;
protected Map<Integer, Integer> metaLightOpacities = new HashMap<>();
protected Map<Integer, Integer> metaResistances = new HashMap<>();
protected boolean[] metaNotNormalBlock;
private boolean opaqueCube = false;
public BlockIEBase(String name, Material material, PropertyEnum<E> mainProperty, Class<? extends ItemBlockIEBase> itemBlock, Object... additionalProperties)
{
super(setTempProperties(material, mainProperty, additionalProperties));
this.name = name;
this.property = mainProperty;
this.enumValues = mainProperty.getValueClass().getEnumConstants();
this.isMetaHidden = new boolean[this.enumValues.length];
this.hasFlavour = new boolean[this.enumValues.length];
this.metaRenderLayers = new Set[this.enumValues.length];
ArrayList<IProperty> propList = new ArrayList<IProperty>();
ArrayList<IUnlistedProperty> unlistedPropList = new ArrayList<IUnlistedProperty>();
for(Object o : additionalProperties)
{
if(o instanceof IProperty)
propList.add((IProperty)o);
if(o instanceof IProperty[])
for(IProperty p : ((IProperty[])o))
propList.add(p);
if(o instanceof IUnlistedProperty)
unlistedPropList.add((IUnlistedProperty)o);
if(o instanceof IUnlistedProperty[])
for(IUnlistedProperty p : ((IUnlistedProperty[])o))
unlistedPropList.add(p);
}
this.additionalProperties = propList.toArray(new IProperty[propList.size()]);
this.additionalUnlistedProperties = unlistedPropList.toArray(new IUnlistedProperty[unlistedPropList.size()]);
this.setDefaultState(getInitDefaultState());
String registryName = createRegistryName();
this.setUnlocalizedName(registryName.replace(':', '.'));
this.setCreativeTab(ImmersiveEngineering.creativeTab);
this.adjustSound();
ImmersiveEngineering.registerBlockByFullName(this, itemBlock, registryName);
IEContent.registeredIEBlocks.add(this);
lightOpacity = 255;
}
@Override
public String getIEBlockName()
{
return this.name;
}
@Override
public Enum[] getMetaEnums()
{
return enumValues;
}
@Override
public IBlockState getInventoryState(int meta)
{
IBlockState state = this.blockState.getBaseState().withProperty(this.property, enumValues[meta]);
// for(int i=0; i<this.additionalProperties.length; i++)
// if(this.additionalProperties[i]!=null && !this.additionalProperties[i].getAllowedValues().isEmpty())
// state = state.withProperty(this.additionalProperties[i], this.additionalProperties[i].getAllowedValues().toArray()[0]);
return state;
}
@Override
public PropertyEnum<E> getMetaProperty()
{
return this.property;
}
@Override
public boolean useCustomStateMapper()
{
return false;
}
@Override
public String getCustomStateMapping(int meta, boolean itemBlock)
{
return null;
}
@Override
@SideOnly(Side.CLIENT)
public StateMapperBase getCustomMapper()
{
return null;
}
@Override
public boolean appendPropertiesToState()
{
return true;
}
public String getUnlocalizedName(ItemStack stack)
{
String subName = getStateFromMeta(stack.getItemDamage()).getValue(property).toString().toLowerCase(Locale.US);
return super.getUnlocalizedName() + "." + subName;
}
protected static Material setTempProperties(Material material, PropertyEnum<?> property, Object... additionalProperties)
{
ArrayList<IProperty> propList = new ArrayList<IProperty>();
ArrayList<IUnlistedProperty> unlistedPropList = new ArrayList<IUnlistedProperty>();
propList.add(property);
for(Object o : additionalProperties)
{
if(o instanceof IProperty)
propList.add((IProperty)o);
if(o instanceof IProperty[])
for(IProperty p : ((IProperty[])o))
propList.add(p);
if(o instanceof IUnlistedProperty)
unlistedPropList.add((IUnlistedProperty)o);
if(o instanceof IUnlistedProperty[])
for(IUnlistedProperty p : ((IUnlistedProperty[])o))
unlistedPropList.add(p);
}
tempProperties = propList.toArray(new IProperty[propList.size()]);
tempUnlistedProperties = unlistedPropList.toArray(new IUnlistedProperty[unlistedPropList.size()]);
return material;
}
protected static Object[] combineProperties(Object[] currentProperties, Object... addedProperties)
{
Object[] array = new Object[currentProperties.length + addedProperties.length];
for(int i=0; i<currentProperties.length; i++)
array[i] = currentProperties[i];
for(int i=0; i<addedProperties.length; i++)
array[currentProperties.length+i] = addedProperties[i];
return array;
}
public BlockIEBase setMetaHidden(int... meta)
{
for(int i : meta)
if(i>=0 && i<this.isMetaHidden.length)
this.isMetaHidden[i] = true;
return this;
}
public BlockIEBase setMetaUnhidden(int... meta)
{
for(int i : meta)
if(i>=0 && i<this.isMetaHidden.length)
this.isMetaHidden[i] = false;
return this;
}
public boolean isMetaHidden(int meta)
{
return this.isMetaHidden[Math.max(0, Math.min(meta, this.isMetaHidden.length-1))];
}
public BlockIEBase setHasFlavour(int... meta)
{
if(meta==null||meta.length<1)
for(int i=0; i<hasFlavour.length; i++)
this.hasFlavour[i] = true;
else
for(int i : meta)
if(i>=0 && i<this.hasFlavour.length)
this.hasFlavour[i] = false;
return this;
}
public boolean hasFlavour(ItemStack stack)
{
return this.hasFlavour[Math.max(0, Math.min(stack.getItemDamage(), this.hasFlavour.length-1))];
}
public BlockIEBase<E> setBlockLayer(BlockRenderLayer... layer)
{
this.renderLayers = Sets.newHashSet(layer);
return this;
}
public BlockIEBase<E> setMetaBlockLayer(int meta, BlockRenderLayer... layer)
{
this.metaRenderLayers[Math.max(0, Math.min(meta, this.metaRenderLayers.length-1))] = Sets.newHashSet(layer);
return this;
}
@Override
public boolean canRenderInLayer(BlockRenderLayer layer)
{
if(cachedTileRequestState!=null)
{
int meta = this.getMetaFromState(cachedTileRequestState);
if(meta>=0 && meta<metaRenderLayers.length && metaRenderLayers[meta]!=null)
return metaRenderLayers[meta].contains(layer);
}
return renderLayers.contains(layer);
}
public BlockIEBase<E> setMetaLightOpacity(int meta, int opacity)
{
metaLightOpacities.put(meta, opacity);
return this;
}
@Override
public int getLightOpacity(IBlockState state, IBlockAccess w, BlockPos pos)
{
int meta = getMetaFromState(state);
if(metaLightOpacities.containsKey(meta))
return metaLightOpacities.get(meta);
return super.getLightOpacity(state,w,pos);
}
public BlockIEBase<E> setMetaExplosionResistance(int meta, int resistance)
{
metaResistances.put(meta, resistance);
return this;
}
@Override
public float getExplosionResistance(World world, BlockPos pos, Entity exploder, Explosion explosion)
{
int meta = getMetaFromState(world.getBlockState(pos));
if(metaResistances.containsKey(meta))
return metaResistances.get(meta);
return super.getExplosionResistance(world, pos, exploder, explosion);
}
public BlockIEBase<E> setNotNormalBlock(int meta)
{
if(metaNotNormalBlock == null)
metaNotNormalBlock = new boolean[this.enumValues.length];
metaNotNormalBlock[meta] = true;
return this;
}
public BlockIEBase<E> setAllNotNormalBlock()
{
if(metaNotNormalBlock == null)
metaNotNormalBlock = new boolean[this.enumValues.length];
for(int i = 0; i < metaNotNormalBlock.length; i++)
metaNotNormalBlock[i] = true;
return this;
}
protected boolean normalBlockCheck(IBlockState state)
{
if(metaNotNormalBlock == null)
return true;
int meta = getMetaFromState(state);
return (meta < 0 || meta >= metaNotNormalBlock.length) || !metaNotNormalBlock[meta];
}
@Override
public boolean isFullBlock(IBlockState state)
{
return normalBlockCheck(state);
}
@Override
public boolean isFullCube(IBlockState state)
{
return normalBlockCheck(state);
}
@Override
public boolean isOpaqueCube(IBlockState state)
{
return normalBlockCheck(state);
}
@Override
public boolean isVisuallyOpaque()
{
if(metaNotNormalBlock == null)
return true;
int majority = 0;
for(boolean b : metaNotNormalBlock)
if(b)
majority++;
return majority<metaNotNormalBlock.length/2;
}
@Override
public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos)
{
return normalBlockCheck(state);
}
//This is a ridiculously hacky workaround, I would not recommend it to anyone.
protected static IBlockState cachedTileRequestState;
@Override
public boolean hasTileEntity(IBlockState state)
{
cachedTileRequestState = state;
return super.hasTileEntity(state);
}
protected BlockStateContainer createNotTempBlockState()
{
IProperty[] array = new IProperty[1+this.additionalProperties.length];
array[0] = this.property;
for(int i=0; i<this.additionalProperties.length; i++)
array[1+i] = this.additionalProperties[i];
if(this.additionalUnlistedProperties.length>0)
return new ExtendedBlockState(this, array, additionalUnlistedProperties);
return new BlockStateContainer(this, array);
}
protected IBlockState getInitDefaultState()
{
IBlockState state = this.blockState.getBaseState().withProperty(this.property, enumValues[0]);
for(int i=0; i<this.additionalProperties.length; i++)
if(this.additionalProperties[i]!=null && !this.additionalProperties[i].getAllowedValues().isEmpty())
state = applyProperty(state, additionalProperties[i], additionalProperties[i].getAllowedValues().iterator().next());
return state;
}
protected <V extends Comparable<V>> IBlockState applyProperty(IBlockState in, IProperty<V> prop, Object val)
{
return in.withProperty(prop, (V)val);
}
public void onIEBlockPlacedBy(World world, BlockPos pos, IBlockState state, EnumFacing side, float hitX, float hitY, float hitZ, EntityLivingBase placer, ItemStack stack)
{
}
public boolean canIEBlockBePlaced(World world, BlockPos pos, IBlockState newState, EnumFacing side, float hitX, float hitY, float hitZ, EntityPlayer player, ItemStack stack)
{
return true;
}
@Override
protected BlockStateContainer createBlockState()
{
if(this.property!=null)
return createNotTempBlockState();
if(tempUnlistedProperties.length>0)
return new ExtendedBlockState(this, tempProperties, tempUnlistedProperties);
return new BlockStateContainer(this, tempProperties);
}
@Override
public void onBlockPlacedBy(World worldIn, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack)
{
super.onBlockPlacedBy(worldIn, pos, state, placer, stack);
}
@Override
public int getMetaFromState(IBlockState state)
{
if(state==null || !this.equals(state.getBlock()))
return 0;
return state.getValue(this.property).getMeta();
}
@Override
public IBlockState getActualState(IBlockState state, IBlockAccess world, BlockPos pos)
{
for(int i=0; i<this.additionalProperties.length; i++)
if(this.additionalProperties[i]!=null && !this.additionalProperties[i].getAllowedValues().isEmpty())
state = applyProperty(state, this.additionalProperties[i], this.additionalProperties[i].getAllowedValues().toArray()[0]);
return state;
}
@Override
public IBlockState getStateFromMeta(int meta)
{
IBlockState state = this.getDefaultState().withProperty(this.property, fromMeta(meta));
for(int i=0; i<this.additionalProperties.length; i++)
if(this.additionalProperties[i]!=null && !this.additionalProperties[i].getAllowedValues().isEmpty())
state = applyProperty(state, this.additionalProperties[i], this.additionalProperties[i].getAllowedValues().toArray()[0]);
return state;
// return this.getDefaultState().withProperty(this.property, fromMeta(meta));
}
protected E fromMeta(int meta)
{
if(meta<0||meta>=enumValues.length)
meta = 0;
return enumValues[meta];
}
@Override
public int damageDropped(IBlockState state)
{
return getMetaFromState(state);
}
@SideOnly(Side.CLIENT)
@Override
public void getSubBlocks(Item item, CreativeTabs tab, List<ItemStack> list)
{
for(E type : this.enumValues)
if(type.listForCreative() && !this.isMetaHidden[type.getMeta()])
list.add(new ItemStack(this, 1, type.getMeta()));
}
void adjustSound()
{
if(this.blockMaterial==Material.ANVIL)
this.blockSoundType = SoundType.ANVIL;
else if(this.blockMaterial==Material.CARPET||this.blockMaterial==Material.CLOTH)
this.blockSoundType = SoundType.CLOTH;
else if(this.blockMaterial==Material.GLASS||this.blockMaterial==Material.ICE)
this.blockSoundType = SoundType.GLASS;
else if(this.blockMaterial==Material.GRASS||this.blockMaterial==Material.TNT||this.blockMaterial==Material.PLANTS||this.blockMaterial==Material.VINE)
this.blockSoundType = SoundType.PLANT;
else if(this.blockMaterial==Material.GROUND)
this.blockSoundType = SoundType.GROUND;
else if(this.blockMaterial==Material.IRON)
this.blockSoundType = SoundType.METAL;
else if(this.blockMaterial==Material.SAND)
this.blockSoundType = SoundType.SAND;
else if(this.blockMaterial==Material.SNOW)
this.blockSoundType = SoundType.SNOW;
else if(this.blockMaterial==Material.ROCK)
this.blockSoundType = SoundType.STONE;
else if(this.blockMaterial==Material.WOOD||this.blockMaterial==Material.CACTUS)
this.blockSoundType = SoundType.WOOD;
}
@Override
public boolean eventReceived(IBlockState state, World worldIn, BlockPos pos, int eventID, int eventParam)
{
if (worldIn.isRemote&&eventID==255)
{
worldIn.notifyBlockUpdate(pos,state,state,3);
return true;
}
return super.eventReceived(state, worldIn, pos, eventID, eventParam);
}
public boolean allowHammerHarvest(IBlockState blockState)
{
return false;
}
public boolean allowWirecutterHarvest(IBlockState blockState)
{
return false;
}
public boolean isOpaqueCube()
{
return opaqueCube;
}
public BlockIEBase<E> setOpaque(boolean isOpaque)
{
opaqueCube = isOpaque;
fullBlock = isOpaque;
return this;
}
@Override
public boolean isToolEffective(String type, IBlockState state)
{
if(allowHammerHarvest(state) && type.equals(Lib.TOOL_HAMMER))
return true;
if(allowWirecutterHarvest(state) && type.equals(Lib.TOOL_WIRECUTTER))
return true;
return super.isToolEffective(type, state);
}
public String createRegistryName()
{
return ImmersiveEngineering.MODID+":"+name;
}
public interface IBlockEnum extends IStringSerializable
{
int getMeta();
boolean listForCreative();
}
public abstract static class IELadderBlock<E extends Enum<E> & IBlockEnum> extends BlockIEBase<E>
{
public IELadderBlock(String name, Material material, PropertyEnum<E> mainProperty,
Class<? extends ItemBlockIEBase> itemBlock, Object... additionalProperties)
{
super(name, material, mainProperty, itemBlock, additionalProperties);
}
@Override
public void onEntityCollidedWithBlock(World worldIn, BlockPos pos, IBlockState state, Entity entityIn)
{
super.onEntityCollidedWithBlock(worldIn, pos, state, entityIn);
if (entityIn instanceof EntityLivingBase&&!((EntityLivingBase) entityIn).isOnLadder()&&isLadder(state, worldIn, pos, (EntityLivingBase)entityIn))
{
float f5 = 0.15F;
if (entityIn.motionX < -f5)
entityIn.motionX = -f5;
if (entityIn.motionX > f5)
entityIn.motionX = f5;
if (entityIn.motionZ < -f5)
entityIn.motionZ = -f5;
if (entityIn.motionZ > f5)
entityIn.motionZ = f5;
entityIn.fallDistance = 0.0F;
if (entityIn.motionY < -0.15D)
entityIn.motionY = -0.15D;
if(entityIn.motionY<0 && entityIn instanceof EntityPlayer && entityIn.isSneaking())
{
entityIn.motionY=.05;
return;
}
if(entityIn.isCollidedHorizontally)
entityIn.motionY=.2;
}
}
}
}