package com.jaquadro.minecraft.storagedrawers.block; import com.jaquadro.minecraft.chameleon.block.properties.UnlistedModelData; import com.jaquadro.minecraft.storagedrawers.StorageDrawers; import com.jaquadro.minecraft.storagedrawers.api.pack.BlockType; import com.jaquadro.minecraft.storagedrawers.api.security.ISecurityProvider; import com.jaquadro.minecraft.storagedrawers.api.storage.EnumBasicDrawer; import com.jaquadro.minecraft.storagedrawers.api.storage.IDrawer; import com.jaquadro.minecraft.storagedrawers.api.storage.INetworked; import com.jaquadro.minecraft.storagedrawers.block.dynamic.StatusModelData; import com.jaquadro.minecraft.storagedrawers.api.storage.attribute.LockAttribute; import com.jaquadro.minecraft.storagedrawers.block.modeldata.DrawerStateModelData; import com.jaquadro.minecraft.storagedrawers.block.tile.TileEntityDrawers; import com.jaquadro.minecraft.storagedrawers.block.tile.TileEntityDrawersStandard; import com.jaquadro.minecraft.storagedrawers.config.ConfigManager; import com.jaquadro.minecraft.storagedrawers.config.PlayerConfigSetting; import com.jaquadro.minecraft.storagedrawers.core.ModCreativeTabs; import com.jaquadro.minecraft.storagedrawers.core.ModItems; import com.jaquadro.minecraft.storagedrawers.inventory.DrawerInventoryHelper; import com.jaquadro.minecraft.storagedrawers.core.handlers.GuiHandler; import com.jaquadro.minecraft.storagedrawers.item.*; import com.jaquadro.minecraft.storagedrawers.security.SecurityManager; import net.minecraft.block.*; import net.minecraft.block.material.Material; import net.minecraft.block.properties.*; import net.minecraft.block.state.BlockStateContainer; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.particle.ParticleManager; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.SoundEvents; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.*; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.text.TextComponentTranslation; 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.IExtendedBlockState; import net.minecraftforge.common.property.IUnlistedProperty; import net.minecraftforge.fml.common.FMLLog; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import org.apache.logging.log4j.Level; import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.List; import java.util.Map; public abstract class BlockDrawers extends BlockContainer implements INetworked { public static final PropertyDirection FACING = PropertyDirection.create("facing", EnumFacing.Plane.HORIZONTAL); public static final IUnlistedProperty<DrawerStateModelData> STATE_MODEL = UnlistedModelData.create(DrawerStateModelData.class); private static final AxisAlignedBB AABB_NORTH_HALF = new AxisAlignedBB(0, 0, .5, 1, 1, 1); private static final AxisAlignedBB AABB_SOUTH_HALF = new AxisAlignedBB(0, 0, 0, 1, 1, .5); private static final AxisAlignedBB AABB_WEST_HALF = new AxisAlignedBB(.5, 0, 0, 1, 1, 1); private static final AxisAlignedBB AABB_EAST_HALF = new AxisAlignedBB(0, 0, 0, .5, 1, 1); @SideOnly(Side.CLIENT) private StatusModelData[] statusInfo; private long ignoreEventTime; private static final ThreadLocal<Boolean> inTileLookup = new ThreadLocal<Boolean>() { @Override protected Boolean initialValue () { return false; } }; public BlockDrawers (String blockName) { this(Material.WOOD, blockName); } protected BlockDrawers (Material material, String blockName) { super(material); this.useNeighborBrightness = true; setCreativeTab(ModCreativeTabs.tabStorageDrawers); setHardness(5f); setSoundType(SoundType.WOOD); setUnlocalizedName(blockName); setRegistryName(blockName); setLightOpacity(255); initDefaultState(); } protected void initDefaultState () { setDefaultState(blockState.getBaseState().withProperty(FACING, EnumFacing.NORTH)); } public boolean retrimBlock (World world, BlockPos pos, ItemStack prototype) { return false; } public BlockType retrimType () { return BlockType.Drawers; } // TODO: ABSTRACT? public int getDrawerCount (IBlockState state) { return 0; } public boolean isHalfDepth (IBlockState state) { return false; } public EnumFacing getDirection (IBlockAccess blockAccess, BlockPos pos) { TileEntityDrawers tile = getTileEntity(blockAccess, pos); return (tile != null) ? EnumFacing.getFront(tile.getDirection()) : EnumFacing.NORTH; } @SideOnly(Side.CLIENT) public void initDynamic () { } @SideOnly(Side.CLIENT) public StatusModelData getStatusInfo (IBlockState state) { return null; } @Override public EnumBlockRenderType getRenderType (IBlockState state) { return EnumBlockRenderType.MODEL; } @Override public BlockRenderLayer getBlockLayer () { return BlockRenderLayer.CUTOUT_MIPPED; } @Override public boolean canRenderInLayer (IBlockState state, BlockRenderLayer layer) { return layer == BlockRenderLayer.CUTOUT_MIPPED || layer == BlockRenderLayer.TRANSLUCENT; } @Override @SuppressWarnings("deprecation") public AxisAlignedBB getBoundingBox (IBlockState state, IBlockAccess blockAccess, BlockPos pos) { TileEntityDrawers tile = getTileEntity(blockAccess, pos); if (tile != null && isHalfDepth(state)) { switch (EnumFacing.getFront(tile.getDirection())) { case NORTH: return AABB_NORTH_HALF; case SOUTH: return AABB_SOUTH_HALF; case WEST: return AABB_WEST_HALF; case EAST: return AABB_EAST_HALF; } } return FULL_BLOCK_AABB; } @Override @SuppressWarnings("deprecation") public void addCollisionBoxToList (IBlockState state, World world, BlockPos pos, AxisAlignedBB aabb, List<AxisAlignedBB> list, Entity entity) { addCollisionBoxToList(pos, aabb, list, getBoundingBox(state, world, pos)); } @Override public void onBlockAdded (World world, BlockPos pos, IBlockState state) { if (!world.isRemote) { IBlockState blockNorth = world.getBlockState(pos.north()); IBlockState blockSouth = world.getBlockState(pos.south()); IBlockState blockWest = world.getBlockState(pos.west()); IBlockState blockEast = world.getBlockState(pos.east()); EnumFacing facing = state.getValue(FACING); if (facing == EnumFacing.NORTH && blockNorth.isFullBlock() && !blockSouth.isFullBlock()) facing = EnumFacing.SOUTH; if (facing == EnumFacing.SOUTH && blockSouth.isFullBlock() && !blockNorth.isFullBlock()) facing = EnumFacing.NORTH; if (facing == EnumFacing.WEST && blockWest.isFullBlock() && !blockEast.isFullBlock()) facing = EnumFacing.EAST; if (facing == EnumFacing.EAST && blockEast.isFullBlock() && !blockWest.isFullBlock()) facing = EnumFacing.WEST; TileEntityDrawers tile = getTileEntitySafe(world, pos); tile.setDirection(facing.ordinal()); tile.markDirty(); world.setBlockState(pos, state.withProperty(FACING, facing)); } super.onBlockAdded(world, pos, state); } @Override public void onBlockPlacedBy (World world, BlockPos pos, IBlockState state, EntityLivingBase entity, ItemStack itemStack) { EnumFacing facing = entity.getHorizontalFacing().getOpposite(); TileEntityDrawers tile = getTileEntitySafe(world, pos); tile.setDirection(facing.ordinal()); tile.markDirty(); if (itemStack.hasDisplayName()) tile.setInventoryName(itemStack.getDisplayName()); world.setBlockState(pos, state.withProperty(FACING, facing), 3); } @Override public boolean onBlockActivated (World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, ItemStack item, EnumFacing side, float hitX, float hitY, float hitZ) { if (hand == EnumHand.OFF_HAND) return false; if (world.isRemote && Minecraft.getSystemTime() == ignoreEventTime) { ignoreEventTime = 0; return false; } TileEntityDrawers tileDrawers = getTileEntitySafe(world, pos); if (!SecurityManager.hasAccess(player.getGameProfile(), tileDrawers)) return false; if (StorageDrawers.config.cache.debugTrace) { FMLLog.log(StorageDrawers.MOD_ID, Level.INFO, "BlockDrawers.onBlockActivated"); FMLLog.log(StorageDrawers.MOD_ID, Level.INFO, (item == null) ? " null item" : " " + item.toString()); } if (item != null && item.getItem() != null) { if (item.getItem() instanceof ItemTrim && player.isSneaking()) { if (!retrimBlock(world, pos, item)) return false; if (!player.capabilities.isCreativeMode) { if (--item.stackSize <= 0) player.inventory.setInventorySlotContents(player.inventory.currentItem, null); } return true; } else if (item.getItem() instanceof ItemUpgrade) { if (item.getItem() == ModItems.upgradeOneStack && !tileDrawers.canAddOneStackUpgrade()) { if (!world.isRemote) player.sendStatusMessage(new TextComponentTranslation("storagedrawers.msg.cannotAddUpgrade")); return false; } if (!tileDrawers.canAddUpgrade(item)) return false; if (!tileDrawers.addUpgrade(item)) { if (!world.isRemote) player.sendStatusMessage(new TextComponentTranslation("storagedrawers.msg.maxUpgrades")); return false; } world.notifyBlockUpdate(pos, state, state, 3); if (!player.capabilities.isCreativeMode) { if (--item.stackSize <= 0) player.inventory.setInventorySlotContents(player.inventory.currentItem, null); } return true; } else if (item.getItem() == ModItems.drawerKey) { boolean locked = tileDrawers.isItemLocked(LockAttribute.LOCK_POPULATED); tileDrawers.setItemLocked(LockAttribute.LOCK_POPULATED, !locked); tileDrawers.setItemLocked(LockAttribute.LOCK_EMPTY, !locked); return true; } else if (item.getItem() == ModItems.shroudKey) { tileDrawers.setIsShrouded(!tileDrawers.isShrouded()); return true; } else if (item.getItem() == ModItems.quantifyKey) { tileDrawers.setIsShowingQuantity(!tileDrawers.isShowingQuantity()); return true; } else if (item.getItem() instanceof ItemPersonalKey) { String securityKey = ((ItemPersonalKey) item.getItem()).getSecurityProviderKey(item.getItemDamage()); ISecurityProvider provider = StorageDrawers.securityRegistry.getProvider(securityKey); if (tileDrawers.getOwner() == null) { tileDrawers.setOwner(player.getPersistentID()); tileDrawers.setSecurityProvider(provider); } else if (SecurityManager.hasOwnership(player.getGameProfile(), tileDrawers)) { tileDrawers.setOwner(null); tileDrawers.setSecurityProvider(null); } else return false; return true; } else if (item.getItem() == ModItems.tape) return false; } else if (item == null && player.isSneaking()) { if (tileDrawers.isSealed()) { tileDrawers.setIsSealed(false); return true; } else if (StorageDrawers.config.cache.enableDrawerUI) { player.openGui(StorageDrawers.instance, GuiHandler.drawersGuiID, world, pos.getX(), pos.getY(), pos.getZ()); return true; } } if (tileDrawers.getDirection() != side.ordinal()) return false; if (tileDrawers.isSealed()) return false; int slot = getDrawerSlot(getDrawerCount(state), side.ordinal(), hitX, hitY, hitZ); tileDrawers.interactPutItemsIntoSlot(slot, player); if (item != null && item.stackSize == 0) player.setHeldItem(hand, null); return true; } protected int getDrawerSlot (int drawerCount, int side, float hitX, float hitY, float hitZ) { return 0; } protected boolean hitTop (float hitY) { return hitY > .5; } protected boolean hitLeft (int side, float hitX, float hitZ) { switch (side) { case 2: return hitX > .5; case 3: return hitX < .5; case 4: return hitZ < .5; case 5: return hitZ > .5; default: return true; } } @Override public void onBlockClicked(World worldIn, BlockPos pos, EntityPlayer playerIn) { if (worldIn.isRemote) { return; } if (StorageDrawers.config.cache.debugTrace) FMLLog.log(StorageDrawers.MOD_ID, Level.INFO, "onBlockClicked"); RayTraceResult rayResult = net.minecraftforge.common.ForgeHooks.rayTraceEyes(playerIn, ((EntityPlayerMP) playerIn).interactionManager.getBlockReachDistance() + 1); if (rayResult == null) return; EnumFacing side = rayResult.sideHit; // adjust hitVec for drawers float hitX = (float)(rayResult.hitVec.xCoord - pos.getX()); float hitY = (float)(rayResult.hitVec.yCoord - pos.getY()); float hitZ = (float)(rayResult.hitVec.zCoord - pos.getZ()); TileEntityDrawers tileDrawers = getTileEntitySafe(worldIn, pos); if (tileDrawers.getDirection() != side.ordinal()) return; if (tileDrawers.isSealed()) return; if (!SecurityManager.hasAccess(playerIn.getGameProfile(), tileDrawers)) return; int slot = getDrawerSlot(getDrawerCount(worldIn.getBlockState(pos)), side.ordinal(), hitX, hitY, hitZ); IDrawer drawer = tileDrawers.getDrawer(slot); ItemStack item; Map<String, PlayerConfigSetting<?>> configSettings = ConfigManager.serverPlayerConfigSettings.get(playerIn.getUniqueID()); boolean invertShift = false; if (configSettings != null) { PlayerConfigSetting<Boolean> setting = (PlayerConfigSetting<Boolean>) configSettings.get("invertShift"); if (setting != null) { invertShift = setting.value; } } if (playerIn.isSneaking() != invertShift) item = tileDrawers.takeItemsFromSlot(slot, drawer.getStoredItemStackSize()); else item = tileDrawers.takeItemsFromSlot(slot, 1); if (StorageDrawers.config.cache.debugTrace) FMLLog.log(StorageDrawers.MOD_ID, Level.INFO, (item == null) ? " null item" : " " + item.toString()); IBlockState state = worldIn.getBlockState(pos); if (item != null && item.stackSize > 0) { if (!playerIn.inventory.addItemStackToInventory(item)) { dropItemStack(worldIn, pos.offset(side), playerIn, item); worldIn.notifyBlockUpdate(pos, state, state, 3); } else worldIn.playSound(null, pos.getX() + .5f, pos.getY() + .5f, pos.getZ() + .5f, SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.PLAYERS, .2f, ((worldIn.rand.nextFloat() - worldIn.rand.nextFloat()) * .7f + 1) * 2); } } @Override public boolean rotateBlock (World world, BlockPos pos, EnumFacing axis) { TileEntityDrawers tile = getTileEntitySafe(world, pos); if (tile.isSealed()) { dropBlockAsItem(world, pos, world.getBlockState(pos), 0); world.setBlockToAir(pos); return true; } boolean result = super.rotateBlock(world, pos, axis); if (result) tile.setDirection(world.getBlockState(pos).getValue(FACING).getIndex()); return result; } @Override public boolean isSideSolid (IBlockState state, @Nonnull IBlockAccess world, @Nonnull BlockPos pos, EnumFacing side) { TileEntityDrawers tile = getTileEntity(world, pos); if (tile == null) return true; if (isHalfDepth(state)) return side.getOpposite().ordinal() == tile.getDirection(); if (side == EnumFacing.DOWN) { Block blockUnder = world.getBlockState(pos.down()).getBlock(); if (blockUnder instanceof BlockChest || blockUnder instanceof BlockEnderChest) return false; } return side.ordinal() != tile.getDirection(); } private void dropItemStack (World world, BlockPos pos, EntityPlayer player, ItemStack stack) { EntityItem entity = new EntityItem(world, pos.getX() + .5f, pos.getY() + .1f, pos.getZ() + .5f, stack); entity.addVelocity(-entity.motionX, -entity.motionY, -entity.motionZ); world.spawnEntity(entity); } @Override public int damageDropped (IBlockState state) { return getMetaFromState(state); } @Override public boolean removedByPlayer (IBlockState state, World world, BlockPos pos, EntityPlayer player, boolean willHarvest) { if (player.capabilities.isCreativeMode) { float blockReachDistance = 0; if (world.isRemote) { blockReachDistance = Minecraft.getMinecraft().playerController.getBlockReachDistance() + 1; } else { blockReachDistance = (float) ((EntityPlayerMP) player).interactionManager.getBlockReachDistance() + 1; } RayTraceResult rayResult = net.minecraftforge.common.ForgeHooks.rayTraceEyes(player, blockReachDistance + 1); if (rayResult == null || getDirection(world, pos) != rayResult.sideHit) world.setBlockState(pos, net.minecraft.init.Blocks.AIR.getDefaultState(), world.isRemote ? 11 : 3); else onBlockClicked(world, pos, player); return false; } return willHarvest || super.removedByPlayer(state, world, pos, player, false); } @Override public void breakBlock (World world, BlockPos pos, IBlockState state) { TileEntityDrawers tile = getTileEntity(world, pos); if (tile != null && !tile.isSealed()) { for (int i = 0; i < tile.getUpgradeSlotCount(); i++) { ItemStack stack = tile.getUpgrade(i); if (stack != null) { if (stack.getItem() instanceof ItemUpgradeCreative) continue; spawnAsEntity(world, pos, stack); } } if (!tile.isVending()) DrawerInventoryHelper.dropInventoryItems(world, pos, tile); } super.breakBlock(world, pos, state); } @Override @Nonnull public List<ItemStack> getDrops (IBlockAccess world, BlockPos pos, IBlockState state, int fortune) { List<ItemStack> drops = new ArrayList<ItemStack>(); drops.add(getMainDrop(world, pos, state)); return drops; } protected ItemStack getMainDrop (IBlockAccess world, BlockPos pos, IBlockState state) { ItemStack drop = new ItemStack(Item.getItemFromBlock(this), 1, state.getBlock().getMetaFromState(state)); TileEntityDrawers tile = getTileEntity(world, pos); if (tile == null) return drop; NBTTagCompound data = drop.getTagCompound(); if (data == null) data = new NBTTagCompound(); if (tile.isSealed()) { NBTTagCompound tiledata = new NBTTagCompound(); tile.writeToNBT(tiledata); data.setTag("tile", tiledata); } drop.setTagCompound(data); return drop; } @Override public void harvestBlock (World world, EntityPlayer player, BlockPos pos, IBlockState state, TileEntity te, ItemStack stack) { super.harvestBlock(world, player, pos, state, te, stack); world.setBlockToAir(pos); } @Override public float getExplosionResistance (World world, BlockPos pos, Entity exploder, Explosion explosion) { TileEntityDrawers tile = getTileEntity(world, pos); if (tile != null) { for (int slot = 0; slot < 5; slot++) { ItemStack stack = tile.getUpgrade(slot); if (stack == null || !(stack.getItem() instanceof ItemUpgradeStorage)) continue; if (EnumUpgradeStorage.byMetadata(stack.getMetadata()) != EnumUpgradeStorage.OBSIDIAN) continue; return 1000; } } return super.getExplosionResistance(world, pos, exploder, explosion); } public TileEntityDrawers getTileEntity (IBlockAccess blockAccess, BlockPos pos) { if (inTileLookup.get()) return null; inTileLookup.set(true); TileEntity tile = blockAccess.getTileEntity(pos); inTileLookup.set(false); return (tile instanceof TileEntityDrawers) ? (TileEntityDrawers) tile : null; } public TileEntityDrawers getTileEntitySafe (World world, BlockPos pos) { TileEntityDrawers tile = getTileEntity(world, pos); if (tile == null) { tile = (TileEntityDrawers) createNewTileEntity(world, 0); world.setTileEntity(pos, tile); } return tile; } @Override @SideOnly(Side.CLIENT) public boolean addHitEffects (IBlockState state, World worldObj, RayTraceResult target, ParticleManager manager) { if (getDirection(worldObj, target.getBlockPos()) == target.sideHit) return true; return super.addHitEffects(state, worldObj, target, manager); } @Override public boolean addDestroyEffects (World world, BlockPos pos, ParticleManager manager) { //TileEntityDrawers tile = getTileEntity(world, pos); //if (tile != null && !tile.getWillDestroy()) // return true; return super.addDestroyEffects(world, pos, manager); } @Override @SuppressWarnings("deprecation") public boolean canProvidePower (IBlockState state) { return true; } @Override @SuppressWarnings("deprecation") public int getWeakPower (IBlockState state, IBlockAccess worldIn, BlockPos pos, EnumFacing side) { if (!canProvidePower(state)) return 0; TileEntityDrawers tile = getTileEntity(worldIn, pos); if (tile == null || !tile.isRedstone()) return 0; return tile.getRedstoneLevel(); } @Override @SuppressWarnings("deprecation") public int getStrongPower (IBlockState state, IBlockAccess worldIn, BlockPos pos, EnumFacing side) { return (side == EnumFacing.UP) ? getWeakPower(state, worldIn, pos, side) : 0; } @Override @SuppressWarnings("deprecation") public IBlockState getActualState (IBlockState state, IBlockAccess worldIn, BlockPos pos) { TileEntityDrawers tile = getTileEntity(worldIn, pos); if (tile == null) return state; EnumFacing facing = EnumFacing.getFront(tile.getDirection()); if (facing.getAxis() == EnumFacing.Axis.Y) facing = EnumFacing.NORTH; return state.withProperty(FACING, facing); } @Override public IBlockState getExtendedState (IBlockState state, IBlockAccess world, BlockPos pos) { state = getActualState(state, world, pos); if (!(state instanceof IExtendedBlockState)) return state; TileEntityDrawers tile = getTileEntity(world, pos); if (tile == null) return state; return ((IExtendedBlockState)state).withProperty(STATE_MODEL, new DrawerStateModelData(tile)); } }