Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add recipe support #5204

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/java/ch/njol/skript/lang/TriggerSection.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ protected void setTriggerItems(List<TriggerItem> items) {
}
}
}

@Override
public TriggerSection setNext(@Nullable TriggerItem next) {
super.setNext(next);
Expand Down
192 changes: 192 additions & 0 deletions src/main/java/ch/njol/skript/sections/recipe/SecCookingRecipe.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/**
* This file is part of Skript.
*
* Skript is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Skript is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Skript. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright Peter Güttinger, SkriptLang team and contributors
*/
package ch.njol.skript.sections.recipe;

import ch.njol.skript.Skript;
import ch.njol.skript.aliases.ItemType;
import ch.njol.skript.config.EntryNode;
import ch.njol.skript.config.Node;
import ch.njol.skript.config.SectionNode;
import ch.njol.skript.config.validate.SectionValidator;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Examples;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.*;
Pikachu920 marked this conversation as resolved.
Show resolved Hide resolved
import ch.njol.skript.log.RetainingLogHandler;
import ch.njol.skript.log.SkriptLogger;
import ch.njol.skript.patterns.PatternCompiler;
import ch.njol.skript.patterns.SkriptPattern;
import ch.njol.skript.util.Timespan;
import ch.njol.skript.util.Utils;
import ch.njol.util.Kleenean;
import com.destroystokyo.paper.Namespaced;
import com.google.common.collect.Iterables;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.event.Event;
import org.bukkit.inventory.BlastingRecipe;
import org.bukkit.inventory.CampfireRecipe;
import org.bukkit.inventory.CookingRecipe;
import org.bukkit.inventory.FurnaceRecipe;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.RecipeChoice;
import org.bukkit.inventory.ShapedRecipe;
import org.bukkit.inventory.ShapelessRecipe;
import org.bukkit.inventory.SmokingRecipe;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

@Name("Cooking Recipe")
@Description("Creates a cooking recipe")
@Examples({
"create a crafting recipe for diamond named \"Dirty Diamond\" with the key \"dirty-diamond\":",
Pikachu920 marked this conversation as resolved.
Show resolved Hide resolved
"\tshape:",
"\t\tdirt, dirt and dirt",
"\t\tdirt, diamond and dirt",
"\t\tdirt, dirt and dirt"
})
@Since("INSERT VERSION")
public class SecCookingRecipe extends Section {

static {
Skript.registerSection(SecCookingRecipe.class, "(create|add|register) [a] (0:blast furnace|1:campfire|2:furnace|3:smoker) recipe for %itemtype% with [the] key %string%");
}

private static SectionValidator validator = new SectionValidator()
.addEntry("ingredient", false)
.addEntry("cook time", false)
.addEntry("group", true)
.addEntry("xp", false);
Pikachu920 marked this conversation as resolved.
Show resolved Hide resolved


enum CookingRecipeType {
BLAST_FURNACE, CAMPFIRE, FURNACE, SMOKER
}

private Expression<String> key;
private CookingRecipeType type;
private Expression<? extends ItemType> ingredient;
private Expression<? extends Timespan> cookTime;
private Expression<? extends Number> xp;
private Expression<ItemType> result;

@SuppressWarnings("unchecked")
@Override
Pikachu920 marked this conversation as resolved.
Show resolved Hide resolved
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult, SectionNode sectionNode, List<TriggerItem> triggerItems) {
Pikachu920 marked this conversation as resolved.
Show resolved Hide resolved
if (!validator.validate(sectionNode))
return false;
try {
ingredient = parse((EntryNode) sectionNode.get("ingredient"), ItemType.class);
cookTime = parse((EntryNode) sectionNode.get("cook time"), Timespan.class);
xp = parse((EntryNode) sectionNode.get("xp"), Number.class);
} catch (IllegalStateException ignored) {
return false;
}
type = CookingRecipeType.values()[parseResult.mark];
result = (Expression<ItemType>) exprs[0];
key = (Expression<String>) exprs[1];
return true;
}

@Override
public String toString(Event event, boolean debug) {
return "create crafting recipe for " + result.toString(event, debug) + " with key " + key.toString(event, debug);
}

@Override
protected TriggerItem walk(Event event) {
execute(event);
return walk(event, false);
Pikachu920 marked this conversation as resolved.
Show resolved Hide resolved
}

private void execute(Event event) {
String key = this.key.getSingle(event);
if (key == null)
return;
ItemType result = this.result.getSingle(event);
if (result == null)
return;
ItemType ingredient = this.ingredient.getSingle(event);
if (ingredient == null)
return;
Number xp = this.xp.getSingle(event);
if (xp == null)
return;
Timespan cookTime = this.cookTime.getSingle(event);
if (cookTime == null)
return;

Set<Material> ingredientMaterials = new HashSet<>();
for (ItemStack itemStack : ingredient.getAll()) {
ingredientMaterials.add(itemStack.getType());
}

// the recipe APIs require an int :(
int cookTimeTicks = (int) cookTime.getTicks_i();
NamespacedKey namespacedKey = Utils.getNamespacedKey(key);
RecipeChoice choice = new RecipeChoice.MaterialChoice(ingredientMaterials.toArray(new Material[0]));
CookingRecipe recipe;
switch (type) {
case SMOKER:
recipe = new SmokingRecipe(namespacedKey, result.getRandom(), choice, xp.floatValue(), cookTimeTicks);
break;
case FURNACE:
recipe = new FurnaceRecipe(namespacedKey, result.getRandom(), choice, xp.floatValue(), cookTimeTicks);
break;
case CAMPFIRE:
recipe = new CampfireRecipe(namespacedKey, result.getRandom(), choice, xp.floatValue(), cookTimeTicks);
break;
case BLAST_FURNACE:
recipe = new BlastingRecipe(namespacedKey, result.getRandom(), choice, xp.floatValue(), cookTimeTicks);
break;
default:
throw new IllegalStateException();
}

try {
Bukkit.getServer().addRecipe(recipe);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check this? it seems to return a boolean not throw an exception now that i look again

} catch (IllegalStateException ignored) {
// Bukkit throws a IllegalStateException if a duplicate recipe is registered
}
Pikachu920 marked this conversation as resolved.
Show resolved Hide resolved
}

private <T> Expression<? extends T> parse(EntryNode node, Class<T> parseAsClass) {
Node originalNode = getParser().getNode();
Pikachu920 marked this conversation as resolved.
Show resolved Hide resolved
SkriptParser parser = new SkriptParser(node.getValue(), SkriptParser.ALL_FLAGS, ParseContext.DEFAULT);
RetainingLogHandler logHandler = SkriptLogger.startRetainingLog();
getParser().setNode(node);
Expression<? extends T> expr = parser.parseExpression(parseAsClass);
if (expr == null) {
logHandler.printErrors("Can't understand this expression: " + key);
throw new IllegalArgumentException();
} else {
logHandler.printLog();
}
getParser().setNode(originalNode);
return expr;
}

}
148 changes: 148 additions & 0 deletions src/main/java/ch/njol/skript/sections/recipe/SecShapedRecipe.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/**
* This file is part of Skript.
*
* Skript is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Skript is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Skript. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright Peter Güttinger, SkriptLang team and contributors
*/
package ch.njol.skript.sections.recipe;

import ch.njol.skript.Skript;
import ch.njol.skript.aliases.ItemType;
import ch.njol.skript.config.Node;
import ch.njol.skript.config.SectionNode;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Examples;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.*;
import ch.njol.skript.log.RetainingLogHandler;
import ch.njol.skript.log.SkriptLogger;
import ch.njol.skript.util.Utils;
import ch.njol.util.Kleenean;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.event.Event;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.RecipeChoice;
import org.bukkit.inventory.ShapedRecipe;
import org.bukkit.inventory.ShapelessRecipe;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

@Name("Crafting Recipe")
@Description("Creates a shaped crafting recipe")
@Examples({
"create a crafting recipe for diamond named \"Dirty Diamond\" with the key \"dirty-diamond\":",
"\tshape:",
"\t\tdirt, dirt and dirt",
"\t\tdirt, diamond and dirt",
"\t\tdirt, dirt and dirt"
})
@Since("INSERT VERSION")
public class SecShapedRecipe extends Section {

static {
Skript.registerSection(SecShapedRecipe.class, "(create|add|register) [a] crafting recipe for %itemtype% with [the] key %string%");
}

private Expression<String> key;
private Expression<ItemType> ingredients;
private Expression<ItemType> result;

@SuppressWarnings("unchecked")
@Override
Pikachu920 marked this conversation as resolved.
Show resolved Hide resolved
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult, SectionNode sectionNode, List<TriggerItem> triggerItems) {
Pikachu920 marked this conversation as resolved.
Show resolved Hide resolved
Node shapeNode = sectionNode.get("shape");
if (!(shapeNode instanceof SectionNode) || Iterables.size((SectionNode) shapeNode) != 3) {
Skript.error("A crafting recipe section must have a 'shape' section with three ingredient lines");
return false;
}
ingredients = parseIngredients((SectionNode) shapeNode);
if (ingredients == null) {
return false;
}
result = (Expression<ItemType>) exprs[0];
key = (Expression<String>) exprs[1];
return true;
}

@Override
public String toString(Event event, boolean debug) {
return "create crafting recipe for " + result.toString(event, debug) + " with key " + key.toString(event, debug);
}

@Override
protected TriggerItem walk(Event event) {
execute(event);
return walk(event, false);
Pikachu920 marked this conversation as resolved.
Show resolved Hide resolved
}

private void execute(Event event) {
String key = this.key.getSingle(event);
if (key == null)
return;
ItemType result = this.result.getSingle(event);
if (result == null)
return;
ItemType[] ingredients = this.ingredients.getArray(event);
if (ingredients.length != 9)
return;
ShapedRecipe recipe = new ShapedRecipe(Utils.getNamespacedKey(key), result.getRandom());
recipe.shape("abc", "def", "ghi");
for (char c = 'a'; c < 'j'; c++) {
ItemStack[] allChoices = Iterables.toArray(ingredients[c - 'a'].getAll(), ItemStack.class);
RecipeChoice choice = new RecipeChoice.ExactChoice(allChoices);
recipe.setIngredient(c, choice);
}
try {
Bukkit.getServer().addRecipe(recipe);
} catch (IllegalStateException ignored) {
// Bukkit throws a IllegalStateException if a duplicate recipe is registered
}
}

private Expression<ItemType> parseIngredients(SectionNode section) {
Node originalNode = getParser().getNode();
List<Expression<? extends ItemType>> ingredients = new ArrayList<>();
for (Node node : section) {
getParser().setNode(node);
String key = node.getKey();
if (key == null) {
getParser().setNode(originalNode);
return null;
}

SkriptParser parser = new SkriptParser(key, SkriptParser.ALL_FLAGS, ParseContext.DEFAULT);
RetainingLogHandler logHandler = SkriptLogger.startRetainingLog();
Expression<? extends ItemType> expr = parser.parseExpression(ItemType.class);
if (!(expr instanceof ExpressionList) || ((ExpressionList<?>) expr).getExpressions().length != 3) {
logHandler.printErrors("Can't understand this expression: " + key);
getParser().setNode(originalNode);
return null;
}
logHandler.printLog();
ingredients.add(expr);

}
getParser().setNode(originalNode);
return new ExpressionList<>(ingredients.toArray(new Expression[0]), ItemType.class, true);
}

}
Loading