/* * This file is part of Applied Energistics 2. * Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved. * * Applied Energistics 2 is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Applied Energistics 2 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Applied Energistics 2. If not, see . */ package appeng.parts; import appeng.api.AEApi; import appeng.api.definitions.IBlockDefinition; import appeng.api.definitions.IItems; import appeng.api.parts.*; import appeng.api.util.AEPartLocation; import appeng.api.util.DimensionalCoord; import appeng.core.AppEng; import appeng.core.sync.network.NetworkHandler; import appeng.core.sync.packets.PacketClick; import appeng.core.sync.packets.PacketPartPlacement; import appeng.facade.IFacadeItem; import appeng.util.LookDirection; import appeng.util.Platform; import net.minecraft.block.Block; import net.minecraft.block.SoundType; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumActionResult; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.SoundCategory; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.player.PlayerDestroyItemEvent; import net.minecraftforge.event.entity.player.PlayerInteractEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.TickEvent; import java.util.ArrayList; import java.util.List; import java.util.Optional; public class PartPlacement { private static float eyeHeight = 0.0f; private final ThreadLocal placing = new ThreadLocal<>(); private boolean wasCanceled = false; public static EnumActionResult place(final ItemStack held, final BlockPos pos, EnumFacing side, final EntityPlayer player, final EnumHand hand, final World world, PlaceType pass, final int depth) { if (depth > 3) { return EnumActionResult.FAIL; } if (!held.isEmpty() && Platform.isWrench(player, held, pos) && player.isSneaking()) { if (!Platform.hasPermissions(new DimensionalCoord(world, pos), player)) { return EnumActionResult.FAIL; } final Block block = world.getBlockState(pos).getBlock(); final TileEntity tile = world.getTileEntity(pos); IPartHost host = null; if (tile instanceof IPartHost) { host = (IPartHost) tile; } if (host != null) { if (!world.isRemote) { final LookDirection dir = Platform.getPlayerRay(player, getEyeOffset(player)); final RayTraceResult mop = block.collisionRayTrace(world.getBlockState(pos), world, pos, dir.getA(), dir.getB()); if (mop != null) { final List is = new ArrayList<>(); final SelectedPart sp = selectPart(player, host, mop.hitVec.addVector(-mop.getBlockPos().getX(), -mop.getBlockPos().getY(), -mop.getBlockPos().getZ())); if (sp.part != null) { is.add(sp.part.getItemStack(PartItemStack.WRENCH)); sp.part.getDrops(is, true); host.removePart(sp.side, false); } if (sp.facade != null) { is.add(sp.facade.getItemStack()); host.getFacadeContainer().removeFacade(host, sp.side); Platform.notifyBlocksOfNeighbors(world, pos); } if (host.isEmpty()) { host.cleanup(); } if (!is.isEmpty()) { Platform.spawnDrops(world, pos, is); } } } else { player.swingArm(hand); NetworkHandler.instance().sendToServer(new PacketPartPlacement(pos, side, getEyeOffset(player), hand)); } return EnumActionResult.SUCCESS; } return EnumActionResult.PASS; } TileEntity tile = world.getTileEntity(pos); IPartHost host = null; if (tile instanceof IPartHost) { host = (IPartHost) tile; } if (!held.isEmpty()) { final IFacadePart fp = isFacade(held, AEPartLocation.fromFacing(side)); if (fp != null) { if (host != null) { if (!world.isRemote) { if (host.getPart(AEPartLocation.INTERNAL) == null) { return EnumActionResult.FAIL; } if (host.canAddPart(held, AEPartLocation.fromFacing(side))) { if (host.getFacadeContainer().addFacade(fp)) { host.markForSave(); host.markForUpdate(); if (!player.capabilities.isCreativeMode) { held.grow(-1); if (held.getCount() == 0) { player.setHeldItem(hand, ItemStack.EMPTY); MinecraftForge.EVENT_BUS.post(new PlayerDestroyItemEvent(player, held, hand)); } } return EnumActionResult.SUCCESS; } } } else { player.swingArm(hand); NetworkHandler.instance().sendToServer(new PacketPartPlacement(pos, side, getEyeOffset(player), hand)); return EnumActionResult.SUCCESS; } } return EnumActionResult.FAIL; } } if (held.isEmpty()) { final Block block = world.getBlockState(pos).getBlock(); if (host != null && player.isSneaking() && block != null) { final LookDirection dir = Platform.getPlayerRay(player, getEyeOffset(player)); final RayTraceResult mop = block.collisionRayTrace(world.getBlockState(pos), world, pos, dir.getA(), dir.getB()); if (mop != null) { mop.hitVec = mop.hitVec.addVector(-mop.getBlockPos().getX(), -mop.getBlockPos().getY(), -mop.getBlockPos().getZ()); final SelectedPart sPart = selectPart(player, host, mop.hitVec); if (sPart != null && sPart.part != null) { if (sPart.part.onShiftActivate(player, hand, mop.hitVec)) { if (world.isRemote) { NetworkHandler.instance().sendToServer(new PacketPartPlacement(pos, side, getEyeOffset(player), hand)); } return EnumActionResult.SUCCESS; } } } } } if (held.isEmpty() || !(held.getItem() instanceof IPartItem)) { return EnumActionResult.PASS; } BlockPos te_pos = pos; final IBlockDefinition multiPart = AEApi.instance().definitions().blocks().multiPart(); if (host == null && pass == PlaceType.PLACE_ITEM) { EnumFacing offset = null; final Block blkID = world.getBlockState(pos).getBlock(); if (blkID != null && !blkID.isReplaceable(world, pos)) { offset = side; if (Platform.isServer()) { side = side.getOpposite(); } } te_pos = offset == null ? pos : pos.offset(offset); tile = world.getTileEntity(te_pos); if (tile instanceof IPartHost) { host = (IPartHost) tile; } final Optional maybeMultiPartStack = multiPart.maybeStack(1); final Optional maybeMultiPartBlock = multiPart.maybeBlock(); final Optional maybeMultiPartItemBlock = multiPart.maybeItemBlock(); final boolean hostIsNotPresent = host == null; final boolean multiPartPresent = maybeMultiPartBlock.isPresent() && maybeMultiPartStack.isPresent() && maybeMultiPartItemBlock.isPresent(); final boolean canMultiPartBePlaced = maybeMultiPartBlock.get().canPlaceBlockAt(world, te_pos); if (hostIsNotPresent && multiPartPresent && canMultiPartBePlaced && maybeMultiPartItemBlock.get() .placeBlockAt(maybeMultiPartStack.get(), player, world, te_pos, side, 0.5f, 0.5f, 0.5f, maybeMultiPartBlock.get().getDefaultState())) { if (!world.isRemote) { tile = world.getTileEntity(te_pos); if (tile instanceof IPartHost) { host = (IPartHost) tile; } pass = PlaceType.INTERACT_SECOND_PASS; } else { player.swingArm(hand); NetworkHandler.instance().sendToServer(new PacketPartPlacement(pos, side, getEyeOffset(player), hand)); return EnumActionResult.SUCCESS; } } else if (host != null && !host.canAddPart(held, AEPartLocation.fromFacing(side))) { return EnumActionResult.FAIL; } } if (host == null) { return EnumActionResult.PASS; } if (!host.canAddPart(held, AEPartLocation.fromFacing(side))) { if (pass == PlaceType.INTERACT_FIRST_PASS || pass == PlaceType.PLACE_ITEM) { te_pos = pos.offset(side); final Block blkID = world.getBlockState(te_pos).getBlock(); if (blkID == null || blkID.isReplaceable(world, te_pos) || host != null) { return place(held, te_pos, side.getOpposite(), player, hand, world, pass == PlaceType.INTERACT_FIRST_PASS ? PlaceType.INTERACT_SECOND_PASS : PlaceType.PLACE_ITEM, depth + 1); } } return EnumActionResult.PASS; } if (!world.isRemote) { final IBlockState state = world.getBlockState(pos); final LookDirection dir = Platform.getPlayerRay(player, getEyeOffset(player)); final RayTraceResult mop = state.getBlock().collisionRayTrace(state, world, pos, dir.getA(), dir.getB()); if (mop != null) { final SelectedPart sp = selectPart(player, host, mop.hitVec.addVector(-mop.getBlockPos().getX(), -mop.getBlockPos().getY(), -mop.getBlockPos().getZ())); if (sp.part != null) { if (!player.isSneaking() && sp.part.onActivate(player, hand, mop.hitVec)) { return EnumActionResult.FAIL; } } } final DimensionalCoord dc = host.getLocation(); if (!Platform.hasPermissions(dc, player)) { return EnumActionResult.FAIL; } final AEPartLocation mySide = host.addPart(held, AEPartLocation.fromFacing(side), player, hand); if (mySide != null) { multiPart.maybeBlock().ifPresent(multiPartBlock -> { final SoundType ss = multiPartBlock.getSoundType(state, world, pos, player); world.playSound(null, pos, ss.getPlaceSound(), SoundCategory.BLOCKS, (ss.getVolume() + 1.0F) / 2.0F, ss.getPitch() * 0.8F); }); if (!player.capabilities.isCreativeMode) { held.grow(-1); if (held.getCount() == 0) { player.setHeldItem(hand, ItemStack.EMPTY); MinecraftForge.EVENT_BUS.post(new PlayerDestroyItemEvent(player, held, hand)); } } } } else { player.swingArm(hand); } return EnumActionResult.SUCCESS; } private static float getEyeOffset(final EntityPlayer p) { if (p.world.isRemote) { return Platform.getEyeOffset(p); } return getEyeHeight(); } private static SelectedPart selectPart(final EntityPlayer player, final IPartHost host, final Vec3d pos) { AppEng.proxy.updateRenderMode(player); final SelectedPart sp = host.selectPart(pos); AppEng.proxy.updateRenderMode(null); return sp; } public static IFacadePart isFacade(final ItemStack held, final AEPartLocation side) { if (held.getItem() instanceof IFacadeItem) { return ((IFacadeItem) held.getItem()).createPartFromItemStack(held, side); } return null; } @SubscribeEvent public void playerInteract(final TickEvent.ClientTickEvent event) { this.wasCanceled = false; } @SubscribeEvent public void playerInteract(final PlayerInteractEvent event) { // Only handle the main hand event if (event.getHand() != EnumHand.MAIN_HAND) { return; } if (event instanceof PlayerInteractEvent.RightClickEmpty && event.getEntityPlayer().world.isRemote) { // re-check to see if this event was already channeled, cause these two events are really stupid... final RayTraceResult mop = Platform.rayTrace(event.getEntityPlayer(), true, false); final Minecraft mc = Minecraft.getMinecraft(); final float f = 1.0F; final double d0 = mc.playerController.getBlockReachDistance(); final Vec3d vec3 = mc.getRenderViewEntity().getPositionEyes(f); if (mop != null && mop.hitVec.distanceTo(vec3) < d0) { final World w = event.getEntity().world; final TileEntity te = w.getTileEntity(mop.getBlockPos()); if (te instanceof IPartHost && this.wasCanceled) { event.setCanceled(true); } } else { final ItemStack held = event.getEntityPlayer().getHeldItem(event.getHand()); final IItems items = AEApi.instance().definitions().items(); boolean supportedItem = items.memoryCard().isSameAs(held); supportedItem |= items.colorApplicator().isSameAs(held); if (event.getEntityPlayer().isSneaking() && !held.isEmpty() && supportedItem) { NetworkHandler.instance().sendToServer(new PacketClick(event.getPos(), event.getFace(), 0, 0, 0, event.getHand())); } } } else if (event instanceof PlayerInteractEvent.RightClickBlock && !event.getEntityPlayer().world.isRemote) { if (this.placing.get() != null) { return; } this.placing.set(event); final ItemStack held = event.getEntityPlayer().getHeldItem(event.getHand()); if (place(held, event.getPos(), event.getFace(), event.getEntityPlayer(), event.getHand(), event.getEntityPlayer().world, PlaceType.INTERACT_FIRST_PASS, 0) == EnumActionResult.SUCCESS) { event.setCanceled(true); this.wasCanceled = true; } this.placing.set(null); } } private static float getEyeHeight() { return eyeHeight; } public static void setEyeHeight(final float eyeHeight) { PartPlacement.eyeHeight = eyeHeight; } public enum PlaceType { PLACE_ITEM, INTERACT_FIRST_PASS, INTERACT_SECOND_PASS } }