Ajouter une recette


  • Moddeurs confirmés Rédacteurs

    Sommaire

    Introduction

    Depuis la 1.12 les recettes se présentent sous forme de fichiers JSONs. Définir les recettes en utilisant des fichiers de ressources est quelque chose que certains mods faisaient déjà, comme EnderIO par exemple. Nous allons donc voir comment créer à présent des recettes pour votre mod.

    Pré-requis

    • Savoir utiliser le format de fichier JSON

    Code

    Où placer les fichiers JSONs et comment les appeler ?

    Les fichiers des recettes doivent se placer dans le package assetsmodidrecipes en remplaçant bien sûr modid par l'id de votre mod (je ne le répèterai pas par la suite car cela va de soi). Vous pouvez appeler vos fichiers comme vous le voulez, cependant il y a 2 règles à respecter :

    • Le nom du fichier ne doit pas commencer par un underscore
    • L'extension du ficher doit être json

    Vous pouvez donner le nom que vous voulez au fichier tant que vous suivez ces deux règles. Le nom du fichier correspond au nom de la recette, plus précisément il correspondra à la clé associée à la recette dans le registre des recettes de Forge.

    La notion de factories :

    Les fichiers JSONs qui contiennent les recettes sont lus par ce que l'on appelle des factories. Il en existe plusieurs, elles sont différentes car elles permettent de créer différentes recettes. Une factory va lire le contenu du JSON et créer un objet IRecipe qui sera stocké avec la clé comme nous l'avons vu précédemment. Il existe 3 types de factories :

    • Factory de recette : parse un JSON pour donner en sortie une instance de IRecipe
    • Factory de condition : parse un JSON pour donner en sortie une instance de BooleanSupplier indiquant si la recette doit être ajoutée au registre
    • Factory d'ingrédient : parse un JSON pour donner en sortie une instance d'Ingredient, ces factories sont utilisées par les factories de recette

    Des factories par défaut existent, ce sont celles que l'on va utiliser les plus souvent, cependant vous pouvez créer les vôtres. Cela peut être très utile, nous verrons une application dans la partie bonus.

    Pour le moment sachez que les factories que vous avez créé doivent être déclarées pour pouvoir être utilisés. Pour déclarer une factory on doit créer un fichier JSON nommé _factories dans le package des recettes. Dedans on va déclarer les 3 catégories de factories qui existent :

    {
    	"ingredients": {
    
    	},
    	"recipes": {
    
    	},
    	"conditions": {
    
    	}
    }
    

    On va ensuite associer le nom de la factory à sa classe, ainsi, si j'ai créé une factory de conditions qui se trouve à modtuto.recipe.factory.FactoryConditionTuto et que je veux l'appeler "mana_enabled" j'obtiens la chose suivante :

    {
        	"ingredients": {
        
        	},
        	"recipes": {
        
        	},
        	"conditions": {
        		"mana_enabled": "modtuto.recipe.factory.FactoryConditionTuto"
        	}
    }
    

    Bien sûr la classe FactoryConditionTuto doit implémenter l'interface adéquate, voici les interfaces pour les différentes factories :

    • Factory de recette : IRecipeFactory
    • Factory de condition : IConditionFactory
    • Factory d'ingrédient : IIngredientFactory

    Il vous suffit juste d'implémenter votre classe de cette interface et ensuite déclarer dans le fichier _factories.json son nom et vous pourrez l'utiliser dans vos fichiers de recettes en utilisant le nom modid:nom. Voici une liste de factories présentes par défaut :

    Type Nom
    Condition forge:mod_loaded
    Condition minecraft:item_exists
    Condition forge:not
    Condition forge:or
    Condition forge:and
    Condition forge:false
    Recipe minecraft:crafting_shaped
    Recipe minecraft:crafting_shapeless
    Recipe forge:ore_shaped
    Recipe forge:ore_shapeless
    Ingredient minecraft:item
    Ingredient minecraft:empty
    Ingredient forge:item_nbt
    Ingredient forge:ore_dict

    Déclarer des constantes

    Si vous utilisez régulièrement certains ingrédients dans vos recettes vous pouvez le définir dans un fichier en tant que constante afin de l'utiliser plus facilement dans vos fichiers de recettes. Pour cela on va définir une liste d'ingrédients auxquels on va donner un nom, tout cela dans un fichier JSON nommé _constants qui se trouve lui aussi dans le package des recettes. Il se présentera de la façon suivante :

    [
        {
            "name": "nom1",
            "ingredient": <ingredient>
        },
        {
            "name": "nom2",
            "ingredient": <ingredient>,
            "conditions": [
                <condition>,
                    <condition>]
        }
    ]
    

    Bien sûr, les <ingredient> et <condition> doivent être remplacés par leur définition. Grâce à ceci vous pourrez ensuite utiliser l'ingrédient directement en mettant un dièse devant son nom.

    Le format d'un fichier de recette :

    Nous allons voir comment se présente un fichier de recette. Une recette possède un tag "type", ce type correspond au nom de la factory de recette que vous voulez utiliser. Puis la suite du fichier dépendra de la factory utilisée. Voyons à présent les conditions, il faut tout d'abord savoir que l'on a la possibilité de ne faire charger une recette qu'à certaines conditions, on définit une liste de conditions que l'on range sous le tag "conditions", il faut que toutes les conditions définies soient remplies pour que la recette soit chargée. Pour résumer ces lignes on a pour le moment la chose suivante :

    {
        "type": <type>,
        "conditions": [
            <condition>,
            <condition>]
    }
    

    Avec :

    Nom du tag Type de donnée Description
    type String Nom de la factory de recette
    conditions Liste d'objets Liste des conditions qui doivent être remplies pour que la recette soit chargée

    Voyons à présent ce à quoi ressemble une condition, le code que je vais mettre sera ce qui remplacera <condition> dans le code prédédent. Une condition aussi à un tag "type" qui correspond à la factory de condition, la suite sera donc là aussi dépendante de la factory. Voici donc la structure d'une condition :

    {
        "type": <type>
    }
    

    Avec :

    Nom du tag Type de donnée Description
    Type String Nom de la factory de condition

    Voilà, je vais à présent lister les attributs des différentes factories (du moins les plus importantes).

    Les conditions :

    • forge:mod_loaded
      Cette condition vérifie si un certain mod est chargé. Remplacez <modid> par l'ID du mod visé.
    {
        "type": "forge:mod_loaded",
        "modid": <modid>
    }
    
    • minecraft:item_exists
      Cette condition vérifie que l'item indiqué existe. Remplacez par le nom de registre du l'item visé.
    {
        "type": "minecraft:item_exists",
        "item": <item>
    }
    
    • forge:not
      Cette condition renvoie vrai si la condition indiquée est fausse. Remplacez <condition> par la condition que vous voulez inverser.
    {
        "type": "forge:not",
        "value": <condition>
    }
    
    • forge:or
      Cette condition renvoie vrai si au moins l'une des deux conditions indiquées est vraie. Remplacez <condition1> et <condition2> par les deux conditions visées.
    {
        "type": "forge:or",
        "values": [
             <condition1>,
             <condition2>]
    }
    

    C'est globalement toutes les conditions utiles.

    Les ingrédients :

    • minecraft:item
      Cet ingrédient désigne un ItemStack de taille 1 et sans aucun tag NBT. Pour ce qui est du metadata, si l'item a plusieurs variantes alors vous devez spécifier son metadata . Remplacez par le nom de registre de l'item et <metadata> par le metadata voulu (seulement si cela est nécessaire).
    {
        "type": "minecraft:item",
        "item": <item>,
        "data": <metadata>
    }
    
    • minecraft:empty
      Cet ingrédient retourne un ItemStack vide.
    {
        "type": "minecraft:empty"
    }
    
    • minecraft:item_nbt
      Cet ingrédient va permettre de rajouter des tags NBT à un item. Remplacez par le nom de registre de l'item voulu, remplacez <metadata> par le metadata de l'item si celui-ci possède plusieurs variantes, <count> par le nombre d'items voulus et <nbt> par les NBT de l'item.
    {
        "type": "minecraft:item_nbt",
        "data": <metadata>,
        "count": <count>,
        "nbt": <nbt>
    }
    
    • forge:ore_dict
      Cet ingrédient fait appel à OreDictionnary pour trouver les items correspondants. Remplacez <ore> par le nom d'OreDictionnary voulu.
    {
        "type": "forge:ore_dict",
        "ore": <ore>
    }
    
    • Avec une constante
      Vous vous rappelez des constantes ? Voici comment créer un ingrédient grâce à une constante. Il faut remplacer <constante> par #nomDeLaConstante.
    {
        "item": <constante>
    }
    

    Les recettes :

    • minecraft:crafting_shaped
      Cette recette permet de créer une recette aillant une forme, c'est à dire que les ingrédients devront être placés d'une certaine façon dans la matrice de craft pour que la recette fonctionne. On va donc créer un pattern à l'aide de caractères et à chaque caractère on va associer un ingrédient. Le pattern doit être rectangulaire, ce qui veut dire que les lignes du pattern doivent avoir la même taille. De plus, vous êtes limités à une taille de 3x3. Il faudra ensuite indiquer le résultat de la recette. Voici à quoi ressemble le fichier :
    {
        "type": "minecraft:crafting_shaped",
        "pattern": <pattern>,
        "key": <keys>,
        "result": <result>
    }
    

    Avec :

    Nom du tag Type de donnée Description
    pattern Liste de String Chaque chaîne de caractère représente une ligne et chaque caractère un emplacement de la matrice de craft.
    key Objet On associe un caractère à un ingrédient, mettez le caractère en clé et l'ingrédient en valeur.
    result Object Ceci est le résultat de la recette, il correspond à un ingrédient, référez-vous à la catégorie Ingrédients pour savoir quoi y placer.

    Je vais vous mettre le fichier de recette des escaliers d'acacia pour que vous aillez une idée du résultat final.

    
    {
        "type": "minecraft:crafting_shaped",
        "pattern": [
        "#  ",
        "## ",
        "###"
        ],
        "key": {
        "#": {
            "item": "minecraft:planks",
            "data": 4
        }
        },
        "result": {
        "type": "minecraft:item_nbt",
        "item": "minecraft:acacia_stairs",
        "count": 4
        }
    }
    
    
    • minecraft:crafting_shapeless
      Cette recette nécessite seulement que les ingrédients indiqués soit présents dans la matrice de craft, peu importe leur position. On a un résultat et une liste d'ingrédients.
    
    {
        "type": "minecraft:crafting_shapeless",
        "ingredients": <ingredients>,
        "result": <result>
    }
    
    

    Avec :

    Nom du tag Type de donnée Description
    ingredient Liste d'objet Une liste contenant tous les ingrédients nécessaires à la recette.
    result Object Ceci correspond à un ingrédient, c'est le résultat de la recette, référez-vous à la catégorie Ingrédients pour savoir quoi y placer.

    Voici le fichier de recette du briquet :

    {
        "type": "minecraft:crafting_shapeless",
        "ingredients": [
        {
            "type": "minecraft:item",
            "item": "minecraft:iron_ingot"
        },
        {
            "type": "minecraft:item",
            "item": "minecraft:flint"
        }
        ],
        "result": {
        "type": "minecraft:item",
        "item": "minecraft:flint_and_steel"
        }
    }
    

    Les recettes d'alchimie :

    Pour créer des recettes d'alchimie vous n'avez pas besoin de faire de fichier de recette, cette fois ci nous allons devoir utiliser les fonctions BrewingRecipeRegistry#addRecipe à appeler de préférence pendant la phase d'initialisation de votre mod.

    Il existe plusieurs prototypes de cette fonction. La plus simple est BrewingRecipeRegistry#addRecipe(IBrewingRecipe), il vous suffit d'implémenter l'interface à une classe et passer une instance de cette classe à la fonction.
    Vous avez ensuite BrewingRecipeRegistry#addRecipe(ItemStack, ItemStack, ItemStack), le premier argument correspond à l'ItemStack qui doit se trouver dans les slots de potion au début de la recette, c'est l'input. Le second argument est l'ItemStack qui doit être mis dans le slot du haut (le même où l'on met la nether wart) afin que la recette se déclenche. Et le dernier argument est le résultat de la recette, c'est l'ItemStack qui sera placé dans les slots à potions.
    Ou vous avez encore BrewingRecipeRegistry#addRecipe(ItemStack, String, ItemStack) qui est la même chose que la précédente mais qui utilise OreDictionnary pour l'ItemStack qui déclenche la recette.

    Bonus

    Comme promis nous allons utiliser notre propre factory pour faire une recette. Pour cela je vais créer une classe dont le chemin complet sera modtuto.recipe.factory.FactoryChestPlate. Vous l'avez peut-être compris, cette factory permettra de faire une recette de plastron plus facilement, il suffira d'indiquer avec quel objet on doit faire le plastron et quel objet en résultera. Avant de créer la classe je vais indiquer dans le fichier _factories.json ma factory. En ajoutant la ligne suivante dans la catégorie "recipes".

    "chestplate": "modtuto.recipe.factory.FactoryChestPlate"
    

    Pas besoin d'indiquer l'ID du mod car il sera automatiquement ajouté si il n'est pas présent. Passons maintenant à la classe, il vous faut créer la classe aillant le chemin indiqué, donc pour moi c'est FactoryChestPlate dans le package modtuto.recipe.factory. Il nous faut ensuite implémenter l'interface IRecipeFactory et ne mettez pas de constructeur. Implémentez donc la méthode de l'interface, nous allons y mettre le code pour que notre recette fonctionne.
    Dans un premier temps nous allons récupérer l'item avec lequel il faudra faire le plastron, nous allons donc récupérer l'ingrédient se trouvant sous le tag "item" grâce à :

    CraftingHelper.getIngredient(json.get("item"), context);
    

    Il nous faut ensuite récupérer le résultat de la recette, ce sera un ItemStack sous le tag "result", on le récupère de la façon suivante :

    CraftingHelper.getItemStack(JsonUtils.getJsonObject(json, "result"), context);
    

    Puis, comme nous allons utiliser ShapedRecipes, il nous faut une liste d'ingrédients, chaque index de la liste correspond à un slot de la matrice de craft. Le craft du plastron a un item sur tous les slots sauf le second (qui a l'index 1 du coup), on va donc créer une liste remplie entièrement de l'ingrédient puis remplacer l'index 1 par un ingrédient vide :

    NonNullList.withSize(9, ingredient);
    

    Puis :

    list.set(1, Ingredient.fromStacks(ItemStack.EMPTY));
    

    On retourne ensuite la recette :

    return new ShapedRecipes("", 3, 3, list, result);
    

    Faites abstraction du premier paramètre. Le premier 3 est la largeur de la recette et le second 3 est la hauteur de la recette.
    Il ne nous reste plus qu'à créer une recette et tester, voici à quoi ressemble le JSON d'une recette de chestplate avec des pommes et qui donne une flèche :

    {
        "type": "chestplate",
        "item": {
            "type": "minecraft:item",
            "item": "minecraft:apple"
        },
        "result": {
            "type": "minecraft:item",
            "item": "minecraft:arrow"
        }
    }
    

    Crédits

    Rédaction :

    Correction :

    Creative Commons
    Ce tutoriel de Minecraft Forge France est mis à disposition selon les termes de la licence Creative Commons Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International

    retourRetour vers le sommaire des tutoriels



  • Bonjour BrokenSwing, j'aurais juste quelques questions. Alors voilà, je suis en train de développer un outil (une version basique et une autre plus avancé plus tard) pour créer facilement des recettes et ayant presque fini je voudrais m'assurer de certaine chose; première question quand on utilise le type item_nbt dans ton exemple:

    {
      "type": "minecraft:item_nbt",
      "data": <metadata>,
      "count": <count>,
      "nbt": <nbt>}
    

    il faut bien rajouter un paramètre item?

    {
      "type": "minecraft:item_nbt",
      "item": "minecraft:stick"
      "data": <metadata>,
      "count": <count>,
      "nbt": <nbt>}
    

    Ensuite je voulais savoir quand on utilise le type empty, il n'y a rien de plus à rajouter d'autre c'est ça ? Et enfin j'ai crue comprendre que le paramètre group en dessous du type de factory étant fait pour le référencement groupé, c'est exact ? Merci d'avance, cordialement EmotionFox.



  • Bonsoir, j'aurais juste une petite question à propos des recettes; alors voilà, quand j'ai les ingrédients nécessaire la plupart des recettes de Minecraft ce débloque automatiquement alors que quand ce sont les miennes il faut crafter une première fois l'objet en question pour débloquer sa recette.



  • Bonjour j'aimerais ajouter le craft de bottes enchantée savez-vous comment faire ?



  • Aypristyle, pour rajouter un nbt (description, enchantement etc) il faut procéder comme ça:

    "result": {
    "type": "minecraft:item_nbt",
    "item": "minecraft:apple",
    "nbt": {
    "ench":[
    {
    "id": <l'id de="" l'enchantement="">,
    "lvl": <le niveau="" d'enchantement="">},
    {
    "id": <l'id d'un="" autre="" enchantement="">,
    "lvl": <le niveau="" d'un="" autre="" enchantement="">}
    ]
    }
    }
    ```</le></l'id></le></l'id>


  • Merci beaucoup tout fonctionne !



  • Merci beaucoup pour ce tutoriel qui contient pleins d'infos difficiles à dénicher !

    J'ai eu un mal fou à trouver des informations (anglais ou français) pour le nouveau système de recettes JSON 1.12,
    quelles sont les sources que tu as utilisé pour écrire ce tutoriel ? Est-ce uniquement par expérimentation ?


  • Moddeurs confirmés Rédacteurs

    J'ai regardé ce que faisait le code, puis j'ai interprété



  • Merci pour ce tutoriel, tout a fonctionné pour moi. Je ne pensais pas que cela fonctionnait sans utiliser de code Java, mais il faut croire que si


  • Moddeurs confirmés Rédacteurs Modérateurs Administrateurs

    Les recettes fonctionnent toujours avec du code Java, c'est juste la rédaction des recettes qui est passée en Json.



  • @'Superloup10':

    Les recettes fonctionnent toujours avec du code Java, c'est juste la rédaction des recettes qui est passée en Json.

    Ce que je veux dire, c'est qu'il n'y a pas besoin de dire où se trouve les recettes d'un bloc ou d'un item dans une classe Java. Forge sait déjà ce qu'il faut faire avec les fichiers json.


  • Moddeurs confirmés Rédacteurs Modérateurs Administrateurs

    Ce n'est pas Forge qui s'occupe de ça, c'est Minecraft qui s'en charge. Forge ne fait qu'indiquer à Minecraft qu'il doit charger les recettes des mods.


Log in to reply