package openblocks.common.block; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import java.util.Map; import java.util.Map.Entry; import java.util.Random; import net.minecraft.block.material.Material; import net.minecraft.client.renderer.texture.IIconRegister; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.ItemStack; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.IIcon; import net.minecraft.util.Vec3; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; import net.minecraftforge.client.event.DrawBlockHighlightEvent; import net.minecraftforge.common.util.ForgeDirection; import openblocks.common.tileentity.TileEntityGuide; import openmods.api.ISelectionAware; import openmods.block.BlockRotationMode; import openmods.geometry.AabbUtils; import openmods.geometry.BlockSpaceTransform; import openmods.geometry.BlockTextureTransform; import openmods.geometry.BoundingBoxMap; import openmods.geometry.HalfAxis; import openmods.geometry.Orientation; import openmods.infobook.BookDocumentation; @BookDocumentation(hasVideo = true) public class BlockGuide extends OpenBlock implements ISelectionAware { private AxisAlignedBB selection; private static double P = 1.0 / 16.0; private static final double SELECTION_BOX_DEPTH = 0.01; private interface IShapeManipulator { public boolean activate(TileEntityGuide te, EntityPlayerMP player); } private static IShapeManipulator createHalfAxisIncrementer(ForgeDirection dir) { final HalfAxis halfAxis = HalfAxis.fromDirection(dir); return new IShapeManipulator() { @Override public boolean activate(TileEntityGuide te, EntityPlayerMP player) { return te.incrementHalfAxis(halfAxis, player); } }; } private static IShapeManipulator createHalfAxisDecrementer(ForgeDirection dir) { final HalfAxis halfAxis = HalfAxis.fromDirection(dir); return new IShapeManipulator() { @Override public boolean activate(TileEntityGuide te, EntityPlayerMP player) { return te.decrementHalfAxis(halfAxis, player); } }; } private static IShapeManipulator createHalfAxisCopier(ForgeDirection dir) { final HalfAxis halfAxis = HalfAxis.fromDirection(dir); return new IShapeManipulator() { @Override public boolean activate(TileEntityGuide te, EntityPlayerMP player) { return te.copyHalfAxis(halfAxis, halfAxis.negate(), player); } }; } private IShapeManipulator createRotationManipulator(final HalfAxis ha) { return new IShapeManipulator() { @Override public boolean activate(TileEntityGuide te, EntityPlayerMP player) { return createRotationHelper(te.getWorldObj(), te.xCoord, te.yCoord, te.zCoord).rotateAroundAxis(ha); } }; } private static AxisAlignedBB addButton(ForgeDirection side, BlockTextureTransform transform, int left, int top, int width, int height) { final BlockTextureTransform.WorldCoords min = transform.textureCoordsToWorldVec(side, left * P, top * P, -SELECTION_BOX_DEPTH); final BlockTextureTransform.WorldCoords max = transform.textureCoordsToWorldVec(side, (left + width) * P, (top + height) * P, SELECTION_BOX_DEPTH); return AabbUtils.createAabb(min.x, min.y, min.z, max.x, max.y, max.z); } private void createNSWESide(BoundingBoxMap<BoundingBoxMap<IShapeManipulator>> output, ForgeDirection face) { final BlockTextureTransform transform = getRotationMode().textureTransform; final BoundingBoxMap<IShapeManipulator> subBoxes = BoundingBoxMap.create(); final ForgeDirection right = face.getRotation(ForgeDirection.DOWN); final ForgeDirection left = right.getOpposite(); final ForgeDirection top = ForgeDirection.UP; final ForgeDirection bottom = ForgeDirection.DOWN; subBoxes.addBox(addButton(face, transform, 4, 1, 3, 3), createHalfAxisDecrementer(top)); subBoxes.addBox(addButton(face, transform, 9, 1, 3, 3), createHalfAxisIncrementer(top)); subBoxes.addBox(addButton(face, transform, 6, 4, 4, 2), createHalfAxisCopier(top)); subBoxes.addBox(addButton(face, transform, 12, 4, 3, 3), createHalfAxisDecrementer(right)); subBoxes.addBox(addButton(face, transform, 12, 9, 3, 3), createHalfAxisIncrementer(right)); subBoxes.addBox(addButton(face, transform, 10, 6, 2, 4), createHalfAxisCopier(right)); subBoxes.addBox(addButton(face, transform, 9, 12, 3, 3), createHalfAxisDecrementer(bottom)); subBoxes.addBox(addButton(face, transform, 4, 12, 3, 3), createHalfAxisIncrementer(bottom)); subBoxes.addBox(addButton(face, transform, 6, 10, 4, 2), createHalfAxisCopier(bottom)); subBoxes.addBox(addButton(face, transform, 1, 9, 3, 3), createHalfAxisDecrementer(left)); subBoxes.addBox(addButton(face, transform, 1, 4, 3, 3), createHalfAxisIncrementer(left)); subBoxes.addBox(addButton(face, transform, 4, 6, 2, 4), createHalfAxisCopier(left)); final BlockTextureTransform.WorldCoords overboxMin = transform.textureCoordsToWorldVec(face, P, P, -SELECTION_BOX_DEPTH); final BlockTextureTransform.WorldCoords overboxMax = transform.textureCoordsToWorldVec(face, 1 - P, 1 - P, SELECTION_BOX_DEPTH); output.addBox(AabbUtils.createAabb(overboxMin.x, overboxMin.y, overboxMin.z, overboxMax.x, overboxMax.y, overboxMax.z), subBoxes); } private void createTopBottomSide(BoundingBoxMap<BoundingBoxMap<IShapeManipulator>> output, ForgeDirection face) { final BlockTextureTransform transform = getRotationMode().textureTransform; final BoundingBoxMap<IShapeManipulator> subBoxes = BoundingBoxMap.create(); subBoxes.addBox(addButton(face, transform, 1, 3, 4, 11), createRotationManipulator(HalfAxis.NEG_Y)); subBoxes.addBox(addButton(face, transform, 11, 3, 4, 11), createRotationManipulator(HalfAxis.POS_Y)); subBoxes.addBox(addButton(face, transform, 5, 2, 6, 3), new IShapeManipulator() { @Override public boolean activate(TileEntityGuide te, EntityPlayerMP player) { te.incrementMode(player); return true; } }); subBoxes.addBox(addButton(face, transform, 5, 8, 6, 3), new IShapeManipulator() { @Override public boolean activate(TileEntityGuide te, EntityPlayerMP player) { te.decrementMode(player); return true; } }); final BlockTextureTransform.WorldCoords overboxMin = transform.textureCoordsToWorldVec(face, P, P, -SELECTION_BOX_DEPTH); final BlockTextureTransform.WorldCoords overboxMax = transform.textureCoordsToWorldVec(face, 1 - P, 1 - P, SELECTION_BOX_DEPTH); output.addBox(AabbUtils.createAabb(overboxMin.x, overboxMin.y, overboxMin.z, overboxMax.x, overboxMax.y, overboxMax.z), subBoxes); } private BoundingBoxMap<BoundingBoxMap<IShapeManipulator>> buttons = BoundingBoxMap.create(); { createTopBottomSide(buttons, ForgeDirection.UP); createNSWESide(buttons, ForgeDirection.NORTH); createNSWESide(buttons, ForgeDirection.SOUTH); createNSWESide(buttons, ForgeDirection.EAST); createNSWESide(buttons, ForgeDirection.WEST); createTopBottomSide(buttons, ForgeDirection.DOWN); } public static class Icons { public static IIcon marker; } public BlockGuide() { super(Material.rock); setLightLevel(0.6f); setRotationMode(BlockRotationMode.THREE_FOUR_DIRECTIONS); setPlacementMode(BlockPlacementMode.SURFACE); } @Override public boolean isOpaqueCube() { return false; } @Override public boolean renderAsNormalBlock() { return true; } protected IIcon centerIcon; @Override @SideOnly(Side.CLIENT) public void registerBlockIcons(IIconRegister registry) { super.registerBlockIcons(registry); Icons.marker = registry.registerIcon("openblocks:guide"); this.centerIcon = registry.registerIcon(getCenterIconName()); setTextures(registry.registerIcon("openblocks:guide_top_new"), ForgeDirection.UP, ForgeDirection.DOWN); setTextures(registry.registerIcon("openblocks:guide_side_new"), ForgeDirection.NORTH, ForgeDirection.SOUTH, ForgeDirection.EAST, ForgeDirection.WEST); } protected String getCenterIconName() { return "openblocks:guide_center_normal"; } public IIcon getCenterTexture() { return centerIcon; } @Override public boolean canBeReplacedByLeaves(IBlockAccess world, int x, int y, int z) { return false; } @Override public boolean isFlammable(IBlockAccess world, int x, int y, int z, ForgeDirection face) { return false; } @Override @SideOnly(Side.CLIENT) public int getRenderBlockPass() { return 1; } @Override public AxisAlignedBB getSelectedBoundingBoxFromPool(World world, int x, int y, int z) { return selection != null? selection : super.getSelectedBoundingBoxFromPool(world, x, y, z); } private Map.Entry<AxisAlignedBB, IShapeManipulator> findClickBox(Vec3 pos) { final Entry<AxisAlignedBB, BoundingBoxMap<IShapeManipulator>> sideBox = buttons.findEntryContainingPoint(pos); if (sideBox != null) { final Entry<AxisAlignedBB, IShapeManipulator> subSideBox = sideBox.getValue().findEntryContainingPoint(pos); if (subSideBox != null) return subSideBox; } return null; } protected boolean areButtonsActive(EntityPlayer player) { return true; } protected boolean onItemUse(EntityPlayerMP player, TileEntityGuide guide, int side, float hitX, float hitY, float hitZ) { return false; } @Override public boolean onSelected(World world, int x, int y, int z, DrawBlockHighlightEvent evt) { if (areButtonsActive(evt.player)) { final Vec3 hitVec = evt.target.hitVec; final int metadata = world.getBlockMetadata(x, y, z); final BlockRotationMode rotationMode = getRotationMode(); final Orientation orientation = rotationMode.fromValue(metadata); final Vec3 localHit = BlockSpaceTransform.instance.mapWorldToBlock(orientation, hitVec.xCoord - x, hitVec.yCoord - y, hitVec.zCoord - z); final Entry<AxisAlignedBB, IShapeManipulator> clickBox = findClickBox(localHit); selection = clickBox != null? BlockSpaceTransform.instance.mapBlockToWorld(orientation, clickBox.getKey()).offset(x, y, z) : null; } else selection = null; return false; } @Override public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float hitX, float hitY, float hitZ) { if (player instanceof EntityPlayerMP) { final TileEntityGuide guide = getTileEntity(world, x, y, z, TileEntityGuide.class); if (guide != null) { final EntityPlayerMP playerMP = (EntityPlayerMP)player; if (areButtonsActive(playerMP)) { final int metadata = world.getBlockMetadata(x, y, z); final BlockRotationMode rotationMode = getRotationMode(); final Orientation orientation = rotationMode.fromValue(metadata); final Vec3 localHit = BlockSpaceTransform.instance.mapWorldToBlock(orientation, hitX, hitY, hitZ); final Entry<AxisAlignedBB, IShapeManipulator> clickBox = findClickBox(localHit); if (clickBox != null) return clickBox.getValue().activate(guide, playerMP); } final ItemStack heldStack = playerMP.getHeldItem(); if (heldStack != null) { if (guide.onItemUse(playerMP, heldStack, side, hitX, hitY, hitZ)) return true; } } } return true; } @Override @SideOnly(Side.CLIENT) public void randomDisplayTick(World world, int x, int y, int z, Random random) { world.spawnParticle("smoke", x + 0.5f, y + 0.7f, z + 0.5f, 0.0D, 0.0D, 0.0D); world.spawnParticle("flame", x + 0.5f, y + 0.7f, z + 0.5f, 0.0D, 0.0D, 0.0D); } }