added recipe_data() and crafting_remaining_item() - for convenient crafting scripts.
This commit is contained in:
1 parent
690c85f3b0
commit
d098087e05
14 files changed
+270
-1
No files matched your search
@@ -11,6 +11,7 @@ version = project.minecraft_version+'-'+project.mod_version
|
||||
group = project.maven_group
|
||||
|
||||
minecraft {
|
||||
accessWidener "src/main/resources/carpet.accesswidener"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -2940,6 +2940,50 @@ item_category('ender_pearl') => misc
|
||||
item_category('stone') => building_blocks
|
||||
</pre>
|
||||
|
||||
### `recipe_data(item, type?)`, `recipe_data(recipe, type?)`
|
||||
|
||||
returns all recipes matching either an `item`, or represent actual `recipe` name. In vanilla datapack, for all items
|
||||
that have one recipe available, the recipe name is the same as the item name but if an item has multiple recipes, its
|
||||
direct name can be different.
|
||||
|
||||
Recipe type can take one of the following options:
|
||||
* `'crafting'` - default, crafting table recipe
|
||||
* `'smelting'` - furnace recipe
|
||||
* `'blasting'` - blast furnace recipe
|
||||
* `'smoking'` - smoker recipe
|
||||
* `'campfire_cooking'` - campfire recipe
|
||||
* `'stonecutting'` - stonecutter recipe
|
||||
|
||||
The return value is a list of available recipes (even if there is only one recipe available). Each recipe contains of
|
||||
an item triple of the crafting result, list of ingredients, each containing a list of possible variants of the
|
||||
ingredients in this slot, as item triples, or `null` if its a shaped recipe and a given slot in the patterns is left
|
||||
empty, and recipe specification as another list. Possible recipe specs is:
|
||||
* `['shaped', width, height]` - shaped crafting. `width` and `height` can be 1, 2 or 3.
|
||||
* `['shapeless']` - shapeless crafting
|
||||
* `['smelting', duration, xp]` - smelting/cooking recipes
|
||||
* `['cutting']` - stonecutter recipe
|
||||
* `['special']` - special crafting recipe, typically not present in the crafting menu
|
||||
* `['custom']` - other recipe types
|
||||
|
||||
Note that ingredients are specified as tripes, with count and nbt information. Currently all recipes require always one
|
||||
of the ingredients, and for some recipes, even if the nbt data for the ingredient is specified (e.g. `dispenser`), it
|
||||
can accept items of any tags.
|
||||
|
||||
Also note that some recipes leave some products in the crafting window, and these can be determined using
|
||||
`crafting_remaining_item()` function
|
||||
|
||||
Examples:
|
||||
<pre>
|
||||
recipe_data('iron_ingot_from_nuggets')
|
||||
recipe_data('iron_ingot')
|
||||
recipe_data('glass', 'smelting')
|
||||
</pre>
|
||||
|
||||
### `crafting_remaining_item(item)`
|
||||
|
||||
returns `null` if the item has no remaining item in the crafting window when used as a crafting ingredient, or an
|
||||
item name that serves as a replacement after crafting is done. Currently it can only be buckets and glass bottles.
|
||||
|
||||
### `inventory_size(inventory)`
|
||||
|
||||
Returns the size of the inventory for the entity or block in question. Returns null if the block or entity don't
|
||||
@@ -3055,6 +3099,8 @@ __on_player_right_clicks_block(player, item_tuple, hand, block, face, hitvec) /
|
||||
__on_player_interacts_with_block(player, hand, block, face, hitvec) //right click on a block resulted in activation of said block
|
||||
__on_player_places_block(player, item_tuple, hand, block) // player have just placed the block.
|
||||
__on_player_interacts_with_entity(player, entity, hand)
|
||||
__on_player_chooses_recipe(player, recipe, full_stack)
|
||||
__on_player_switches_slot(player, from, to)
|
||||
__on_player_attacks_entity(player, entity)
|
||||
__on_player_takes_damage(player, amount, source, source_entity)
|
||||
__on_player_deals_damage(player, amount, entity)
|
||||
|
||||
@@ -32,6 +32,8 @@ __on_player_right_clicks_block(player, item_tuple, hand, block, face, hitvec) /
|
||||
__on_player_interacts_with_block(player, hand, block, face, hitvec) //right click on a block resulted in activation of said block
|
||||
__on_player_places_block(player, item_tuple, hand, block) // player have just placed the block.
|
||||
__on_player_interacts_with_entity(player, entity, hand)
|
||||
__on_player_chooses_recipe(player, recipe, full_stack)
|
||||
__on_player_switches_slot(player, from, to)
|
||||
__on_player_attacks_entity(player, entity)
|
||||
__on_player_takes_damage(player, amount, source, source_entity)
|
||||
__on_player_deals_damage(player, amount, entity)
|
||||
|
||||
@@ -12,6 +12,7 @@ blocks_daylight
|
||||
blocks_movement
|
||||
brightness
|
||||
chunk_tickets
|
||||
crafting_remaining_item
|
||||
create_marker
|
||||
current_dimension
|
||||
day_time
|
||||
@@ -67,6 +68,7 @@ power
|
||||
property
|
||||
query
|
||||
random_tick
|
||||
recipe_data
|
||||
rect
|
||||
reload_chunk
|
||||
remove_all_markers
|
||||
|
||||
@@ -6,6 +6,7 @@ __on_player_attacks_entity
|
||||
__on_player_breaks_block
|
||||
__on_player_clicks_block
|
||||
__on_player_changes_dimension
|
||||
__on_player_chooses_recipe
|
||||
__on_player_deploys_elytra
|
||||
__on_player_drops_item
|
||||
__on_player_drops_stack
|
||||
@@ -21,6 +22,7 @@ __on_player_starts_sneaking
|
||||
__on_player_starts_sprinting
|
||||
__on_player_stops_sneaking
|
||||
__on_player_stops_sprinting
|
||||
__on_player_switches_slot
|
||||
__on_player_uses_item
|
||||
__on_player_wakes_up
|
||||
__on_statistic
|
||||
|
||||
@@ -62,7 +62,9 @@
|
||||
|
||||
player spawn entity_id entity_list entity_area entity_selector query modify entity_event
|
||||
|
||||
stack_limit item_category inventory_size inventory_has_items inventory_get inventory_set inventory_find inventory_remove drop_item
|
||||
stack_limit item_category recipe_data crafting_remaining_item
|
||||
|
||||
inventory_size inventory_has_items inventory_get inventory_set inventory_find inventory_remove drop_item
|
||||
|
||||
scan volume neighbours rect diamond
|
||||
|
||||
@@ -87,6 +89,7 @@
|
||||
__on_player_breaks_block
|
||||
__on_player_clicks_block
|
||||
__on_player_changes_dimension
|
||||
__on_player_chooses_recipe
|
||||
__on_player_deploys_elytra
|
||||
__on_player_drops_item
|
||||
__on_player_drops_stack
|
||||
@@ -102,6 +105,7 @@
|
||||
__on_player_starts_sprinting
|
||||
__on_player_stops_sneaking
|
||||
__on_player_stops_sprinting
|
||||
__on_player_switches_slot
|
||||
__on_player_uses_item
|
||||
__on_player_wakes_up
|
||||
__on_statistic
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package carpet.fakes;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public interface IngredientInterface
|
||||
{
|
||||
List<Collection<ItemStack>> getRecipeStacks();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package carpet.fakes;
|
||||
|
||||
import net.minecraft.recipe.Recipe;
|
||||
import net.minecraft.recipe.RecipeType;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface RecipeManagerInterface
|
||||
{
|
||||
List<Recipe<?>> getAllMatching(RecipeType type, Identifier output);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.fakes.IngredientInterface;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.recipe.Ingredient;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mixin(Ingredient.class)
|
||||
public class Ingredient_scarpetMixin implements IngredientInterface
|
||||
{
|
||||
@Shadow @Final private Ingredient.Entry[] entries;
|
||||
|
||||
@Override
|
||||
public List<Collection<ItemStack>> getRecipeStacks()
|
||||
{
|
||||
List<Collection<ItemStack>> res = Arrays.stream(entries).map(Ingredient.Entry::getStacks).collect(Collectors.toList());
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.fakes.RecipeManagerInterface;
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.recipe.Recipe;
|
||||
import net.minecraft.recipe.RecipeManager;
|
||||
import net.minecraft.recipe.RecipeType;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mixin(RecipeManager.class)
|
||||
public class RecipeManager_scarpetMixin implements RecipeManagerInterface
|
||||
{
|
||||
|
||||
@Shadow private Map<RecipeType<?>, Map<Identifier, Recipe<?>>> recipes;
|
||||
|
||||
@Override
|
||||
public List<Recipe<?>> getAllMatching(RecipeType type, Identifier output)
|
||||
{
|
||||
Map<Identifier, Recipe<?>> typeRecipes = recipes.get(type);
|
||||
if (typeRecipes.containsKey(output)) return Collections.singletonList(typeRecipes.get(output));
|
||||
return Lists.newArrayList(typeRecipes.values().stream().filter(
|
||||
r -> Registry.ITEM.getId(r.getOutput().getItem()).equals(output)).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,10 @@ package carpet.script;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import carpet.fakes.ChunkTicketManagerInterface;
|
||||
import carpet.fakes.IngredientInterface;
|
||||
import carpet.fakes.MinecraftServerInterface;
|
||||
import carpet.fakes.BiomeArrayInterface;
|
||||
import carpet.fakes.RecipeManagerInterface;
|
||||
import carpet.fakes.ServerChunkManagerInterface;
|
||||
import carpet.fakes.StatTypeInterface;
|
||||
import carpet.fakes.ThreadedAnvilChunkStorageInterface;
|
||||
@@ -80,6 +82,13 @@ import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.particle.ParticleEffect;
|
||||
import net.minecraft.predicate.entity.EntityPredicates;
|
||||
import net.minecraft.recipe.AbstractCookingRecipe;
|
||||
import net.minecraft.recipe.CuttingRecipe;
|
||||
import net.minecraft.recipe.Recipe;
|
||||
import net.minecraft.recipe.RecipeType;
|
||||
import net.minecraft.recipe.ShapedRecipe;
|
||||
import net.minecraft.recipe.ShapelessRecipe;
|
||||
import net.minecraft.recipe.SpecialCraftingRecipe;
|
||||
import net.minecraft.scoreboard.Scoreboard;
|
||||
import net.minecraft.scoreboard.ScoreboardCriterion;
|
||||
import net.minecraft.scoreboard.ScoreboardObjective;
|
||||
@@ -131,6 +140,7 @@ import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
@@ -1471,6 +1481,121 @@ public class CarpetExpression
|
||||
return (_c, _t) -> res;
|
||||
});
|
||||
|
||||
this.expr.addLazyFunction("recipe_data", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
if (lv.size() < 1) throw new InternalExpressionException("'recipe_data' requires at least one argument");
|
||||
String recipeName = lv.get(0).evalValue(c).getString();
|
||||
RecipeType type = RecipeType.CRAFTING;
|
||||
if (lv.size() > 1)
|
||||
{
|
||||
String recipeType = lv.get(1).evalValue(c).getString();
|
||||
try
|
||||
{
|
||||
type = Registry.RECIPE_TYPE.get(new Identifier(recipeType));
|
||||
}
|
||||
catch (InvalidIdentifierException ignored)
|
||||
{
|
||||
throw new InternalExpressionException("Unknown crafting category: " + recipeType);
|
||||
}
|
||||
}
|
||||
List<Recipe<?>> recipes;
|
||||
try
|
||||
{
|
||||
recipes = ((RecipeManagerInterface) cc.s.getMinecraftServer().getRecipeManager()).getAllMatching(type, new Identifier(recipeName));
|
||||
}
|
||||
catch (InvalidIdentifierException ignored)
|
||||
{
|
||||
return LazyValue.NULL;
|
||||
}
|
||||
if (recipes.isEmpty())
|
||||
return LazyValue.NULL;
|
||||
List<Value> recipesOutput = new ArrayList<>();
|
||||
for (Recipe<?> recipe: recipes)
|
||||
{
|
||||
ItemStack result = recipe.getOutput();
|
||||
List<Value> ingredientValue = new ArrayList<>();
|
||||
CarpetSettings.LOG.error("input size: "+recipe.getPreviewInputs().size());
|
||||
recipe.getPreviewInputs().forEach(
|
||||
ingredient ->
|
||||
{
|
||||
// I am flattening ingredient lists per slot.
|
||||
// consider recipe_data('wooden_sword','crafting') and ('iron_nugget', 'blasting') and notice difference
|
||||
// in depths of lists.
|
||||
List<Collection<ItemStack>> stacks = ((IngredientInterface) (Object) ingredient).getRecipeStacks();
|
||||
if (stacks.isEmpty())
|
||||
{
|
||||
ingredientValue.add(Value.NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
List<Value> alternatives = new ArrayList<>();
|
||||
stacks.forEach(col -> col.stream().map(ListValue::fromItemStack).forEach(alternatives::add));
|
||||
ingredientValue.add(ListValue.wrap(alternatives));
|
||||
}
|
||||
}
|
||||
);
|
||||
Value recipeSpec;
|
||||
if (recipe instanceof ShapedRecipe)
|
||||
{
|
||||
recipeSpec = ListValue.of(
|
||||
new StringValue("shaped"),
|
||||
new NumericValue(((ShapedRecipe) recipe).getWidth()),
|
||||
new NumericValue(((ShapedRecipe) recipe).getHeight())
|
||||
);
|
||||
}
|
||||
else if (recipe instanceof ShapelessRecipe)
|
||||
{
|
||||
recipeSpec = ListValue.of(new StringValue("shapeless"));
|
||||
}
|
||||
else if (recipe instanceof AbstractCookingRecipe)
|
||||
{
|
||||
recipeSpec = ListValue.of(
|
||||
new StringValue("smelting"),
|
||||
new NumericValue(((AbstractCookingRecipe) recipe).getCookTime()),
|
||||
new NumericValue(((AbstractCookingRecipe) recipe).getExperience())
|
||||
);
|
||||
}
|
||||
else if (recipe instanceof CuttingRecipe)
|
||||
{
|
||||
recipeSpec = ListValue.of(new StringValue("cutting"));
|
||||
}
|
||||
else if (recipe instanceof SpecialCraftingRecipe)
|
||||
{
|
||||
recipeSpec = ListValue.of(new StringValue("special"));
|
||||
}
|
||||
else
|
||||
{
|
||||
recipeSpec = ListValue.of(new StringValue("custom"));
|
||||
}
|
||||
|
||||
recipesOutput.add(ListValue.of(ListValue.fromItemStack(result), ListValue.wrap(ingredientValue), recipeSpec));
|
||||
}
|
||||
Value ret = ListValue.wrap(recipesOutput);
|
||||
return (_c, _t) -> ret;
|
||||
});
|
||||
|
||||
this.expr.addLazyFunction("crafting_remaining_item", 1, (c, t, lv) ->
|
||||
{
|
||||
String itemStr = lv.get(0).evalValue(c).getString();
|
||||
Item item;
|
||||
try
|
||||
{
|
||||
Identifier id = new Identifier(itemStr);
|
||||
item = Registry.ITEM.get(id);
|
||||
if (item == Items.AIR && !id.getPath().equalsIgnoreCase("air"))
|
||||
throw new InvalidIdentifierException("boo");
|
||||
}
|
||||
catch (InvalidIdentifierException ignored)
|
||||
{
|
||||
throw new InternalExpressionException("Incorrect item: "+itemStr);
|
||||
}
|
||||
if (!item.hasRecipeRemainder()) return LazyValue.NULL;
|
||||
Value ret = new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.ITEM.getId(item.getRecipeRemainder())));
|
||||
return (_c, _t ) -> ret;
|
||||
});
|
||||
|
||||
|
||||
this.expr.addLazyFunction("inventory_size", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext) c;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
accessWidener v1 named
|
||||
Accessible class net/minecraft/recipe/Ingredient$Entry
|
||||
@@ -157,6 +157,9 @@
|
||||
"ServerWorld_scarpetEventMixin",
|
||||
"HungerManagerMixin_scarpetEventsMixin",
|
||||
|
||||
"RecipeManager_scarpetMixin",
|
||||
"Ingredient_scarpetMixin",
|
||||
|
||||
"PistonBlock_movableTEMixin",
|
||||
"PistonBlockEntity_movableTEMixin",
|
||||
"World_movableTEMixin",
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"mixins": [
|
||||
"carpet.mixins.json"
|
||||
],
|
||||
"accessWidener" : "carpet.accesswidener",
|
||||
|
||||
"depends": {
|
||||
"minecraft": "1.15.x",
|
||||
|
||||
Reference in new issue
Block a user