minecraft-src/net/minecraft/world/inventory/GrindstoneMenu.java
2025-07-04 02:00:41 +03:00

264 lines
8.7 KiB
Java

package net.minecraft.world.inventory;
import it.unimi.dsi.fastutil.objects.Object2IntMap.Entry;
import net.minecraft.core.Holder;
import net.minecraft.core.component.DataComponents;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.EnchantmentTags;
import net.minecraft.world.Container;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.ExperienceOrb;
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.Items;
import net.minecraft.world.item.enchantment.Enchantment;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.item.enchantment.ItemEnchantments;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.phys.Vec3;
public class GrindstoneMenu extends AbstractContainerMenu {
public static final int MAX_NAME_LENGTH = 35;
public static final int INPUT_SLOT = 0;
public static final int ADDITIONAL_SLOT = 1;
public 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;
/**
* The inventory slot that stores the output of the crafting recipe.
*/
private final Container resultSlots = new ResultContainer();
final Container repairSlots = new SimpleContainer(2) {
@Override
public void setChanged() {
super.setChanged();
GrindstoneMenu.this.slotsChanged(this);
}
};
private final ContainerLevelAccess access;
public GrindstoneMenu(int containerId, Inventory playerInventory) {
this(containerId, playerInventory, ContainerLevelAccess.NULL);
}
public GrindstoneMenu(int containerId, Inventory playerInventory, ContainerLevelAccess access) {
super(MenuType.GRINDSTONE, containerId);
this.access = access;
this.addSlot(new Slot(this.repairSlots, 0, 49, 19) {
@Override
public boolean mayPlace(ItemStack stack) {
return stack.isDamageableItem() || EnchantmentHelper.hasAnyEnchantments(stack);
}
});
this.addSlot(new Slot(this.repairSlots, 1, 49, 40) {
@Override
public boolean mayPlace(ItemStack stack) {
return stack.isDamageableItem() || EnchantmentHelper.hasAnyEnchantments(stack);
}
});
this.addSlot(
new Slot(this.resultSlots, 2, 129, 34) {
@Override
public boolean mayPlace(ItemStack stack) {
return false;
}
@Override
public void onTake(Player player, ItemStack stack) {
access.execute((level, blockPos) -> {
if (level instanceof ServerLevel) {
ExperienceOrb.award((ServerLevel)level, Vec3.atCenterOf(blockPos), this.getExperienceAmount(level));
}
level.levelEvent(1042, blockPos, 0);
});
GrindstoneMenu.this.repairSlots.setItem(0, ItemStack.EMPTY);
GrindstoneMenu.this.repairSlots.setItem(1, ItemStack.EMPTY);
}
/**
* Returns the total amount of XP stored in all the input slots of this container. The return value is randomized, so that it returns between 50% and 100% of the total XP.
*/
private int getExperienceAmount(Level level) {
int i = 0;
i += this.getExperienceFromItem(GrindstoneMenu.this.repairSlots.getItem(0));
i += this.getExperienceFromItem(GrindstoneMenu.this.repairSlots.getItem(1));
if (i > 0) {
int j = (int)Math.ceil(i / 2.0);
return j + level.random.nextInt(j);
} else {
return 0;
}
}
/**
* Returns the total amount of XP stored in the enchantments of this stack.
*/
private int getExperienceFromItem(ItemStack stack) {
int i = 0;
ItemEnchantments itemEnchantments = EnchantmentHelper.getEnchantmentsForCrafting(stack);
for (Entry<Holder<Enchantment>> entry : itemEnchantments.entrySet()) {
Holder<Enchantment> holder = (Holder<Enchantment>)entry.getKey();
int j = entry.getIntValue();
if (!holder.is(EnchantmentTags.CURSE)) {
i += holder.value().getMinCost(j);
}
}
return i;
}
}
);
this.addStandardInventorySlots(playerInventory, 8, 84);
}
@Override
public void slotsChanged(Container container) {
super.slotsChanged(container);
if (container == this.repairSlots) {
this.createResult();
}
}
private void createResult() {
this.resultSlots.setItem(0, this.computeResult(this.repairSlots.getItem(0), this.repairSlots.getItem(1)));
this.broadcastChanges();
}
private ItemStack computeResult(ItemStack inputItem, ItemStack additionalItem) {
boolean bl = !inputItem.isEmpty() || !additionalItem.isEmpty();
if (!bl) {
return ItemStack.EMPTY;
} else if (inputItem.getCount() <= 1 && additionalItem.getCount() <= 1) {
boolean bl2 = !inputItem.isEmpty() && !additionalItem.isEmpty();
if (!bl2) {
ItemStack itemStack = !inputItem.isEmpty() ? inputItem : additionalItem;
return !EnchantmentHelper.hasAnyEnchantments(itemStack) ? ItemStack.EMPTY : this.removeNonCursesFrom(itemStack.copy());
} else {
return this.mergeItems(inputItem, additionalItem);
}
} else {
return ItemStack.EMPTY;
}
}
private ItemStack mergeItems(ItemStack inputItem, ItemStack additionalItem) {
if (!inputItem.is(additionalItem.getItem())) {
return ItemStack.EMPTY;
} else {
int i = Math.max(inputItem.getMaxDamage(), additionalItem.getMaxDamage());
int j = inputItem.getMaxDamage() - inputItem.getDamageValue();
int k = additionalItem.getMaxDamage() - additionalItem.getDamageValue();
int l = j + k + i * 5 / 100;
int m = 1;
if (!inputItem.isDamageableItem()) {
if (inputItem.getMaxStackSize() < 2 || !ItemStack.matches(inputItem, additionalItem)) {
return ItemStack.EMPTY;
}
m = 2;
}
ItemStack itemStack = inputItem.copyWithCount(m);
if (itemStack.isDamageableItem()) {
itemStack.set(DataComponents.MAX_DAMAGE, i);
itemStack.setDamageValue(Math.max(i - l, 0));
}
this.mergeEnchantsFrom(itemStack, additionalItem);
return this.removeNonCursesFrom(itemStack);
}
}
private void mergeEnchantsFrom(ItemStack inputItem, ItemStack additionalItem) {
EnchantmentHelper.updateEnchantments(inputItem, mutable -> {
ItemEnchantments itemEnchantments = EnchantmentHelper.getEnchantmentsForCrafting(additionalItem);
for (Entry<Holder<Enchantment>> entry : itemEnchantments.entrySet()) {
Holder<Enchantment> holder = (Holder<Enchantment>)entry.getKey();
if (!holder.is(EnchantmentTags.CURSE) || mutable.getLevel(holder) == 0) {
mutable.upgrade(holder, entry.getIntValue());
}
}
});
}
private ItemStack removeNonCursesFrom(ItemStack item) {
ItemEnchantments itemEnchantments = EnchantmentHelper.updateEnchantments(item, mutable -> mutable.removeIf(holder -> !holder.is(EnchantmentTags.CURSE)));
if (item.is(Items.ENCHANTED_BOOK) && itemEnchantments.isEmpty()) {
item = item.transmuteCopy(Items.BOOK);
}
int i = 0;
for (int j = 0; j < itemEnchantments.size(); j++) {
i = AnvilMenu.calculateIncreasedRepairCost(i);
}
item.set(DataComponents.REPAIR_COST, i);
return item;
}
@Override
public void removed(Player player) {
super.removed(player);
this.access.execute((level, blockPos) -> this.clearContainer(player, this.repairSlots));
}
@Override
public boolean stillValid(Player player) {
return stillValid(this.access, player, Blocks.GRINDSTONE);
}
@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();
ItemStack itemStack3 = this.repairSlots.getItem(0);
ItemStack itemStack4 = this.repairSlots.getItem(1);
if (index == 2) {
if (!this.moveItemStackTo(itemStack2, 3, 39, true)) {
return ItemStack.EMPTY;
}
slot.onQuickCraft(itemStack2, itemStack);
} else if (index != 0 && index != 1) {
if (!itemStack3.isEmpty() && !itemStack4.isEmpty()) {
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, 0, 2, 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;
}
}