Finally got all the recipes fixed

This commit is contained in:
Sollace 2020-05-02 18:20:41 +02:00
parent 070aeaefe3
commit 8e30e8539a
65 changed files with 861 additions and 607 deletions

View file

@ -8,7 +8,7 @@ import com.google.common.collect.Sets;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException; import com.google.gson.JsonSyntaxException;
import com.minelittlepony.unicopia.recipe.ingredient.Ingredient; import com.minelittlepony.unicopia.recipe.ingredient.PredicatedIngredient;
import net.minecraft.inventory.CraftingInventory; import net.minecraft.inventory.CraftingInventory;
import net.minecraft.util.DefaultedList; import net.minecraft.util.DefaultedList;
@ -20,42 +20,42 @@ public class Pattern {
public static Pattern read(PacketByteBuf buf) { public static Pattern read(PacketByteBuf buf) {
return new Pattern( return new Pattern(
Utils.read(buf, Ingredient.EMPTY, Ingredient::read), Utils.read(buf, PredicatedIngredient.EMPTY, PredicatedIngredient::read),
buf.readVarInt(), buf.readVarInt(),
buf.readVarInt() buf.readVarInt()
); );
} }
public static Pattern read(JsonObject json) { public static Pattern read(JsonObject json) {
String[] patterns = combinePatternMatrix(getPattern(JsonHelper.getArray(json, "pattern"))); String[] patterns = removePadding(readPattern(JsonHelper.getArray(json, "pattern")));
Map<String, Ingredient> ingredients = readIngredients(JsonHelper.getObject(json, "key")); Map<String, PredicatedIngredient> symbols = readSymbols(JsonHelper.getObject(json, "key"));
return new Pattern(patterns, ingredients); return new Pattern(patterns, symbols);
} }
public final DefaultedList<Ingredient> matrix; public final DefaultedList<PredicatedIngredient> matrix;
public final int width; public final int width;
public final int height; public final int height;
public Pattern(DefaultedList<Ingredient> matrix, int width, int height) { public Pattern(DefaultedList<PredicatedIngredient> matrix, int width, int height) {
this.matrix = matrix; this.matrix = matrix;
this.width = width; this.width = width;
this.height = height; this.height = height;
} }
public Pattern(String[] pattern, Map<String, Ingredient> ingredients) { public Pattern(String[] pattern, Map<String, PredicatedIngredient> ingredients) {
this(pattern, ingredients, pattern[0].length(), pattern.length); this(pattern, ingredients, pattern[0].length(), pattern.length);
} }
private Pattern(String[] pattern, Map<String, Ingredient> ingredients, int width, int height) { private Pattern(String[] pattern, Map<String, PredicatedIngredient> ingredients, int width, int height) {
this(buildIngredientMatrix(pattern, ingredients, width, height), width, height); this(buildIngredientMatrix(pattern, ingredients, width, height), width, height);
} }
public boolean matches(CraftingInventory inv) { public boolean matches(CraftingInventory inv) {
for(int x = 0; x <= inv.getWidth() - width; x++) { for(int x = 0; x <= inv.getWidth() - width; x++) {
for(int y = 0; y <= inv.getHeight() - height; y++) { for(int y = 0; y <= inv.getHeight() - height; y++) {
if (matchesSmall(inv, x, y, true) || matchesSmall(inv, x, y, false)) { if (matchesPattern(inv, x, y, true) || matchesPattern(inv, x, y, false)) {
return true; return true;
} }
} }
@ -65,7 +65,7 @@ public class Pattern {
} }
public void write(PacketByteBuf buf) { public void write(PacketByteBuf buf) {
Utils.write(buf, matrix, Ingredient::write); Utils.write(buf, matrix, PredicatedIngredient::write);
buf.writeVarInt(width); buf.writeVarInt(width);
buf.writeVarInt(height); buf.writeVarInt(height);
} }
@ -74,17 +74,19 @@ public class Pattern {
return matrix.size(); return matrix.size();
} }
private boolean matchesSmall(CraftingInventory inv, int offsetX, int offsetY, boolean reflected) { private boolean matchesPattern(CraftingInventory inv, int offsetX, int offsetY, boolean reflected) {
for(int x = 0; x < inv.getWidth(); ++x) { for(int x = 0; x < inv.getWidth(); ++x) {
for(int y = 0; y < inv.getHeight(); ++y) { for(int y = 0; y < inv.getHeight(); ++y) {
int k = x - offsetX; int left = x - offsetX;
int l = y - offsetY; int top = y - offsetY;
Ingredient ingredient = Ingredient.EMPTY;
if (k >= 0 && l >= 0 && k < width && l < this.height) { PredicatedIngredient ingredient = PredicatedIngredient.EMPTY;
if (left >= 0 && top >= 0 && left < width && top < height) {
if (reflected) { if (reflected) {
ingredient = matrix.get(width - k - 1 + l * this.width); ingredient = matrix.get(width - left - 1 + top * width);
} else { } else {
ingredient = matrix.get(k + l * this.width); ingredient = matrix.get(left + top * width);
} }
} }
@ -97,111 +99,130 @@ public class Pattern {
return true; return true;
} }
static DefaultedList<Ingredient> buildIngredientMatrix(String[] pattern, Map<String, Ingredient> ingredients, int width, int height) { static DefaultedList<PredicatedIngredient> buildIngredientMatrix(String[] pattern, Map<String, PredicatedIngredient> symbols, int width, int height) {
DefaultedList<Ingredient> result = DefaultedList.ofSize(width * height, Ingredient.EMPTY); DefaultedList<PredicatedIngredient> result = DefaultedList.ofSize(width * height, PredicatedIngredient.EMPTY);
Set<String> unsolved = Sets.newHashSet(ingredients.keySet()); Set<String> unresolved = Sets.newHashSet(symbols.keySet());
unsolved.remove(" "); unresolved.remove(" ");
for(int i = 0; i < pattern.length; ++i) { for(int i = 0; i < pattern.length; ++i) {
for(int j = 0; j < pattern[i].length(); ++j) { for(int j = 0; j < pattern[i].length(); ++j) {
String key = pattern[i].substring(j, j + 1); String key = pattern[i].substring(j, j + 1);
Ingredient ingredient = ingredients.get(key); PredicatedIngredient ingredient = symbols.get(key);
if (ingredient == null) { if (ingredient == null) {
throw new JsonSyntaxException("Pattern references symbol '" + key + "' but it's not defined in the key"); throw new JsonSyntaxException("Pattern references symbol '" + key + "' but it's not defined in the key");
} }
unsolved.remove(key); unresolved.remove(key);
result.set(j + width * i, ingredient); result.set(j + width * i, ingredient);
} }
} }
if (!unsolved.isEmpty()) { if (!unresolved.isEmpty()) {
throw new JsonSyntaxException("Key defines symbols that aren't used in pattern: " + unsolved); throw new JsonSyntaxException("Key defines symbols that aren't used in pattern: " + unresolved);
} }
return result; return result;
} }
static String[] combinePatternMatrix(String... lines) { /**
int i = Integer.MAX_VALUE; * Removes empty space from around the recipe pattern.
int j = 0; *
int k = 0; * Turns patterns such as:
int l = 0; * " o"
* " a"
* " "
* Into:
* "o"
* "a"
*
* @param pattern
* @return A new recipe pattern with all leading and trailing empty rows/columns removed.
*/
static String[] removePadding(String... pattern) {
int left = Integer.MAX_VALUE;
int right = 0;
for(int row = 0; row < lines.length; ++row) { int top = 0;
String line = lines[row]; int bottom = 0;
i = Math.min(i, lookAhead(line)); for(int yPosition = 0; yPosition < pattern.length; ++yPosition) {
String row = pattern[yPosition];
int n = lookBack(line); left = Math.min(left, findFirstSymbol(row));
j = Math.max(j, n); int rowEnd = findLastSymbol(row);
if (n < 0) {
if (k == row) { right = Math.max(right, rowEnd);
++k;
if (rowEnd < 0) {
if (top == yPosition) {
top++;
} }
++l; bottom++;
} else { } else {
l = 0; bottom = 0;
} }
} }
if (lines.length == l) { if (pattern.length == bottom) {
return new String[0]; return new String[0];
} }
String[] strings = new String[lines.length - l - k]; String[] strings = new String[pattern.length - bottom - top];
for(int o = 0; o < strings.length; ++o) { for(int i = 0; i < strings.length; i++) {
strings[o] = lines[o + k].substring(i, j + 1); strings[i] = pattern[i + top].substring(left, right + 1);
} }
return strings; return strings;
} }
private static int lookAhead(String pattern) { private static int findFirstSymbol(String pattern) {
int i; int i;
for(i = 0; i < pattern.length() && pattern.charAt(i) == ' '; ++i) {} for(i = 0; i < pattern.length() && pattern.charAt(i) == ' '; ++i) {}
return i; return i;
} }
private static int lookBack(String pattern) { private static int findLastSymbol(String pattern) {
int i; int i;
for(i = pattern.length() - 1; i >= 0 && pattern.charAt(i) == ' '; --i) {} for(i = pattern.length() - 1; i >= 0 && pattern.charAt(i) == ' '; --i) {}
return i; return i;
} }
static String[] getPattern(JsonArray json) { static String[] readPattern(JsonArray json) {
String[] strings = new String[json.size()]; String[] rows = new String[json.size()];
if (strings.length > 3) {
if (rows.length > 3) {
throw new JsonSyntaxException("Invalid pattern: too many rows, 3 is maximum"); throw new JsonSyntaxException("Invalid pattern: too many rows, 3 is maximum");
} else if (strings.length == 0) { }
if (rows.length == 0) {
throw new JsonSyntaxException("Invalid pattern: empty pattern not allowed"); throw new JsonSyntaxException("Invalid pattern: empty pattern not allowed");
} else { }
for(int i = 0; i < strings.length; ++i) {
String string = JsonHelper.asString(json.get(i), "pattern[" + i + "]"); for(int i = 0; i < rows.length; ++i) {
if (string.length() > 3) { String column = JsonHelper.asString(json.get(i), "pattern[" + i + "]");
if (column.length() > 3) {
throw new JsonSyntaxException("Invalid pattern: too many columns, 3 is maximum"); throw new JsonSyntaxException("Invalid pattern: too many columns, 3 is maximum");
} }
if (i > 0 && strings[0].length() != string.length()) { if (i > 0 && rows[0].length() != column.length()) {
throw new JsonSyntaxException("Invalid pattern: each row must be the same width"); throw new JsonSyntaxException("Invalid pattern: each row must be the same width");
} }
strings[i] = string; rows[i] = column;
} }
return strings; return rows;
}
} }
static Map<String, Ingredient> readIngredients(JsonObject json) { static Map<String, PredicatedIngredient> readSymbols(JsonObject json) {
return Util.make(json.entrySet().stream().collect(Collectors.toMap(e -> { return Util.make(json.entrySet().stream().collect(Collectors.toMap(e -> {
if (e.getKey().length() != 1) { if (e.getKey().length() != 1) {
throw new JsonSyntaxException("Invalid key entry: '" + e.getKey() + "' is an invalid symbol (must be 1 character only)."); throw new JsonSyntaxException("Invalid key entry: '" + e.getKey() + "' is an invalid symbol (must be 1 character only).");
@ -213,8 +234,7 @@ public class Pattern {
return e.getKey(); return e.getKey();
}, },
e -> Ingredient.one(e.getValue()) e -> PredicatedIngredient.one(e.getValue())
)), m -> m.put(" ", Ingredient.EMPTY)); )), m -> m.put(" ", PredicatedIngredient.EMPTY));
} }
} }

View file

@ -3,10 +3,11 @@ package com.minelittlepony.unicopia.recipe;
import java.util.Random; import java.util.Random;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.minelittlepony.unicopia.recipe.ingredient.Ingredient; import com.minelittlepony.unicopia.recipe.ingredient.PredicatedIngredient;
import net.minecraft.inventory.CraftingInventory; import net.minecraft.inventory.CraftingInventory;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.recipe.Ingredient;
import net.minecraft.recipe.RecipeSerializer; import net.minecraft.recipe.RecipeSerializer;
import net.minecraft.recipe.SpecialCraftingRecipe; import net.minecraft.recipe.SpecialCraftingRecipe;
import net.minecraft.util.DefaultedList; import net.minecraft.util.DefaultedList;
@ -20,10 +21,10 @@ public class ShapedSpecialRecipe extends SpecialCraftingRecipe {
private static final Random RANDOM = new Random(); private static final Random RANDOM = new Random();
private final Pattern pattern; private final Pattern pattern;
private final Ingredient output; private final PredicatedIngredient output;
private final String group; private final String group;
public ShapedSpecialRecipe(Identifier id, String group, Pattern pattern, Ingredient output) { public ShapedSpecialRecipe(Identifier id, String group, Pattern pattern, PredicatedIngredient output) {
super(id); super(id);
this.group = group; this.group = group;
this.pattern = pattern; this.pattern = pattern;
@ -57,7 +58,7 @@ public class ShapedSpecialRecipe extends SpecialCraftingRecipe {
@Override @Override
public RecipeSerializer<?> getSerializer() { public RecipeSerializer<?> getSerializer() {
return null; return URecipes.CRAFTING_SHAPED;
} }
@Override @Override
@ -66,8 +67,8 @@ public class ShapedSpecialRecipe extends SpecialCraftingRecipe {
} }
@Override @Override
public DefaultedList<net.minecraft.recipe.Ingredient> getPreviewInputs() { public DefaultedList<Ingredient> getPreviewInputs() {
return Ingredient.preview(pattern.matrix); return PredicatedIngredient.preview(pattern.matrix);
} }
public static class Serializer implements RecipeSerializer<ShapedSpecialRecipe> { public static class Serializer implements RecipeSerializer<ShapedSpecialRecipe> {
@ -76,7 +77,7 @@ public class ShapedSpecialRecipe extends SpecialCraftingRecipe {
return new ShapedSpecialRecipe(id, return new ShapedSpecialRecipe(id,
JsonHelper.getString(json, "group", ""), JsonHelper.getString(json, "group", ""),
Pattern.read(json), Pattern.read(json),
Ingredient.one(json.get("result")) PredicatedIngredient.one(json.get("result"))
); );
} }
@ -85,7 +86,7 @@ public class ShapedSpecialRecipe extends SpecialCraftingRecipe {
return new ShapedSpecialRecipe(id, return new ShapedSpecialRecipe(id,
buf.readString(32767), buf.readString(32767),
Pattern.read(buf), Pattern.read(buf),
Ingredient.read(buf) PredicatedIngredient.read(buf)
); );
} }

View file

@ -2,10 +2,11 @@ package com.minelittlepony.unicopia.recipe;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParseException; import com.google.gson.JsonParseException;
import com.minelittlepony.unicopia.recipe.ingredient.Ingredient; import com.minelittlepony.unicopia.recipe.ingredient.PredicatedIngredient;
import net.minecraft.inventory.CraftingInventory; import net.minecraft.inventory.CraftingInventory;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.recipe.Ingredient;
import net.minecraft.recipe.RecipeSerializer; import net.minecraft.recipe.RecipeSerializer;
import net.minecraft.recipe.SpecialCraftingRecipe; import net.minecraft.recipe.SpecialCraftingRecipe;
import net.minecraft.util.DefaultedList; import net.minecraft.util.DefaultedList;
@ -17,11 +18,11 @@ import net.minecraft.world.World;
public class ShapelessSpecialRecipe extends SpecialCraftingRecipe { public class ShapelessSpecialRecipe extends SpecialCraftingRecipe {
private final String group; private final String group;
private final Ingredient output; private final PredicatedIngredient output;
private final DefaultedList<Ingredient> input; private final DefaultedList<PredicatedIngredient> input;
public ShapelessSpecialRecipe(Identifier id, String group, Ingredient output, DefaultedList<Ingredient> input) { public ShapelessSpecialRecipe(Identifier id, String group, PredicatedIngredient output, DefaultedList<PredicatedIngredient> input) {
super(id); super(id);
this.group = group; this.group = group;
this.output = output; this.output = output;
@ -66,8 +67,8 @@ public class ShapelessSpecialRecipe extends SpecialCraftingRecipe {
} }
@Override @Override
public DefaultedList<net.minecraft.recipe.Ingredient> getPreviewInputs() { public DefaultedList<Ingredient> getPreviewInputs() {
return Ingredient.preview(input); return PredicatedIngredient.preview(input);
} }
@Override @Override
@ -77,11 +78,11 @@ public class ShapelessSpecialRecipe extends SpecialCraftingRecipe {
public static class Serializer implements RecipeSerializer<ShapelessSpecialRecipe> { public static class Serializer implements RecipeSerializer<ShapelessSpecialRecipe> {
@Override @Override
public ShapelessSpecialRecipe read(Identifier identifier, JsonObject jsonObject) { public ShapelessSpecialRecipe read(Identifier identifier, JsonObject json) {
return new ShapelessSpecialRecipe(identifier, return new ShapelessSpecialRecipe(identifier,
JsonHelper.getString(jsonObject, "group", ""), JsonHelper.getString(json, "group", ""),
Ingredient.one(jsonObject.get("output")), PredicatedIngredient.one(json.get("result")),
Ingredient.many(JsonHelper.getArray(jsonObject, "ingredients")) PredicatedIngredient.many(JsonHelper.getArray(json, "ingredients"))
); );
} }
@ -89,8 +90,8 @@ public class ShapelessSpecialRecipe extends SpecialCraftingRecipe {
public ShapelessSpecialRecipe read(Identifier identifier, PacketByteBuf buf) { public ShapelessSpecialRecipe read(Identifier identifier, PacketByteBuf buf) {
return new ShapelessSpecialRecipe(identifier, return new ShapelessSpecialRecipe(identifier,
buf.readString(32767), buf.readString(32767),
Ingredient.read(buf), PredicatedIngredient.read(buf),
Utils.read(buf, Ingredient.EMPTY, Ingredient::read)); Utils.read(buf, PredicatedIngredient.EMPTY, PredicatedIngredient::read));
} }
@Override @Override
@ -98,7 +99,7 @@ public class ShapelessSpecialRecipe extends SpecialCraftingRecipe {
buf.writeString(recipe.group); buf.writeString(recipe.group);
recipe.output.write(buf); recipe.output.write(buf);
buf.writeVarInt(recipe.input.size()); buf.writeVarInt(recipe.input.size());
Utils.write(buf, recipe.input, Ingredient::write); Utils.write(buf, recipe.input, PredicatedIngredient::write);
} }
} }
} }

View file

@ -3,10 +3,11 @@ package com.minelittlepony.unicopia.recipe;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.minelittlepony.unicopia.container.SpellBookInventory; import com.minelittlepony.unicopia.container.SpellBookInventory;
import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.recipe.ingredient.Ingredient; import com.minelittlepony.unicopia.recipe.ingredient.PredicatedIngredient;
import net.minecraft.inventory.CraftingInventory; import net.minecraft.inventory.CraftingInventory;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.recipe.Ingredient;
import net.minecraft.recipe.Recipe; import net.minecraft.recipe.Recipe;
import net.minecraft.recipe.RecipeSerializer; import net.minecraft.recipe.RecipeSerializer;
import net.minecraft.recipe.RecipeType; import net.minecraft.recipe.RecipeType;
@ -21,19 +22,17 @@ import net.minecraft.world.World;
*/ */
public class SpellBookRecipe implements Recipe<CraftingInventory> { public class SpellBookRecipe implements Recipe<CraftingInventory> {
private final Ingredient input;
private final Ingredient output;
private final DefaultedList<Ingredient> ingredients;
private final Identifier id; private final Identifier id;
private final PredicatedIngredient input;
private final PredicatedIngredient output;
private final DefaultedList<PredicatedIngredient> ingredients;
public SpellBookRecipe(Identifier id, Ingredient input, Ingredient output, DefaultedList<Ingredient> ingredients) {
public SpellBookRecipe(Identifier id, String group, PredicatedIngredient input, PredicatedIngredient output, DefaultedList<PredicatedIngredient> ingredients) {
this.id = id; this.id = id;
this.input = input;
this.output = output; this.output = output;
this.ingredients = ingredients; this.ingredients = ingredients;
this.input = input;
} }
@Override @Override
@ -68,8 +67,8 @@ public class SpellBookRecipe implements Recipe<CraftingInventory> {
} }
@Override @Override
public DefaultedList<net.minecraft.recipe.Ingredient> getPreviewInputs() { public DefaultedList<Ingredient> getPreviewInputs() {
return Ingredient.preview(ingredients, DefaultedList.copyOf(null, input.getPreview())); return PredicatedIngredient.preview(ingredients, DefaultedList.copyOf(null, input.getPreview()));
} }
@Override @Override
@ -96,18 +95,20 @@ public class SpellBookRecipe implements Recipe<CraftingInventory> {
@Override @Override
public SpellBookRecipe read(Identifier id, JsonObject json) { public SpellBookRecipe read(Identifier id, JsonObject json) {
return new SpellBookRecipe(id, return new SpellBookRecipe(id,
Ingredient.one(json.get("input")), JsonHelper.getString(json, "group", ""),
Ingredient.one(json.get("result")), PredicatedIngredient.one(JsonHelper.getObject(json, "input")),
Ingredient.many(JsonHelper.getArray(json, "ingredients")) PredicatedIngredient.one(JsonHelper.getObject(json, "result")),
PredicatedIngredient.many(JsonHelper.getArray(json, "ingredients"))
); );
} }
@Override @Override
public SpellBookRecipe read(Identifier id, PacketByteBuf buf) { public SpellBookRecipe read(Identifier id, PacketByteBuf buf) {
return new SpellBookRecipe(id, return new SpellBookRecipe(id,
Ingredient.read(buf), buf.readString(32767),
Ingredient.read(buf), PredicatedIngredient.read(buf),
Utils.read(buf, Ingredient.EMPTY, Ingredient::read) PredicatedIngredient.read(buf),
Utils.read(buf, PredicatedIngredient.EMPTY, PredicatedIngredient::read)
); );
} }
@ -115,7 +116,7 @@ public class SpellBookRecipe implements Recipe<CraftingInventory> {
public void write(PacketByteBuf buf, SpellBookRecipe recipe) { public void write(PacketByteBuf buf, SpellBookRecipe recipe) {
recipe.input.write(buf); recipe.input.write(buf);
recipe.output.write(buf); recipe.output.write(buf);
Utils.write(buf, recipe.ingredients, Ingredient::write); Utils.write(buf, recipe.ingredients, PredicatedIngredient::write);
} }
} }
} }

View file

@ -6,18 +6,24 @@ import java.util.function.BiConsumer;
import java.util.function.Function; import java.util.function.Function;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.minelittlepony.unicopia.recipe.ingredient.Ingredient; import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.minelittlepony.unicopia.recipe.ingredient.PredicatedIngredient;
import net.minecraft.inventory.Inventory; import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.DefaultedList; import net.minecraft.util.DefaultedList;
import net.minecraft.util.Identifier;
import net.minecraft.util.JsonHelper;
import net.minecraft.util.PacketByteBuf; import net.minecraft.util.PacketByteBuf;
public class Utils { public final class Utils {
private Utils() {}
static final Random RANDOM = new Random(); public static final Random RANDOM = new Random();
static <T> DefaultedList<T> read(PacketByteBuf buf, T def, Function<PacketByteBuf, T> reader) { public static <T> DefaultedList<T> read(PacketByteBuf buf, T def, Function<PacketByteBuf, T> reader) {
DefaultedList<T> list = DefaultedList.ofSize(buf.readInt(), def); DefaultedList<T> list = DefaultedList.ofSize(buf.readInt(), def);
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
list.set(i, reader.apply(buf)); list.set(i, reader.apply(buf));
@ -25,18 +31,32 @@ public class Utils {
return list; return list;
} }
static <T> void write(PacketByteBuf buf, DefaultedList<T> list, BiConsumer<T, PacketByteBuf> writer) { public static <T> void write(PacketByteBuf buf, DefaultedList<T> list, BiConsumer<T, PacketByteBuf> writer) {
buf.writeInt(list.size()); buf.writeInt(list.size());
list.forEach(i -> writer.accept(i, buf)); list.forEach(i -> writer.accept(i, buf));
} }
public static Identifier asIdentifier(JsonElement json) {
return new Identifier(json.getAsString());
}
public static <C extends Inventory> boolean matchShapeless(DefaultedList<Ingredient> ingredients, C inv, int mult) { public static Identifier getIdentifier(JsonObject json, String property) {
return new Identifier(JsonHelper.getString(json, property));
}
public static <T> T require(T reference, String errorMessage) {
if (reference == null) {
throw new JsonParseException(errorMessage);
}
return reference;
}
public static <C extends Inventory> boolean matchShapeless(DefaultedList<PredicatedIngredient> ingredients, C inv, int mult) {
if (mult == 0) { if (mult == 0) {
return false; return false;
} }
List<Ingredient> toMatch = Lists.newArrayList(ingredients); List<PredicatedIngredient> toMatch = Lists.newArrayList(ingredients);
for (int i = 0; i < inv.getInvSize(); i++) { for (int i = 0; i < inv.getInvSize(); i++) {
ItemStack stack = inv.getInvStack(i); ItemStack stack = inv.getInvStack(i);
@ -51,7 +71,7 @@ public class Utils {
return toMatch.isEmpty(); return toMatch.isEmpty();
} }
private static boolean removeMatch(List<Ingredient> toMatch, ItemStack stack, int materialMult) { private static boolean removeMatch(List<PredicatedIngredient> toMatch, ItemStack stack, int materialMult) {
return toMatch.stream() return toMatch.stream()
.filter(s -> s.matches(stack, materialMult)) .filter(s -> s.matches(stack, materialMult))
.findFirst() .findFirst()

View file

@ -1,25 +1,27 @@
package com.minelittlepony.unicopia.recipe.ingredient; package com.minelittlepony.unicopia.recipe.ingredient;
import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.stream.Stream; import java.util.stream.Stream;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.minelittlepony.unicopia.recipe.Utils;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.DefaultedList;
import net.minecraft.util.PacketByteBuf; import net.minecraft.util.PacketByteBuf;
/** /**
* Requires only one of the sub-ingredients to match when matching. * Requires only one of the sub-ingredients to match when matching.
* Makes a random choice from a pool of alternatives when crafting. * Makes a random choice from a pool of alternatives when crafting.
*/ */
class ChoicePredicate implements Ingredient.Predicate { class ChoicePredicate implements Predicate {
static Ingredient.Predicate read(JsonArray arr) { static Predicate read(JsonArray arr) {
return new ChoicePredicate(Ingredient.many(arr)); return new ChoicePredicate(PredicatedIngredient.many(arr));
} }
private final List<Ingredient> options; private final DefaultedList<PredicatedIngredient> options;
ChoicePredicate(List<Ingredient> options) { ChoicePredicate(DefaultedList<PredicatedIngredient> options) {
this.options = options; this.options = options;
} }
@ -34,13 +36,24 @@ class ChoicePredicate implements Ingredient.Predicate {
} }
@Override @Override
public void write(PacketByteBuf buf) { public Stream<ItemStack> getMatchingStacks() {
buf.writeInt(options.size()); return options.stream().flatMap(PredicatedIngredient::getMatchingStacks).distinct();
options.forEach(i -> i.write(buf));
} }
@Override @Override
public Stream<ItemStack> getMatchingStacks() { public PredicateSerializer<?> getSerializer() {
return options.stream().flatMap(Ingredient::getMatchingStacks).distinct(); return PredicateSerializer.CHOICE;
}
static final class Serializer implements PredicateSerializer<ChoicePredicate> {
@Override
public Predicate read(PacketByteBuf buf) {
return new ChoicePredicate(Utils.read(buf, PredicatedIngredient.EMPTY, PredicatedIngredient::read));
}
@Override
public void write(PacketByteBuf buf, ChoicePredicate predicate) {
Utils.write(buf, predicate.options, PredicatedIngredient::write);
}
} }
} }

View file

@ -7,10 +7,10 @@ import java.util.function.BiPredicate;
import java.util.stream.Stream; import java.util.stream.Stream;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.minelittlepony.unicopia.recipe.Utils;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.JsonHelper; import net.minecraft.util.JsonHelper;
import net.minecraft.util.Lazy;
import net.minecraft.util.PacketByteBuf; import net.minecraft.util.PacketByteBuf;
import net.minecraft.util.Util; import net.minecraft.util.Util;
@ -18,7 +18,7 @@ import net.minecraft.util.Util;
* Tests for whether a tag contains the input when matching. * Tests for whether a tag contains the input when matching.
* Supplies a random item from the tag as the output when crafting. * Supplies a random item from the tag as the output when crafting.
*/ */
class DamagePredicate implements Ingredient.Predicate { class DamagePredicate implements Predicate {
private static final Map<String, BiPredicate<Integer, Integer>> OPERATIONS = Util.make(new HashMap<>(), map -> { private static final Map<String, BiPredicate<Integer, Integer>> OPERATIONS = Util.make(new HashMap<>(), map -> {
map.put("==", (a, b) -> a == b); map.put("==", (a, b) -> a == b);
map.put("!=", (a, b) -> a != b); map.put("!=", (a, b) -> a != b);
@ -28,35 +28,15 @@ class DamagePredicate implements Ingredient.Predicate {
map.put("<=", (a, b) -> a <= b); map.put("<=", (a, b) -> a <= b);
}); });
static Ingredient.Predicate read(PacketByteBuf buf) {
int count = buf.readInt();
if (count == 0) {
return EMPTY;
}
return new DamagePredicate(buf.readInt(), buf.readString(32767));
}
static Ingredient.Predicate read(JsonObject json) {
if (!json.has("damage")) {
return EMPTY;
}
JsonObject o = JsonHelper.getObject(json, "damage");
return new DamagePredicate(
JsonHelper.getInt(o, "damage"),
JsonHelper.getString(o, "op")
);
}
private final String op; private final String op;
private final int damage; private final int damage;
private final Lazy<BiPredicate<Integer, Integer>> operation; private final BiPredicate<Integer, Integer> operation;
DamagePredicate(int damage, String op) { DamagePredicate(int damage, String op) {
this.op = op.trim(); this.op = op.trim();
this.damage = damage; this.damage = damage;
this.operation = new Lazy<>(() -> OPERATIONS.get(op)); this.operation = Utils.require(OPERATIONS.get(op), "Invalid damage tag '" + op + "' " + damage);
} }
@Override @Override
@ -72,13 +52,41 @@ class DamagePredicate implements Ingredient.Predicate {
@Override @Override
public boolean matches(ItemStack other, int materialMult) { public boolean matches(ItemStack other, int materialMult) {
return !other.isEmpty() && operation.get().test(other.getDamage(), damage); return !other.isEmpty() && operation.test(other.getDamage(), damage);
} }
@Override @Override
public void write(PacketByteBuf buf) { public PredicateSerializer<?> getSerializer() {
buf.writeInt(1); return PredicateSerializer.DAMAGE;
buf.writeInt(damage); }
buf.writeString(op);
static final class Serializer implements PredicateSerializer<DamagePredicate>, PredicateSerializer.JsonReader {
@Override
public Predicate read(PacketByteBuf buf) {
int count = buf.readInt();
if (count == 0) {
return Predicate.EMPTY;
}
return new DamagePredicate(buf.readInt(), buf.readString(32767));
}
@Override
public void write(PacketByteBuf buf, DamagePredicate predicate) {
buf.writeInt(predicate.damage);
buf.writeString(predicate.op);
}
@Override
public Predicate read(JsonObject json) {
if (!json.has("damage")) {
return Predicate.EMPTY;
}
JsonObject o = JsonHelper.getObject(json, "damage");
return new DamagePredicate(
JsonHelper.getInt(o, "damage"),
JsonHelper.getString(o, "op")
);
}
} }
} }

View file

@ -5,12 +5,14 @@ import java.util.stream.Stream;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParseException; import com.minelittlepony.unicopia.recipe.Utils;
import net.minecraft.enchantment.Enchantment; import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.JsonHelper;
import net.minecraft.util.PacketByteBuf; import net.minecraft.util.PacketByteBuf;
import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.Registry;
@ -18,48 +20,20 @@ import net.minecraft.util.registry.Registry;
* A predicate that tests for a specific enchantment on an input when matching. * A predicate that tests for a specific enchantment on an input when matching.
* Appends that enchantment to the output when crafting. * Appends that enchantment to the output when crafting.
*/ */
class EnchantmentPredicate implements Ingredient.Predicate { class EnchantmentPredicate implements Predicate {
public static Ingredient.Predicate read(PacketByteBuf buf) {
int level = buf.readInt();
if (level == 0) {
return EMPTY;
}
return new EnchantmentPredicate(Registry.ENCHANTMENT.get(buf.readIdentifier()), level);
}
static Ingredient.Predicate read(JsonObject json) {
if (!json.has("enchantment")) {
return EMPTY;
}
JsonElement e = json.get("enchantment");
if (e.isJsonObject()) {
JsonObject o = e.getAsJsonObject();
Enchantment enchantment = Registry.ENCHANTMENT.get(new Identifier(o.get("id").getAsString()));
int level = o.has("level") ? o.get("level").getAsInt() : 1;
return new EnchantmentPredicate(enchantment, level);
}
Enchantment enchantment = Registry.ENCHANTMENT.get(new Identifier(e.getAsString()));
return new EnchantmentPredicate(enchantment, 1);
}
private final int level; private final int level;
private final Enchantment enchantment; private final Enchantment enchantment;
EnchantmentPredicate(Enchantment enchantment, int level) { EnchantmentPredicate(Identifier id, int level) {
this.enchantment = enchantment; this.enchantment = Utils.require(Registry.ENCHANTMENT.get(id), "Invalid enchantment tag '" + id + "'");
this.level = level; this.level = level;
if (enchantment == null) {
throw new JsonParseException("Invalid enchantment (null)");
}
} }
@Override @Override
public ItemStack applyModifiers(ItemStack output, Random random) { public ItemStack applyModifiers(ItemStack output, Random random) {
if (output.isEmpty()) {
output = new ItemStack(Items.ENCHANTED_BOOK);
}
output.addEnchantment(enchantment, level); output.addEnchantment(enchantment, level);
return output; return output;
} }
@ -69,14 +43,47 @@ class EnchantmentPredicate implements Ingredient.Predicate {
return EnchantmentHelper.getLevel(enchantment, stack) >= level; return EnchantmentHelper.getLevel(enchantment, stack) >= level;
} }
@Override
public void write(PacketByteBuf buf) {
buf.writeInt(level);
buf.writeIdentifier(Registry.ENCHANTMENT.getId(enchantment));
}
@Override @Override
public Stream<ItemStack> getMatchingStacks() { public Stream<ItemStack> getMatchingStacks() {
return Stream.empty(); return Stream.empty();
} }
@Override
public PredicateSerializer<?> getSerializer() {
return PredicateSerializer.ENCHANTMENT;
}
static final class Serializer implements PredicateSerializer<EnchantmentPredicate>, PredicateSerializer.JsonReader {
@Override
public Predicate read(PacketByteBuf buf) {
int level = buf.readInt();
if (level == 0) {
return Predicate.EMPTY;
}
return new EnchantmentPredicate(buf.readIdentifier(), level);
}
@Override
public void write(PacketByteBuf buf, EnchantmentPredicate predicate) {
buf.writeInt(predicate.level);
buf.writeIdentifier(Registry.ENCHANTMENT.getId(predicate.enchantment));
}
@Override
public Predicate read(JsonObject json) {
if (!json.has("enchantment")) {
return Predicate.EMPTY;
}
JsonElement e = json.get("enchantment");
if (e.isJsonObject()) {
JsonObject o = e.getAsJsonObject();
return new EnchantmentPredicate(Utils.getIdentifier(o, "id"), Math.max(1, JsonHelper.getInt(o, "level", 1)));
}
return new EnchantmentPredicate(Utils.asIdentifier(e), 1);
}
}
} }

View file

@ -1,140 +0,0 @@
package com.minelittlepony.unicopia.recipe.ingredient;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.concurrent.Immutable;
import com.google.common.collect.Lists;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import net.minecraft.item.ItemStack;
import net.minecraft.util.DefaultedList;
import net.minecraft.util.Lazy;
import net.minecraft.util.PacketByteBuf;
@Immutable
public class Ingredient {
public static final Ingredient EMPTY = new Ingredient(Predicate.EMPTY, Predicate.EMPTY, Predicate.EMPTY, Predicate.EMPTY, Predicate.EMPTY);
private final List<Predicate> predicates;
private final Lazy<List<ItemStack>> matchingStacks;
private final Lazy<net.minecraft.recipe.Ingredient> preview;
Ingredient(Predicate... predicates) {
this.predicates = Lists.newArrayList(predicates);
this.matchingStacks = new Lazy<>(() -> {
return this.predicates.stream()
.flatMap(Predicate::getMatchingStacks)
.filter(s -> matches(s, 1))
.collect(Collectors.toList());
});
this.preview = new Lazy<>(() -> {
return net.minecraft.recipe.Ingredient.ofStacks(getMatchingStacks().toArray(ItemStack[]::new));
});
}
public Stream<ItemStack> getMatchingStacks() {
return matchingStacks.get().stream();
}
public net.minecraft.recipe.Ingredient getPreview() {
return preview.get();
}
public ItemStack getStack(Random random) {
ItemStack[] output = new ItemStack[] { ItemStack.EMPTY.copy() };
predicates.forEach(p -> output[0] = p.applyModifiers(output[0], random));
return output[0];
}
public boolean matches(ItemStack other, int materialMult) {
return predicates.stream().allMatch(p -> p.matches(other, materialMult));
}
public void write(PacketByteBuf buf) {
predicates.forEach(p -> p.write(buf));
}
public static Ingredient read(PacketByteBuf buf) {
return new Ingredient(
StackPredicate.read(buf),
TagPredicate.read(buf),
SpellPredicate.read(buf),
EnchantmentPredicate.read(buf),
ToxicityPredicate.read(buf));
}
public static Ingredient one(JsonElement json) {
if (json.isJsonArray()) {
return new Ingredient(
ChoicePredicate.read(json.getAsJsonArray()),
Predicate.EMPTY,
Predicate.EMPTY,
Predicate.EMPTY,
Predicate.EMPTY);
}
JsonObject obj = json.getAsJsonObject();
return new Ingredient(
StackPredicate.read(obj),
TagPredicate.read(obj),
SpellPredicate.read(obj),
EnchantmentPredicate.read(obj),
ToxicityPredicate.read(obj));
}
public static DefaultedList<Ingredient> many(JsonArray arr) {
DefaultedList<Ingredient> ingredients = DefaultedList.copyOf(EMPTY);
arr.forEach(i -> ingredients.add(one(i)));
if (ingredients.isEmpty()) {
throw new JsonParseException("Recipe cannot have 0 ingredients");
}
return ingredients;
}
public static DefaultedList<net.minecraft.recipe.Ingredient> preview(DefaultedList<Ingredient> input) {
return preview(input, DefaultedList.of());
}
public static DefaultedList<net.minecraft.recipe.Ingredient> preview(DefaultedList<Ingredient> input, DefaultedList<net.minecraft.recipe.Ingredient> output) {
input.stream().map(Ingredient::getPreview).forEach(output::add);
return output;
}
public interface Predicate {
Predicate EMPTY = new Predicate() {
@Override
public Stream<ItemStack> getMatchingStacks() {
return Stream.empty();
}
@Override
public boolean matches(ItemStack stack, int materialMult) {
return true;
}
};
Stream<ItemStack> getMatchingStacks();
boolean matches(ItemStack stack, int materialMult);
default ItemStack applyModifiers(ItemStack output, Random random) {
return output;
}
default void write(PacketByteBuf buf) {
buf.writeInt(0);
}
}
}

View file

@ -0,0 +1,88 @@
package com.minelittlepony.unicopia.recipe.ingredient;
import java.util.Random;
import java.util.stream.Stream;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.minelittlepony.unicopia.recipe.Utils;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.potion.Potion;
import net.minecraft.potion.PotionUtil;
import net.minecraft.potion.Potions;
import net.minecraft.util.Identifier;
import net.minecraft.util.JsonHelper;
import net.minecraft.util.PacketByteBuf;
import net.minecraft.util.registry.Registry;
/**
* A predicate that tests for a specific enchantment on an input when matching.
* Appends that enchantment to the output when crafting.
*/
class PotionPredicate implements Predicate {
private final Potion potion;
PotionPredicate(Identifier id, int level) {
this.potion = Registry.POTION.get(id);
if (potion == Potions.EMPTY) {
throw new JsonParseException("Invalid potion tag '" + id + "'");
}
}
@Override
public ItemStack applyModifiers(ItemStack output, Random random) {
if (output.isEmpty()) {
output = new ItemStack(Items.POTION);
}
PotionUtil.setPotion(output, potion);
return output;
}
@Override
public boolean matches(ItemStack stack, int materialMult) {
return PotionUtil.getPotion(stack) == potion;
}
@Override
public Stream<ItemStack> getMatchingStacks() {
return Stream.empty();
}
@Override
public PredicateSerializer<?> getSerializer() {
return PredicateSerializer.ENCHANTMENT;
}
static final class Serializer implements PredicateSerializer<PotionPredicate>, PredicateSerializer.JsonReader {
@Override
public Predicate read(PacketByteBuf buf) {
return new PotionPredicate(buf.readIdentifier(), 1);
}
@Override
public void write(PacketByteBuf buf, PotionPredicate predicate) {
buf.writeIdentifier(Registry.POTION.getId(predicate.potion));
}
@Override
public Predicate read(JsonObject json) {
if (!json.has("potion")) {
return Predicate.EMPTY;
}
JsonElement e = json.get("potion");
if (e.isJsonObject()) {
JsonObject o = e.getAsJsonObject();
return new PotionPredicate(Utils.getIdentifier(o, "id"), Math.max(1, JsonHelper.getInt(o, "level", 1)));
}
return new PotionPredicate(Utils.asIdentifier(e), 1);
}
}
}

View file

@ -0,0 +1,46 @@
package com.minelittlepony.unicopia.recipe.ingredient;
import java.util.Random;
import java.util.stream.Stream;
import net.minecraft.item.ItemStack;
import net.minecraft.util.PacketByteBuf;
public interface Predicate {
Predicate EMPTY = new Predicate() {
@Override
public Stream<ItemStack> getMatchingStacks() {
return Stream.empty();
}
@Override
public boolean matches(ItemStack stack, int materialMult) {
return true;
}
@Override
public PredicateSerializer<? super Predicate> getSerializer() {
return PredicateSerializer.EMPTY;
}
};
Stream<ItemStack> getMatchingStacks();
boolean matches(ItemStack stack, int materialMult);
PredicateSerializer<?> getSerializer();
default ItemStack applyModifiers(ItemStack output, Random random) {
return output;
}
default void write(PacketByteBuf buf) {
@SuppressWarnings("unchecked")
PredicateSerializer<Predicate> serializer = (PredicateSerializer<Predicate>)getSerializer();
buf.writeIdentifier(PredicateSerializer.REGISTRY.getId(serializer));
serializer.write(buf, this);
}
static Predicate read(PacketByteBuf buf) {
return PredicateSerializer.REGISTRY.get(buf.readIdentifier()).read(buf);
}
}

View file

@ -0,0 +1,49 @@
package com.minelittlepony.unicopia.recipe.ingredient;
import com.google.gson.JsonObject;
import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.SimpleRegistry;
public interface PredicateSerializer<T extends Predicate> {
Registry<PredicateSerializer<?>> REGISTRY = new SimpleRegistry<>();
Registry<JsonReader> JSON_READERS = new SimpleRegistry<>();
PredicateSerializer<Predicate> EMPTY = register("empty", new PredicateSerializer<Predicate>() {
@Override
public Predicate read(PacketByteBuf buf) {
return Predicate.EMPTY;
}
@Override
public void write(PacketByteBuf buf, Predicate predicate) {
}
});
PredicateSerializer<StackPredicate> STACK = register("stack", new StackPredicate.Serializer());
PredicateSerializer<TagPredicate> TAG = register("tag", new TagPredicate.Serializer());
PredicateSerializer<ChoicePredicate> CHOICE = register("choice", new ChoicePredicate.Serializer());
PredicateSerializer<SpellPredicate> SPELL = register("spell", new SpellPredicate.Serializer());
PredicateSerializer<EnchantmentPredicate> ENCHANTMENT = register("enchantment", new EnchantmentPredicate.Serializer());
PredicateSerializer<PotionPredicate> POTION = register("potion", new PotionPredicate.Serializer());
PredicateSerializer<DamagePredicate> DAMAGE = register("damage", new DamagePredicate.Serializer());
PredicateSerializer<ToxicityPredicate> TOXICITY = register("toxicity", new ToxicityPredicate.Serializer());
static <T extends Predicate> PredicateSerializer<T> register(String name, PredicateSerializer<T> entry) {
Identifier id = new Identifier("unicopia", name);
if (entry instanceof JsonReader) {
Registry.register(JSON_READERS, id, (JsonReader)entry);
}
return Registry.register(REGISTRY, id, entry);
}
Predicate read(PacketByteBuf buf);
void write(PacketByteBuf buf, T predicate);
@FunctionalInterface
interface JsonReader {
Predicate read(JsonObject json);
}
}

View file

@ -0,0 +1,123 @@
package com.minelittlepony.unicopia.recipe.ingredient;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.concurrent.Immutable;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.minelittlepony.unicopia.recipe.Utils;
import net.minecraft.item.ItemStack;
import net.minecraft.recipe.Ingredient;
import net.minecraft.util.DefaultedList;
import net.minecraft.util.Lazy;
import net.minecraft.util.PacketByteBuf;
@Immutable
public class PredicatedIngredient {
public static final PredicatedIngredient EMPTY = new PredicatedIngredient(DefaultedList.of());
private final DefaultedList<Predicate> predicates;
private final Lazy<List<ItemStack>> matchingStacks;
private final Lazy<net.minecraft.recipe.Ingredient> preview;
PredicatedIngredient(DefaultedList<Predicate> predicates) {
this.predicates = predicates;
this.matchingStacks = new Lazy<>(() -> {
return this.predicates.stream()
.flatMap(Predicate::getMatchingStacks)
.filter(s -> matches(s, 1))
.collect(Collectors.toList());
});
this.preview = new Lazy<>(() -> {
return net.minecraft.recipe.Ingredient.ofStacks(getMatchingStacks().toArray(ItemStack[]::new));
});
}
public Stream<ItemStack> getMatchingStacks() {
return matchingStacks.get().stream();
}
public net.minecraft.recipe.Ingredient getPreview() {
return preview.get();
}
public ItemStack getStack(Random random) {
ItemStack[] output = new ItemStack[] { ItemStack.EMPTY.copy() };
predicates.forEach(p -> output[0] = p.applyModifiers(output[0], random));
return output[0];
}
public boolean matches(ItemStack other, int materialMult) {
return predicates.stream().allMatch(p -> p.matches(other, materialMult));
}
public void write(PacketByteBuf buf) {
Utils.write(buf, predicates, Predicate::write);
}
public static PredicatedIngredient read(PacketByteBuf buf) {
return new PredicatedIngredient(Utils.read(buf, Predicate.EMPTY, Predicate::read));
}
public static PredicatedIngredient one(JsonElement json) {
if (json.isJsonArray()) {
return new PredicatedIngredient(DefaultedList.copyOf(Predicate.EMPTY, ChoicePredicate.read(json.getAsJsonArray())));
}
JsonObject obj = json.getAsJsonObject();
Predicate primary = StackPredicate.read(obj);
Predicate secondary = TagPredicate.read(obj);
if (primary != Predicate.EMPTY && secondary != Predicate.EMPTY) {
throw new JsonParseException("Invalid ingredient. Cannot have both an item and a tag requirement.");
}
if (primary == secondary) {
throw new JsonParseException("Invalid ingredient. Must have either an item or tag requirement.");
}
DefaultedList<Predicate> predicates = DefaultedList.of();
predicates.add(primary);
predicates.add(secondary);
PredicateSerializer.JSON_READERS.stream()
.map(reader -> reader.read(obj))
.filter(i -> i != Predicate.EMPTY)
.forEach(predicates::add);
return new PredicatedIngredient(predicates);
}
public static DefaultedList<PredicatedIngredient> many(JsonArray arr) {
if (arr.size() == 0) {
throw new JsonParseException("Recipe cannot have 0 ingredients");
}
DefaultedList<PredicatedIngredient> ingredients = DefaultedList.ofSize(arr.size(), EMPTY);
for (int i = 0; i < arr.size(); i++) {
ingredients.set(i, one(arr.get(i)));
}
return ingredients;
}
public static DefaultedList<Ingredient> preview(DefaultedList<PredicatedIngredient> input) {
return preview(input, DefaultedList.of());
}
public static DefaultedList<Ingredient> preview(DefaultedList<PredicatedIngredient> input, DefaultedList<Ingredient> output) {
input.stream().map(PredicatedIngredient::getPreview).forEach(output::add);
return output;
}
}

View file

@ -4,7 +4,10 @@ import java.util.Random;
import java.util.stream.Stream; import java.util.stream.Stream;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.magic.MagicEffect;
import com.minelittlepony.unicopia.magic.spell.SpellRegistry; import com.minelittlepony.unicopia.magic.spell.SpellRegistry;
import com.minelittlepony.unicopia.recipe.Utils;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.PacketByteBuf; import net.minecraft.util.PacketByteBuf;
@ -13,47 +16,57 @@ import net.minecraft.util.PacketByteBuf;
* A predicate that tests for a specific spell on an input when matching. * A predicate that tests for a specific spell on an input when matching.
* Appends that spell to the output when crafting. * Appends that spell to the output when crafting.
*/ */
class SpellPredicate implements Ingredient.Predicate { class SpellPredicate implements Predicate {
static Ingredient.Predicate read(PacketByteBuf buf) { private final MagicEffect spell;
int level = buf.readInt();
if (level == 0) {
return EMPTY;
}
return new SpellPredicate(buf.readString());
}
static Ingredient.Predicate read(JsonObject json) {
if (!json.has("spell")) {
return EMPTY;
}
return new SpellPredicate(json.get("spell").getAsString());
}
private final String spell;
SpellPredicate(String spell) { SpellPredicate(String spell) {
this.spell = spell; this.spell = Utils.require(SpellRegistry.instance().getSpellFromName(spell), "Unknown spell tag '" + spell + "'");
} }
@Override @Override
public ItemStack applyModifiers(ItemStack output, Random random) { public ItemStack applyModifiers(ItemStack output, Random random) {
return SpellRegistry.instance().enchantStack(output, spell); if (output.isEmpty()) {
output = new ItemStack(UItems.GEM);
}
return SpellRegistry.instance().enchantStack(output, spell.getName());
} }
@Override @Override
public boolean matches(ItemStack stack, int materialMult) { public boolean matches(ItemStack stack, int materialMult) {
return SpellRegistry.getKeyFromStack(stack).equals(spell); return SpellRegistry.getKeyFromStack(stack).equals(spell.getName());
}
@Override
public void write(PacketByteBuf buf) {
buf.writeInt(1);
buf.writeString(spell);
} }
@Override @Override
public Stream<ItemStack> getMatchingStacks() { public Stream<ItemStack> getMatchingStacks() {
return Stream.empty(); return Stream.empty();
} }
@Override
public PredicateSerializer<?> getSerializer() {
return PredicateSerializer.SPELL;
}
static final class Serializer implements PredicateSerializer<SpellPredicate>, PredicateSerializer.JsonReader {
@Override
public Predicate read(PacketByteBuf buf) {
int level = buf.readInt();
if (level == 0) {
return SpellPredicate.EMPTY;
}
return new SpellPredicate(buf.readString());
}
@Override
public void write(PacketByteBuf buf, SpellPredicate predicate) {
buf.writeString(predicate.spell.getName());
}
@Override
public Predicate read(JsonObject json) {
if (!json.has("spell")) {
return SpellPredicate.EMPTY;
}
return new SpellPredicate(json.get("spell").getAsString());
}
}
} }

View file

@ -6,12 +6,14 @@ import java.util.stream.Stream;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException; import com.google.gson.JsonSyntaxException;
import com.minelittlepony.unicopia.recipe.Utils;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup; import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.item.Items; import net.minecraft.item.Items;
import net.minecraft.util.DefaultedList; import net.minecraft.util.DefaultedList;
import net.minecraft.util.Identifier; import net.minecraft.util.JsonHelper;
import net.minecraft.util.PacketByteBuf; import net.minecraft.util.PacketByteBuf;
import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.Registry;
@ -19,26 +21,8 @@ import net.minecraft.util.registry.Registry;
* Tests for a specific item, stack size, and damage value when matching. * Tests for a specific item, stack size, and damage value when matching.
* Presents that item as the output when crafting. * Presents that item as the output when crafting.
*/ */
class StackPredicate implements Ingredient.Predicate { class StackPredicate implements Predicate {
static Ingredient.Predicate read(PacketByteBuf buf) { static Predicate read(JsonObject json) {
int count = buf.readInt();
if (count == 0) {
return EMPTY;
}
if (count > 0) {
return new StackPredicate(buf.readItemStack());
}
DefaultedList<Ingredient> items = DefaultedList.copyOf(Ingredient.EMPTY);
while (items.size() < count) {
items.add(Ingredient.read(buf));
}
return new ChoicePredicate(items);
}
static Ingredient.Predicate read(JsonObject json) {
if (!json.has("item")) { if (!json.has("item")) {
return EMPTY; return EMPTY;
} }
@ -49,15 +33,16 @@ class StackPredicate implements Ingredient.Predicate {
return ChoicePredicate.read(e.getAsJsonArray()); return ChoicePredicate.read(e.getAsJsonArray());
} }
int count = Math.max(1, JsonHelper.getInt(json, "count", 1));
if (e.isJsonObject()) { if (e.isJsonObject()) {
JsonObject o = e.getAsJsonObject(); JsonObject o = e.getAsJsonObject();
Item item = o.has("item") ? Registry.ITEM.get(new Identifier(o.get("item").getAsString())) : Items.AIR; Item item = o.has("item") ? Registry.ITEM.get(Utils.getIdentifier(o, "item")) : Items.AIR;
int size = o.has("count") ? Math.max(1, o.get("count").getAsInt()) : 1; return new StackPredicate(new ItemStack(item, count));
return new StackPredicate(new ItemStack(item, size));
} }
return new StackPredicate(new ItemStack(Registry.ITEM.get(new Identifier(e.getAsString())))); return new StackPredicate(new ItemStack(Registry.ITEM.get(Utils.asIdentifier(e)), count));
} }
private final ItemStack stack; private final ItemStack stack;
@ -101,8 +86,20 @@ class StackPredicate implements Ingredient.Predicate {
} }
@Override @Override
public void write(PacketByteBuf buf) { public PredicateSerializer<?> getSerializer() {
buf.writeInt(-1); return PredicateSerializer.STACK;
buf.writeItemStack(stack); }
static final class Serializer implements PredicateSerializer<StackPredicate> {
@Override
public Predicate read(PacketByteBuf buf) {
return new StackPredicate(buf.readItemStack());
}
@Override
public void write(PacketByteBuf buf, StackPredicate predicate) {
buf.writeItemStack(predicate.stack);
}
} }
} }

View file

@ -5,57 +5,41 @@ import java.util.stream.Stream;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException; import com.minelittlepony.unicopia.recipe.Utils;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.tag.ItemTags; import net.minecraft.tag.ItemTags;
import net.minecraft.tag.Tag; import net.minecraft.tag.Tag;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.JsonHelper;
import net.minecraft.util.PacketByteBuf; import net.minecraft.util.PacketByteBuf;
/** /**
* Tests for whether a tag contains the input when matching. * Tests for whether a tag contains the input when matching.
* Supplies a random item from the tag as the output when crafting. * Supplies a random item from the tag as the output when crafting.
*/ */
class TagPredicate implements Ingredient.Predicate { class TagPredicate implements Predicate {
static Ingredient.Predicate read(PacketByteBuf buf) { static Predicate read(JsonObject json) {
int count = buf.readInt();
if (count == 0) {
return EMPTY;
}
return new TagPredicate(buf.readIdentifier(), count);
}
static Ingredient.Predicate read(JsonObject json) {
if (!json.has("tag")) { if (!json.has("tag")) {
return EMPTY; return Predicate.EMPTY;
} }
JsonElement e = json.get("tag"); JsonElement e = json.get("tag");
if (e.isJsonObject()) { if (e.isJsonObject()) {
JsonObject o = e.getAsJsonObject(); JsonObject o = e.getAsJsonObject();
return new TagPredicate(Utils.getIdentifier(o, "id"), Math.max(1, JsonHelper.getInt(o, "count", 1)));
Identifier id = new Identifier(o.get("id").getAsString());
int count = o.has("count") ? Math.max(1, o.get("count").getAsInt()) : 1;
if (count == 0) {
return EMPTY;
} }
return new TagPredicate(id, count); return new TagPredicate(Utils.asIdentifier(e), 1);
}
return new TagPredicate(new Identifier(json.getAsString()), 1);
} }
private final Tag<Item> tag; private final Tag<Item> tag;
private final int count; private final int count;
TagPredicate(Identifier res, int count) { TagPredicate(Identifier res, int count) {
tag = ItemTags.getContainer().get(res); tag = Utils.require(ItemTags.getContainer().get(res), "Unknown item tag '" + res + "'");
this.count = count; this.count = count;
if (tag == null) {
throw new JsonSyntaxException("Unknown item tag '" + res + "'");
}
} }
@Override @Override
@ -80,4 +64,26 @@ class TagPredicate implements Ingredient.Predicate {
buf.writeInt(count); buf.writeInt(count);
buf.writeIdentifier(tag.getId()); buf.writeIdentifier(tag.getId());
} }
@Override
public PredicateSerializer<?> getSerializer() {
return PredicateSerializer.TAG;
}
static final class Serializer implements PredicateSerializer<TagPredicate> {
@Override
public Predicate read(PacketByteBuf buf) {
int count = buf.readInt();
if (count == 0) {
return Predicate.EMPTY;
}
return new TagPredicate(buf.readIdentifier(), count);
}
@Override
public void write(PacketByteBuf buf, TagPredicate predicate) {
buf.writeInt(predicate.count);
buf.writeIdentifier(predicate.tag.getId());
}
}
} }

View file

@ -5,35 +5,21 @@ import java.util.stream.Stream;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.minelittlepony.unicopia.toxin.Toxicity; import com.minelittlepony.unicopia.toxin.Toxicity;
import com.minelittlepony.unicopia.recipe.Utils;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.JsonHelper;
import net.minecraft.util.PacketByteBuf; import net.minecraft.util.PacketByteBuf;
/** /**
* A predicate that tests for a specific spell on an input when matching. * A predicate that tests for a specific spell on an input when matching.
* Appends that spell to the output when crafting. * Appends that spell to the output when crafting.
*/ */
class ToxicityPredicate implements Ingredient.Predicate { class ToxicityPredicate implements Predicate {
static Ingredient.Predicate read(PacketByteBuf buf) {
int ordinal = buf.readInt();
if (ordinal == 0) {
return EMPTY;
}
return new ToxicityPredicate(Toxicity.values()[ordinal - 1]);
}
static Ingredient.Predicate read(JsonObject json) {
if (!json.has("toxicity")) {
return EMPTY;
}
return new ToxicityPredicate(Toxicity.byName(json.get("toxicity").getAsString()));
}
private final Toxicity toxicity; private final Toxicity toxicity;
ToxicityPredicate(Toxicity toxicity) { ToxicityPredicate(String name) {
this.toxicity = toxicity; this.toxicity = Utils.require(Toxicity.byName(name), "Unknown toxicity tag '" + name + "'");
} }
@Override @Override
@ -46,13 +32,37 @@ class ToxicityPredicate implements Ingredient.Predicate {
return Toxicity.fromStack(stack) == toxicity; return Toxicity.fromStack(stack) == toxicity;
} }
@Override
public void write(PacketByteBuf buf) {
buf.writeInt(toxicity.ordinal() + 1);
}
@Override @Override
public Stream<ItemStack> getMatchingStacks() { public Stream<ItemStack> getMatchingStacks() {
return Stream.empty(); return Stream.empty();
} }
@Override
public PredicateSerializer<?> getSerializer() {
return PredicateSerializer.TOXICITY;
}
public static final class Serializer implements PredicateSerializer<ToxicityPredicate>, PredicateSerializer.JsonReader {
@Override
public Predicate read(PacketByteBuf buf) {
return new ToxicityPredicate(Toxicity.values()[buf.readInt()].name());
}
@Override
public void write(PacketByteBuf buf, ToxicityPredicate predicate) {
buf.writeInt(predicate.toxicity.ordinal());
}
@Override
public Predicate read(JsonObject json) {
if (!json.has("toxicity")) {
return Predicate.EMPTY;
}
return new ToxicityPredicate(JsonHelper.getString(json, "toxicity"));
}
}
} }

View file

@ -81,13 +81,13 @@ public enum Toxicity implements Toxin {
if (stack.hasTag()) { if (stack.hasTag()) {
Tag tag = stack.getTag().get("toxicity"); Tag tag = stack.getTag().get("toxicity");
if (tag != null) { if (tag != null) {
return byName(tag.asString()); return REGISTRY.getOrDefault(tag.asString(), SAFE);
} }
} }
return SAFE; return SAFE;
} }
public static Toxicity byName(String name) { public static Toxicity byName(String name) {
return REGISTRY.getOrDefault(name.toUpperCase(), SAFE); return REGISTRY.get(name.toUpperCase());
} }
} }

View file

@ -10,7 +10,7 @@
{ "item": "unicopia:sugar_block" } { "item": "unicopia:sugar_block" }
], ],
"*": [ "*": [
{ "item": "unicopia:crereal" } { "item": "unicopia:cereal" }
] ]
}, },
"result": { "item": "unicopia:boop_o_roops" } "result": { "item": "unicopia:boop_o_roops" }

View file

@ -7,7 +7,7 @@
], ],
"key": { "key": {
"#": [ "#": [
{ "item": "unicopia:chitin_block" } { "item": "unicopia:chitin_shell_block" }
] ]
}, },
"result": { "item": "unicopia:chiseled_chitin_shell_block", "count": 4 } "result": { "item": "unicopia:chiseled_chitin_shell_block", "count": 4 }

View file

@ -10,5 +10,5 @@
{ "item": "unicopia:cloud_block" } { "item": "unicopia:cloud_block" }
] ]
}, },
"result": { "item": "unicopia:anvil", "data": 0, "count": 1 } "result": { "item": "unicopia:cloud_anvil", "count": 1 }
} }

View file

@ -1,5 +1,5 @@
{ {
"type": "minecraft:crafting_shaped", "type": "unicopia:crafting_shaped",
"pattern": [ "pattern": [
" B ", " B ",
"FFF", "FFF",
@ -10,15 +10,8 @@
{ "item": "minecraft:bread" } { "item": "minecraft:bread" }
], ],
"F": [ "F": [
{ "item": "minecraft:red_flower", "data": 1 }, { "tag": "unicopia:non_toxic" }
{ "item": "minecraft:red_flower", "data": 2 },
{ "item": "minecraft:red_flower", "data": 3 },
{ "item": "minecraft:red_flower", "data": 4 },
{ "item": "minecraft:red_flower", "data": 5 },
{ "item": "minecraft:red_flower", "data": 6 },
{ "item": "minecraft:red_flower", "data": 7 },
{ "item": "minecraft:yellow_flower", "data": 0 }
] ]
}, },
"result": { "item": "unicopia:daffodil_daisy_sandwich", "data": 0, "count": 2 } "result": { "item": "unicopia:daffodil_daisy_sandwich", "tocicity": "safe", "count": 2 }
} }

View file

@ -1,5 +1,5 @@
{ {
"type": "minecraft:crafting_shaped", "type": "unicopia:crafting_shaped",
"pattern": [ "pattern": [
" B ", " B ",
"FFF", "FFF",
@ -10,17 +10,9 @@
{ "item": "minecraft:bread" } { "item": "minecraft:bread" }
], ],
"F": [ "F": [
{ "item": "minecraft:red_flower", "data": 0 }, { "tag": "unicopia:non_toxic" },
{ "item": "minecraft:red_flower", "data": 1 }, { "tag": "unicopia:fairly_toxic" }
{ "item": "minecraft:red_flower", "data": 2 },
{ "item": "minecraft:red_flower", "data": 3 },
{ "item": "minecraft:red_flower", "data": 4 },
{ "item": "minecraft:red_flower", "data": 5 },
{ "item": "minecraft:red_flower", "data": 6 },
{ "item": "minecraft:red_flower", "data": 7 },
{ "item": "minecraft:red_flower", "data": 8 },
{ "item": "minecraft:yellow_flower", "data": 0 }
] ]
}, },
"result": { "item": "unicopia:daffodil_daisy_sandwich", "data": 2, "count": 2 } "result": { "item": "unicopia:daffodil_daisy_sandwich", "toxicity": "fair", "count": 2 }
} }

View file

@ -1,5 +1,6 @@
{ {
"type": "unicopia:shapeless_crafting_spell", "type": "unicopia:enchanting_spell",
"input": { "item": "unicopia:corrupted_gem" },
"ingredients": [ "ingredients": [
{ "item": "unicopia:gem", "spell": "inferno" }, { "item": "unicopia:gem", "spell": "inferno" },
{ "item": "unicopia:gem", "spell": "darkness" }, { "item": "unicopia:gem", "spell": "darkness" },

View file

@ -2,9 +2,9 @@
"type": "unicopia:enchanting_spell", "type": "unicopia:enchanting_spell",
"input": { "item": "unicopia:gem" }, "input": { "item": "unicopia:gem" },
"ingredients": [ "ingredients": [
{ "tag": "#unicopia:unaligned" }, { "tag": "unicopia:harmonic_elementals" },
{ "tag": "#unicopia:unaligned" }, { "tag": "unicopia:harmonic_elementals" },
{ "tag": "#unicopia:unaligned" } { "tag": "unicopia:harmonic_elementals" }
], ],
"result": { "result": {
"item": [ "item": [

View file

@ -2,9 +2,9 @@
"type": "unicopia:enchanting_spell", "type": "unicopia:enchanting_spell",
"input": { "item": "unicopia:gem" }, "input": { "item": "unicopia:gem" },
"ingredients": [ "ingredients": [
{ "tag": "unicopia:magic_energetic" }, { "tag": "unicopia:knowledge_elementals" },
{ "item": "unicopia:gem", "spell": "fire" }, { "item": "unicopia:gem", "spell": "fire" },
{ "tag": "unicopia:magic_firey" } { "tag": "unicopia:fire_elementals" }
], ],
"result": { "item": "unicopia:gem", "spell": "charge" } "result": { "item": "unicopia:gem", "spell": "charge" }
} }

View file

@ -4,8 +4,8 @@
"ingredients": [ "ingredients": [
{ "item": "unicopia:gem", "spell": "charge" }, { "item": "unicopia:gem", "spell": "charge" },
{ "item": "unicopia:corrupted_gem" }, { "item": "unicopia:corrupted_gem" },
{ "tag": "unicopia:life" }, { "tag": "unicopia:life_elementals" },
{ "item": "minecraft:red_flower", "data": 0 } { "item": "minecraft:poppy" }
], ],
"result": { "item": "unicopia:gem", "spell": "drake" } "result": { "item": "unicopia:gem", "spell": "drake" }
} }

View file

@ -3,8 +3,8 @@
"input": { "item": "unicopia:gem" }, "input": { "item": "unicopia:gem" },
"ingredients": [ "ingredients": [
{ "item": "unicopia:gem", "spell": "flame" }, { "item": "unicopia:gem", "spell": "flame" },
{ "tag": "unicopia:fire" }, { "tag": "unicopia:fire_elementals" },
{ "tag": "unicopia:fire" } { "tag": "unicopia:fire_elementals" }
], ],
"result": { "item": "unicopia:gem", "spell": "fire" } "result": { "item": "unicopia:gem", "spell": "fire" }
} }

View file

@ -2,9 +2,9 @@
"type": "unicopia:enchanting_spell", "type": "unicopia:enchanting_spell",
"input": { "item": "unicopia:gem" }, "input": { "item": "unicopia:gem" },
"ingredients": [ "ingredients": [
{ "tag": "unicopia:fire" }, { "tag": "unicopia:fire_elementals" },
{ "tag": "unicopia:fire" }, { "tag": "unicopia:fire_elementals" },
{ "tag": "unicopia:fire" } { "tag": "unicopia:fire_elementals" }
], ],
"result": { "item": "unicopia:gem", "spell": "fire" } "result": { "item": "unicopia:gem", "spell": "fire" }
} }

View file

@ -2,9 +2,9 @@
"type": "unicopia:enchanting_spell", "type": "unicopia:enchanting_spell",
"input": { "item": "unicopia:gem" }, "input": { "item": "unicopia:gem" },
"ingredients": [ "ingredients": [
{ "tag": "unicopia:ice" }, { "tag": "unicopia:ice_elementals" },
{ "tag": "unicopia:ice" }, { "tag": "unicopia:ice_elementals" },
{ "tag": "unicopia:ice" } { "tag": "unicopia:ice_elementals" }
], ],
"result": { "item": "unicopia:gem", "spell": "ice" } "result": { "item": "unicopia:gem", "spell": "ice" }
} }

View file

@ -3,8 +3,8 @@
"input": { "item": "unicopia:gem" }, "input": { "item": "unicopia:gem" },
"ingredients": [ "ingredients": [
{ "item": "unicopia:gem", "spell": "fire" }, { "item": "unicopia:gem", "spell": "fire" },
{ "tag": "unicopia:fire" }, { "tag": "unicopia:fire_elementals" },
{ "tag": "unicopia:fire" } { "tag": "unicopia:fire_elementals" }
], ],
"result": { "item": "unicopia:corrupted_gem", "spell": "inferno" } "result": { "item": "unicopia:corrupted_gem", "spell": "inferno" }
} }

View file

@ -2,14 +2,9 @@
"type": "unicopia:enchanting_spell", "type": "unicopia:enchanting_spell",
"input": { "item": "unicopia:gem" }, "input": { "item": "unicopia:gem" },
"ingredients": [ "ingredients": [
{ "tag": "unicopia:light" }, { "tag": "unicopia:light_elementals" },
{ "item": "unicopia:gem", "spell": "fire" }, { "item": "unicopia:gem", "spell": "fire" },
[ { "tag": "unicopia:fresh_apples" }
{ "item": "minecraft:apple" },
{ "item": "unicopia:apple_green" },
{ "item": "unicopia:apple_sweet" },
{ "item": "unicopia:apple_sour" }
]
], ],
"result": { "item": "unicopia:gem", "spell": "light" } "result": { "item": "unicopia:gem", "spell": "light" }
} }

View file

@ -1,12 +1,12 @@
{ {
"type": "unicopia:enchanting_spell", "type": "unicopia:enchanting_spell",
"input": { "tag": "unicopia:sound"}, "input": { "tag": "unicopia:sound_elementals"},
"ingredients": [ "ingredients": [
{ "tag": "unicopia:apple_bloom" }, { "tag": "unicopia:apple_bloom_spirit" },
{ "tag": "unicopia:scootaloo" }, { "tag": "unicopia:scootaloo_spirit" },
{ "tag": "unicopia:sweetie_belle" } { "tag": "unicopia:sweetie_belle_spirit" }
], ],
"output": { "result": {
"item": "unicopia:music_disc_crusade" "item": "unicopia:music_disc_crusade"
} }
} }

View file

@ -1,12 +1,12 @@
{ {
"type": "unicopia:enchanting_spell", "type": "unicopia:enchanting_spell",
"input": { "tag": "unicopia:sound"}, "input": { "tag": "unicopia:sound_elementals"},
"ingredients": [ "ingredients": [
{ "item": "minecraft:diamond" }, { "item": "minecraft:diamond" },
{ "item": "minecraft:diamond" }, { "item": "minecraft:diamond" },
{ "item": "unicopia:boop_o_roops" } { "item": "unicopia:boop_o_roops" }
], ],
"output": { "result": {
"item": "unicopia:music_disc_funk" "item": "unicopia:music_disc_funk"
} }
} }

View file

@ -1,12 +1,12 @@
{ {
"type": "unicopia:enchanting_spell", "type": "unicopia:enchanting_spell",
"input": { "tag": "unicopia:sound"}, "input": { "tag": "unicopia:sound_elementals"},
"ingredients": [ "ingredients": [
{ "item": "unicopia:gem", "spell": "awkward" }, { "item": "unicopia:gem", "spell": "awkward" },
{ "item": "minecraft:feather" }, { "item": "minecraft:feather" },
{ "item": "minecraft:carrot" } { "item": "minecraft:carrot" }
], ],
"output": { "result": {
"item": "unicopia:music_disc_pet" "item": "unicopia:music_disc_pet"
} }
} }

View file

@ -1,12 +1,12 @@
{ {
"type": "unicopia:enchanting_spell", "type": "unicopia:enchanting_spell",
"input": { "tag": "unicopia:sound"}, "input": { "tag": "unicopia:sound_elementals"},
"ingredients": [ "ingredients": [
{ "item": "minecraft:diamond" }, { "item": "minecraft:diamond" },
{ "item": "minecraft:diamond" }, { "item": "minecraft:diamond" },
{ "item": "minecraft:diamond" } { "item": "minecraft:diamond" }
], ],
"output": { "result": {
"item": "unicopia:music_disc_popular" "item": "unicopia:music_disc_popular"
} }
} }

View file

@ -1,12 +1,12 @@
{ {
"type": "unicopia:enchanting_spell", "type": "unicopia:enchanting_spell",
"input": { "tag": "unicopia:sound"}, "input": { "tag": "unicopia:sound_elementals"},
"ingredients": [ "ingredients": [
{ "item": "unicopia:gem", "spell": "awkward" }, { "item": "unicopia:gem", "spell": "awkward" },
{ "item": "unicopia:gem", "spell": "awkward" }, { "item": "unicopia:gem", "spell": "awkward" },
{ "item": "unicopia:gem", "spell": "awkward" } { "item": "unicopia:gem", "spell": "awkward" }
], ],
"output": { "result": {
"tag": "unicopia:sound" "tag": "unicopia:sound_elementals"
} }
} }

View file

@ -2,12 +2,9 @@
"type": "unicopia:enchanting_spell", "type": "unicopia:enchanting_spell",
"input": { "item": "unicopia:gem" }, "input": { "item": "unicopia:gem" },
"ingredients": [ "ingredients": [
{ "tag": "unicopia:blood" }, { "tag": "unicopia:death_elementals" },
{ "tag": "unicopia:energy" }, { "tag": "unicopia:knowledge_elementals" },
{ "tag": "unicopia:rot" } { "tag": "unicopia:rotting_elementals" }
], ],
"result": { "result": { "item": "unicopia:corrupted_gem", "spell": "necromancy" }
"item": "unicopia:corrupted_gem",
"spell": "necromancy"
}
} }

View file

@ -2,8 +2,8 @@
"type": "unicopia:enchanting_spell", "type": "unicopia:enchanting_spell",
"input": { "item": "unicopia:gem" }, "input": { "item": "unicopia:gem" },
"ingredients": [ "ingredients": [
{ "tag": "unicopia:energy" }, { "tag": "unicopia:knowledge_elementals" },
{ "tag": "unicopia:life" }, { "tag": "unicopia:life_elementals" },
{ "item": "unicopia:gem", "spell": "charge" } { "item": "unicopia:gem", "spell": "charge" }
], ],
"result": { "item": "unicopia:gem", "spell": "siphon" } "result": { "item": "unicopia:gem", "spell": "siphon" }

View file

@ -2,9 +2,9 @@
"type": "unicopia:enchanting_spell", "type": "unicopia:enchanting_spell",
"input": { "item": "unicopia:gem" }, "input": { "item": "unicopia:gem" },
"ingredients": [ "ingredients": [
{ "tag": "unicopia:sight" }, { "tag": "unicopia:sight_elementals" },
{ "item": "minecraft:ghast_tear" }, { "item": "minecraft:ghast_tear" },
{ "tag": "unicopia:energy" }, { "tag": "unicopia:knowledge_elementals" },
{ "item": "unicopia:gem", "spell": "fire" } { "item": "unicopia:gem", "spell": "fire" }
], ],
"result": { "item": "unicopia:gem", "spell": "portal" } "result": { "item": "unicopia:gem", "spell": "portal" }

View file

@ -2,10 +2,10 @@
"type": "unicopia:enchanting_spell", "type": "unicopia:enchanting_spell",
"input": { "item": "unicopia:gem" }, "input": { "item": "unicopia:gem" },
"ingredients": [ "ingredients": [
{ "tag": "unicopia:fire" }, { "tag": "unicopia:fire_elementals" },
{ "tag": "unicopia:shell" }, { "tag": "unicopia:shells" },
{ "item": "unicopia:gem", "spell": "shield" }, { "item": "unicopia:gem", "spell": "shield" },
{ "tag": "unicopia:shell" } { "tag": "unicopia:shells" }
], ],
"result": { "result": {
"item": "unicopia:corrupted_gem", "item": "unicopia:corrupted_gem",

View file

@ -2,9 +2,9 @@
"type": "unicopia:enchanting_spell", "type": "unicopia:enchanting_spell",
"input": { "item": "unicopia:gem" }, "input": { "item": "unicopia:gem" },
"ingredients": [ "ingredients": [
{ "id": "unicopia:shell" }, { "tag": "unicopia:shells" },
{ "id": "unicopia:shell" }, { "tag": "unicopia:shells" },
{ "id": "unicopia:shard" } { "tag": "unicopia:shards" }
], ],
"result": { "item": "unicopia:gem", "spell": "shield" } "result": { "item": "unicopia:gem", "spell": "shield" }
} }

View file

@ -2,9 +2,9 @@
"type": "unicopia:enchanting_spell", "type": "unicopia:enchanting_spell",
"input": { "item": "unicopia:gem" }, "input": { "item": "unicopia:gem" },
"ingredients": [ "ingredients": [
{ "id": "unicopia:energy" }, { "tag": "unicopia:knowledge_elementals" },
{ "id": "unicopia:life" }, { "tag": "unicopia:life_elementals" },
{ "id": "unicopia:dark" }, { "tag": "unicopia:dark_elementals" },
{ "item": "unicopia:gem", "spell": "siphon" } { "item": "unicopia:gem", "spell": "siphon" }
], ],
"result": { "item": "unicopia:corrupted_gem", "spell": "siphon" } "result": { "item": "unicopia:corrupted_gem", "spell": "siphon" }

View file

@ -3,7 +3,7 @@
"input": { "item": "unicopia:gem" }, "input": { "item": "unicopia:gem" },
"ingredients": [ "ingredients": [
{ "item": "minecraft:fire_charge" }, { "item": "minecraft:fire_charge" },
{ "tag": "unicopia:blood" }, { "tag": "unicopia:death_elementals" },
{ "item": "unicopia:gem", "spell": "fire" } { "item": "unicopia:gem", "spell": "fire" }
], ],
"result": { "item": "unicopia:corrupted_gem", "spell": "vortex" } "result": { "item": "unicopia:corrupted_gem", "spell": "vortex" }

View file

@ -2,7 +2,7 @@
"type": "unicopia:enchanting_spell", "type": "unicopia:enchanting_spell",
"input": { "item": "unicopia:gem" }, "input": { "item": "unicopia:gem" },
"ingredients": [ "ingredients": [
{ "tag": "unicopia:shell" }, { "tag": "unicopia:shells" },
{ "item": "unicopia:gem", "spell": "shield" }, { "item": "unicopia:gem", "spell": "shield" },
{ "item": "unicopia:gem", "spell": "portal" } { "item": "unicopia:gem", "spell": "portal" }
], ],

View file

@ -10,7 +10,22 @@
{ "item": "minecraft:andesite" } { "item": "minecraft:andesite" }
], ],
"C": [ "C": [
{ "item": "#minecraft:stained_hardened_clay" } { "item": "minecraft:white_concrete" },
{ "item": "minecraft:orange_concrete" },
{ "item": "minecraft:magenta_concrete" },
{ "item": "minecraft:light_blue_concrete" },
{ "item": "minecraft:yellow_concrete" },
{ "item": "minecraft:lime_concrete" },
{ "item": "minecraft:pink_concrete" },
{ "item": "minecraft:gray_concrete" },
{ "item": "minecraft:light_gray_concrete" },
{ "item": "minecraft:cyan_concrete" },
{ "item": "minecraft:purple_concrete" },
{ "item": "minecraft:blue_concrete" },
{ "item": "minecraft:brown_concrete" },
{ "item": "minecraft:green_concrete" },
{ "item": "minecraft:red_concrete" },
{ "item": "minecraft:black_concrete" }
], ],
"B": [ "B": [
{ "item": "minecraft:black_dye" } { "item": "minecraft:black_dye" }

View file

@ -1,5 +1,5 @@
{ {
"type": "minecraft:crafting_shaped", "type": "unicopia:crafting_shaped",
"pattern": [ "pattern": [
"AAA", "AAA",
"FTF", "FTF",
@ -14,15 +14,8 @@
{ "item": "unicopia:cloudsdale_tomato" } { "item": "unicopia:cloudsdale_tomato" }
], ],
"F": [ "F": [
{ "item": "minecraft:red_flower", "data": 1 }, { "tag": "unicopia:non_toxic" }
{ "item": "minecraft:red_flower", "data": 2 },
{ "item": "minecraft:red_flower", "data": 3 },
{ "item": "minecraft:red_flower", "data": 4 },
{ "item": "minecraft:red_flower", "data": 5 },
{ "item": "minecraft:red_flower", "data": 6 },
{ "item": "minecraft:red_flower", "data": 7 },
{ "item": "minecraft:yellow_flower", "data": 0 }
] ]
}, },
"result": { "item": "unicopia:hay_burger" } "result": { "item": "unicopia:hay_burger", "toxicity": "safe" }
} }

View file

@ -1,5 +1,5 @@
{ {
"type": "minecraft:crafting_shaped", "type": "unicopia:crafting_shaped",
"pattern": [ "pattern": [
"AAA", "AAA",
"FTF", "FTF",
@ -10,23 +10,15 @@
{ "item": "unicopia:alfalfa_leaves" } { "item": "unicopia:alfalfa_leaves" }
], ],
"T": [ "T": [
{ "item": "unicopia:tomato", "data": 0 }, { "item": "unicopia:tomato" },
{ "item": "unicopia:tomato", "data": 1 }, { "item": "unicopia:rotten_tomato" },
{ "item": "unicopia:cloudsdale_tomato", "data": 0 }, { "item": "unicopia:cloudsdale_tomato" },
{ "item": "unicopia:cloudsdale_tomato", "data": 1 } { "item": "unicopia:rotten_cloudsdale_tomato" }
], ],
"F": [ "F": [
{ "item": "minecraft:red_flower", "data": 0 }, { "tag": "unicopia:non_toxic" },
{ "item": "minecraft:red_flower", "data": 1 }, { "tag": "unicopia:fairly_toxic" }
{ "item": "minecraft:red_flower", "data": 2 },
{ "item": "minecraft:red_flower", "data": 3 },
{ "item": "minecraft:red_flower", "data": 4 },
{ "item": "minecraft:red_flower", "data": 5 },
{ "item": "minecraft:red_flower", "data": 6 },
{ "item": "minecraft:red_flower", "data": 7 },
{ "item": "minecraft:red_flower", "data": 8 },
{ "item": "minecraft:yellow_flower", "data": 0 }
] ]
}, },
"result": { "item": "unicopia:hay_burger", "data": 2, "count": 1 } "result": { "item": "unicopia:hay_burger", "toxicity": "fair", "count": 1 }
} }

View file

@ -7,8 +7,8 @@
], ],
"key": { "key": {
"#": [ "#": [
{ "item": "minecraft:stained_white_glass_block" } { "item": "minecraft:white_stained_glass" }
] ]
}, },
"result": { "item": "unicopia:misted_glass_door", "data": 0, "count": 1 } "result": { "item": "unicopia:misted_glass_door", "count": 1 }
} }

View file

@ -10,8 +10,8 @@
{ "item": "minecraft:bowl" } { "item": "minecraft:bowl" }
], ],
"F": [ "F": [
{ "tag": "#unicopia:non_toxic" }, { "tag": "unicopia:non_toxic" },
{ "tag": "#unicopia:fairly_toxic" }, { "tag": "unicopia:fairly_toxic" },
{ "item": "unicopia:alfalfa_leaves" } { "item": "unicopia:alfalfa_leaves" }
] ]
}, },

View file

@ -13,9 +13,9 @@
{ "item": "minecraft:golden_apple", "data": 0 }, { "item": "minecraft:golden_apple", "data": 0 },
{ "item": "unicopia:zap_apple", "data": 0 }, { "item": "unicopia:zap_apple", "data": 0 },
{ "item": "unicopia:corrupted_gem" }, { "item": "unicopia:corrupted_gem" },
{ "tag": "#unicopia:non_toxic" }, { "tag": "unicopia:non_toxic" },
{ "tag": "#unicopia:fairly_toxic" }, { "tag": "unicopia:fairly_toxic" },
{ "tag": "#unicopia:severely_toxic" }, { "tag": "unicopia:severely_toxic" },
{ "item": "unicopia:alfalfa_leaves" }, { "item": "unicopia:alfalfa_leaves" },
{ "item": "unicopia:wheat_worms" } { "item": "unicopia:wheat_worms" }
] ]

View file

@ -10,7 +10,7 @@
{ "item": "minecraft:bowl" } { "item": "minecraft:bowl" }
], ],
"F": [ "F": [
{ "tag": "#unicopia:non_toxic" }, { "tag": "unicopia:non_toxic" },
{ "item": "unicopia:alfalfa_leaves" } { "item": "unicopia:alfalfa_leaves" }
] ]
}, },

View file

@ -10,9 +10,9 @@
{ "item": "minecraft:bowl" } { "item": "minecraft:bowl" }
], ],
"F": [ "F": [
{ "tag": "#unicopia:non_toxic" }, { "tag": "unicopia:non_toxic" },
{ "tag": "#unicopia:fairly_toxic" }, { "tag": "unicopia:fairly_toxic" },
{ "tag": "#unicopia:severely_toxic" }, { "tag": "unicopia:severely_toxic" },
{ "item": "unicopia:alfalfa_leaves" }, { "item": "unicopia:alfalfa_leaves" },
{ "item": "unicopia:wheat_worms" } { "item": "unicopia:wheat_worms" }
] ]

View file

@ -7,11 +7,11 @@
], ],
"key": { "key": {
"#": [ "#": [
{ "item": "minecraft:slime" } { "item": "minecraft:slime_block" }
], ],
"*": [ "*": [
{ "item": "unicopia:gem" } { "item": "unicopia:gem" }
] ]
}, },
"result": { "item": "unicopia:cuccoon", "count": 1 } "result": { "item": "unicopia:slime_drop", "count": 1 }
} }

View file

@ -5,7 +5,7 @@
], ],
"key": { "key": {
"#": [ "#": [
{ "item": "minecraft:slime" } { "item": "minecraft:slime_block" }
] ]
}, },
"result": { "item": "unicopia:slime_layer", "count": 6 } "result": { "item": "unicopia:slime_layer", "count": 6 }

View file

@ -1,7 +1,7 @@
{ {
"type": "minecraft:crafting_shapeless", "type": "minecraft:crafting_shapeless",
"ingredients": [ "ingredients": [
{ "item": "#unicopia:fresh_tomatoes" } { "tag": "unicopia:fresh_tomatoes" }
], ],
"result": { "item": "unicopia:tomato_seeds", "count": 3 } "result": { "item": "unicopia:tomato_seeds", "count": 3 }
} }

View file

@ -1,8 +1,11 @@
{ {
"type": "minecraft:crafting_shapeless", "type": "unicopia:crafting_shapeless",
"ingredients": [ "ingredients": [
{ "item": "unicopia:zap_apple", "data": 0 }, { "item": "unicopia:zap_apple" },
{ "item": "minecraft:dye", "data": 2 } { "item": "minecraft:green_dye" }
], ],
"result": { "item": "unicopia:zap_apple", "count": 1, "data": 2 } "result": {
"item": "unicopia:zap_apple",
"appearance": "unicopia:green_apple"
}
} }

View file

@ -1,8 +1,11 @@
{ {
"type": "minecraft:crafting_shapeless", "type": "unicopia:crafting_shapeless",
"ingredients": [ "ingredients": [
{ "item": "unicopia:zap_apple", "data": 0 }, { "item": "unicopia:zap_apple" },
{ "item": "minecraft:dye", "data": 1 } { "item": "minecraft:red_dye" }
], ],
"result": { "item": "unicopia:zap_apple", "count": 1, "data": 1 } "result": {
"item": "unicopia:zap_apple",
"appearance": "minecraft:apple"
}
} }

View file

@ -1,8 +1,11 @@
{ {
"type": "minecraft:crafting_shapeless", "type": "unicopia:crafting_shapeless",
"ingredients": [ "ingredients": [
{ "item": "unicopia:zap_apple", "data": 0 }, { "item": "unicopia:zap_apple" },
{ "item": "minecraft:dye", "data": 15 } { "item": "minecraft:yellow_dye" }
], ],
"result": { "item": "unicopia:zap_apple", "count": 1, "data": 4 } "result": {
"item": "unicopia:zap_apple",
"appearance": "unicopia:sour_apple"
}
} }

View file

@ -1,8 +1,11 @@
{ {
"type": "minecraft:crafting_shapeless", "type": "unicopia:crafting_shapeless",
"ingredients": [ "ingredients": [
{ "item": "unicopia:zap_apple", "data": 0 }, { "item": "unicopia:zap_apple" },
{ "item": "minecraft:dye", "data": 14 } { "item": "minecraft:orange_dye" }
], ],
"result": { "item": "unicopia:zap_apple", "count": 1, "data": 3 } "result": {
"item": "unicopia:zap_apple",
"appearance": "unicopia:sweet_apple"
}
} }

View file

@ -1,8 +1,11 @@
{ {
"type": "minecraft:crafting_shapeless", "type": "unicopia:crafting_shapeless",
"ingredients": [ "ingredients": [
{ "item": "unicopia:zap_apple", "data": 0 }, { "item": "unicopia:zap_apple" },
{ "item": "minecraft:rotten_flesh" } { "item": "minecraft:rotten_flesh" }
], ],
"result": { "item": "unicopia:zap_apple", "count": 1, "data": 5 } "result": {
"item": "unicopia:zap_apple",
"appearance": "unicopia:cooked_zap_apple"
}
} }

View file

@ -1,5 +1,5 @@
{ {
"type": "minecraft:crafting_shaped", "type": "unicopia:crafting_shaped",
"pattern": [ "pattern": [
"***", "***",
" * ", " * ",
@ -15,8 +15,6 @@
}, },
"result": { "result": {
"item": "minecraft:potion", "item": "minecraft:potion",
"data": 0, "potion": "minecraft:water"
"count": 1,
"nbt": { "Potion": "minecraft:water" }
} }
} }

View file

@ -13,5 +13,5 @@
{ "item": "unicopia:dew_drop" } { "item": "unicopia:dew_drop" }
] ]
}, },
"result": { "item": "minecraft:water_bucket", "count": 1 } "result": { "item": "minecraft:water_bucket" }
} }

View file

@ -11,6 +11,6 @@
"minecraft:cornflower", "minecraft:cornflower",
"minecraft:rose_bush", "minecraft:rose_bush",
"minecraft:peony", "minecraft:peony",
"tall_grass" "minecraft:tall_grass"
] ]
} }

View file

@ -4,7 +4,7 @@
"minecraft:fern", "minecraft:fern",
"minecraft:dead_bush", "minecraft:dead_bush",
"minecraft:poppy", "minecraft:poppy",
"minecraft:oxey_daisy", "minecraft:oxeye_daisy",
"minecraft:large_fern" "minecraft:large_fern"
] ]
} }