Begin to implement the Uncrafter. (WIP)
This commit is contained in:
parent
09e93001b5
commit
9f36566fa6
4 changed files with 315 additions and 0 deletions
|
@ -3,6 +3,7 @@ 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;
|
||||
|
@ -16,8 +17,13 @@ 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;
|
||||
|
@ -30,6 +36,10 @@ 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) {
|
||||
|
@ -216,10 +226,40 @@ public class UncrafterBlockEntity extends BlockEntity implements MenuProvider
|
|||
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<Item> 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))
|
||||
{
|
||||
|
|
|
@ -1,18 +1,26 @@
|
|||
package dev.zontreck.otemod.items;
|
||||
|
||||
import dev.zontreck.libzontreck.util.ChatHelpers;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.*;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.commands.GiveCommand;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PartialItem extends Item
|
||||
{
|
||||
private static final String TAG_UNCRAFT_REMAIN = "remaining";
|
||||
private static final String TAG_UNCRAFT_LIST = "Items";
|
||||
|
||||
|
||||
public PartialItem() {
|
||||
super (new Properties().fireResistant());
|
||||
|
@ -32,4 +40,31 @@ public class PartialItem extends Item
|
|||
tooltip.add(ChatHelpers.macro("!Dark_Red!This partial item appears to be invalid, and contains no item fragments."));
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Item> getRemainingIngredients(ItemStack stack)
|
||||
{
|
||||
List<Item> itx = new ArrayList<>();
|
||||
if(stack.getTag()!=null)
|
||||
{
|
||||
if(stack.getTag().contains(TAG_UNCRAFT_LIST))
|
||||
{
|
||||
ListTag lst = stack.getTag().getList(TAG_UNCRAFT_LIST, ListTag.TAG_STRING);
|
||||
|
||||
for (Tag tag :
|
||||
lst) {
|
||||
StringTag st = (StringTag)tag;
|
||||
itx.add(deserializeItemType(st.getAsString()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return itx;
|
||||
}
|
||||
|
||||
private static Item deserializeItemType(String item)
|
||||
{
|
||||
return ForgeRegistries.ITEMS.getValue(new ResourceLocation(item));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ public class ModRecipes {
|
|||
|
||||
public static final RegistryObject<RecipeSerializer<CompressionChamberRecipe>> COMPRESSING_SERIALIZER = SERIALIZERS.register("compressing", ()->CompressionChamberRecipe.Serializer.INSTANCE);
|
||||
|
||||
public static final RegistryObject<RecipeSerializer<UncraftingRecipe>> UNCRAFTING_SERIALIZER = SERIALIZERS.register("uncrafting", UncraftingRecipe.Serializer::new);
|
||||
|
||||
public static void register(IEventBus bus)
|
||||
{
|
||||
SERIALIZERS.register(bus);
|
||||
|
|
238
src/main/java/dev/zontreck/otemod/recipe/UncraftingRecipe.java
Normal file
238
src/main/java/dev/zontreck/otemod/recipe/UncraftingRecipe.java
Normal file
|
@ -0,0 +1,238 @@
|
|||
package dev.zontreck.otemod.recipe;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.GsonHelper;
|
||||
import net.minecraft.world.inventory.CraftingContainer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.crafting.*;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraftforge.common.crafting.IShapedRecipe;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
public record UncraftingRecipe(ResourceLocation recipeID, int width, int height, Ingredient input, int count, NonNullList<Ingredient> resultItems) implements CraftingRecipe, IShapedRecipe<CraftingContainer> {
|
||||
|
||||
@Override //This method is never used, but it has to be implemented
|
||||
public boolean matches(CraftingContainer container, Level level) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override //We have to implement this method, can't really be used since we have multiple outputs
|
||||
public ItemStack assemble(CraftingContainer container, RegistryAccess access) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override //We have to implement this method, returns the count just in case
|
||||
public ItemStack getResultItem(RegistryAccess access) {
|
||||
return new ItemStack(Items.AIR, this.count);
|
||||
}
|
||||
|
||||
@Override //Could probably be set to return true, since the recipe serializer doesn't let a bigger number through.
|
||||
public boolean canCraftInDimensions(int width, int height) {
|
||||
return (width >= this.width && height >= this.height);
|
||||
}
|
||||
|
||||
//Checks if the itemStack is a part of the ingredient when UncraftingMenu's getRecipesFor() method iterates through all recipes.
|
||||
public boolean isItemStackAnIngredient(ItemStack stack) {
|
||||
return Arrays.stream(this.input().getItems()).anyMatch(i -> (stack.getItem() == i.getItem() && stack.getCount() >= this.count()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getId() {
|
||||
return this.recipeID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeSerializer<?> getSerializer() {
|
||||
return ModRecipes.UNCRAFTING_SERIALIZER.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeType<?> getType() {
|
||||
return Type.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CraftingBookCategory category() {
|
||||
return CraftingBookCategory.MISC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecipeWidth() {
|
||||
return this.width();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecipeHeight() {
|
||||
return this.height();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonNullList<Ingredient> getIngredients() {
|
||||
return this.resultItems();
|
||||
}
|
||||
|
||||
public static class Serializer implements RecipeSerializer<UncraftingRecipe> {
|
||||
/**
|
||||
* This is mostly vanilla's shaped recipe serializer, with some changes made to make it work with the slightly different recipe type.
|
||||
* The recipe json has inputs for "cost", which determines how many levels the recipe will cost.
|
||||
* "input", which is made to be an ingredient instead of an itemStack, so the recipe can have multiple input options, such as any member of an item tag.
|
||||
* "count" is how many of the same item are required by the recipe, since we're dealing with ingredients and not itemStacks, we get this separately.
|
||||
* "key" and "pattern", which work just like vanilla except this is output and not input, since we're uncrafting.
|
||||
* Width and height get assigned automatically.
|
||||
*/
|
||||
@Override
|
||||
public UncraftingRecipe fromJson(ResourceLocation id, JsonObject json) {
|
||||
JsonObject input = GsonHelper.getAsJsonObject(json, "input");
|
||||
|
||||
JsonElement jsonelement = (GsonHelper.isArrayNode(input, "ingredient") ? GsonHelper.getAsJsonArray(input, "ingredient") : GsonHelper.getAsJsonObject(input, "ingredient"));
|
||||
Ingredient ingredient = Ingredient.fromJson(jsonelement);
|
||||
|
||||
int count = GsonHelper.getAsInt(input, "count", 1);
|
||||
|
||||
Map<String, Ingredient> key = keyFromJson(GsonHelper.getAsJsonObject(json, "key"));
|
||||
String[] pattern = shrink(patternFromJson(GsonHelper.getAsJsonArray(json, "pattern")));
|
||||
|
||||
int width = pattern[0].length();
|
||||
int height = pattern.length;
|
||||
|
||||
NonNullList<Ingredient> ingredients = dissolvePattern(pattern, key, width, height);
|
||||
|
||||
return new UncraftingRecipe(id, width, height, ingredient, count, ingredients);
|
||||
}
|
||||
|
||||
private static Map<String, Ingredient> keyFromJson(JsonObject json) {
|
||||
Map<String, Ingredient> map = Maps.newHashMap();
|
||||
for (Map.Entry<String, JsonElement> entry : json.entrySet()) {
|
||||
if (entry.getKey().length() != 1)
|
||||
throw new JsonSyntaxException("Invalid key entry: '" + entry.getKey() + "' is an invalid symbol (must be 1 character only).");
|
||||
if (" ".equals(entry.getKey()))
|
||||
throw new JsonSyntaxException("Invalid key entry: ' ' is a reserved symbol.");
|
||||
map.put(entry.getKey(), Ingredient.fromJson(entry.getValue()));
|
||||
}
|
||||
map.put(" ", Ingredient.EMPTY);
|
||||
return map;
|
||||
}
|
||||
|
||||
static String[] shrink(String... prePattern) {
|
||||
int i = Integer.MAX_VALUE;
|
||||
int j = 0;
|
||||
int k = 0;
|
||||
int l = 0;
|
||||
|
||||
for (int i1 = 0; i1 < prePattern.length; ++i1) {
|
||||
String s = prePattern[i1];
|
||||
i = Math.min(i, firstNonSpace(s));
|
||||
int j1 = lastNonSpace(s);
|
||||
j = Math.max(j, j1);
|
||||
if (j1 < 0) {
|
||||
if (k == i1) ++k;
|
||||
++l;
|
||||
} else l = 0;
|
||||
}
|
||||
|
||||
if (prePattern.length == l) return new String[0];
|
||||
else {
|
||||
String[] shrunk = new String[prePattern.length - l - k];
|
||||
for (int k1 = 0; k1 < shrunk.length; ++k1) shrunk[k1] = prePattern[k1 + k].substring(i, j + 1);
|
||||
return shrunk;
|
||||
}
|
||||
}
|
||||
|
||||
private static int firstNonSpace(String first) {
|
||||
int i;
|
||||
i = 0;
|
||||
while (i < first.length() && first.charAt(i) == ' ') ++i;
|
||||
return i;
|
||||
}
|
||||
|
||||
private static int lastNonSpace(String last) {
|
||||
int i;
|
||||
i = last.length() - 1;
|
||||
while (i >= 0 && last.charAt(i) == ' ') --i;
|
||||
return i;
|
||||
}
|
||||
|
||||
private static String[] patternFromJson(JsonArray pattern) {
|
||||
String[] stringPattern = new String[pattern.size()];
|
||||
if (stringPattern.length > 3) {
|
||||
throw new JsonSyntaxException("Invalid pattern: too many rows, 3 is maximum");
|
||||
} else if (stringPattern.length == 0) {
|
||||
throw new JsonSyntaxException("Invalid pattern: empty pattern not allowed");
|
||||
} else {
|
||||
for (int i = 0; i < stringPattern.length; ++i) {
|
||||
String s = GsonHelper.convertToString(pattern.get(i), "pattern[" + i + "]");
|
||||
if (s.length() > 3)
|
||||
throw new JsonSyntaxException("Invalid pattern: too many columns, 3 is maximum");
|
||||
if (i > 0 && stringPattern[0].length() != s.length())
|
||||
throw new JsonSyntaxException("Invalid pattern: each row must be the same width");
|
||||
stringPattern[i] = s;
|
||||
}
|
||||
return stringPattern;
|
||||
}
|
||||
}
|
||||
|
||||
private static NonNullList<Ingredient> dissolvePattern(String[] pattern, Map<String, Ingredient> key, int width, int height) {
|
||||
NonNullList<Ingredient> results = NonNullList.withSize(width * height, Ingredient.EMPTY);
|
||||
Set<String> set = Sets.newHashSet(key.keySet());
|
||||
set.remove(" ");
|
||||
|
||||
for (int i = 0; i < pattern.length; ++i) {
|
||||
for (int j = 0; j < pattern[i].length(); ++j) {
|
||||
String s = pattern[i].substring(j, j + 1);
|
||||
Ingredient ingredient = key.get(s);
|
||||
if (ingredient == null)
|
||||
throw new JsonSyntaxException("Pattern references symbol '" + s + "' but it's not defined in the key");
|
||||
set.remove(s);
|
||||
results.set(j + width * i, ingredient);
|
||||
}
|
||||
}
|
||||
|
||||
if (!set.isEmpty())
|
||||
throw new JsonSyntaxException("Key defines symbols that aren't used in pattern: " + set);
|
||||
else return results;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public UncraftingRecipe fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
|
||||
int width = buffer.readVarInt();
|
||||
int height = buffer.readVarInt();
|
||||
Ingredient result = Ingredient.fromNetwork(buffer);
|
||||
int count = buffer.readVarInt();
|
||||
NonNullList<Ingredient> ingredients = NonNullList.withSize(width * height, Ingredient.EMPTY);
|
||||
ingredients.replaceAll(ignored -> Ingredient.fromNetwork(buffer));
|
||||
return new UncraftingRecipe(id, width, height, result, count, ingredients);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toNetwork(FriendlyByteBuf buffer, UncraftingRecipe recipe) {
|
||||
buffer.writeVarInt(recipe.width());
|
||||
buffer.writeVarInt(recipe.height());
|
||||
recipe.input().toNetwork(buffer);
|
||||
buffer.writeVarInt(recipe.count());
|
||||
for (Ingredient i : recipe.resultItems()) i.toNetwork(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Type implements RecipeType<UncraftingRecipe> {
|
||||
private Type(){}
|
||||
|
||||
public static final UncraftingRecipe.Type INSTANCE = new UncraftingRecipe.Type();
|
||||
public static final String ID = "uncrafting";
|
||||
}
|
||||
}
|
Reference in a new issue