1
mirror of https://github.com/StarWishsama/Slimefun4.git synced 2024-09-20 03:35:51 +00:00

Created abstract auto crafter framework

This commit is contained in:
TheBusyBiscuit 2021-02-02 18:57:45 +01:00
parent 5a0a52baab
commit 0aa5a999d4
3 changed files with 324 additions and 0 deletions

View File

@ -0,0 +1,257 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.electric.crafters;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.apache.commons.lang.Validate;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.inventory.InvUtils;
import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon;
import io.github.thebusybiscuit.slimefun4.api.items.ItemState;
import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent;
import io.github.thebusybiscuit.slimefun4.core.handlers.BlockUseHandler;
import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNetComponentType;
import io.papermc.lib.PaperLib;
import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AContainer;
import me.mrCookieSlime.Slimefun.Objects.handlers.BlockTicker;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
public abstract class AbstractAutoCrafter extends SlimefunItem implements EnergyNetComponent {
private int energyConsumedPerTick = -1;
private int energyCapacity = -1;
@ParametersAreNonnullByDefault
public AbstractAutoCrafter(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe);
addItemHandler(onRightClick());
addItemHandler(new BlockTicker() {
@Override
public void tick(Block b, SlimefunItem item, Config data) {
AbstractAutoCrafter.this.tick(b, data);
}
@Override
public boolean isSynchronized() {
return true;
}
});
}
@Nonnull
private BlockUseHandler onRightClick() {
return e -> e.getClickedBlock().ifPresent(b -> onRightClick(e.getPlayer(), b));
}
protected void tick(@Nonnull Block b, @Nonnull Config data) {
AbstractRecipe recipe = getSelectedRecipe(b);
if (recipe == null || getCharge(b.getLocation(), data) < getEnergyConsumption()) {
// No valid recipe selected, abort...
return;
}
Block chest = b.getRelative(BlockFace.DOWN);
if (chest.getType() == Material.CHEST || chest.getType() == Material.TRAPPED_CHEST) {
BlockState state = PaperLib.getBlockState(chest, false).getState();
if (state instanceof InventoryHolder) {
Inventory inv = ((InventoryHolder) state).getInventory();
if (craft(inv, recipe)) {
removeCharge(b.getLocation(), getEnergyConsumption());
}
}
}
}
/**
* This method returns the currently selected {@link AbstractRecipe} for the given
* {@link Block}.
*
* @param b
* The {@link Block}
*
* @return The currently selected {@link AbstractRecipe} or null
*/
@Nullable
public abstract AbstractRecipe getSelectedRecipe(@Nonnull Block b);
/**
* This method is called when a {@link Player} right clicks the {@link AbstractAutoCrafter}.
* Use it to choose the {@link AbstractRecipe}.
*
* @param p
* The {@link Player} who clicked
* @param b
* The {@link Block} which was clicked
*/
protected abstract void onRightClick(@Nonnull Player p, @Nonnull Block b);
/**
* This method checks whether the given {@link Predicate} matches the provided {@link ItemStack}.
*
* @param item
* The {@link ItemStack} to check
* @param predicate
* The {@link Predicate}
*
* @return Whether the {@link Predicate} matches the {@link ItemStack}
*/
@ParametersAreNonnullByDefault
protected boolean matches(ItemStack item, Predicate<ItemStack> predicate) {
return predicate.test(item);
}
@ParametersAreNonnullByDefault
protected boolean matchesAny(Inventory inv, Map<Integer, Integer> itemQuantities, Predicate<ItemStack> predicate) {
for (int slot = 0; slot < inv.getSize(); slot++) {
ItemStack item = inv.getItem(slot);
if (item != null) {
int amount = itemQuantities.getOrDefault(slot, item.getAmount());
if (amount > 0 && matches(item, predicate)) {
// Update our local quantity map
itemQuantities.put(slot, amount - 1);
return true;
}
}
}
return false;
}
public boolean craft(@Nonnull Inventory inv, @Nonnull AbstractRecipe recipe) {
Validate.notNull(inv, "The Inventory must not be null");
Validate.notNull(recipe, "The Recipe shall not be null");
if (InvUtils.fits(inv, recipe.getResult())) {
Map<Integer, Integer> itemQuantities = new HashMap<>();
for (Predicate<ItemStack> predicate : recipe.getInputs()) {
// Check if any Item matches the Predicate
if (!matchesAny(inv, itemQuantities, predicate)) {
return false;
}
}
// Remove ingredients
for (Map.Entry<Integer, Integer> entry : itemQuantities.entrySet()) {
ItemStack item = inv.getItem(entry.getKey());
// Double-check to be extra safe
if (item != null) {
item.setAmount(entry.getValue());
}
}
// All Predicates have found a match
return true;
}
return false;
}
/**
* This method returns the max amount of electricity this machine can hold.
*
* @return The max amount of electricity this Block can store.
*/
@Override
public int getCapacity() {
return energyCapacity;
}
/**
* This method returns the amount of energy that is consumed per operation.
*
* @return The rate of energy consumption
*/
public int getEnergyConsumption() {
return energyConsumedPerTick;
}
/**
* This sets the energy capacity for this machine.
* This method <strong>must</strong> be called before registering the item
* and only before registering.
*
* @param capacity
* The amount of energy this machine can store
*
* @return This method will return the current instance of {@link AContainer}, so that can be chained.
*/
public final AbstractAutoCrafter setCapacity(int capacity) {
Validate.isTrue(capacity > 0, "The capacity must be greater than zero!");
if (getState() == ItemState.UNREGISTERED) {
this.energyCapacity = capacity;
return this;
} else {
throw new IllegalStateException("You cannot modify the capacity after the Item was registered.");
}
}
/**
* This method sets the energy consumed by this machine per tick.
*
* @param energyConsumption
* The energy consumed per tick
*
* @return This method will return the current instance of {@link AContainer}, so that can be chained.
*/
public final AbstractAutoCrafter setEnergyConsumption(int energyConsumption) {
Validate.isTrue(energyConsumption > 0, "The energy consumption must be greater than zero!");
Validate.isTrue(energyCapacity > 0, "You must specify the capacity before you can set the consumption amount.");
Validate.isTrue(energyConsumption <= energyCapacity, "The energy consumption cannot be higher than the capacity (" + energyCapacity + ')');
this.energyConsumedPerTick = energyConsumption;
return this;
}
@Override
public void register(@Nonnull SlimefunAddon addon) {
this.addon = addon;
if (getCapacity() <= 0) {
warn("The capacity has not been configured correctly. The Item was disabled.");
warn("Make sure to call '" + getClass().getSimpleName() + "#setEnergyCapacity(...)' before registering!");
}
if (getEnergyConsumption() <= 0) {
warn("The energy consumption has not been configured correctly. The Item was disabled.");
warn("Make sure to call '" + getClass().getSimpleName() + "#setEnergyConsumption(...)' before registering!");
}
if (getCapacity() > 0 && getEnergyConsumption() > 0) {
super.register(addon);
}
}
@Override
public final EnergyNetComponentType getEnergyComponentType() {
return EnergyNetComponentType.CONSUMER;
}
}

View File

@ -0,0 +1,66 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.electric.crafters;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.apache.commons.lang.Validate;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.RecipeChoice;
import org.bukkit.inventory.ShapedRecipe;
import org.bukkit.inventory.ShapelessRecipe;
public class AbstractRecipe {
private final Collection<Predicate<ItemStack>> inputs;
private final ItemStack result;
@ParametersAreNonnullByDefault
public AbstractRecipe(Collection<Predicate<ItemStack>> inputs, ItemStack result) {
Validate.notEmpty(inputs, "The input predicates cannot be null or an empty array");
Validate.notNull(result, "The recipe result must not be null!");
this.inputs = inputs;
this.result = result;
}
public AbstractRecipe(@Nonnull ShapelessRecipe recipe) {
this(new ArrayList<>(recipe.getChoiceList()), recipe.getResult());
}
public AbstractRecipe(@Nonnull ShapedRecipe recipe) {
this(getChoices(recipe), recipe.getResult());
}
@Nonnull
private static Collection<Predicate<ItemStack>> getChoices(@Nonnull ShapedRecipe recipe) {
List<Predicate<ItemStack>> choices = new ArrayList<>();
for (String row : recipe.getShape()) {
for (char c : row.toCharArray()) {
RecipeChoice choice = recipe.getChoiceMap().get(c);
if (choice != null) {
choices.add(choice);
}
}
}
return choices;
}
@Nonnull
public Collection<Predicate<ItemStack>> getInputs() {
return inputs;
}
@Nonnull
public ItemStack getResult() {
return result;
}
}

View File

@ -141,6 +141,7 @@ public abstract class AContainer extends SlimefunItem implements InventoryBlock,
*
* @return The max amount of electricity this Block can store.
*/
@Override
public int getCapacity() {
return energyCapacity;
}