18 août 2017, 21:23

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