Add part placement preview, render all part boxes on hover (#349)
This commit is contained in:
@@ -41,6 +41,13 @@ import appeng.api.util.AEPartLocation;
|
||||
public interface IFacadeContainer
|
||||
{
|
||||
|
||||
/**
|
||||
* Checks if the {@link IFacadePart} can be added to the given side.
|
||||
*
|
||||
* @return true if the facade can be successfully added
|
||||
*/
|
||||
boolean canAddFacade( IFacadePart a );
|
||||
|
||||
/**
|
||||
* Attempts to add the {@link IFacadePart} to the given side.
|
||||
*
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
package appeng.api.parts;
|
||||
|
||||
|
||||
import appeng.api.util.AEPartLocation;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumActionResult;
|
||||
@@ -32,6 +33,8 @@ import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
public interface IPartHelper
|
||||
{
|
||||
@@ -53,4 +56,51 @@ public interface IPartHelper
|
||||
* @return the render mode
|
||||
*/
|
||||
CableRenderMode getCableRenderMode();
|
||||
|
||||
/**
|
||||
* Try to get a part at a specific place in the world.
|
||||
*
|
||||
* @param w world the part is in
|
||||
* @param pos pos of the part host
|
||||
* @param side side to test for a part on.
|
||||
*
|
||||
* @return the part if it exists, null otherwise
|
||||
*/
|
||||
@Nullable
|
||||
IPart getPart( World w, BlockPos pos, AEPartLocation side );
|
||||
|
||||
/**
|
||||
* Try to get a part host at a specific place in the world.
|
||||
*
|
||||
* @param w world the part host is in
|
||||
* @param pos pos of the part host
|
||||
*
|
||||
* @return the part host if it exists, null otherwise
|
||||
*/
|
||||
@Nullable
|
||||
IPartHost getPartHost( World w, BlockPos pos );
|
||||
|
||||
/**
|
||||
* Get a part host if it exists, or place one if it doesn't.
|
||||
*
|
||||
* @param w world the part host is in
|
||||
* @param pos pos to get or place the part host
|
||||
* @param force whether to skip permission and existing block checks and forcibly place the part host here.
|
||||
* @param p the placing player, or null if none
|
||||
*
|
||||
* @return the existing or created part host, or null if it didn't already exist and part host is unable to be placed.
|
||||
*/
|
||||
@Nullable
|
||||
IPartHost getOrPlacePartHost( World w, BlockPos pos, boolean force, @Nullable EntityPlayer p );
|
||||
|
||||
/**
|
||||
* Test if a part host can be successfully placed at a given position by the provided player.
|
||||
*
|
||||
* @param w world the part host is in
|
||||
* @param pos pos to test for part host placement
|
||||
* @param p the placing player, or null if none
|
||||
*
|
||||
* @return if the part can be placed at the provided world and position
|
||||
*/
|
||||
boolean canPlacePartHost( World w, BlockPos pos, @Nullable EntityPlayer p );
|
||||
}
|
||||
|
||||
@@ -145,6 +145,17 @@ public interface IPartHost extends ICustomCableConnection
|
||||
*/
|
||||
SelectedPart selectPart( Vec3d pos );
|
||||
|
||||
/**
|
||||
* Same as {@link #selectPart(Vec3d)}, but with global instead of local coordinates.
|
||||
*/
|
||||
default SelectedPart selectPartGlobal( Vec3d pos ) {
|
||||
DimensionalCoord globalPos = getLocation();
|
||||
return selectPart(pos.subtract(
|
||||
globalPos.getPos().getX(),
|
||||
globalPos.getPos().getY(),
|
||||
globalPos.getPos().getZ()));
|
||||
}
|
||||
|
||||
/**
|
||||
* can be used by parts to trigger the tile or part to save.
|
||||
*/
|
||||
|
||||
@@ -83,6 +83,7 @@ public final class AEConfig extends Configuration implements IConfigurableObject
|
||||
private int craftingCalculationTimePerTick = 5;
|
||||
private PowerUnits selectedPowerUnit = PowerUnits.AE;
|
||||
private boolean showCraftableTooltip = true;
|
||||
private boolean showPlacementPreview = true;
|
||||
|
||||
// Spatial IO/Dimension
|
||||
private int storageProviderID = -1;
|
||||
@@ -257,6 +258,7 @@ public final class AEConfig extends Configuration implements IConfigurableObject
|
||||
this.useLargeFonts = this.get("Client", "useTerminalUseLargeFont", false).getBoolean(false);
|
||||
this.useColoredCraftingStatus = this.get("Client", "useColoredCraftingStatus", true).getBoolean(true);
|
||||
this.showCraftableTooltip = this.get("Client", "showCraftableTooltip", true, "Whether to add \"Craftable\" to item tooltips when they can be crafted automatically.").getBoolean(true);
|
||||
this.showPlacementPreview = this.get("Client", "showPlacementPreview", true, "Whether to show a preview of part and facade placement.").getBoolean(true);
|
||||
|
||||
// load buttons..
|
||||
for (int btnNum = 0; btnNum < 4; btnNum++) {
|
||||
@@ -514,6 +516,10 @@ public final class AEConfig extends Configuration implements IConfigurableObject
|
||||
return this.showCraftableTooltip;
|
||||
}
|
||||
|
||||
public boolean showPlacementPreview() {
|
||||
return this.showPlacementPreview;
|
||||
}
|
||||
|
||||
public boolean isDisableColoredCableRecipesInJEI() {
|
||||
return this.disableColoredCableRecipesInJEI;
|
||||
}
|
||||
|
||||
@@ -54,13 +54,13 @@ import appeng.core.stats.PartItemPredicate;
|
||||
import appeng.core.stats.Stats;
|
||||
import appeng.core.worlddata.SpatialDimensionManager;
|
||||
import appeng.fluids.registries.BasicFluidCellGuiHandler;
|
||||
import appeng.hooks.WrenchClickHook;
|
||||
import appeng.hooks.TickHandler;
|
||||
import appeng.items.materials.ItemMaterial;
|
||||
import appeng.items.parts.ItemFacade;
|
||||
import appeng.items.parts.ItemPart;
|
||||
import appeng.loot.ChestLoot;
|
||||
import appeng.me.cache.*;
|
||||
import appeng.parts.PartPlacement;
|
||||
import appeng.recipes.AEItemResolver;
|
||||
import appeng.recipes.AERecipeLoader;
|
||||
import appeng.recipes.game.DisassembleRecipe;
|
||||
@@ -109,7 +109,6 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
@@ -193,7 +192,7 @@ final class Registration {
|
||||
|
||||
MinecraftForge.EVENT_BUS.register(TickHandler.INSTANCE);
|
||||
|
||||
MinecraftForge.EVENT_BUS.register(new PartPlacement());
|
||||
MinecraftForge.EVENT_BUS.register(new WrenchClickHook());
|
||||
|
||||
if (AEConfig.instance().isFeatureEnabled(AEFeature.CHEST_LOOT)) {
|
||||
MinecraftForge.EVENT_BUS.register(new ChestLoot());
|
||||
|
||||
@@ -19,28 +19,91 @@
|
||||
package appeng.core.api;
|
||||
|
||||
|
||||
import appeng.api.AEApi;
|
||||
import appeng.api.definitions.ITileDefinition;
|
||||
import appeng.api.parts.CableRenderMode;
|
||||
import appeng.api.parts.IPart;
|
||||
import appeng.api.parts.IPartHelper;
|
||||
import appeng.core.AppEng;
|
||||
import appeng.parts.PartPlacement;
|
||||
import appeng.api.parts.IPartHost;
|
||||
import appeng.api.util.AEPartLocation;
|
||||
import appeng.util.Platform;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
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.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
||||
public class ApiPart implements IPartHelper {
|
||||
|
||||
@Override
|
||||
public EnumActionResult placeBus(final ItemStack is, final BlockPos pos, final EnumFacing side, final EntityPlayer player, final EnumHand hand, final World w) {
|
||||
return PartPlacement.place(is, pos, side, player, hand, w, PartPlacement.PlaceType.PLACE_ITEM, 0);
|
||||
return PartPlacement.place(is, pos, side, player, hand, w);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CableRenderMode getCableRenderMode() {
|
||||
return AppEng.proxy.getRenderMode();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IPart getPart(World w, BlockPos pos, AEPartLocation side) {
|
||||
final TileEntity tile = w.getTileEntity(pos);
|
||||
if (tile instanceof IPartHost partHost) {
|
||||
return partHost.getPart(side);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IPartHost getPartHost(World w, BlockPos pos) {
|
||||
final TileEntity tile = w.getTileEntity(pos);
|
||||
if (tile instanceof IPartHost partHost) {
|
||||
return partHost;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IPartHost getOrPlacePartHost(World w, BlockPos pos, boolean force, @Nullable EntityPlayer p) {
|
||||
final TileEntity tile = w.getTileEntity(pos);
|
||||
if (tile instanceof IPartHost partHost) {
|
||||
return partHost;
|
||||
} else {
|
||||
if (!force && !canPlacePartHost(w, pos, p)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final ITileDefinition multiPart = AEApi.instance().definitions().blocks().multiPart();
|
||||
if (!multiPart.isEnabled()) return null;
|
||||
Block blk = multiPart.maybeBlock().orElse(null);
|
||||
if (blk == null) return null;
|
||||
|
||||
final IBlockState state = blk.getDefaultState();
|
||||
w.setBlockState(pos, state, 3);
|
||||
return w.getTileEntity(pos) instanceof IPartHost host ? host : null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlacePartHost(World w, BlockPos pos, @Nullable EntityPlayer p) {
|
||||
if (p != null && !Platform.hasPermissions(w, pos, p)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Block blk = w.getBlockState(pos).getBlock();
|
||||
return blk == null || blk.isReplaceable(w, pos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,6 @@ public class AppEngPacketHandlerBase {
|
||||
|
||||
PACKET_CONFIG_BUTTON(PacketConfigButton.class),
|
||||
|
||||
PACKET_PART_PLACEMENT(PacketPartPlacement.class),
|
||||
|
||||
PACKET_LIGHTNING(PacketLightning.class),
|
||||
|
||||
PACKET_MATTER_CANNON(PacketMatterCannon.class),
|
||||
|
||||
@@ -19,21 +19,13 @@
|
||||
package appeng.core.sync.packets;
|
||||
|
||||
|
||||
import appeng.api.AEApi;
|
||||
import appeng.api.definitions.IComparableDefinition;
|
||||
import appeng.api.definitions.IItems;
|
||||
import appeng.api.implementations.items.IMemoryCard;
|
||||
import appeng.api.implementations.items.MemoryCardMessages;
|
||||
import appeng.block.networking.BlockCableBus;
|
||||
import appeng.core.sync.AppEngPacket;
|
||||
import appeng.core.sync.network.INetworkInfo;
|
||||
import appeng.items.tools.ToolNetworkTool;
|
||||
import appeng.items.tools.powered.ToolColorApplicator;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -99,31 +91,12 @@ public class PacketClick extends AppEngPacket {
|
||||
|
||||
@Override
|
||||
public void serverPacketData(final INetworkInfo manager, final AppEngPacket packet, final EntityPlayer player) {
|
||||
final ItemStack is = player.inventory.getCurrentItem();
|
||||
final IItems items = AEApi.instance().definitions().items();
|
||||
final IComparableDefinition maybeMemoryCard = items.memoryCard();
|
||||
final IComparableDefinition maybeColorApplicator = items.colorApplicator();
|
||||
final BlockPos pos = new BlockPos(this.x, this.y, this.z);
|
||||
if (this.leftClick) {
|
||||
final Block block = player.world.getBlockState(pos).getBlock();
|
||||
if (block instanceof BlockCableBus) {
|
||||
((BlockCableBus) block).onBlockClickPacket(player.world, pos, player, this.hand, new Vec3d(this.hitX, this.hitY, this.hitZ));
|
||||
}
|
||||
} else {
|
||||
if (!is.isEmpty()) {
|
||||
if (is.getItem() instanceof ToolNetworkTool) {
|
||||
final ToolNetworkTool tnt = (ToolNetworkTool) is.getItem();
|
||||
tnt.serverSideToolLogic(is, player, this.hand, player.world, pos, this.side, this.hitX, this.hitY,
|
||||
this.hitZ);
|
||||
} else if (maybeMemoryCard.isSameAs(is)) {
|
||||
final IMemoryCard mem = (IMemoryCard) is.getItem();
|
||||
mem.notifyUser(player, MemoryCardMessages.SETTINGS_CLEARED);
|
||||
is.setTagCompound(null);
|
||||
} else if (maybeColorApplicator.isSameAs(is)) {
|
||||
final ToolColorApplicator mem = (ToolColorApplicator) is.getItem();
|
||||
mem.cycleColors(is, mem.getColor(is), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* This file is part of Applied Energistics 2.
|
||||
* Copyright (c) 2013 - 2014, 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 <http://www.gnu.org/licenses/lgpl>.
|
||||
*/
|
||||
|
||||
package appeng.core.sync.packets;
|
||||
|
||||
|
||||
import appeng.core.AppEng;
|
||||
import appeng.core.sync.AppEngPacket;
|
||||
import appeng.core.sync.network.INetworkInfo;
|
||||
import appeng.parts.PartPlacement;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
|
||||
public class PacketPartPlacement extends AppEngPacket {
|
||||
|
||||
private int x;
|
||||
private int y;
|
||||
private int z;
|
||||
private int face;
|
||||
private float eyeHeight;
|
||||
private EnumHand hand;
|
||||
|
||||
// automatic.
|
||||
public PacketPartPlacement(final ByteBuf stream) {
|
||||
this.x = stream.readInt();
|
||||
this.y = stream.readInt();
|
||||
this.z = stream.readInt();
|
||||
this.face = stream.readByte();
|
||||
this.eyeHeight = stream.readFloat();
|
||||
this.hand = EnumHand.values()[stream.readByte()];
|
||||
}
|
||||
|
||||
// api
|
||||
public PacketPartPlacement(final BlockPos pos, final EnumFacing face, final float eyeHeight, final EnumHand hand) {
|
||||
final ByteBuf data = Unpooled.buffer();
|
||||
|
||||
data.writeInt(this.getPacketID());
|
||||
data.writeInt(pos.getX());
|
||||
data.writeInt(pos.getY());
|
||||
data.writeInt(pos.getZ());
|
||||
data.writeByte(face.ordinal());
|
||||
data.writeFloat(eyeHeight);
|
||||
data.writeByte(hand.ordinal());
|
||||
|
||||
this.configureWrite(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serverPacketData(final INetworkInfo manager, final AppEngPacket packet, final EntityPlayer player) {
|
||||
final EntityPlayerMP sender = (EntityPlayerMP) player;
|
||||
AppEng.proxy.updateRenderMode(sender);
|
||||
PartPlacement.setEyeHeight(this.eyeHeight);
|
||||
PartPlacement.place(sender.getHeldItem(this.hand), new BlockPos(this.x, this.y, this.z), EnumFacing.VALUES[this.face], sender, this.hand,
|
||||
sender.world,
|
||||
PartPlacement.PlaceType.INTERACT_FIRST_PASS, 0);
|
||||
AppEng.proxy.updateRenderMode(null);
|
||||
}
|
||||
}
|
||||
@@ -44,9 +44,14 @@ public class FacadeContainer implements IFacadeContainer {
|
||||
this.storage = cbs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAddFacade(IFacadePart a) {
|
||||
return this.getFacade(a.getSide()) == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addFacade(final IFacadePart a) {
|
||||
if (this.getFacade(a.getSide()) == null) {
|
||||
if (canAddFacade(a)) {
|
||||
this.storage.setFacade(a.getSide().ordinal(), a);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
package appeng.hooks;
|
||||
|
||||
import appeng.api.AEApi;
|
||||
import appeng.api.parts.IFacadePart;
|
||||
import appeng.api.parts.IPart;
|
||||
import appeng.api.parts.IPartCollisionHelper;
|
||||
import appeng.api.parts.IPartHost;
|
||||
import appeng.api.parts.IPartItem;
|
||||
import appeng.api.parts.SelectedPart;
|
||||
import appeng.api.util.AEPartLocation;
|
||||
import appeng.core.AEConfig;
|
||||
import appeng.facade.FacadePart;
|
||||
import appeng.facade.IFacadeItem;
|
||||
import appeng.items.parts.ItemFacade;
|
||||
import appeng.parts.BusCollisionHelper;
|
||||
import appeng.parts.PartPlacement;
|
||||
import appeng.parts.PartPlacement.Placement;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.RenderGlobal;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraftforge.client.event.DrawBlockHighlightEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class RenderBlockOutlineHook {
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onDrawHighlightEvent(DrawBlockHighlightEvent event) {
|
||||
if (event.getTarget() == null) return;
|
||||
// noinspection ConstantConditions
|
||||
if (event.getTarget().getBlockPos() == null) return;
|
||||
if (event.getTarget().typeOfHit != RayTraceResult.Type.BLOCK) return;
|
||||
|
||||
EntityPlayer player = event.getPlayer();
|
||||
ItemStack stack = player.getHeldItemMainhand();
|
||||
RayTraceResult hitResult = event.getTarget();
|
||||
|
||||
if (player.world.getBlockState(hitResult.getBlockPos()).getBlock() == Blocks.AIR) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (replaceBlockOutline(player, stack, hitResult, event.getPartialTicks())) {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean replaceBlockOutline(EntityPlayer player, ItemStack stack, RayTraceResult hitResult, float partialTicks) {
|
||||
BlockPos pos = hitResult.getBlockPos();
|
||||
|
||||
// Render the placement preview
|
||||
if (AEConfig.instance().showPlacementPreview()) {
|
||||
renderPartPlacementPreview(player, hitResult, stack, partialTicks);
|
||||
}
|
||||
|
||||
IPartHost host = AEApi.instance().partHelper().getPartHost(player.world, pos);
|
||||
if (host != null) {
|
||||
|
||||
// Try to render facade placement preview here, since it's a
|
||||
// convenient time to do it due to having the Part Host already.
|
||||
if (AEConfig.instance().showPlacementPreview()) {
|
||||
renderFacadePlacementPreview(host, player, hitResult, stack, partialTicks);
|
||||
}
|
||||
|
||||
// Render the Part Host block outline, which is done differently from default behavior
|
||||
SelectedPart selectedPart = host.selectPartGlobal(hitResult.hitVec);
|
||||
if (selectedPart.facade != null) {
|
||||
renderFacade(selectedPart.facade, host, pos, selectedPart.side.getFacing(), player, partialTicks, false, false);
|
||||
return true;
|
||||
}
|
||||
if (selectedPart.part != null) {
|
||||
renderPart(selectedPart.part, pos, selectedPart.side.getFacing(), player, partialTicks, false, false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Render a placement preview for a part item, if possible. */
|
||||
private static void renderPartPlacementPreview(EntityPlayer player, RayTraceResult hitResult, ItemStack stack, float partialTicks) {
|
||||
if (!(stack.getItem() instanceof IPartItem<?> partItem)) return;
|
||||
|
||||
Placement placement = PartPlacement.getPartPlacement(player, player.world, stack, hitResult.getBlockPos(), hitResult.sideHit);
|
||||
if (placement == null) return;
|
||||
if (!player.world.getWorldBorder().contains(placement.pos())) return;
|
||||
|
||||
IPart part = partItem.createPartFromItemStack(stack);
|
||||
if (part == null) return;
|
||||
|
||||
// Render with two depth passes to render behind blocks
|
||||
renderPart(part, placement.pos(), placement.side(), player, partialTicks, true, true);
|
||||
renderPart(part, placement.pos(), placement.side(), player, partialTicks, true, false);
|
||||
}
|
||||
|
||||
/** Render a placement preview for a facade item, if possible. */
|
||||
private static void renderFacadePlacementPreview(@Nonnull IPartHost host, EntityPlayer player, RayTraceResult hitResult, ItemStack stack, float partialTicks) {
|
||||
if (!(stack.getItem() instanceof IFacadeItem facadeItem)) return;
|
||||
|
||||
Placement placement = PartPlacement.getPartPlacement(player, player.world, stack, hitResult.getBlockPos(), hitResult.sideHit);
|
||||
if (placement == null) return;
|
||||
|
||||
FacadePart part = facadeItem.createPartFromItemStack(stack, AEPartLocation.fromFacing(placement.side()));
|
||||
if (part == null) return;
|
||||
if (!ItemFacade.canPlaceFacade(host, part)) return;
|
||||
|
||||
// Render with two depth passes to render behind blocks
|
||||
renderFacade(part, host, placement.pos(), placement.side(), player, partialTicks, true, true);
|
||||
renderFacade(part, host, placement.pos(), placement.side(), player, partialTicks, true, false);
|
||||
}
|
||||
|
||||
/** Render a part block outline. */
|
||||
private static void renderPart(IPart part, BlockPos pos, EnumFacing side, EntityPlayer player, float partialTicks, boolean preview, boolean insideBlock) {
|
||||
List<AxisAlignedBB> boxes = new ArrayList<>();
|
||||
IPartCollisionHelper helper = new BusCollisionHelper(boxes, AEPartLocation.fromFacing(side), player, true);
|
||||
part.getBoxes(helper);
|
||||
offsetBoxes(boxes, pos, player, partialTicks);
|
||||
renderBoxes(boxes, preview, insideBlock);
|
||||
}
|
||||
|
||||
/** Render a facade block outline. */
|
||||
private static void renderFacade(IFacadePart facade, IPartHost host, BlockPos pos, EnumFacing side, EntityPlayer player, float partialTicks, boolean preview, boolean insideBlock) {
|
||||
List<AxisAlignedBB> boxes = new ArrayList<>();
|
||||
IPartCollisionHelper helper = new BusCollisionHelper(boxes, AEPartLocation.fromFacing(side), player, true);
|
||||
facade.getBoxes(helper, player);
|
||||
|
||||
// Render a cable anchor part box as well if there is no part
|
||||
// attachment on this side, and if we are in a preview render pass.
|
||||
if (host.getPart(side) == null && preview) {
|
||||
addAnchorBox(helper);
|
||||
}
|
||||
|
||||
offsetBoxes(boxes, pos, player, partialTicks);
|
||||
renderBoxes(boxes, preview, insideBlock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the provided list of AABB boxes as a block outline.
|
||||
*
|
||||
* @param preview Whether this is a preview placement or a normal block outline. Determines coloration of the outline.
|
||||
* @param insideBlock Whether to disable depth test and darken the outline. Will draw behind other blocks.
|
||||
*/
|
||||
private static void renderBoxes(List<AxisAlignedBB> boxes, boolean preview, boolean insideBlock) {
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.tryBlendFuncSeparate(
|
||||
GlStateManager.SourceFactor.SRC_ALPHA,
|
||||
GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA,
|
||||
GlStateManager.SourceFactor.ONE,
|
||||
GlStateManager.DestFactor.ZERO);
|
||||
GlStateManager.glLineWidth(2.0F);
|
||||
GlStateManager.disableTexture2D();
|
||||
GlStateManager.depthMask(false);
|
||||
|
||||
if (insideBlock) {
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
for (AxisAlignedBB box : boxes) {
|
||||
RenderGlobal.drawSelectionBoundingBox(
|
||||
box,
|
||||
preview ? 1 : 0,
|
||||
preview ? 1 : 0,
|
||||
preview ? 1 : 0,
|
||||
insideBlock ? 0.2F : preview ? 0.6F : 0.4F);
|
||||
}
|
||||
|
||||
if (insideBlock) {
|
||||
GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
GlStateManager.depthMask(true);
|
||||
GlStateManager.enableTexture2D();
|
||||
GlStateManager.disableBlend();
|
||||
}
|
||||
|
||||
/** Offset each box in the list to the appropriate render position. */
|
||||
private static void offsetBoxes(List<AxisAlignedBB> boxes, BlockPos pos, EntityPlayer player, float partialTicks) {
|
||||
double dX = player.lastTickPosX + (player.posX - player.lastTickPosX) * partialTicks;
|
||||
double dY = player.lastTickPosY + (player.posY - player.lastTickPosY) * partialTicks;
|
||||
double dZ = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * partialTicks;
|
||||
boxes.replaceAll(box -> box.offset(pos.getX() - dX, pos.getY() - dY, pos.getZ() - dZ).grow(0.002D));
|
||||
}
|
||||
|
||||
/** Adds a cable anchor box to the collision helper. This does NOT offset the box! */
|
||||
private static void addAnchorBox(IPartCollisionHelper helper) {
|
||||
ItemStack anchorStack = AEApi.instance().definitions().parts().cableAnchor().maybeStack(1).orElse(null);
|
||||
if (anchorStack != null && anchorStack.getItem() instanceof IPartItem<?> anchorPartItem) {
|
||||
IPart anchorPart = anchorPartItem.createPartFromItemStack(anchorStack);
|
||||
if (anchorPart != null) {
|
||||
anchorPart.getBoxes(helper);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package appeng.hooks;
|
||||
|
||||
import appeng.api.parts.IPartHost;
|
||||
import appeng.api.parts.PartItemStack;
|
||||
import appeng.api.parts.SelectedPart;
|
||||
import appeng.api.util.DimensionalCoord;
|
||||
import appeng.util.LookDirection;
|
||||
import appeng.util.Platform;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Wrench action, handled in event rather than item to support implementors of our wrench api
|
||||
*/
|
||||
public class WrenchClickHook {
|
||||
|
||||
@SubscribeEvent
|
||||
public void playerInteract(final PlayerInteractEvent event) {
|
||||
// Only handle the main hand event
|
||||
if (event.getHand() != EnumHand.MAIN_HAND) return;
|
||||
|
||||
if (event instanceof PlayerInteractEvent.RightClickBlock && !event.getEntityPlayer().world.isRemote) {
|
||||
EntityPlayer player = event.getEntityPlayer();
|
||||
EnumHand hand = event.getHand();
|
||||
BlockPos pos = event.getPos();
|
||||
World world = event.getWorld();
|
||||
ItemStack held = event.getItemStack();
|
||||
|
||||
if (!Platform.hasPermissions(new DimensionalCoord(world, pos), player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.isSneaking() && Platform.isWrench(player, held, pos)) {
|
||||
Block block = world.getBlockState(pos).getBlock();
|
||||
TileEntity tile = world.getTileEntity(pos);
|
||||
if (!(tile instanceof IPartHost host)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final LookDirection dir = Platform.getPlayerRay(player, player.getEyeHeight());
|
||||
final RayTraceResult mop = block.collisionRayTrace(world.getBlockState(pos), world, pos, dir.getA(), dir.getB());
|
||||
if (mop != null) {
|
||||
final SelectedPart sp = host.selectPartGlobal(mop.hitVec);
|
||||
if (sp == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<ItemStack> is = new ArrayList<>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,8 @@ package appeng.items.parts;
|
||||
import appeng.api.AEApi;
|
||||
import appeng.api.exceptions.MissingDefinitionException;
|
||||
import appeng.api.parts.IAlphaPassItem;
|
||||
import appeng.api.parts.IFacadePart;
|
||||
import appeng.api.parts.IPartHost;
|
||||
import appeng.api.util.AEPartLocation;
|
||||
import appeng.core.AELog;
|
||||
import appeng.core.FacadeConfig;
|
||||
@@ -40,7 +42,9 @@ import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.*;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.property.IExtendedBlockState;
|
||||
import net.minecraftforge.event.entity.player.PlayerDestroyItemEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -59,7 +63,62 @@ public class ItemFacade extends AEBaseItem implements IFacadeItem, IAlphaPassIte
|
||||
|
||||
@Override
|
||||
public EnumActionResult onItemUseFirst(final EntityPlayer player, final World world, final BlockPos pos, final EnumFacing side, final float hitX, final float hitY, final float hitZ, final EnumHand hand) {
|
||||
return AEApi.instance().partHelper().placeBus(player.getHeldItem(hand), pos, side, player, hand, world);
|
||||
ItemStack stack = player.getHeldItem(hand);
|
||||
if (stack.getItem() != this) {
|
||||
return EnumActionResult.PASS;
|
||||
}
|
||||
|
||||
FacadePart facade = createPartFromItemStack(stack, AEPartLocation.fromFacing(side));
|
||||
if (!placeFacade(facade, world, pos)) {
|
||||
return EnumActionResult.FAIL;
|
||||
}
|
||||
|
||||
if (!world.isRemote) {
|
||||
if (!player.isCreative()) {
|
||||
stack.grow(-1);
|
||||
if (stack.isEmpty()) {
|
||||
player.setHeldItem(hand, ItemStack.EMPTY);
|
||||
MinecraftForge.EVENT_BUS.post(new PlayerDestroyItemEvent(player, stack, hand));
|
||||
}
|
||||
}
|
||||
return EnumActionResult.SUCCESS;
|
||||
} else {
|
||||
player.swingArm(hand);
|
||||
return EnumActionResult.PASS;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean placeFacade(FacadePart facade, World world, BlockPos pos) {
|
||||
IPartHost host = AEApi.instance().partHelper().getPartHost(world, pos);
|
||||
if (host == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!canPlaceFacade(host, facade)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!host.getFacadeContainer().addFacade(facade)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
host.markForSave();
|
||||
host.markForUpdate();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean canPlaceFacade(IPartHost host, FacadePart facade) {
|
||||
if (host.getPart(AEPartLocation.INTERNAL) == null) {
|
||||
return false;
|
||||
}
|
||||
return host.getFacadeContainer().canAddFacade(facade);
|
||||
}
|
||||
|
||||
public static IFacadePart createFacade(ItemStack held, AEPartLocation side) {
|
||||
if (held.getItem() instanceof IFacadeItem facadeItem) {
|
||||
return facadeItem.createPartFromItemStack(held, side);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -60,6 +60,7 @@ import net.minecraft.item.ItemSnowball;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.EnumActionResult;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
@@ -97,8 +98,22 @@ public class ToolColorApplicator extends AEBasePoweredItem implements IStorageCe
|
||||
return this.onItemUse(p.getHeldItem(hand), p, w, pos, hand, side, hitX, hitY, hitZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult<ItemStack> onItemRightClick(World w, EntityPlayer p, EnumHand hand) {
|
||||
ItemStack stack = p.getHeldItem(hand);
|
||||
if (p.isSneaking()) {
|
||||
if (!w.isRemote) {
|
||||
cycleColors(stack, getColor(stack), 1);
|
||||
}
|
||||
return ActionResult.newResult(EnumActionResult.SUCCESS, stack);
|
||||
}
|
||||
return ActionResult.newResult(EnumActionResult.PASS, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumActionResult onItemUse(ItemStack is, EntityPlayer p, World w, BlockPos pos, EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ) {
|
||||
if (p.isSneaking()) return EnumActionResult.PASS;
|
||||
|
||||
final Block blk = w.getBlockState(pos).getBlock();
|
||||
|
||||
ItemStack paintBall = this.getColor(is);
|
||||
@@ -158,10 +173,6 @@ public class ToolColorApplicator extends AEBasePoweredItem implements IStorageCe
|
||||
}
|
||||
}
|
||||
|
||||
if (p.isSneaking()) {
|
||||
this.cycleColors(is, paintBall, 1);
|
||||
}
|
||||
|
||||
return EnumActionResult.FAIL;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import appeng.core.AELog;
|
||||
import appeng.core.AEConfig;
|
||||
import appeng.facade.FacadeContainer;
|
||||
import appeng.helpers.AEMultiTile;
|
||||
import appeng.items.parts.ItemFacade;
|
||||
import appeng.me.GridConnection;
|
||||
import appeng.parts.networking.PartCable;
|
||||
import appeng.util.Platform;
|
||||
@@ -110,7 +111,7 @@ public class CableBusContainer extends CableBusStorage implements AEMultiTile, I
|
||||
|
||||
@Override
|
||||
public boolean canAddPart(ItemStack is, final AEPartLocation side) {
|
||||
if (PartPlacement.isFacade(is, side) != null) {
|
||||
if (ItemFacade.createFacade(is, side) != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,377 +18,113 @@
|
||||
|
||||
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 com.github.bsideup.jabel.Desugar;
|
||||
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;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PartPlacement {
|
||||
|
||||
private static float eyeHeight = 0.0f;
|
||||
private final ThreadLocal<Object> 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) {
|
||||
if (!(held.getItem() instanceof IPartItem<?>)) {
|
||||
return EnumActionResult.PASS;
|
||||
}
|
||||
|
||||
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) {
|
||||
// determine where the part would be placed
|
||||
Placement placement = getPartPlacement(player, world, held, pos, side);
|
||||
if (placement == null) {
|
||||
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<ItemStack> is = new ArrayList<>();
|
||||
final SelectedPart sp = selectPart(player, host,
|
||||
mop.hitVec.add(-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.add(-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<ItemStack> maybeMultiPartStack = multiPart.maybeStack(1);
|
||||
final Optional<Block> maybeMultiPartBlock = multiPart.maybeBlock();
|
||||
final Optional<ItemBlock> 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;
|
||||
// then try to place it
|
||||
IPart part = placePart(player, world, held, placement.pos(), placement.side(), hand);
|
||||
if (part == null) {
|
||||
return EnumActionResult.FAIL;
|
||||
}
|
||||
|
||||
// handle placement logic with the stack
|
||||
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.add(-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));
|
||||
}
|
||||
if (player != null && !player.isCreative()) {
|
||||
held.shrink(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);
|
||||
return EnumActionResult.PASS;
|
||||
}
|
||||
return EnumActionResult.SUCCESS;
|
||||
}
|
||||
|
||||
private static float getEyeOffset(final EntityPlayer p) {
|
||||
if (p.world.isRemote) {
|
||||
return Platform.getEyeOffset(p);
|
||||
public static IPart placePart(@Nullable EntityPlayer player, World world, ItemStack partItem, BlockPos pos, EnumFacing side, EnumHand hand) {
|
||||
IPartHost host = AEApi.instance().partHelper().getOrPlacePartHost(world, pos, false, player);
|
||||
if (host == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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);
|
||||
AEPartLocation location = host.addPart(partItem, AEPartLocation.fromFacing(side), player, hand);
|
||||
IPart part = host.getPart(location);
|
||||
if (part == null) {
|
||||
if (host.isEmpty()) {
|
||||
host.cleanup();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
IBlockState multiPartState = AEApi.instance().definitions().blocks().multiPart().maybeBlock().get().getDefaultState();
|
||||
SoundType soundType = multiPartState.getBlock().getSoundType(multiPartState, world, pos, null);
|
||||
world.playSound(null, pos, soundType.getPlaceSound(), SoundCategory.BLOCKS, (soundType.getVolume() + 1.0F) / 2.0F, soundType.getPitch() * 0.8F);
|
||||
return part;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Placement getPartPlacement(@Nullable EntityPlayer player, World world, ItemStack partStack, BlockPos pos, EnumFacing side) {
|
||||
if (canPlacePartOnBlock(player, world, partStack, pos, side)) {
|
||||
return new Placement(pos, side);
|
||||
}
|
||||
|
||||
// If the part cannot be placed directly in the block, try the opposite side of
|
||||
// the adjacent block. This is somewhat similar to how torches are placed.
|
||||
pos = pos.offset(side);
|
||||
side = side.getOpposite();
|
||||
if (canPlacePartOnBlock(player, world, partStack, pos, side)) {
|
||||
return new Placement(pos, side);
|
||||
}
|
||||
|
||||
// can't place the part
|
||||
return null;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void playerInteract(final TickEvent.ClientTickEvent event) {
|
||||
this.wasCanceled = false;
|
||||
}
|
||||
public static boolean canPlacePartOnBlock(@Nullable EntityPlayer player, World world, ItemStack partStack, BlockPos pos, EnumFacing side) {
|
||||
IPartHost host = AEApi.instance().partHelper().getPartHost(world, pos);
|
||||
|
||||
@SubscribeEvent
|
||||
public void playerInteract(final PlayerInteractEvent event) {
|
||||
// Only handle the main hand event
|
||||
if (event.getHand() != EnumHand.MAIN_HAND) {
|
||||
return;
|
||||
// There is no host at the location, we also cannot place one
|
||||
if (host == null && !AEApi.instance().partHelper().canPlacePartHost(world, pos, player)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
// Either there is no host, then we assume a freshly placed host will always accept our part,
|
||||
// or there is a host, and it has a free side.
|
||||
return host == null || host.canAddPart(partStack, AEPartLocation.fromFacing(side));
|
||||
}
|
||||
|
||||
private static float getEyeHeight() {
|
||||
return eyeHeight;
|
||||
@Desugar
|
||||
public record Placement(BlockPos pos, EnumFacing side) {
|
||||
}
|
||||
|
||||
public static void setEyeHeight(final float eyeHeight) {
|
||||
PartPlacement.eyeHeight = eyeHeight;
|
||||
}
|
||||
|
||||
public enum PlaceType {
|
||||
PLACE_ITEM, INTERACT_FIRST_PASS, INTERACT_SECOND_PASS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,6 +384,10 @@ public class Platform {
|
||||
return dc.getWorld().canMineBlockBody(player, dc.getPos());
|
||||
}
|
||||
|
||||
public static boolean hasPermissions(final World world, final BlockPos pos, final EntityPlayer player) {
|
||||
return world.canMineBlockBody(player, pos);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks to see if a block is air?
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user