mirror of
https://github.com/GTNewHorizons/Applied-Energistics-2-Unofficial.git
synced 2026-11-14 01:14:11 +00:00
[Feature] Creative-like pick block with withdrawl from AE system (#945)
Co-authored-by: Martin Robertz <[email protected]> Co-authored-by: Kogepan229 <[email protected]> Co-authored-by: EnderProyects <[email protected]>
This commit is contained in:
11 files changed
+553
-4
No files matched your search
+1
-1
@@ -33,7 +33,7 @@ dependencies {
|
||||
}
|
||||
|
||||
compileOnly('com.github.GTNewHorizons:Baubles-Expanded:2.2.6-GTNH:dev')
|
||||
|
||||
|
||||
compileOnly("org.jetbrains:annotations:26.0.2")
|
||||
|
||||
//Uncomment to add thermal expansion + foundation for testing
|
||||
|
||||
@@ -8,6 +8,8 @@ public enum ActionKey {
|
||||
|
||||
SEARCH_CONNECTED_INVENTORIES(Keyboard.KEY_NONE),
|
||||
|
||||
PICK_BLOCK(Keyboard.KEY_NONE),
|
||||
|
||||
CONTROL_OPERATION(Keyboard.KEY_LCONTROL);
|
||||
|
||||
private final int defaultKey;
|
||||
|
||||
@@ -41,6 +41,8 @@ import net.minecraftforge.common.MinecraftForge;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.event.PickBlockEvent;
|
||||
|
||||
import appeng.api.parts.CableRenderMode;
|
||||
import appeng.api.util.AEColor;
|
||||
import appeng.block.AEBaseBlock;
|
||||
@@ -109,6 +111,7 @@ public class ClientHelper extends ServerHelper {
|
||||
MinecraftForge.EVENT_BUS.register(new HighlighterManager());
|
||||
MinecraftForge.EVENT_BUS.register(BlockRendererPreviewEvent.getInstance());
|
||||
FMLCommonHandler.instance().bus().register(BlockRendererPreviewEvent.getInstance());
|
||||
FMLCommonHandler.instance().bus().register(new KeyBindHandler());
|
||||
|
||||
for (ActionKey key : ActionKey.values()) {
|
||||
final KeyBinding binding = new KeyBinding(key.getTranslationKey(), key.getDefaultKey(), KEY_CATEGORY);
|
||||
@@ -448,6 +451,26 @@ public class ClientHelper extends ServerHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not run the vanilla pick block logic if it is double bound with the AE2 pick block keybind, and the player is
|
||||
* not in creative mode. The vanilla pick block event should be canceled and only the AE2 pick block logic should
|
||||
* run. This is to prevent conflicts from the client-side vanilla pick-block and the server-side AE2 pick block.
|
||||
*/
|
||||
@SubscribeEvent
|
||||
public void onPickBlockEvent(final PickBlockEvent event) {
|
||||
var minecraft = Minecraft.getMinecraft();
|
||||
if (!minecraft.theWorld.isRemote) {
|
||||
return;
|
||||
}
|
||||
|
||||
var isCreative = minecraft.thePlayer.capabilities.isCreativeMode;
|
||||
var vanillaKeybind = minecraft.gameSettings.keyBindPickBlock.getKeyCode();
|
||||
var ae2Keybind = CommonHelper.proxy.getKeybind(ActionKey.PICK_BLOCK);
|
||||
if (!isCreative && vanillaKeybind == ae2Keybind) {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isKeyPressed(ActionKey key) {
|
||||
return this.bindings.get(key).isPressed();
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package appeng.client;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.entity.EntityClientPlayerMP;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
|
||||
import appeng.core.CommonHelper;
|
||||
import appeng.core.sync.network.NetworkHandler;
|
||||
import appeng.core.sync.packets.PacketPickBlock;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.common.gameevent.InputEvent;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class KeyBindHandler {
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@SubscribeEvent
|
||||
public void onKeyInput(InputEvent.KeyInputEvent event) {
|
||||
if (CommonHelper.proxy.isKeyPressed(ActionKey.PICK_BLOCK)) {
|
||||
handlePickBlock();
|
||||
}
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@SubscribeEvent
|
||||
public void onMouseInput(InputEvent.MouseInputEvent event) {
|
||||
if (CommonHelper.proxy.isKeyPressed(ActionKey.PICK_BLOCK)) {
|
||||
handlePickBlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePickBlock() {
|
||||
Minecraft minecraft = Minecraft.getMinecraft();
|
||||
if (minecraft.currentScreen != null) return; // Don't act if a GUI is open
|
||||
|
||||
EntityClientPlayerMP player = minecraft.thePlayer;
|
||||
if (player == null) return;
|
||||
|
||||
// Use vanilla pick block when in creative
|
||||
if (player.capabilities.isCreativeMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the block the player is currently looking at
|
||||
MovingObjectPosition movingObject = minecraft.objectMouseOver;
|
||||
if (movingObject == null || movingObject.typeOfHit != MovingObjectPosition.MovingObjectType.BLOCK) {
|
||||
return;
|
||||
}
|
||||
|
||||
final var packet = new PacketPickBlock(
|
||||
movingObject.blockX,
|
||||
movingObject.blockY,
|
||||
movingObject.blockZ,
|
||||
movingObject.sideHit,
|
||||
movingObject.hitVec);
|
||||
NetworkHandler.instance.sendToServer(packet);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -79,7 +79,7 @@ public final class AppEng {
|
||||
+ '.' // revisionVersion
|
||||
+ net.minecraftforge.common.ForgeVersion.buildVersion
|
||||
+ ",);" // buildVersion
|
||||
+ "required-after:gtnhlib@[0.8.34,)"; // require gtnhlib
|
||||
+ "required-after:gtnhlib@[0.9.5,)"; // require gtnhlib
|
||||
|
||||
@Nonnull
|
||||
private static final AppEng INSTANCE = new AppEng();
|
||||
|
||||
@@ -53,6 +53,7 @@ public enum PlayerMessages implements Localization {
|
||||
MachineHighlightedNamed,
|
||||
StorageInOtherDim,
|
||||
StorageHighlighted,
|
||||
PickBlockTerminalNotFound,
|
||||
PatternRepeaterProvider,
|
||||
PatternRepeaterAccessor;
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ import appeng.core.sync.packets.PacketPartialItem;
|
||||
import appeng.core.sync.packets.PacketPatternMultiSet;
|
||||
import appeng.core.sync.packets.PacketPatternSlot;
|
||||
import appeng.core.sync.packets.PacketPatternValueSet;
|
||||
import appeng.core.sync.packets.PacketPickBlock;
|
||||
import appeng.core.sync.packets.PacketPinsUpdate;
|
||||
import appeng.core.sync.packets.PacketSwapSlots;
|
||||
import appeng.core.sync.packets.PacketSwitchGuis;
|
||||
@@ -125,8 +126,9 @@ public class AppEngPacketHandlerBase {
|
||||
PACKET_OPTIMIZE_PATTERNS(PacketOptimizePatterns.class),
|
||||
PACKET_NETWORK_STATUS_SELECTED(PacketNetworkStatusSelected.class),
|
||||
PACKET_PINS_UPDATE(PacketPinsUpdate.class),
|
||||
PACKET_MONITORABLE_ACTION(PacketMonitorableAction.class),
|
||||
PACKET_HIGHLIGHT_BLOCKS(PacketHighlightBlockStorage.class),
|
||||
PACKET_PICK_BLOCK(PacketPickBlock.class),
|
||||
PACKET_MONITORABLE_ACTION(PacketMonitorableAction.class),
|
||||
PACKET_VIRTUAL_SLOT(PacketVirtualSlot.class),
|
||||
PACKET_COLOR_SELECT(PacketColorSelect.class);
|
||||
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
package appeng.core.sync.packets;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemBlock;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.network.play.server.S09PacketHeldItemChange;
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import appeng.api.AEApi;
|
||||
import appeng.api.config.Actionable;
|
||||
import appeng.api.networking.IGridHost;
|
||||
import appeng.api.networking.security.PlayerSource;
|
||||
import appeng.api.networking.storage.IStorageGrid;
|
||||
import appeng.api.storage.IMEInventoryHandler;
|
||||
import appeng.api.storage.data.IAEItemStack;
|
||||
import appeng.api.storage.data.IAEStack;
|
||||
import appeng.core.localization.PlayerMessages;
|
||||
import appeng.core.sync.AppEngPacket;
|
||||
import appeng.core.sync.network.INetworkInfo;
|
||||
import appeng.util.PlayerInventoryUtil;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
public class PacketPickBlock extends AppEngPacket {
|
||||
|
||||
private final int blockX;
|
||||
private final int blockY;
|
||||
private final int blockZ;
|
||||
private final int blockSide;
|
||||
private final Vec3 hitVec;
|
||||
|
||||
// Reflection
|
||||
public PacketPickBlock(final ByteBuf stream) {
|
||||
this.blockX = stream.readInt();
|
||||
this.blockY = stream.readInt();
|
||||
this.blockZ = stream.readInt();
|
||||
this.blockSide = stream.readInt();
|
||||
|
||||
double hitVecX = stream.readDouble();
|
||||
double hitVecY = stream.readDouble();
|
||||
double hitVecZ = stream.readDouble();
|
||||
this.hitVec = Vec3.createVectorHelper(hitVecX, hitVecY, hitVecZ);
|
||||
}
|
||||
|
||||
// Public API
|
||||
public PacketPickBlock(int blockX, int blockY, int blockZ, int blockSide, Vec3 hitVec) {
|
||||
this.blockX = blockX;
|
||||
this.blockY = blockY;
|
||||
this.blockZ = blockZ;
|
||||
this.blockSide = blockSide;
|
||||
this.hitVec = hitVec;
|
||||
|
||||
final ByteBuf data = Unpooled.buffer();
|
||||
|
||||
data.writeInt(this.getPacketID());
|
||||
data.writeInt(this.blockX);
|
||||
data.writeInt(this.blockY);
|
||||
data.writeInt(this.blockZ);
|
||||
data.writeInt(this.blockSide);
|
||||
data.writeDouble(this.hitVec.xCoord);
|
||||
data.writeDouble(this.hitVec.yCoord);
|
||||
data.writeDouble(this.hitVec.zCoord);
|
||||
|
||||
this.configureWrite(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serverPacketData(final INetworkInfo network, final AppEngPacket packet, final EntityPlayer player) {
|
||||
final EntityPlayerMP sender = (EntityPlayerMP) player;
|
||||
|
||||
ItemStack itemToFind = getPickBlock(sender, this.blockX, this.blockY, this.blockZ);
|
||||
|
||||
// 1. Scan through the player's main inventory to categorize existing stacks of the target block:
|
||||
// - If a full stack (stackSize >= maxStackSize) is found, record its slot and stop searching.
|
||||
// This indicates the player already has the maximum possible stack, so no withdrawal is needed.
|
||||
// - Otherwise, collect all partial stack slots (stacks that match the item but aren't full).
|
||||
// Partial stacks will be consolidated in a later step.
|
||||
int fullStackSlot = -1;
|
||||
List<Integer> partialStackSlotsList = new ArrayList<>();
|
||||
for (int i = 0; i < sender.inventory.mainInventory.length; i++) {
|
||||
ItemStack stackInSlot = sender.inventory.mainInventory[i];
|
||||
if (stackInSlot != null && stackInSlot.isItemEqual(itemToFind)
|
||||
&& ItemStack.areItemStackTagsEqual(stackInSlot, itemToFind)) {
|
||||
if (stackInSlot.stackSize >= stackInSlot.getMaxStackSize()) {
|
||||
fullStackSlot = i;
|
||||
break; // Found a full stack, no need to continue consolidating.
|
||||
}
|
||||
partialStackSlotsList.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. If a full stack already exists, move it into the correct slot
|
||||
if (fullStackSlot >= 0) {
|
||||
movePickBlockItemStack(sender, fullStackSlot);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. If there are no partial stacks and the player's inventory is full,
|
||||
// then return since we cannot add a retrieved stack to a full inventory
|
||||
int nextEmptySlot = sender.inventory.getFirstEmptyStack();
|
||||
if (partialStackSlotsList.isEmpty() && nextEmptySlot == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. Consolidate all partial stacks of target block into 1 ItemStack.
|
||||
// If a full stack is obtained, set it as the active slot and return.
|
||||
ItemStack consolidatedStack = null;
|
||||
int consolidatedStackSlot = -1;
|
||||
for (Integer partialStackSlot : partialStackSlotsList) {
|
||||
if (consolidatedStack == null) {
|
||||
consolidatedStack = sender.inventory.getStackInSlot(partialStackSlot);
|
||||
consolidatedStackSlot = partialStackSlot;
|
||||
} else {
|
||||
var newStack = PlayerInventoryUtil
|
||||
.consolidateItemStacks(sender, partialStackSlot, consolidatedStackSlot);
|
||||
if (newStack != null) {
|
||||
consolidatedStack = newStack;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we created a full stack of items
|
||||
if (consolidatedStack.stackSize == consolidatedStack.getMaxStackSize()) {
|
||||
movePickBlockItemStack(sender, consolidatedStackSlot);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine which of consolidated ItemStack or nextEmptySlot to use as our pick block ItemStack
|
||||
int pickBlockSlot = consolidatedStack == null ? nextEmptySlot : consolidatedStackSlot;
|
||||
ItemStack pickBlockItemStack = consolidatedStack;
|
||||
|
||||
// 5. Calculate withdrawal amount
|
||||
int amountToWithdraw = pickBlockItemStack == null ? itemToFind.getMaxStackSize()
|
||||
: itemToFind.getMaxStackSize() - pickBlockItemStack.stackSize;
|
||||
if (amountToWithdraw <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure the player has a wireless terminal with access to an AE2 network.
|
||||
ItemStack wirelessTerminal = PlayerInventoryUtil.getFirstWirelessTerminal(sender);
|
||||
if (wirelessTerminal == null) {
|
||||
sender.addChatMessage(PlayerMessages.PickBlockTerminalNotFound.toChat());
|
||||
return;
|
||||
}
|
||||
var wirelessInventory = getWirelessItemInventory(sender, wirelessTerminal);
|
||||
if (wirelessInventory == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create an IAEItemStack for the target block with the calculated amount
|
||||
ItemStack targetItemStack = itemToFind.copy();
|
||||
targetItemStack.stackSize = amountToWithdraw;
|
||||
IAEItemStack targetAeItemStack = AEApi.instance().storage().createItemStack(targetItemStack);
|
||||
if (targetAeItemStack == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 6. Extract items from the network
|
||||
PlayerSource source = new PlayerSource(player, null);
|
||||
IAEStack<IAEItemStack> extractedStack = wirelessInventory
|
||||
.extractItems(targetAeItemStack, Actionable.MODULATE, source);
|
||||
if (extractedStack instanceof IAEItemStack extractedAeItemStack && extractedStack.getStackSize() > 0) {
|
||||
ItemStack itemsToGive = extractedAeItemStack.getItemStack();
|
||||
// Update the player's inventory with the withdrawn items
|
||||
if (itemsToGive != null && itemsToGive.stackSize > 0) {
|
||||
if (pickBlockItemStack == null) {
|
||||
pickBlockItemStack = itemsToGive;
|
||||
sender.inventory.setInventorySlotContents(pickBlockSlot, pickBlockItemStack);
|
||||
} else {
|
||||
pickBlockItemStack.stackSize += itemsToGive.stackSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the pick block item stack is still null, we had no consolidated stacks and did not withdraw any items.
|
||||
// So, we do nothing.
|
||||
if (pickBlockItemStack == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Move the target item stack to the correct slot in the player's hotbar.
|
||||
movePickBlockItemStack(sender, pickBlockSlot);
|
||||
}
|
||||
|
||||
private IMEInventoryHandler<IAEItemStack> getWirelessItemInventory(EntityPlayer player,
|
||||
ItemStack wirelessTerminal) {
|
||||
if (wirelessTerminal == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!AEApi.instance().registries().wireless().performCheck(wirelessTerminal, player)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var wirelessHandler = AEApi.instance().registries().wireless().getWirelessTerminalHandler(wirelessTerminal);
|
||||
if (wirelessHandler == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var encryptionKey = wirelessHandler.getEncryptionKey(wirelessTerminal);
|
||||
if (encryptionKey == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var securityTerminal = (IGridHost) AEApi.instance().registries().locatable()
|
||||
.getLocatableBy(Long.parseLong(encryptionKey));
|
||||
if (securityTerminal == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var wirelessGridNode = securityTerminal.getGridNode(ForgeDirection.UNKNOWN);
|
||||
if (wirelessGridNode == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var wirelessGrid = wirelessGridNode.getGrid();
|
||||
if (wirelessGrid == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
IStorageGrid wirelessGridCache = wirelessGrid.getCache(IStorageGrid.class);
|
||||
if (wirelessGridCache == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return wirelessGridCache.getItemInventory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the position of the pick block ItemStack. If it is in the hotbar, set the active slot to the pick block slot.
|
||||
* If it is not in the hotbar, move it to the first empty slot in the hotbar and set it as active slot. If there is
|
||||
* no available hotbar slot, swap it with the active slot.
|
||||
*
|
||||
* @param player the player whose inventory to be modified
|
||||
* @param pickBlockInventorySlot the inventory slot of the ItemStack to move
|
||||
*/
|
||||
private void movePickBlockItemStack(EntityPlayerMP player, int pickBlockInventorySlot) {
|
||||
var firstEmptyHotbarSlot = PlayerInventoryUtil.getFirstEmptyHotbarSlot(player);
|
||||
if (pickBlockInventorySlot <= 8) {
|
||||
PlayerInventoryUtil.setSlotAsActiveSlot(player, pickBlockInventorySlot);
|
||||
} else if (firstEmptyHotbarSlot >= 0) {
|
||||
player.inventory.currentItem = firstEmptyHotbarSlot;
|
||||
player.playerNetServerHandler.sendPacket(new S09PacketHeldItemChange(firstEmptyHotbarSlot));
|
||||
PlayerInventoryUtil.setSlotAsActiveSlot(player, pickBlockInventorySlot);
|
||||
} else {
|
||||
PlayerInventoryUtil.setSlotAsActiveSlot(player, pickBlockInventorySlot);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy of the vanilla pick block function, as the vanilla version uses a client-side only function ( getItem() ).
|
||||
*/
|
||||
private ItemStack getPickBlock(EntityPlayerMP player, int x, int y, int z) {
|
||||
// Get the target block
|
||||
World world = player.worldObj;
|
||||
Block targetBlock = world.getBlock(this.blockX, this.blockY, this.blockZ);
|
||||
if (targetBlock == null || targetBlock == Blocks.air) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Item item = Item.getItemFromBlock(targetBlock);
|
||||
if (item == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Block block = item instanceof ItemBlock ? Block.getBlockFromItem(item) : targetBlock;
|
||||
return new ItemStack(item, 1, block.getDamageValue(world, x, y, z));
|
||||
}
|
||||
}
|
||||
@@ -177,7 +177,7 @@ public class Platform {
|
||||
private static final DecimalFormat df = new DecimalFormat("#.##");
|
||||
public static final boolean isAE2FCLoaded = Loader.isModLoaded("ae2fc");
|
||||
public static final boolean isEIOLoaded = Loader.isModLoaded("EnderIO");
|
||||
public static boolean isBaublesLoaded = Loader.isModLoaded("Baubles");
|
||||
public static final boolean isBaublesLoaded = Loader.isModLoaded("Baubles|Expanded");
|
||||
|
||||
static {
|
||||
BYTE_LIMIT = new double[10];
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
package appeng.util;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.network.play.server.S09PacketHeldItemChange;
|
||||
import net.minecraft.network.play.server.S2FPacketSetSlot;
|
||||
|
||||
import appeng.api.AEApi;
|
||||
import baubles.api.BaublesApi;
|
||||
|
||||
/**
|
||||
* A collection of utility functions for manipulating player inventories.
|
||||
*/
|
||||
public class PlayerInventoryUtil {
|
||||
|
||||
/**
|
||||
* Sets the specified inventory slot as the player's active (held) slot. If the target slot is in the hotbar (slots
|
||||
* 0-8), that slot is set as the active slot. If the target slot is outside the hotbar, it swaps the contents of
|
||||
* that slot with the current active slot.
|
||||
*
|
||||
* @param player The player whose active slot should be changed.
|
||||
* @param slot The inventory slot index to make active (0-8 for hotbar, 9+ for main inventory).
|
||||
*/
|
||||
public static void setSlotAsActiveSlot(EntityPlayerMP player, int slot) {
|
||||
if (slot >= 0 && slot <= 8) {
|
||||
player.inventory.currentItem = slot;
|
||||
player.playerNetServerHandler.sendPacket(new S09PacketHeldItemChange(slot));
|
||||
} else {
|
||||
swapInventorySlots(player, player.inventory.currentItem, slot);
|
||||
}
|
||||
player.inventory.markDirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves an ItemStack from a source slot to a destination slot in the player's inventory. If the destination slot is
|
||||
* occupied, the items are swapped.
|
||||
*
|
||||
* @param player The player whose inventory is to be modified.
|
||||
* @param slot1 The index of the first ItemStack to move.
|
||||
* @param slot2 The index of the second ItemStack to move.
|
||||
*/
|
||||
public static void swapInventorySlots(EntityPlayerMP player, int slot1, int slot2) {
|
||||
if (slot1 == slot2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the stacks from both slots
|
||||
ItemStack sourceStack = player.inventory.getStackInSlot(slot1);
|
||||
ItemStack destinationStack = player.inventory.getStackInSlot(slot2);
|
||||
|
||||
// Copy the item stacks to prevents any potential shared reference issue.
|
||||
if (sourceStack != null) {
|
||||
sourceStack = sourceStack.copy();
|
||||
}
|
||||
if (destinationStack != null) {
|
||||
destinationStack = destinationStack.copy();
|
||||
}
|
||||
|
||||
// Set the destination slot with the source stack (even if it's null)
|
||||
player.inventory.setInventorySlotContents(slot2, sourceStack);
|
||||
|
||||
// Set the source slot with the original destination stack
|
||||
player.inventory.setInventorySlotContents(slot1, destinationStack);
|
||||
|
||||
// Mark the inventory as dirty to ensure changes are saved and synced
|
||||
player.inventory.markDirty();
|
||||
player.playerNetServerHandler.sendPacket(new S2FPacketSetSlot(-2, slot2, sourceStack));
|
||||
player.playerNetServerHandler.sendPacket(new S2FPacketSetSlot(-2, slot1, destinationStack));
|
||||
}
|
||||
|
||||
/**
|
||||
* Consolidate ItemStacks from a source slot to a destination slot in the player's inventory.
|
||||
*
|
||||
* @param player The player whose inventory is to be modified.
|
||||
* @param sourceSlot The index of the slot to move the item from.
|
||||
* @param destinationSlot The index of the slot to move the item to.
|
||||
* @return the consolidated item stack
|
||||
*/
|
||||
public static ItemStack consolidateItemStacks(EntityPlayerMP player, int sourceSlot, int destinationSlot) {
|
||||
ItemStack sourceStack = player.inventory.getStackInSlot(sourceSlot);
|
||||
ItemStack destinationStack = player.inventory.getStackInSlot(destinationSlot);
|
||||
|
||||
// We can only consolidate if both the source and destination slots have item stacks.
|
||||
if (sourceStack == null || destinationStack == null) {
|
||||
return destinationStack;
|
||||
}
|
||||
|
||||
if (!sourceStack.isItemEqual(destinationStack)
|
||||
|| !ItemStack.areItemStackTagsEqual(sourceStack, destinationStack)) {
|
||||
return destinationStack;
|
||||
}
|
||||
|
||||
// Copy the item stacks to prevents any potential shared reference issue.
|
||||
sourceStack = sourceStack.copy();
|
||||
destinationStack = destinationStack.copy();
|
||||
|
||||
int missingQuantity = destinationStack.getMaxStackSize() - destinationStack.stackSize;
|
||||
if (missingQuantity >= sourceStack.stackSize) {
|
||||
destinationStack.stackSize = destinationStack.stackSize + sourceStack.stackSize;
|
||||
sourceStack = null;
|
||||
} else {
|
||||
sourceStack.stackSize -= missingQuantity;
|
||||
destinationStack.stackSize += missingQuantity;
|
||||
}
|
||||
|
||||
// Update the inventory stacks
|
||||
player.inventory.setInventorySlotContents(destinationSlot, destinationStack);
|
||||
player.inventory.setInventorySlotContents(sourceSlot, sourceStack);
|
||||
|
||||
// Mark the inventory as dirty to ensure changes are saved and synced
|
||||
player.inventory.markDirty();
|
||||
player.playerNetServerHandler.sendPacket(new S2FPacketSetSlot(-2, destinationSlot, destinationStack));
|
||||
player.playerNetServerHandler.sendPacket(new S2FPacketSetSlot(-2, sourceSlot, sourceStack));
|
||||
|
||||
return player.inventory.getStackInSlot(destinationSlot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first wireless terminal in the player's inventory.
|
||||
*
|
||||
* @param player the player to check
|
||||
* @return the wireless terminal ItemStack, or null if not found
|
||||
*/
|
||||
public static ItemStack getFirstWirelessTerminal(EntityPlayer player) {
|
||||
// Check bauble slots
|
||||
if (Platform.isBaublesLoaded) {
|
||||
ItemStack terminal = getWirelessTerminalFromBaubles(player);
|
||||
if (terminal != null) {
|
||||
return terminal;
|
||||
}
|
||||
}
|
||||
|
||||
// Check hotbar and main inventory (slots 0-35)
|
||||
for (int i = 0; i < player.inventory.mainInventory.length; i++) {
|
||||
ItemStack stack = player.inventory.mainInventory[i];
|
||||
if (stack != null && AEApi.instance().registries().wireless().isWirelessTerminal(stack)) {
|
||||
return stack;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first empty hotbar slot.
|
||||
*
|
||||
* @param player the player whose inventory to check
|
||||
* @return the slot number of the first empty hotbar slot, otherwise -1
|
||||
*/
|
||||
public static int getFirstEmptyHotbarSlot(EntityPlayer player) {
|
||||
for (int i = 0; i <= 8; ++i) {
|
||||
if (player.inventory.mainInventory[i] == null) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first wireless terminal in the player's bauble slots.
|
||||
*
|
||||
* @param player the player to check
|
||||
* @return the wireless terminal ItemStack, or null if not found
|
||||
*/
|
||||
@cpw.mods.fml.common.Optional.Method(modid = "Baubles|Expanded")
|
||||
public static ItemStack getWirelessTerminalFromBaubles(EntityPlayer player) {
|
||||
IInventory baubles = BaublesApi.getBaubles(player);
|
||||
if (baubles != null) {
|
||||
for (int i = 0; i < baubles.getSizeInventory(); i++) {
|
||||
ItemStack stack = baubles.getStackInSlot(i);
|
||||
if (stack != null && AEApi.instance().registries().wireless().isWirelessTerminal(stack)) {
|
||||
return stack;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -137,6 +137,7 @@ chat.appliedenergistics2.MachineHighlighted=The %d is now highlighted at %d, %d,
|
||||
chat.appliedenergistics2.MachineHighlightedNamed=The %d "%d" is now highlighted at %d, %d, %d
|
||||
chat.appliedenergistics2.StorageHighlighted=The %d containing %d %d is now highlighted at %d, %d, %d
|
||||
chat.appliedenergistics2.StorageInOtherDim=%d containing %d %d is located in dimension: %d and cant be highlighted
|
||||
chat.appliedenergistics2.PickBlockTerminalNotFound=AE2 wireless terminal not found.
|
||||
chat.appliedenergistics2.PatternRepeaterProvider=§5Provider§r - will provide pattern to current system from in front §5Accessor§r.
|
||||
chat.appliedenergistics2.PatternRepeaterAccessor=§5Accessor§r - will provide pattern to in front §5Provider§r.
|
||||
|
||||
@@ -956,6 +957,7 @@ stat.ae2.TurnedCranks=Turned Cranks
|
||||
|
||||
# Keys
|
||||
key.appliedenergistics2.category=Applied Energistics 2
|
||||
key.pick_block.desc=AE2 Pick Block
|
||||
key.toggle_focus.desc=Toggle search box focus
|
||||
key.search_connected_inventories.desc=Search network inventories for item
|
||||
key.control_operation.desc=Control operation
|
||||
|
||||
Reference in new issue
Block a user