package dev.zontreck.otemod.blocks.entity; import dev.zontreck.otemod.implementation.OutputItemStackHandler; import dev.zontreck.otemod.implementation.energy.OTEEnergy; import dev.zontreck.otemod.implementation.uncrafting.UncrafterMenu; import dev.zontreck.otemod.items.PartialItem; import dev.zontreck.otemod.networking.ModMessages; import dev.zontreck.otemod.networking.packets.EnergySyncS2CPacket; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.world.Containers; import net.minecraft.world.MenuProvider; import net.minecraft.world.SimpleContainer; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.ContainerData; import net.minecraft.world.inventory.CraftingContainer; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.crafting.CraftingRecipe; import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.energy.IEnergyStorage; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.ItemStackHandler; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class UncrafterBlockEntity extends BlockEntity implements MenuProvider { public UncrafterBlockEntity(BlockPos position, BlockState state) { super(ModEntities.UNCRAFTER.get(), position, state); outputSlots = new OutputItemStackHandler(outputItems); this.data = new ContainerData() { @Override public int get(int i) { switch (i) { case 0: { return UncrafterBlockEntity.this.progress; } default: return 0; } } @Override public void set(int i, int i1) { switch (i) { case 0: { UncrafterBlockEntity.this.progress = i1; } } } @Override public int getCount() { return 1; } }; } protected final ContainerData data; protected int progress = 0; private static final int ENERGY_REQUIREMENT = 250; public static int PROCESSING_TICKS = (3 * 20); // 3 seconds to uncraft protected final ItemStackHandler itemHandler = new ItemStackHandler(1) { @Override protected void onContentsChanged(int slot) { setChanged(); } }; protected final ItemStackHandler outputItems = new ItemStackHandler(1){ @Override protected void onContentsChanged(int slot) { setChanged(); } }; private ItemStackHandler outputSlots; private LazyOptional lazyEnergyHandler = LazyOptional.empty(); private LazyOptional lazyItemHandler = LazyOptional.empty(); private LazyOptional lazyOutputItems = LazyOptional.empty(); private final OTEEnergy ENERGY_STORAGE = new OTEEnergy(ENERGY_REQUIREMENT * 10, ENERGY_REQUIREMENT*2) { @Override public void onChanged() { setChanged(); ModMessages.sendToAll(new EnergySyncS2CPacket(energy, getBlockPos())); } }; @Override public Component getDisplayName() { return Component.literal("Uncrafting Factory"); } @Nullable @Override public AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) { return new UncrafterMenu(i, inventory, this, data); } @Override public void onLoad() { super.onLoad(); lazyItemHandler = LazyOptional.of(()->itemHandler); lazyOutputItems = LazyOptional.of(()->outputSlots); lazyEnergyHandler = LazyOptional.of(()->ENERGY_STORAGE); } @Override public void invalidateCaps() { super.invalidateCaps(); lazyItemHandler.invalidate(); lazyOutputItems.invalidate(); lazyEnergyHandler.invalidate(); } @Override protected void saveAdditional(CompoundTag nbt) { nbt.put("inventory", itemHandler.serializeNBT()); nbt.put("output", outputItems.serializeNBT()); nbt.putInt("prog", progress); nbt.putInt("energy", ENERGY_STORAGE.getEnergyStored()); super.saveAdditional(nbt); } @Override public void load(CompoundTag nbt){ super.load(nbt); itemHandler.deserializeNBT(nbt.getCompound("inventory")); outputItems.deserializeNBT(nbt.getCompound("output")); progress = nbt.getInt("prog"); ENERGY_STORAGE.setEnergy(nbt.getInt("energy")); } @Override public @NotNull LazyOptional getCapability(@NotNull Capability cap, @Nullable Direction side) { if(cap == ForgeCapabilities.ENERGY) // all sides accept power { return lazyEnergyHandler.cast(); } if(cap == ForgeCapabilities.FLUID_HANDLER) { //return lazyFluidHandler.cast(); // TODO: Implement a fluid storage, and add a spot for it on the GUI } if(cap == ForgeCapabilities.ITEM_HANDLER && side == Direction.DOWN) { return lazyOutputItems.cast(); }else return lazyItemHandler.cast(); // all sides except bottom of block } public void doDrop() { SimpleContainer cont = new SimpleContainer(itemHandler.getSlots()); for (int i = 0; i < itemHandler.getSlots(); i++) { cont.setItem(i, itemHandler.getStackInSlot(i)); } cont = new SimpleContainer(outputItems.getSlots()); for (int i = 0; i < outputItems.getSlots(); i++) { cont.setItem(i, outputItems.getStackInSlot(i)); } Containers.dropContents(this.level, this.worldPosition, cont); } public static void tick(Level lvl, BlockPos pos, BlockState state, UncrafterBlockEntity entity) { if(lvl.isClientSide())return; if(hasRecipe(entity)) { if(!hasEnergy(entity))return; // Halt until sufficient energy has been received entity.progress++; setChanged(lvl, pos, state); drain(entity); if(entity.progress >= UncrafterBlockEntity.PROCESSING_TICKS) { uncraftItem(entity); } }else { if(entity.progress>0){ entity.resetProgress(); setChanged(lvl, pos, state); } } } private static void drain(UncrafterBlockEntity entity) { entity.ENERGY_STORAGE.extractEnergy(ENERGY_REQUIREMENT, false); } private static boolean hasEnergy(UncrafterBlockEntity entity) { return (entity.ENERGY_STORAGE.getEnergyStored() >= ENERGY_REQUIREMENT); } private ItemStack[] getIngredients(Recipe recipe) { ItemStack[] stacks = new ItemStack[recipe.getIngredients().size()]; for (int i = 0; i < recipe.getIngredients().size(); i++) { ItemStack[] matchingStacks = Arrays.stream(recipe.getIngredients().get(i).getItems()).toArray(ItemStack[]::new); stacks[i] = matchingStacks.length > 0 ? matchingStacks[Math.floorMod(this.ingredientsInCycle, matchingStacks.length)] : ItemStack.EMPTY; } return stacks; } private int ingredientsInCycle=0; private static CraftingRecipe[] getRecipesFor(CraftingContainer matrix, Level world) { return world.getRecipeManager().getRecipesFor(RecipeType.CRAFTING, matrix, world).toArray(new CraftingRecipe[0]); } private static void uncraftItem(UncrafterBlockEntity entity) { if(hasRecipe(entity)) { ItemStack existing = entity.outputItems.getStackInSlot(0); List INGREDIENTS = new ArrayList<>(); if(existing.getItem() instanceof PartialItem pi) { INGREDIENTS = PartialItem.getRemainingIngredients(existing); } else { // Reverse recipe } existing.setCount(existing.getCount()+1); if(existing.is(Items.AIR)) { existing = makeOutputItems(entity.itemHandler.getStackInSlot(0)); } entity.itemHandler.extractItem(0, 1, false); entity.outputItems.setStackInSlot(0, existing); entity.resetProgress(); } } protected static ItemStack makeOutputItems(ItemStack original) { ItemStack newItem = new ItemStack(original.getItem(),1); return newItem; } private void resetProgress() { progress=0; } public IEnergyStorage getEnergyStorage() { return ENERGY_STORAGE; } public void setEnergy(int energy) { ENERGY_STORAGE.setEnergy(energy); } private static boolean hasRecipe(UncrafterBlockEntity entity) { SimpleContainer inventory = new SimpleContainer(entity.itemHandler.getSlots()); for(int i=0;i existing.getCount()); boolean sameType = (existing.getItem() == result.getItem()); boolean outputEmpty = existing.isEmpty(); if(outputEmpty)return true; return (stackCompat && sameType); } }