package net.minecraft.world.inventory; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundSource; import net.minecraft.world.Container; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.npc.ClientSideMerchant; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.trading.ItemCost; import net.minecraft.world.item.trading.Merchant; import net.minecraft.world.item.trading.MerchantOffer; import net.minecraft.world.item.trading.MerchantOffers; public class MerchantMenu extends AbstractContainerMenu { protected static final int PAYMENT1_SLOT = 0; protected static final int PAYMENT2_SLOT = 1; protected static final int RESULT_SLOT = 2; private static final int INV_SLOT_START = 3; private static final int INV_SLOT_END = 30; private static final int USE_ROW_SLOT_START = 30; private static final int USE_ROW_SLOT_END = 39; private static final int SELLSLOT1_X = 136; private static final int SELLSLOT2_X = 162; private static final int BUYSLOT_X = 220; private static final int ROW_Y = 37; private final Merchant trader; private final MerchantContainer tradeContainer; private int merchantLevel; private boolean showProgressBar; private boolean canRestock; public MerchantMenu(int containerId, Inventory playerInventory) { this(containerId, playerInventory, new ClientSideMerchant(playerInventory.player)); } public MerchantMenu(int containerId, Inventory playerInventory, Merchant trader) { super(MenuType.MERCHANT, containerId); this.trader = trader; this.tradeContainer = new MerchantContainer(trader); this.addSlot(new Slot(this.tradeContainer, 0, 136, 37)); this.addSlot(new Slot(this.tradeContainer, 1, 162, 37)); this.addSlot(new MerchantResultSlot(playerInventory.player, trader, this.tradeContainer, 2, 220, 37)); this.addStandardInventorySlots(playerInventory, 108, 84); } public void setShowProgressBar(boolean showProgressBar) { this.showProgressBar = showProgressBar; } @Override public void slotsChanged(Container container) { this.tradeContainer.updateSellItem(); super.slotsChanged(container); } public void setSelectionHint(int currentRecipeIndex) { this.tradeContainer.setSelectionHint(currentRecipeIndex); } @Override public boolean stillValid(Player player) { return this.trader.stillValid(player); } public int getTraderXp() { return this.trader.getVillagerXp(); } public int getFutureTraderXp() { return this.tradeContainer.getFutureXp(); } public void setXp(int xp) { this.trader.overrideXp(xp); } public int getTraderLevel() { return this.merchantLevel; } public void setMerchantLevel(int level) { this.merchantLevel = level; } public void setCanRestock(boolean canRestock) { this.canRestock = canRestock; } public boolean canRestock() { return this.canRestock; } @Override public boolean canTakeItemForPickAll(ItemStack stack, Slot slot) { return false; } @Override public ItemStack quickMoveStack(Player player, int index) { ItemStack itemStack = ItemStack.EMPTY; Slot slot = this.slots.get(index); if (slot != null && slot.hasItem()) { ItemStack itemStack2 = slot.getItem(); itemStack = itemStack2.copy(); if (index == 2) { if (!this.moveItemStackTo(itemStack2, 3, 39, true)) { return ItemStack.EMPTY; } slot.onQuickCraft(itemStack2, itemStack); this.playTradeSound(); } else if (index != 0 && index != 1) { if (index >= 3 && index < 30) { if (!this.moveItemStackTo(itemStack2, 30, 39, false)) { return ItemStack.EMPTY; } } else if (index >= 30 && index < 39 && !this.moveItemStackTo(itemStack2, 3, 30, false)) { return ItemStack.EMPTY; } } else if (!this.moveItemStackTo(itemStack2, 3, 39, false)) { return ItemStack.EMPTY; } if (itemStack2.isEmpty()) { slot.setByPlayer(ItemStack.EMPTY); } else { slot.setChanged(); } if (itemStack2.getCount() == itemStack.getCount()) { return ItemStack.EMPTY; } slot.onTake(player, itemStack2); } return itemStack; } private void playTradeSound() { if (!this.trader.isClientSide()) { Entity entity = (Entity)this.trader; entity.level().playLocalSound(entity.getX(), entity.getY(), entity.getZ(), this.trader.getNotifyTradeSound(), SoundSource.NEUTRAL, 1.0F, 1.0F, false); } } @Override public void removed(Player player) { super.removed(player); this.trader.setTradingPlayer(null); if (!this.trader.isClientSide()) { if (!player.isAlive() || player instanceof ServerPlayer && ((ServerPlayer)player).hasDisconnected()) { ItemStack itemStack = this.tradeContainer.removeItemNoUpdate(0); if (!itemStack.isEmpty()) { player.drop(itemStack, false); } itemStack = this.tradeContainer.removeItemNoUpdate(1); if (!itemStack.isEmpty()) { player.drop(itemStack, false); } } else if (player instanceof ServerPlayer) { player.getInventory().placeItemBackInInventory(this.tradeContainer.removeItemNoUpdate(0)); player.getInventory().placeItemBackInInventory(this.tradeContainer.removeItemNoUpdate(1)); } } } public void tryMoveItems(int selectedMerchantRecipe) { if (selectedMerchantRecipe >= 0 && this.getOffers().size() > selectedMerchantRecipe) { ItemStack itemStack = this.tradeContainer.getItem(0); if (!itemStack.isEmpty()) { if (!this.moveItemStackTo(itemStack, 3, 39, true)) { return; } this.tradeContainer.setItem(0, itemStack); } ItemStack itemStack2 = this.tradeContainer.getItem(1); if (!itemStack2.isEmpty()) { if (!this.moveItemStackTo(itemStack2, 3, 39, true)) { return; } this.tradeContainer.setItem(1, itemStack2); } if (this.tradeContainer.getItem(0).isEmpty() && this.tradeContainer.getItem(1).isEmpty()) { MerchantOffer merchantOffer = (MerchantOffer)this.getOffers().get(selectedMerchantRecipe); this.moveFromInventoryToPaymentSlot(0, merchantOffer.getItemCostA()); merchantOffer.getItemCostB().ifPresent(itemCost -> this.moveFromInventoryToPaymentSlot(1, itemCost)); } } } private void moveFromInventoryToPaymentSlot(int paymentSlotIndex, ItemCost payment) { for (int i = 3; i < 39; i++) { ItemStack itemStack = this.slots.get(i).getItem(); if (!itemStack.isEmpty() && payment.test(itemStack)) { ItemStack itemStack2 = this.tradeContainer.getItem(paymentSlotIndex); if (itemStack2.isEmpty() || ItemStack.isSameItemSameComponents(itemStack, itemStack2)) { int j = itemStack.getMaxStackSize(); int k = Math.min(j - itemStack2.getCount(), itemStack.getCount()); ItemStack itemStack3 = itemStack.copyWithCount(itemStack2.getCount() + k); itemStack.shrink(k); this.tradeContainer.setItem(paymentSlotIndex, itemStack3); if (itemStack3.getCount() >= j) { break; } } } } } /** * {@link net.minecraft.client.multiplayer.ClientPacketListener} uses this to set offers for the client side MerchantContainer. */ public void setOffers(MerchantOffers offers) { this.trader.overrideOffers(offers); } public MerchantOffers getOffers() { return this.trader.getOffers(); } public boolean showProgressBar() { return this.showProgressBar; } }