summaryrefslogtreecommitdiff
path: root/src/main/java/lv/enes/mc/eris_alchemy/recipe/SimplifiedRecipe.java
blob: 6bb1274da90df364e99a1abc3a5f619234fb1f92 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package lv.enes.mc.eris_alchemy.recipe;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import earth.terrarium.chipped.common.recipe.ChippedRecipe;
import jakarta.annotation.Nonnull;
import lv.enes.mc.eris_alchemy.utils.ForeignUtils;
import lv.enes.mc.eris_alchemy.utils.IngredientProvider;
import lv.enes.mc.eris_alchemy.utils.ItemUtils;
import lv.enes.mc.eris_alchemy.utils.RecipeUtils;
import net.minecraft.core.Holder;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.item.crafting.ShapedRecipe;

import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;

public record SimplifiedRecipe(ItemStack output, List<ItemStack> remainder, List<Supplier<Ingredient>> input) {
	public static List<SimplifiedRecipe> of(Recipe<?> recipe, RegistryAccess registryAccess) {
		if (ForeignUtils.IS_CHIPPED_AVAILABLE && recipe instanceof ChippedRecipe chippedRecipe) {
			var remainder = List.<ItemStack>of();
			return chippedRecipe.tags()
					.stream()
					.flatMap(tag -> {
						var items = tag.stream().map(Holder::value).toList();
						Predicate<Item> isOutput = item -> ItemUtils.getId(item).getNamespace().equals("chipped");
						var inputs = items.stream()
								.filter(item -> !isOutput.test(item))
								.map(Ingredient::of)
								.map(x -> (Supplier<Ingredient>) () -> x)
								.map(List::of)
								.toList();
						var outputs = items.stream().filter(isOutput).map(Item::getDefaultInstance).toList();
						return outputs.stream()
								.flatMap(
										output ->
												inputs.stream()
														.map(input -> new SimplifiedRecipe(output, remainder, input))
								);
					})
					.toList();
		}

		return List.of(new SimplifiedRecipe(
				RecipeUtils.getOutput(recipe, registryAccess),
				List.of(), // TODO
				RecipeUtils.getIngredients(recipe)
						.stream()
						.filter(ingredient -> !ingredient.isEmpty())
						.map(x -> (Supplier<Ingredient>)() -> x)
						.toList()
		));
	}

	public Stream<ResourceLocation> dependencies() {
		return Stream.concat(
				input.stream().map(Supplier::get).map(Ingredient::getItems).flatMap(Arrays::stream),
				remainder.stream()
		).map(ItemUtils::getId);
	}

	public boolean hasDuplication() {
		return remainder.stream().anyMatch(stack -> ItemStack.isSameItem(stack, output))
				|| input.stream().anyMatch(ingredient -> ingredient.get().test(output));
	}

	public boolean isAllowed(BannedRecipe ban) {
		if (!ban.output().get().test(output())) {
			return true;
		}

		return dependencies().noneMatch(dep -> ban.input().get().test(ItemUtils.get(dep).getDefaultInstance()));
	}

	public boolean isAllowed(Collection<BannedRecipe> bans) {
		return bans.stream().allMatch(this::isAllowed);
	}

	public static class Deserializer implements JsonDeserializer<SimplifiedRecipe> {
		@Nonnull
		@Override
		public SimplifiedRecipe deserialize(
				JsonElement jsonElement,
				Type type,
				JsonDeserializationContext jsonDeserializationContext
		) throws JsonParseException {
			if (!jsonElement.isJsonObject()) {
				throw new JsonParseException("Recipe must be an object");
			}
			var obj = jsonElement.getAsJsonObject();

			if (obj.get("output") == null || obj.get("input") == null) {
				throw new JsonParseException("Recipe must have 'output' and 'input' fields");
			}

			var output = parseOutputOrRemainder(obj.get("output"));
			var remainder = parseRemainders(obj.get("remainder"));
			var input = parseInputs(obj.get("input"));
			return new SimplifiedRecipe(output, remainder, input);
		}

		private List<Supplier<Ingredient>> parseInputs(JsonElement el) {
			if (el.isJsonArray()) {
				return el.getAsJsonArray().asList().stream().map(IngredientProvider::deserialize).toList();
			}

			return List.of(IngredientProvider.deserialize(el));
		}

		private ItemStack parseOutputOrRemainder(JsonElement el) {
			if (el.isJsonObject()) {
				return ShapedRecipe.itemStackFromJson(el.getAsJsonObject());
			} else if (el.isJsonPrimitive() && el.getAsJsonPrimitive().isString()) {
				var id = new ResourceLocation(el.getAsString());
				return BuiltInRegistries.ITEM.get(id).getDefaultInstance();
			} else {
				throw new JsonParseException("Recipe's output or remainder must be an object or a string");
			}
		}

		private List<ItemStack> parseRemainders(JsonElement el) {
			if (el == null) {
				return List.of();
			} else if (el.isJsonArray()) {
				return el.getAsJsonArray().asList().stream().map(this::parseOutputOrRemainder).toList();
			} else {
				return List.of(parseOutputOrRemainder(el));
			}
		}
	}
}