package net.minecraft.world.level.block.entity; import net.minecraft.world.level.block.state.AbstractStateHolder; import net.minecraft.world.entity.player.StackedContents; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.ExperienceOrb; import java.util.Collection; import com.google.common.collect.Lists; import net.minecraft.world.entity.player.Player; import java.util.List; import net.minecraft.core.Direction; import java.util.function.Function; import javax.annotation.Nullable; import net.minecraft.world.level.block.state.properties.Property; import net.minecraft.world.level.block.AbstractFurnaceBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.util.Mth; import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.ContainerHelper; import net.minecraft.nbt.CompoundTag; import java.util.Iterator; import net.minecraft.tags.Tag; import net.minecraft.tags.ItemTags; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.ItemLike; import net.minecraft.world.item.Items; import net.minecraft.world.item.Item; import com.google.common.collect.Maps; import net.minecraft.world.item.crafting.AbstractCookingRecipe; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.resources.ResourceLocation; import java.util.Map; import net.minecraft.world.inventory.ContainerData; import net.minecraft.world.item.ItemStack; import net.minecraft.core.NonNullList; import net.minecraft.world.inventory.StackedContentsCompatible; import net.minecraft.world.inventory.RecipeHolder; import net.minecraft.world.WorldlyContainer; public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntity implements WorldlyContainer, RecipeHolder, StackedContentsCompatible, TickableBlockEntity { private static final int[] SLOTS_FOR_UP; private static final int[] SLOTS_FOR_DOWN; private static final int[] SLOTS_FOR_SIDES; protected NonNullList items; private int litTime; private int litDuration; private int cookingProgress; private int cookingTotalTime; protected final ContainerData dataAccess; private final Map recipesUsed; protected final RecipeType recipeType; protected AbstractFurnaceBlockEntity(final BlockEntityType bwj, final RecipeType bgw) { super(bwj); this.items = NonNullList.withSize(3, ItemStack.EMPTY); this.dataAccess = new ContainerData() { @Override public int get(final int integer) { switch (integer) { case 0: { return AbstractFurnaceBlockEntity.this.litTime; } case 1: { return AbstractFurnaceBlockEntity.this.litDuration; } case 2: { return AbstractFurnaceBlockEntity.this.cookingProgress; } case 3: { return AbstractFurnaceBlockEntity.this.cookingTotalTime; } default: { return 0; } } } @Override public void set(final int integer1, final int integer2) { switch (integer1) { case 0: { AbstractFurnaceBlockEntity.this.litTime = integer2; break; } case 1: { AbstractFurnaceBlockEntity.this.litDuration = integer2; break; } case 2: { AbstractFurnaceBlockEntity.this.cookingProgress = integer2; break; } case 3: { AbstractFurnaceBlockEntity.this.cookingTotalTime = integer2; break; } } } @Override public int getCount() { return 4; } }; this.recipesUsed = Maps.newHashMap(); this.recipeType = bgw; } public static Map getFuel() { final Map map1 = Maps.newLinkedHashMap(); add(map1, Items.LAVA_BUCKET, 20000); add(map1, Blocks.COAL_BLOCK, 16000); add(map1, Items.BLAZE_ROD, 2400); add(map1, Items.COAL, 1600); add(map1, Items.CHARCOAL, 1600); add(map1, ItemTags.LOGS, 300); add(map1, ItemTags.PLANKS, 300); add(map1, ItemTags.WOODEN_STAIRS, 300); add(map1, ItemTags.WOODEN_SLABS, 150); add(map1, ItemTags.WOODEN_TRAPDOORS, 300); add(map1, ItemTags.WOODEN_PRESSURE_PLATES, 300); add(map1, Blocks.OAK_FENCE, 300); add(map1, Blocks.BIRCH_FENCE, 300); add(map1, Blocks.SPRUCE_FENCE, 300); add(map1, Blocks.JUNGLE_FENCE, 300); add(map1, Blocks.DARK_OAK_FENCE, 300); add(map1, Blocks.ACACIA_FENCE, 300); add(map1, Blocks.OAK_FENCE_GATE, 300); add(map1, Blocks.BIRCH_FENCE_GATE, 300); add(map1, Blocks.SPRUCE_FENCE_GATE, 300); add(map1, Blocks.JUNGLE_FENCE_GATE, 300); add(map1, Blocks.DARK_OAK_FENCE_GATE, 300); add(map1, Blocks.ACACIA_FENCE_GATE, 300); add(map1, Blocks.NOTE_BLOCK, 300); add(map1, Blocks.BOOKSHELF, 300); add(map1, Blocks.LECTERN, 300); add(map1, Blocks.JUKEBOX, 300); add(map1, Blocks.CHEST, 300); add(map1, Blocks.TRAPPED_CHEST, 300); add(map1, Blocks.CRAFTING_TABLE, 300); add(map1, Blocks.DAYLIGHT_DETECTOR, 300); add(map1, ItemTags.BANNERS, 300); add(map1, Items.BOW, 300); add(map1, Items.FISHING_ROD, 300); add(map1, Blocks.LADDER, 300); add(map1, ItemTags.SIGNS, 200); add(map1, Items.WOODEN_SHOVEL, 200); add(map1, Items.WOODEN_SWORD, 200); add(map1, Items.WOODEN_HOE, 200); add(map1, Items.WOODEN_AXE, 200); add(map1, Items.WOODEN_PICKAXE, 200); add(map1, ItemTags.WOODEN_DOORS, 200); add(map1, ItemTags.BOATS, 1200); add(map1, ItemTags.WOOL, 100); add(map1, ItemTags.WOODEN_BUTTONS, 100); add(map1, Items.STICK, 100); add(map1, ItemTags.SAPLINGS, 100); add(map1, Items.BOWL, 100); add(map1, ItemTags.CARPETS, 67); add(map1, Blocks.DRIED_KELP_BLOCK, 4001); add(map1, Items.CROSSBOW, 300); add(map1, Blocks.BAMBOO, 50); add(map1, Blocks.DEAD_BUSH, 100); add(map1, Blocks.SCAFFOLDING, 400); add(map1, Blocks.LOOM, 300); add(map1, Blocks.BARREL, 300); add(map1, Blocks.CARTOGRAPHY_TABLE, 300); add(map1, Blocks.FLETCHING_TABLE, 300); add(map1, Blocks.SMITHING_TABLE, 300); add(map1, Blocks.COMPOSTER, 300); return map1; } private static void add(final Map map, final Tag aaz, final int integer) { for (final Item bef5 : aaz.getValues()) { map.put(bef5, integer); } } private static void add(final Map map, final ItemLike bjs, final int integer) { map.put(bjs.asItem(), integer); } private boolean isLit() { return this.litTime > 0; } @Override public void load(final CompoundTag jt) { super.load(jt); ContainerHelper.loadAllItems(jt, this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY)); this.litTime = jt.getShort("BurnTime"); this.cookingProgress = jt.getShort("CookTime"); this.cookingTotalTime = jt.getShort("CookTimeTotal"); this.litDuration = this.getBurnDuration(this.items.get(1)); for (int integer3 = jt.getShort("RecipesUsedSize"), integer4 = 0; integer4 < integer3; ++integer4) { final ResourceLocation sm5 = new ResourceLocation(jt.getString("RecipeLocation" + integer4)); final int integer5 = jt.getInt("RecipeAmount" + integer4); this.recipesUsed.put(sm5, integer5); } } @Override public CompoundTag save(final CompoundTag jt) { super.save(jt); jt.putShort("BurnTime", (short)this.litTime); jt.putShort("CookTime", (short)this.cookingProgress); jt.putShort("CookTimeTotal", (short)this.cookingTotalTime); ContainerHelper.saveAllItems(jt, this.items); jt.putShort("RecipesUsedSize", (short)this.recipesUsed.size()); int integer3 = 0; for (final Map.Entry entry5 : this.recipesUsed.entrySet()) { jt.putString("RecipeLocation" + integer3, entry5.getKey().toString()); jt.putInt("RecipeAmount" + integer3, entry5.getValue()); ++integer3; } return jt; } @Override public void tick() { final boolean boolean2 = this.isLit(); boolean boolean3 = false; if (this.isLit()) { --this.litTime; } if (!this.level.isClientSide) { final ItemStack bek4 = this.items.get(1); if (this.isLit() || (!bek4.isEmpty() && !this.items.get(0).isEmpty())) { final Recipe bgt5 = this.level.getRecipeManager().getRecipeFor(this.recipeType, this, this.level).orElse((AbstractCookingRecipe)null); if (!this.isLit() && this.canBurn(bgt5)) { this.litTime = this.getBurnDuration(bek4); this.litDuration = this.litTime; if (this.isLit()) { boolean3 = true; if (!bek4.isEmpty()) { final Item bef6 = bek4.getItem(); bek4.shrink(1); if (bek4.isEmpty()) { final Item bef7 = bef6.getCraftingRemainingItem(); this.items.set(1, (bef7 == null) ? ItemStack.EMPTY : new ItemStack(bef7)); } } } } if (this.isLit() && this.canBurn(bgt5)) { ++this.cookingProgress; if (this.cookingProgress == this.cookingTotalTime) { this.cookingProgress = 0; this.cookingTotalTime = this.getTotalCookTime(); this.burn(bgt5); boolean3 = true; } } else { this.cookingProgress = 0; } } else if (!this.isLit() && this.cookingProgress > 0) { this.cookingProgress = Mth.clamp(this.cookingProgress - 2, 0, this.cookingTotalTime); } if (boolean2 != this.isLit()) { boolean3 = true; this.level.setBlock(this.worldPosition, ((AbstractStateHolder)this.level.getBlockState(this.worldPosition)).setValue((Property)AbstractFurnaceBlock.LIT, this.isLit()), 3); } } if (boolean3) { this.setChanged(); } } protected boolean canBurn(@Nullable final Recipe bgt) { if (this.items.get(0).isEmpty() || bgt == null) { return false; } final ItemStack bek3 = bgt.getResultItem(); if (bek3.isEmpty()) { return false; } final ItemStack bek4 = this.items.get(2); return bek4.isEmpty() || (bek4.sameItem(bek3) && ((bek4.getCount() < this.getMaxStackSize() && bek4.getCount() < bek4.getMaxStackSize()) || bek4.getCount() < bek3.getMaxStackSize())); } private void burn(@Nullable final Recipe bgt) { if (bgt == null || !this.canBurn(bgt)) { return; } final ItemStack bek3 = this.items.get(0); final ItemStack bek4 = bgt.getResultItem(); final ItemStack bek5 = this.items.get(2); if (bek5.isEmpty()) { this.items.set(2, bek4.copy()); } else if (bek5.getItem() == bek4.getItem()) { bek5.grow(1); } if (!this.level.isClientSide) { this.setRecipeUsed(bgt); } if (bek3.getItem() == Blocks.WET_SPONGE.asItem() && !this.items.get(1).isEmpty() && this.items.get(1).getItem() == Items.BUCKET) { this.items.set(1, new ItemStack(Items.WATER_BUCKET)); } bek3.shrink(1); } protected int getBurnDuration(final ItemStack bek) { if (bek.isEmpty()) { return 0; } final Item bef3 = bek.getItem(); return getFuel().getOrDefault(bef3, 0); } protected int getTotalCookTime() { return this.level.getRecipeManager().getRecipeFor(this.recipeType, this, this.level).map(AbstractCookingRecipe::getCookingTime).orElse(200); } public static boolean isFuel(final ItemStack bek) { return getFuel().containsKey(bek.getItem()); } @Override public int[] getSlotsForFace(final Direction fp) { if (fp == Direction.DOWN) { return AbstractFurnaceBlockEntity.SLOTS_FOR_DOWN; } if (fp == Direction.UP) { return AbstractFurnaceBlockEntity.SLOTS_FOR_UP; } return AbstractFurnaceBlockEntity.SLOTS_FOR_SIDES; } @Override public boolean canPlaceItemThroughFace(final int integer, final ItemStack bek, @Nullable final Direction fp) { return this.canPlaceItem(integer, bek); } @Override public boolean canTakeItemThroughFace(final int integer, final ItemStack bek, final Direction fp) { if (fp == Direction.DOWN && integer == 1) { final Item bef5 = bek.getItem(); if (bef5 != Items.WATER_BUCKET && bef5 != Items.BUCKET) { return false; } } return true; } @Override public int getContainerSize() { return this.items.size(); } @Override public boolean isEmpty() { for (final ItemStack bek3 : this.items) { if (!bek3.isEmpty()) { return false; } } return true; } @Override public ItemStack getItem(final int integer) { return this.items.get(integer); } @Override public ItemStack removeItem(final int integer1, final int integer2) { return ContainerHelper.removeItem(this.items, integer1, integer2); } @Override public ItemStack removeItemNoUpdate(final int integer) { return ContainerHelper.takeItem(this.items, integer); } @Override public void setItem(final int integer, final ItemStack bek) { final ItemStack bek2 = this.items.get(integer); final boolean boolean5 = !bek.isEmpty() && bek.sameItem(bek2) && ItemStack.tagMatches(bek, bek2); this.items.set(integer, bek); if (bek.getCount() > this.getMaxStackSize()) { bek.setCount(this.getMaxStackSize()); } if (integer == 0 && !boolean5) { this.cookingTotalTime = this.getTotalCookTime(); this.cookingProgress = 0; this.setChanged(); } } @Override public boolean stillValid(final Player ayg) { return this.level.getBlockEntity(this.worldPosition) == this && ayg.distanceToSqr(this.worldPosition.getX() + 0.5, this.worldPosition.getY() + 0.5, this.worldPosition.getZ() + 0.5) <= 64.0; } @Override public boolean canPlaceItem(final int integer, final ItemStack bek) { if (integer == 2) { return false; } if (integer == 1) { final ItemStack bek2 = this.items.get(1); return isFuel(bek) || (bek.getItem() == Items.BUCKET && bek2.getItem() != Items.BUCKET); } return true; } @Override public void clearContent() { this.items.clear(); } @Override public void setRecipeUsed(@Nullable final Recipe bgt) { if (bgt != null) { this.recipesUsed.compute(bgt.getId(), (sm, integer) -> 1 + ((integer == null) ? 0 : integer)); } } @Nullable @Override public Recipe getRecipeUsed() { return null; } @Override public void awardAndReset(final Player ayg) { } public void awardResetAndExperience(final Player ayg) { final List> list3 = Lists.newArrayList(); for (final Map.Entry entry5 : this.recipesUsed.entrySet()) { final List list4; final Map.Entry entry6; ayg.level.getRecipeManager().byKey(entry5.getKey()).ifPresent(bgt -> { list4.add(bgt); createExperience(ayg, entry6.getValue(), bgt.getExperience()); return; }); } ayg.awardRecipes(list3); this.recipesUsed.clear(); } private static void createExperience(final Player ayg, int integer, final float float3) { if (float3 == 0.0f) { integer = 0; } else if (float3 < 1.0f) { int integer2 = Mth.floor(integer * float3); if (integer2 < Mth.ceil(integer * float3) && Math.random() < integer * float3 - integer2) { ++integer2; } integer = integer2; } while (integer > 0) { final int integer2 = ExperienceOrb.getExperienceValue(integer); integer -= integer2; ayg.level.addFreshEntity(new ExperienceOrb(ayg.level, ayg.getX(), ayg.getY() + 0.5, ayg.getZ() + 0.5, integer2)); } } @Override public void fillStackedContents(final StackedContents ayi) { for (final ItemStack bek4 : this.items) { ayi.accountStack(bek4); } } static { SLOTS_FOR_UP = new int[] { 0 }; SLOTS_FOR_DOWN = new int[] { 2, 1 }; SLOTS_FOR_SIDES = new int[] { 1 }; } }