cn.nukkit.inventory.ShapedRecipe Maven / Gradle / Ivy
package cn.nukkit.inventory;
import cn.nukkit.item.Item;
import io.netty.util.collection.CharObjectHashMap;
import java.util.*;
import static cn.nukkit.inventory.Recipe.matchItemList;
/**
* @author MagicDroidX (Nukkit Project)
*/
public class ShapedRecipe implements CraftingRecipe {
private String recipeId;
private final Item primaryResult;
private final List- extraResults = new ArrayList<>();
private final List
- ingredientsAggregate;
private long least,most;
private final String[] shape;
private final int priority;
private final CharObjectHashMap
- ingredients = new CharObjectHashMap<>();
public ShapedRecipe(Item primaryResult, String[] shape, Map
ingredients, List- extraResults) {
this(null, 1, primaryResult, shape, ingredients, extraResults);
}
/**
* Constructs a ShapedRecipe instance.
*
* @param primaryResult Primary result of the recipe
* @param shape
Array of 1, 2, or 3 strings representing the rows of the recipe.
* This accepts an array of 1, 2 or 3 strings. Each string should be of the same length and must be at most 3
* characters long. Each character represents a unique type of ingredient. Spaces are interpreted as air.
* @param ingredients
Char => Item map of items to be set into the shape.
* This accepts an array of Items, indexed by character. Every unique character (except space) in the shape
* array MUST have a corresponding item in this list. Space character is automatically treated as air.
* @param extraResults
List of additional result items to leave in the crafting grid afterwards. Used for things like cake recipe
* empty buckets.
*
* Note: Recipes **do not** need to be square. Do NOT add padding for empty rows/columns.
*/
public ShapedRecipe(String recipeId, int priority, Item primaryResult, String[] shape, Map ingredients, List- extraResults) {
this.recipeId = recipeId;
this.priority = priority;
int rowCount = shape.length;
if (rowCount > 3 || rowCount <= 0) {
throw new RuntimeException("Shaped recipes may only have 1, 2 or 3 rows, not " + rowCount);
}
int columnCount = shape[0].length();
if (columnCount > 3 || columnCount <= 0) {
throw new RuntimeException("Shaped recipes may only have 1, 2 or 3 columns, not " + columnCount);
}
for (int i = 0, shapeLength = shape.length; i < shapeLength; i++) {
String row = shape[i];
if (row.length() != columnCount) {
throw new RuntimeException("Shaped recipe rows must all have the same length (expected " + columnCount + ", got " + row.length() + ")");
}
for (int x = 0; x < columnCount; ++x) {
char c = row.charAt(x);
if (c != ' ' && !ingredients.containsKey(c)) {
throw new RuntimeException("No item specified for symbol '" + c + "'");
}
}
shape[i] = row.intern();
}
this.primaryResult = primaryResult.clone();
this.extraResults.addAll(extraResults);
this.shape = shape;
for (Map.Entry
entry : ingredients.entrySet()) {
this.setIngredient(entry.getKey(), entry.getValue());
}
this.ingredientsAggregate = new ArrayList<>();
for (char c : String.join("", this.shape).toCharArray()) {
if (c == ' ')
continue;
Item ingredient = this.ingredients.get(c).clone();
for (Item existingIngredient : this.ingredientsAggregate) {
if (existingIngredient.equals(ingredient, ingredient.hasMeta(), ingredient.hasCompoundTag())) {
existingIngredient.setCount(existingIngredient.getCount() + ingredient.getCount());
ingredient = null;
break;
}
}
if (ingredient != null)
this.ingredientsAggregate.add(ingredient);
}
this.ingredientsAggregate.sort(CraftingManager.recipeComparator);
}
public int getWidth() {
return this.shape[0].length();
}
public int getHeight() {
return this.shape.length;
}
@Override
public Item getResult() {
return this.primaryResult;
}
@Override
public String getRecipeId() {
return this.recipeId;
}
@Override
public UUID getId() {
return new UUID(least, most);
}
@Override
public void setId(UUID uuid) {
this.least = uuid.getLeastSignificantBits();
this.most = uuid.getMostSignificantBits();
if (this.recipeId == null) {
this.recipeId = getId().toString();
}
}
public ShapedRecipe setIngredient(String key, Item item) {
return this.setIngredient(key.charAt(0), item);
}
public ShapedRecipe setIngredient(char key, Item item) {
if (String.join("", this.shape).indexOf(key) < 0) {
throw new RuntimeException("Symbol does not appear in the shape: " + key);
}
this.ingredients.put(key, item);
return this;
}
public List- getIngredientList() {
List
- items = new ArrayList<>();
for (int y = 0, y2 = getHeight(); y < y2; ++y) {
for (int x = 0, x2 = getWidth(); x < x2; ++x) {
items.add(getIngredient(x, y));
}
}
return items;
}
public Map
> getIngredientMap() {
Map> ingredients = new LinkedHashMap<>();
for (int y = 0, y2 = getHeight(); y < y2; ++y) {
Map m = new LinkedHashMap<>();
for (int x = 0, x2 = getWidth(); x < x2; ++x) {
m.put(x, getIngredient(x, y));
}
ingredients.put(y, m);
}
return ingredients;
}
public Item getIngredient(int x, int y) {
Item item = this.ingredients.get(this.shape[y].charAt(x));
return item != null ? item.clone() : Item.get(Item.AIR);
}
public String[] getShape() {
return shape;
}
@Override
public void registerToCraftingManager(CraftingManager manager) {
manager.registerShapedRecipe(this);
}
@Override
public RecipeType getType() {
return RecipeType.SHAPED;
}
@Override
public List- getExtraResults() {
return extraResults;
}
@Override
public List
- getAllResults() {
List
- list = new ArrayList<>();
list.add(primaryResult);
list.addAll(extraResults);
return list;
}
@Override
public int getPriority() {
return this.priority;
}
@Override
public boolean matchItems(List
- inputList, List
- extraOutputList, int multiplier) {
List
- haveInputs = new ArrayList<>();
for (Item item : inputList) {
if (item.isNull())
continue;
haveInputs.add(item.clone());
}
List
- needInputs = new ArrayList<>();
if(multiplier != 1){
for (Item item : ingredientsAggregate) {
if (item.isNull())
continue;
Item itemClone = item.clone();
itemClone.setCount(itemClone.getCount() * multiplier);
needInputs.add(itemClone);
}
} else {
for (Item item : ingredientsAggregate) {
if (item.isNull())
continue;
needInputs.add(item.clone());
}
}
if (!matchItemList(haveInputs, needInputs)) {
return false;
}
List
- haveOutputs = new ArrayList<>();
for (Item item : extraOutputList) {
if (item.isNull())
continue;
haveOutputs.add(item.clone());
}
haveOutputs.sort(CraftingManager.recipeComparator);
List
- needOutputs = new ArrayList<>();
if(multiplier != 1){
for (Item item : getExtraResults()) {
if (item.isNull())
continue;
Item itemClone = item.clone();
itemClone.setCount(itemClone.getCount() * multiplier);
needOutputs.add(itemClone);
}
} else {
for (Item item : getExtraResults()) {
if (item.isNull())
continue;
needOutputs.add(item.clone());
}
}
needOutputs.sort(CraftingManager.recipeComparator);
return matchItemList(haveOutputs, needOutputs);
}
/**
* Returns whether the specified list of crafting grid inputs and outputs matches this recipe. Outputs DO NOT
* include the primary result item.
*
* @param inputList list of items taken from the crafting grid
* @param extraOutputList list of items put back into the crafting grid (secondary results)
* @return bool
*/
@Override
public boolean matchItems(List
- inputList, List
- extraOutputList) {
return matchItems(inputList, extraOutputList, 1);
}
@Override
public String toString() {
StringJoiner joiner = new StringJoiner(", ");
ingredients.forEach((character, item) -> joiner.add(item.getName() + ":" + item.getDamage()));
return joiner.toString();
}
@Override
public boolean requiresCraftingTable() {
return this.getHeight() > 2 || this.getWidth() > 2;
}
@Override
public List
- getIngredientsAggregate() {
return ingredientsAggregate;
}
public static class Entry {
public final int x;
public final int y;
public Entry(int x, int y) {
this.x = x;
this.y = y;
}
}
}