Refactor again and add recipe types for the crafting table \o/

This commit is contained in:
Sollace 2020-05-01 17:23:40 +02:00
parent 94952f5c16
commit e9041347e0
14 changed files with 660 additions and 116 deletions

View file

@ -1,77 +0,0 @@
package com.minelittlepony.unicopia.recipe;
import java.util.List;
import com.google.common.collect.Lists;
import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack;
import net.minecraft.recipe.Recipe;
import net.minecraft.util.DefaultedList;
import net.minecraft.util.Identifier;
import net.minecraft.world.World;
/**
* Basic crafting recipe that uses our custom ingredients.
*/
public abstract class AbstractShapelessPredicatedRecipe<C extends Inventory> implements Recipe<C> {
protected final Ingredient output;
protected final DefaultedList<Ingredient> ingredients;
private final Identifier id;
public AbstractShapelessPredicatedRecipe(Identifier id, Ingredient output, DefaultedList<Ingredient> ingredients) {
this.id = id;
this.output = output;
this.ingredients = ingredients;
}
protected abstract int getInputMultiplier(C inv, World worldIn);
@Override
public boolean matches(C inv, World worldIn) {
int materialMult = getInputMultiplier(inv, worldIn);
if (materialMult == 0) {
return false;
}
List<Ingredient> toMatch = Lists.newArrayList(ingredients);
for (int i = 0; i < inv.getInvSize(); i++) {
ItemStack stack = inv.getInvStack(i);
if (!stack.isEmpty()) {
if (toMatch.isEmpty() || !removeMatch(toMatch, stack, materialMult)) {
return false;
}
}
}
return toMatch.isEmpty();
}
private boolean removeMatch(List<Ingredient> toMatch, ItemStack stack, int materialMult) {
return toMatch.stream()
.filter(s -> s.matches(stack, materialMult))
.findFirst()
.filter(toMatch::remove)
.isPresent();
}
@Override
public Identifier getId() {
return id;
}
@Override
public ItemStack craft(C inv) {
return getOutput();
}
@Override
public boolean fits(int width, int height) {
return width * height < ingredients.size();
}
}

View file

@ -0,0 +1,220 @@
package com.minelittlepony.unicopia.recipe;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import com.google.common.collect.Sets;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import com.minelittlepony.unicopia.recipe.ingredient.Ingredient;
import net.minecraft.inventory.CraftingInventory;
import net.minecraft.util.DefaultedList;
import net.minecraft.util.JsonHelper;
import net.minecraft.util.PacketByteBuf;
import net.minecraft.util.Util;
public class Pattern {
public static Pattern read(PacketByteBuf buf) {
return new Pattern(
Utils.read(buf, Ingredient.EMPTY, Ingredient::read),
buf.readVarInt(),
buf.readVarInt()
);
}
public static Pattern read(JsonObject json) {
String[] patterns = combinePatternMatrix(getPattern(JsonHelper.getArray(json, "pattern")));
Map<String, Ingredient> ingredients = readIngredients(JsonHelper.getObject(json, "key"));
return new Pattern(patterns, ingredients);
}
public final DefaultedList<Ingredient> matrix;
public final int width;
public final int height;
public Pattern(DefaultedList<Ingredient> matrix, int width, int height) {
this.matrix = matrix;
this.width = width;
this.height = height;
}
public Pattern(String[] pattern, Map<String, Ingredient> ingredients) {
this(pattern, ingredients, pattern[0].length(), pattern.length);
}
private Pattern(String[] pattern, Map<String, Ingredient> ingredients, int width, int height) {
this(buildIngredientMatrix(pattern, ingredients, width, height), width, height);
}
public boolean matches(CraftingInventory inv) {
for(int x = 0; x <= inv.getWidth() - width; x++) {
for(int y = 0; y <= inv.getHeight() - height; y++) {
if (matchesSmall(inv, x, y, true) || matchesSmall(inv, x, y, false)) {
return true;
}
}
}
return false;
}
public void write(PacketByteBuf buf) {
Utils.write(buf, matrix, Ingredient::write);
buf.writeVarInt(width);
buf.writeVarInt(height);
}
public int size() {
return matrix.size();
}
private boolean matchesSmall(CraftingInventory inv, int offsetX, int offsetY, boolean reflected) {
for(int x = 0; x < inv.getWidth(); ++x) {
for(int y = 0; y < inv.getHeight(); ++y) {
int k = x - offsetX;
int l = y - offsetY;
Ingredient ingredient = Ingredient.EMPTY;
if (k >= 0 && l >= 0 && k < width && l < this.height) {
if (reflected) {
ingredient = matrix.get(width - k - 1 + l * this.width);
} else {
ingredient = matrix.get(k + l * this.width);
}
}
if (!ingredient.matches(inv.getInvStack(x + y * inv.getWidth()), 1)) {
return false;
}
}
}
return true;
}
static DefaultedList<Ingredient> buildIngredientMatrix(String[] pattern, Map<String, Ingredient> ingredients, int width, int height) {
DefaultedList<Ingredient> result = DefaultedList.ofSize(width * height, Ingredient.EMPTY);
Set<String> unsolved = Sets.newHashSet(ingredients.keySet());
unsolved.remove(" ");
for(int i = 0; i < pattern.length; ++i) {
for(int j = 0; j < pattern[i].length(); ++j) {
String key = pattern[i].substring(j, j + 1);
Ingredient ingredient = ingredients.get(key);
if (ingredient == null) {
throw new JsonSyntaxException("Pattern references symbol '" + key + "' but it's not defined in the key");
}
unsolved.remove(key);
result.set(j + width * i, ingredient);
}
}
if (!unsolved.isEmpty()) {
throw new JsonSyntaxException("Key defines symbols that aren't used in pattern: " + unsolved);
}
return result;
}
static String[] combinePatternMatrix(String... lines) {
int i = Integer.MAX_VALUE;
int j = 0;
int k = 0;
int l = 0;
for(int row = 0; row < lines.length; ++row) {
String line = lines[row];
i = Math.min(i, lookAhead(line));
int n = lookBack(line);
j = Math.max(j, n);
if (n < 0) {
if (k == row) {
++k;
}
++l;
} else {
l = 0;
}
}
if (lines.length == l) {
return new String[0];
}
String[] strings = new String[lines.length - l - k];
for(int o = 0; o < strings.length; ++o) {
strings[o] = lines[o + k].substring(i, j + 1);
}
return strings;
}
private static int lookAhead(String pattern) {
int i;
for(i = 0; i < pattern.length() && pattern.charAt(i) == ' '; ++i) {}
return i;
}
private static int lookBack(String pattern) {
int i;
for(i = pattern.length() - 1; i >= 0 && pattern.charAt(i) == ' '; --i) {}
return i;
}
static String[] getPattern(JsonArray json) {
String[] strings = new String[json.size()];
if (strings.length > 3) {
throw new JsonSyntaxException("Invalid pattern: too many rows, 3 is maximum");
} else if (strings.length == 0) {
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 + "]");
if (string.length() > 3) {
throw new JsonSyntaxException("Invalid pattern: too many columns, 3 is maximum");
}
if (i > 0 && strings[0].length() != string.length()) {
throw new JsonSyntaxException("Invalid pattern: each row must be the same width");
}
strings[i] = string;
}
return strings;
}
}
static Map<String, Ingredient> readIngredients(JsonObject json) {
return Util.make(json.entrySet().stream().collect(Collectors.toMap(e -> {
if (e.getKey().length() != 1) {
throw new JsonSyntaxException("Invalid key entry: '" + e.getKey() + "' is an invalid symbol (must be 1 character only).");
}
if (" ".equals(e.getKey())) {
throw new JsonSyntaxException("Invalid key entry: ' ' is a reserved symbol.");
}
return e.getKey();
},
e -> Ingredient.one(e.getValue())
)), m -> m.put(" ", Ingredient.EMPTY));
}
}

View file

@ -0,0 +1,99 @@
package com.minelittlepony.unicopia.recipe;
import java.util.Random;
import com.google.gson.JsonObject;
import com.minelittlepony.unicopia.recipe.ingredient.Ingredient;
import net.minecraft.inventory.CraftingInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.recipe.RecipeSerializer;
import net.minecraft.recipe.SpecialCraftingRecipe;
import net.minecraft.util.DefaultedList;
import net.minecraft.util.Identifier;
import net.minecraft.util.JsonHelper;
import net.minecraft.util.PacketByteBuf;
import net.minecraft.world.World;
public class ShapedSpecialRecipe extends SpecialCraftingRecipe {
private static final Random RANDOM = new Random();
private final Pattern pattern;
private final Ingredient output;
private final String group;
public ShapedSpecialRecipe(Identifier id, String group, Pattern pattern, Ingredient output) {
super(id);
this.group = group;
this.pattern = pattern;
this.output = output;
}
@Override
public boolean matches(CraftingInventory inv, World world) {
return pattern.matches(inv);
}
@Override
public ItemStack craft(CraftingInventory inv) {
return getOutput().copy();
}
@Override
public boolean fits(int width, int height) {
return width * height >= pattern.size();
}
@Override
public ItemStack getOutput() {
return output.getStack(RANDOM);
}
@Override
public String getGroup() {
return this.group;
}
@Override
public RecipeSerializer<?> getSerializer() {
return null;
}
@Override
public boolean isIgnoredInRecipeBook() {
return false;
}
@Override
public DefaultedList<net.minecraft.recipe.Ingredient> getPreviewInputs() {
return Ingredient.preview(pattern.matrix);
}
public static class Serializer implements RecipeSerializer<ShapedSpecialRecipe> {
@Override
public ShapedSpecialRecipe read(Identifier id, JsonObject json) {
return new ShapedSpecialRecipe(id,
JsonHelper.getString(json, "group", ""),
Pattern.read(json),
Ingredient.one(json.get("result"))
);
}
@Override
public ShapedSpecialRecipe read(Identifier id, PacketByteBuf buf) {
return new ShapedSpecialRecipe(id,
buf.readString(32767),
Pattern.read(buf),
Ingredient.read(buf)
);
}
@Override
public void write(PacketByteBuf buf, ShapedSpecialRecipe recipe) {
buf.writeString(recipe.group);
recipe.pattern.write(buf);
recipe.output.write(buf);
}
}
}

View file

@ -0,0 +1,104 @@
package com.minelittlepony.unicopia.recipe;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.minelittlepony.unicopia.recipe.ingredient.Ingredient;
import net.minecraft.inventory.CraftingInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.recipe.RecipeSerializer;
import net.minecraft.recipe.SpecialCraftingRecipe;
import net.minecraft.util.DefaultedList;
import net.minecraft.util.Identifier;
import net.minecraft.util.JsonHelper;
import net.minecraft.util.PacketByteBuf;
import net.minecraft.world.World;
public class ShapelessSpecialRecipe extends SpecialCraftingRecipe {
private final String group;
private final Ingredient output;
private final DefaultedList<Ingredient> input;
public ShapelessSpecialRecipe(Identifier id, String group, Ingredient output, DefaultedList<Ingredient> input) {
super(id);
this.group = group;
this.output = output;
this.input = input;
if (input.isEmpty()) {
throw new JsonParseException("No ingredients for shapeless recipe");
}
if (input.size() > 9) {
throw new JsonParseException("Too many ingredients for shapeless recipe");
}
}
@Override
public boolean matches(CraftingInventory inv, World world) {
return Utils.matchShapeless(input, inv, 1);
}
@Override
public ItemStack craft(CraftingInventory inv) {
return getOutput().copy();
}
@Override
public boolean fits(int width, int height) {
return width * height >= input.size();
}
@Override
public ItemStack getOutput() {
return output.getStack(Utils.RANDOM);
}
@Override
public String getGroup() {
return group;
}
@Override
public RecipeSerializer<?> getSerializer() {
return URecipes.CRAFTING_SHAPELESS;
}
@Override
public DefaultedList<net.minecraft.recipe.Ingredient> getPreviewInputs() {
return Ingredient.preview(input);
}
@Override
public boolean isIgnoredInRecipeBook() {
return false;
}
public static class Serializer implements RecipeSerializer<ShapelessSpecialRecipe> {
@Override
public ShapelessSpecialRecipe read(Identifier identifier, JsonObject jsonObject) {
return new ShapelessSpecialRecipe(identifier,
JsonHelper.getString(jsonObject, "group", ""),
Ingredient.one(jsonObject.get("output")),
Ingredient.many(JsonHelper.getArray(jsonObject, "ingredients"))
);
}
@Override
public ShapelessSpecialRecipe read(Identifier identifier, PacketByteBuf buf) {
return new ShapelessSpecialRecipe(identifier,
buf.readString(32767),
Ingredient.read(buf),
Utils.read(buf, Ingredient.EMPTY, Ingredient::read));
}
@Override
public void write(PacketByteBuf buf, ShapelessSpecialRecipe recipe) {
buf.writeString(recipe.group);
recipe.output.write(buf);
buf.writeVarInt(recipe.input.size());
Utils.write(buf, recipe.input, Ingredient::write);
}
}
}

View file

@ -1,52 +1,80 @@
package com.minelittlepony.unicopia.recipe; package com.minelittlepony.unicopia.recipe;
import java.util.Random;
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 net.minecraft.inventory.CraftingInventory; import net.minecraft.inventory.CraftingInventory;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
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;
import net.minecraft.util.DefaultedList; import net.minecraft.util.DefaultedList;
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.world.World; import net.minecraft.world.World;
/** /**
* Spellbook recipe accepting an item to enchant, a number of ingredients to use, an ingredient to compose the output. * Spellbook recipe accepting an item to enchant, a number of ingredients to use, an ingredient to compose the output.
*/ */
public class SpellBookRecipe extends AbstractShapelessPredicatedRecipe<CraftingInventory> { public class SpellBookRecipe implements Recipe<CraftingInventory> {
private static final Random RANDOM = new Random(); private final Ingredient input;
protected final Ingredient input; private final Ingredient output;
private final DefaultedList<Ingredient> ingredients;
private final Identifier id;
public SpellBookRecipe(Identifier id, Ingredient input, Ingredient output, DefaultedList<Ingredient> ingredients) { public SpellBookRecipe(Identifier id, Ingredient input, Ingredient output, DefaultedList<Ingredient> ingredients) {
super(id, output, ingredients); this.id = id;
this.output = output;
this.ingredients = ingredients;
this.input = input; this.input = input;
} }
@Override @Override
protected int getInputMultiplier(CraftingInventory inv, World worldIn) { public boolean matches(CraftingInventory inv, World world) {
ItemStack enchantedStack = ((SpellBookInventory)inv).getCraftResultMatrix().getInvStack(0); ItemStack enchantedStack = ((SpellBookInventory)inv).getCraftResultMatrix().getInvStack(0);
if (enchantedStack.isEmpty() || enchantedStack.getItem() == null) { if (enchantedStack.isEmpty() || enchantedStack.getItem() == null) {
return 0; return false;
} }
if (!input.matches(enchantedStack, enchantedStack.getCount())) { if (!input.matches(enchantedStack, enchantedStack.getCount())) {
return 0; return false;
} }
return enchantedStack.getCount(); return Utils.matchShapeless(ingredients, inv, enchantedStack.getCount());
}
@Override
public Identifier getId() {
return id;
}
@Override
public ItemStack craft(CraftingInventory inv) {
return getOutput();
}
@Override
public boolean fits(int width, int height) {
return width * height < ingredients.size();
}
@Override
public DefaultedList<net.minecraft.recipe.Ingredient> getPreviewInputs() {
return Ingredient.preview(ingredients, DefaultedList.copyOf(null, input.getPreview()));
} }
@Override @Override
public ItemStack getOutput() { public ItemStack getOutput() {
return output.getStack(RANDOM); return output.getStack(Utils.RANDOM);
} }
@Override @Override
@ -70,30 +98,24 @@ public class SpellBookRecipe extends AbstractShapelessPredicatedRecipe<CraftingI
return new SpellBookRecipe(id, return new SpellBookRecipe(id,
Ingredient.one(json.get("input")), Ingredient.one(json.get("input")),
Ingredient.one(json.get("result")), Ingredient.one(json.get("result")),
Ingredient.many(json.get("ingredients").getAsJsonArray()) Ingredient.many(JsonHelper.getArray(json, "ingredients"))
); );
} }
@Override @Override
public SpellBookRecipe read(Identifier id, PacketByteBuf buf) { public SpellBookRecipe read(Identifier id, PacketByteBuf buf) {
Ingredient input = Ingredient.read(buf); return new SpellBookRecipe(id,
Ingredient output = Ingredient.read(buf); Ingredient.read(buf),
int length = buf.readInt(); Ingredient.read(buf),
DefaultedList<Ingredient> ingredients = DefaultedList.copyOf(Ingredient.EMPTY); Utils.read(buf, Ingredient.EMPTY, Ingredient::read)
while (ingredients.size() < length) { );
ingredients.add(Ingredient.read(buf));
}
return new SpellBookRecipe(id, input, output, ingredients);
} }
@Override @Override
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);
DefaultedList<Ingredient> ingredients = recipe.ingredients; Utils.write(buf, recipe.ingredients, Ingredient::write);
buf.writeInt(ingredients.size());
ingredients.forEach(i -> i.write(buf));
} }
} }
} }

View file

@ -10,7 +10,8 @@ public interface URecipes {
RecipeType<SpellBookRecipe> SPELL_BOOK = register("spell_book"); RecipeType<SpellBookRecipe> SPELL_BOOK = register("spell_book");
RecipeSerializer<SpellBookRecipe> ENCHANTING_SPELL_SERIALIZER = register("enchanting_spell", new SpellBookRecipe.Serializer()); RecipeSerializer<SpellBookRecipe> ENCHANTING_SPELL_SERIALIZER = register("enchanting_spell", new SpellBookRecipe.Serializer());
RecipeSerializer<SpellBookRecipe> CRAFTING_SPELL_SERIALIZER = register("crafting_spell", new SpellBookRecipe.Serializer()); RecipeSerializer<ShapelessSpecialRecipe> CRAFTING_SHAPELESS = register("crafting_shapeless", new ShapelessSpecialRecipe.Serializer());
RecipeSerializer<ShapedSpecialRecipe> CRAFTING_SHAPED = register("crafting_shaped", new ShapedSpecialRecipe.Serializer());
static <T extends Recipe<?>> RecipeType<T> register(final String id) { static <T extends Recipe<?>> RecipeType<T> register(final String id) {
return Registry.register(Registry.RECIPE_TYPE, new Identifier("unicopia", id), new RecipeType<T>() { return Registry.register(Registry.RECIPE_TYPE, new Identifier("unicopia", id), new RecipeType<T>() {

View file

@ -0,0 +1,62 @@
package com.minelittlepony.unicopia.recipe;
import java.util.List;
import java.util.Random;
import java.util.function.BiConsumer;
import java.util.function.Function;
import com.google.common.collect.Lists;
import com.minelittlepony.unicopia.recipe.ingredient.Ingredient;
import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack;
import net.minecraft.util.DefaultedList;
import net.minecraft.util.PacketByteBuf;
public class Utils {
static final Random RANDOM = new Random();
static <T> DefaultedList<T> read(PacketByteBuf buf, T def, Function<PacketByteBuf, T> reader) {
DefaultedList<T> list = DefaultedList.ofSize(buf.readInt(), def);
for (int i = 0; i < list.size(); i++) {
list.set(i, reader.apply(buf));
}
return list;
}
static <T> void write(PacketByteBuf buf, DefaultedList<T> list, BiConsumer<T, PacketByteBuf> writer) {
buf.writeInt(list.size());
list.forEach(i -> writer.accept(i, buf));
}
public static <C extends Inventory> boolean matchShapeless(DefaultedList<Ingredient> ingredients, C inv, int mult) {
if (mult == 0) {
return false;
}
List<Ingredient> toMatch = Lists.newArrayList(ingredients);
for (int i = 0; i < inv.getInvSize(); i++) {
ItemStack stack = inv.getInvStack(i);
if (!stack.isEmpty()) {
if (toMatch.isEmpty() || !removeMatch(toMatch, stack, mult)) {
return false;
}
}
}
return toMatch.isEmpty();
}
private static boolean removeMatch(List<Ingredient> toMatch, ItemStack stack, int materialMult) {
return toMatch.stream()
.filter(s -> s.matches(stack, materialMult))
.findFirst()
.filter(toMatch::remove)
.isPresent();
}
}

View file

@ -1,4 +1,4 @@
package com.minelittlepony.unicopia.recipe; package com.minelittlepony.unicopia.recipe.ingredient;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
@ -8,8 +8,11 @@ import com.google.gson.JsonArray;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.PacketByteBuf; import net.minecraft.util.PacketByteBuf;
/**
* Requires only one of the sub-ingredients to match when matching.
* Makes a random choice from a pool of alternatives when crafting.
*/
class ChoicePredicate implements Ingredient.Predicate { class ChoicePredicate implements Ingredient.Predicate {
static Ingredient.Predicate read(JsonArray arr) { static Ingredient.Predicate read(JsonArray arr) {
return new ChoicePredicate(Ingredient.many(arr)); return new ChoicePredicate(Ingredient.many(arr));
} }

View file

@ -0,0 +1,84 @@
package com.minelittlepony.unicopia.recipe.ingredient;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.function.BiPredicate;
import java.util.stream.Stream;
import com.google.gson.JsonObject;
import net.minecraft.item.ItemStack;
import net.minecraft.util.JsonHelper;
import net.minecraft.util.Lazy;
import net.minecraft.util.PacketByteBuf;
import net.minecraft.util.Util;
/**
* Tests for whether a tag contains the input when matching.
* Supplies a random item from the tag as the output when crafting.
*/
class DamagePredicate implements Ingredient.Predicate {
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);
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 int damage;
private final Lazy<BiPredicate<Integer, Integer>> operation;
DamagePredicate(int damage, String op) {
this.op = op.trim();
this.damage = damage;
this.operation = new Lazy<>(() -> OPERATIONS.get(op));
}
@Override
public ItemStack applyModifiers(ItemStack output, Random random) {
output.setDamage(damage);
return output;
}
@Override
public Stream<ItemStack> getMatchingStacks() {
return Stream.empty();
}
@Override
public boolean matches(ItemStack other, int materialMult) {
return !other.isEmpty() && operation.get().test(other.getDamage(), damage);
}
@Override
public void write(PacketByteBuf buf) {
buf.writeInt(1);
buf.writeInt(damage);
buf.writeString(op);
}
}

View file

@ -1,4 +1,4 @@
package com.minelittlepony.unicopia.recipe; package com.minelittlepony.unicopia.recipe.ingredient;
import java.util.Random; import java.util.Random;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -15,10 +15,10 @@ import net.minecraft.util.PacketByteBuf;
import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.Registry;
/** /**
* A predicate that tests for a specific enchantment on an input item. * A predicate that tests for a specific enchantment on an input when matching.
* Appends that enchantment to the output when crafting.
*/ */
class EnchantmentPredicate implements Ingredient.Predicate { class EnchantmentPredicate implements Ingredient.Predicate {
public static Ingredient.Predicate read(PacketByteBuf buf) { public static Ingredient.Predicate read(PacketByteBuf buf) {
int level = buf.readInt(); int level = buf.readInt();
if (level == 0) { if (level == 0) {

View file

@ -1,4 +1,4 @@
package com.minelittlepony.unicopia.recipe; package com.minelittlepony.unicopia.recipe.ingredient;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
@ -18,7 +18,7 @@ import net.minecraft.util.Lazy;
import net.minecraft.util.PacketByteBuf; import net.minecraft.util.PacketByteBuf;
@Immutable @Immutable
class Ingredient { public class Ingredient {
public static final Ingredient EMPTY = new Ingredient(Predicate.EMPTY, Predicate.EMPTY, Predicate.EMPTY, Predicate.EMPTY); public static final Ingredient EMPTY = new Ingredient(Predicate.EMPTY, Predicate.EMPTY, Predicate.EMPTY, Predicate.EMPTY);
private final Predicate stack; private final Predicate stack;
@ -27,6 +27,7 @@ class Ingredient {
private final Predicate enchantment; private final Predicate enchantment;
private final Lazy<List<ItemStack>> matchingStacks; private final Lazy<List<ItemStack>> matchingStacks;
private final Lazy<net.minecraft.recipe.Ingredient> preview;
Ingredient(Predicate stack, Predicate tag, Predicate spell, Predicate enchantment) { Ingredient(Predicate stack, Predicate tag, Predicate spell, Predicate enchantment) {
this.stack = stack; this.stack = stack;
@ -42,12 +43,19 @@ class Ingredient {
).filter(s -> matches(s, 1)) ).filter(s -> matches(s, 1))
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
this.preview = new Lazy<>(() -> {
return net.minecraft.recipe.Ingredient.ofStacks(getMatchingStacks().toArray(ItemStack[]::new));
});
} }
public Stream<ItemStack> getMatchingStacks() { public Stream<ItemStack> getMatchingStacks() {
return matchingStacks.get().stream(); return matchingStacks.get().stream();
} }
public net.minecraft.recipe.Ingredient getPreview() {
return preview.get();
}
public ItemStack getStack(Random random) { public ItemStack getStack(Random random) {
ItemStack output = ItemStack.EMPTY.copy(); ItemStack output = ItemStack.EMPTY.copy();
stack.applyModifiers(output, random); stack.applyModifiers(output, random);
@ -64,7 +72,6 @@ class Ingredient {
&& enchantment.matches(other, materialMult); && enchantment.matches(other, materialMult);
} }
public void write(PacketByteBuf buf) { public void write(PacketByteBuf buf) {
stack.write(buf); stack.write(buf);
tag.write(buf); tag.write(buf);
@ -110,6 +117,15 @@ class Ingredient {
return 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 { public interface Predicate {
Predicate EMPTY = new Predicate() { Predicate EMPTY = new Predicate() {
@Override @Override

View file

@ -1,4 +1,4 @@
package com.minelittlepony.unicopia.recipe; package com.minelittlepony.unicopia.recipe.ingredient;
import java.util.Random; import java.util.Random;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -9,6 +9,10 @@ import com.minelittlepony.unicopia.magic.spell.SpellRegistry;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.PacketByteBuf; import net.minecraft.util.PacketByteBuf;
/**
* A predicate that tests for a specific spell on an input when matching.
* Appends that spell to the output when crafting.
*/
class SpellPredicate implements Ingredient.Predicate { class SpellPredicate implements Ingredient.Predicate {
static Ingredient.Predicate read(PacketByteBuf buf) { static Ingredient.Predicate read(PacketByteBuf buf) {
int level = buf.readInt(); int level = buf.readInt();

View file

@ -1,10 +1,8 @@
package com.minelittlepony.unicopia.recipe; package com.minelittlepony.unicopia.recipe.ingredient;
import java.util.Optional;
import java.util.Random; import java.util.Random;
import java.util.stream.Stream; import java.util.stream.Stream;
import com.google.common.collect.Streams;
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;
@ -17,6 +15,10 @@ import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf; import net.minecraft.util.PacketByteBuf;
import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.Registry;
/**
* Tests for a specific item, stack size, and damage value when matching.
* Presents that item as the output when crafting.
*/
class StackPredicate implements Ingredient.Predicate { class StackPredicate implements Ingredient.Predicate {
static Ingredient.Predicate read(PacketByteBuf buf) { static Ingredient.Predicate read(PacketByteBuf buf) {
int count = buf.readInt(); int count = buf.readInt();
@ -81,7 +83,7 @@ class StackPredicate implements Ingredient.Predicate {
return subItems.stream(); return subItems.stream();
} }
return Streams.stream(Optional.ofNullable(stack)); return Stream.of(stack);
} }
@Override @Override

View file

@ -1,4 +1,4 @@
package com.minelittlepony.unicopia.recipe; package com.minelittlepony.unicopia.recipe.ingredient;
import java.util.Random; import java.util.Random;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -13,6 +13,10 @@ import net.minecraft.tag.Tag;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf; import net.minecraft.util.PacketByteBuf;
/**
* Tests for whether a tag contains the input when matching.
* Supplies a random item from the tag as the output when crafting.
*/
class TagPredicate implements Ingredient.Predicate { class TagPredicate implements Ingredient.Predicate {
static Ingredient.Predicate read(PacketByteBuf buf) { static Ingredient.Predicate read(PacketByteBuf buf) {
int count = buf.readInt(); int count = buf.readInt();