Créer un item simple



  • Sommaire

    Introduction

    Dans ce tutoriel vous allez apprendre à créer des items basiques en 1.11.

    Pré-requis

    Code

    La classe des items :

    Nous allons donc commencer par créer une classe qui regroupera l'intégralité des items que nous allons créer dans le mod. Commencez par créer un package pour tous les items. Dans mon cas ce sera "fr.minecraftforgefrance.tutorial.items" car le package de mon mod est "fr.minecraftforgefrance.tutorial". Adaptez le nom du vôtre en conséquence.
    Nous allons créer une classe nommée "[ModId]Items" en remplaçant [ModId] par votre modid. Par exemple "TutorialItems".

    Dans cette classe, nous allons ajouter trois fonctions : setItemName pour donner un nom à notre objet, registerItemModels présente uniquement du côté client pour l'enregistrement des modèles et registerModel présente uniquement du côté client pour simplifier l'enregistrement des modèles.
    Attention ! Selon votre version, certaines parties changent.

    package fr.minecraftforgefrance.tutorial.items;
    
    import net.minecraftforge.fml.relauncher.Side;
    import net.minecraftforge.fml.relauncher.SideOnly;
    
    /** EventBusSubscriber ne doit être utilisé qu'à partir de la 1.12\. L'annotation n'est pas nécessaire en 1.11.
    N'oubliez pas d'importer net.minecraftforge.fml.common.Mod.EventBusSubscriber.
    value contient le côté que nous souhaitons utiliser. Ici, nous voulons uniquement le côté Client, puisque nous utiliserons seulement l'event pour enregistrer les modèles.
    modid est votre modid. */
    @EventBusSubscriber(value = Side.CLIENT, modid = ModTutorial.MODID)
    public class TutorialItems
    {
        // ici on va déclarer les items
    
        public static void setItemName(Item item, String name)
        {
            // ici on va attribuer un nom à nos objets
        }
    
        // Cette version de la fonction ne doit être utilisée qu'en 1.11.x
        @SideOnly(Side.CLIENT)
        public static void registerItemModels()
        {
            // ici on va appeler la fonction registerModel pour chaque item.
        }
    
        /* Cette version de la fonction ne doit être utilisée qu'à partir de la 1.12.
    Pensez à inclure net.minecraftforge.client.event.ModelRegistryEvent. */
        @SideOnly(Side.CLIENT)
    @SubscribeEvent
        public static void registerItemModels(ModelRegistryEvent event)
        {
            // ici on va appeler la fonction registerModel pour chaque item.
        }
    
        @SideOnly(Side.CLIENT)
        public static void registerModel(Item item, int metadata)
        {
            // et ici on va enregistrer les modèles (fichiers json)
        }
    }
    

    Ce code servira pour tous les items que nous allons créer, et ne doit être présent qu'une seule fois par mod.

    Par contre, la suite est à répéter pour chaque item que vous souhaitez créer.

        public static final Item NOM = new ItemTutorial();
    

    Ceci est la déclaration de l'item. Nous déclarons une constante, et la convention est d'écrire le nom entièrement en majuscule et de séparer les différents mots par un underscore (_). Remplacez NOM par le nom de votre variable. J'ai appelé mon item TUTORIAL et c'est une instance de l'objet ItemTutorial, que nous allons prochainement créer.

    Nous allons définir la fonction setItemName comme ceci :

    public static void setItemName(Item item, String name)
    {
        item.setRegistryName(ClassePrincipale.MODID, name).setUnlocalizedName(ClassePrincipale.MODID + "." + name);
    }
    

    Cette ligne attribue à l'Item le nom que nous passons en paramètre de la fonction comme nom de registre et nous attribuons le même nom précédé du modid et d'un point au nom non-localisé. Il sera utilisé pour les fichiers .lang.
    Par exemple, pour mon item, mon modid étant "tutorial", je devrais ajouter cette ligne dans les fichiers .lang :

    item.tutorial.item_tutorial.name=Tutorial Item
    

    Le jeu affichera comme nom d'objet "Tutorial Item".
    Pour le nom de registre, il est conseillé de n'utiliser que des minuscules et des underscore au lieu des espaces (attention, si les majuscules sont déconseillées, les espaces sont interdits !). Ces conseils permettent de rester en accord avec l'écriture de Mojang. Vous pouvez voir ce nom pour n'importe quel item avec la combinaison de touches F3 + H, il sera affiché tout en bas de la description, en gris. Pour les objets vanilla, vous verrez un nom du type minecraft:stone, avec stone remplacé par un autre nom selon l'objet. (Tip : Lorsque vous faites la combinaison F3 + H, vous devriez avoir un message qui s'affiche pour vous dire si l'option a été activée ou désactivée).
    Nous utiliserons cette fonction plus tard.

    Pour finir, nous allons enregistrer le modèle de l'item. Placez-vous dans la fonction registerItemModels et écrivez ceci :

            registerModel(NOM, 0);
    

    Remplacez ici NOM par le nom de votre variable (le même que celui dans la déclaration). 0 est la valeur de metadata de votre item. Ici, nous ne souhaitons pas créer un objet ayant plusieurs valeurs de metadata, alors nous le mettons à 0, qui est la valeur par défaut.
    Nous allons donc définir registerModel :

    @SideOnly(Side.CLIENT)
    public static void registerModel(Item item, int metadata)
    {
        if (metadata < 0) metadata = 0;
        String resourceName = item.getRegistryName();
        if (metadata > 0) resourceName += "_m" + String.valueOf(metadata);
    
        ModelLoader.setCustomModelResourceLocation(item, metadata, new ModelResourceLocation(resourceName, "inventory");
    }
    

    Tout d'abord, nous vérifions si la metadata est positive ou nulle parce qu'il ne peut pas y avoir de valeur négative.

    String resourceName = item.getUnlocalizedName().subString(5).replace('.', ':');
    

    Cette ligne récupère le nom de la ressource. getUnlocalizedName va renvoyer le nom non-localisé que nous avons défini sous cette forme : "item.modid.nom_item". Avec substring(5), nous retirons la partie "item.", qui ne nous est pas utile. Il nous reste donc "modid.nom_objet". Le nom du modèle doit être passé sous le format "modid:nom_modele". Ici, nous allons utiliser pour "nom_modele" la même valeur que "nom_item", alors nous avons uniquement besoin de changer le '.' en ':'.

    Si la metadata est supérieure à 0, nous ajoutons un suffixe au nom de notre modèle, qui sera donc de la forme "modid:nom_modele_mX" avec X le nombre choisi pour la metadata.

    Ensuite, nous enregistrons le modèle, avec comme premier paramètre l'item auquel nous voulons attribuer ce modèle, en second la fameuse metadata et en dernier un ModelResourceLocation prenant comme paramètres le nom de la ressource et "inventory".

    Voici le résultat que vous devriez obtenir (avec des noms différents) :

    package fr.minecraftforgefrance.tutorial.items;
    
    import fr.minecraftforgefrance.tutorial.ModTutorial;
    import net.minecraft.client.resources.model.ModelResourceLocation;
    import net.minecraft.item.Item;
    import net.minecraftforge.client.event.ModelRegistryEvent;
    import net.minecraftforge.client.model.ModelLoader;
    import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
    import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
    import net.minecraftforge.fml.relauncher.Side;
    import net.minecraftforge.fml.relauncher.SideOnly;
    
    @EventBusSubscriber(value = Side.CLIENT, modid = ModTutorial.MODID) // En 1.12+
    public class TutorialItems
    {
        public static final Item TUTORIAL = new ItemTutorial();
    
        public static void setItemName(Item item, String name)
        {
            item.setRegistryName(ModTutorial.MODID, name).setUnlocalizedName(ModTutorial.MODID + "." + name);
        }
    
        // En 1.11.x
        @SideOnly(Side.CLIENT)
        public static void registerItemModels()
        {
            registerModel(TUTORIAL, 0);
        }
    
        // En 1.12+
        @SideOnly(Side.CLIENT)
        @SubscribeEvent
        public static void registerItemsModels(ModelRegistryEvent event)
        {
        registerModel(TUTORIAL, 0);
            registerModel(BLOCK_TUTORIAL_ITEM, 0);
        }
    
        @SideOnly(Side.CLIENT)
        public static void registerModel(Item item, int metadata)
        {
            if (metadata < 0) metadata = 0;
            String resourceName = item.getUnlocalizedName().subString(5).replace('.', ':');
            if (metadata > 0) resourceName += "_m" + String.valueOf(metadata);
    
            ModelLoader.setCustomModelResourceLocation(item, metadata, new ModelResourceLocation(resourceName, "inventory");
        }
    
    }
    

    La classe de l'item :

    Créez la classe de votre Item, elle doit hériter de la classe net.minecraft.item.Item.

    public class ItemTutorial extends Item
    {
        public static final String NAME = "item_tutorial";
    }
    

    Définissez NAME avec le nom d'objet que vous souhaitez attribuer. Nous allons utiliser ce nom pour le nom non-localisé, le nom dans le registre et le nom du modèle.

    Le but étant de créer un item simple, nous n'avons rien à ajouter, si ce n'est que quelques petites informations pour finir la création de l'item. Ajoutez un constructeur, comme celui-ci :

        public ItemTutorial()
        {
            super();
    
            TutorialItems.setItemName(this, NAME);
            setCreativeTab(CreativeTabs.MISC);
        }
    

    Nous appelons la fonction setItemName dans TutorialItems, en passant comme paramètre l'item (puisque nous sommes dans la définition-même de l'item, nous pouvons utiliser this) et son nom, définit quelques lignes plus haut.
    setCreativeTab permet de placer l'objet dans un onglet créatif. Cet appel n'est pas nécessaire, un objet peut très bien ne pas apparaître dans le menu créatif.

    On peut maintenant s'amuser à ajouter d'autres caractéristiques dans notre constructeur.

            setMaxStackSize(1);
    

    Cette ligne fait que l'objet ne peut être empilé que par 1 au maximum. Si vous mettez une valeur supérieure à 64, le maximum sera mis à 64, mais l'onglet créatif aura quelques bugs. Alors ne jouez pas avec le jeu, vous ne gagnerez pas.
    Un autre exemple :

        @Override
        public ActionResult <ItemStack>onItemRightClick(World world, EntityPlayer player, EnumHand hand)
        {
            if(!world.isRemote)
            {
                player.addChatMessage(new ChatComponentText("bonjour " + player.getName()));
            }
            return new ActionResult(EnumActionResult.PASS, playerIn.getHeldItem(handIn));
        }
    

    Celui-ci affiche "bonjour " avec le nom du joueur à la suite dans le chat, lorsque celui-ci fait un clic droit avec l'objet en main.
    Je vous laisse regarder la classe Item, vous y trouverez de nombreuses choses intéressantes !

    La classe client et la classe principale :

    Encore une fois, le code change selon votre version. Suivez attentivement les instructions.

    [1.11.x]
    Dans la classe client (ou client proxy), appelez la fonction registerItemModels :

    package fr.minecraftforgefrance.tutorial.client;
    
    import java.io.File;
    
    import fr.minecraftforgefrance.tutorial.TutorialCommon;
    import fr.minecraftforgefrance.tutorial.items.TutorialItems;
    
    public class TutorialClient extends TutorialCommon
    {
        @Override
        public void preInit(File configFile)
        {
            super.preInit(configFile);
            TutorialItems.registerItemModels();
        }
    
        @Override
        public void init()
        {
            super.init();
        }
    }
    

    Toutes les versions :
    Ensuite, dans votre classe principale (pour moi, ce sera ModTutorial), ajoutez un constructeur, comme ceci :

    public ModTutorial() {
        MinecraftForge.EVENT_BUS.register(new RegisteringHandler());
    }
    

    Nous allons aller légèrement plus loin qu'un simple objet, puisqu'ici, nous ajoutons un event. Oui, vous avez bien entendu, un event.
    En réalité, nous l'avons déjà fait depuis le début, mais j'ai passé ces détails pour l'instant.
    Qu'est-ce qu'un event ? C'est un événement appelé lors d'une action spécifique. Ici, nous disons à Forge que nous avons une classe RegisteringHandler qui contient des événements à traiter et qu'il faut donc les appeler.
    Nous allons donc créer cette classe RegisteringHandler !

    Vous pouvez trouver plus d'information sur les events ici.

    La classe RegisteringHandler :

    Nous voici donc avec la dernière classe.
    Celle-ci va uniquement nous servir à enregistrer les items. Parce que oui, nous avons tout préparé pour les enregistrer, mais nous n'avons dit nulle part à forge "enregistre nos items".

    Voici donc la classe, que nous allons gentiment décortiquer :

    public class RegisteringHandler
    {
        @SubscribeEvent
        public void registerItems(RegistryEvent.Register<Item> event) {
            event.getRegistry().registerAll(TutorialItems.TUTORIAL); 
        }
    }
    

    @SubscribeEvent signifie que la fonction qui suit utilise un événement, ici, l'événement est RegistryEvent. Cet événement est appelé une fois par Registre. Ici, nous voulons enregistrer un Item, alors nous allons utiliser le registre des items, soit Register. Cette fonction sera appelée une fois AVANT le pre-init. J'insiste sur le mot avant. Vos items sont enregistrés AVANT que le pre-init soit appelé. Vous devez le savoir.

    event.getRegistry().registerAll(TutorialItems.TUTORIAL);
    

    Cette ligne enregistre tous les items passés en argument. Nous ne voulons en enregistrer qu'un : TUTORIAL. Mais si nous en avions plusieurs, nous devrions écrire :

    event.getRegistry().registerAll(block1, block2, block3);
    

    Il n'y a aucune limite de nombre. Vous pouvez en enregistrer autant que vous voulez. TutorialItems est le nom de la classe que nous avons créée en début de tutoriel, il devrait être différent pour vous.

    Vous en avez fini avec votre Item, il est fonctionnel ! Lancez le jeu, vous verrez par vous-même !
    Je ne suis absolument pas responsable de tout problème technique comme un nom un peu étrange ou des textures manquantes.

    Le modèle et la texture :

    Dans un premier temps, voici le fichier JSON de cet item, c'est le fichier JSON de base pour tous les items basiques de Minecraft. Il faut le placer dans assets/modid/models/item/nom_non_enregistre.json, en remplaçant modid par votre modid et nom_non_enregistre par votre nom non enregistré, simple comme bon jeu !

    {
        "parent": "item/generated",
        "textures": {
            "layer0": "tutorial:items/tutorial_item"
        }
    }
    

    Je vais vous expliquer ce petit bout de code :

    La ligne "parent" permet de définir un "json mère", en quelque sorte. Ici, nous voulons créer un item se basant sur la base d'un item minecraft "item/generated".
    La ligne "textures" indique à Forge que nous allons définir toutes nos textures entre les accolades.
    La ligne "layer0" sert à définir la texture de notre Item
    (équivalent, à l'époque de la 1.7.x, au this.setTextureName() )

    Changez "tutorial" et "tutorial_item" par le modid de votre mod et le nom de votre texture.
    La texture doit toujours se trouver dans vos assets ! (assets/modid/textures/items/nomdelatexture.png)

    Si vous souhaitez modifier le fichier JSON tout en évitant les erreurs je vous conseille ce site qui corrigera vos erreurs s'il y en a : http://jsoneditoronline.com
    Il existe également des plug-ins Eclipse créés pour l'édition des JSON.

    Il y a aussi les détails complets pour modifier votre fichier JSON sur le wiki officiel de Minecraft
    Comme par exemple les particules, les layers...

    Vous avez maintenant tous les outils en main pour personnaliser le fichier JSON de votre item !

    Une dernière chose, modifiez le fichier de langue de votre mod. Ajoutez la ligne suivante dedans :

    item.nom non localisé.name=Nom localisé
    

    Un exemple concret :

    • pour le fichier en_US.lang :
    item.tutorial.name=Tutorial Item
    
    • pour le fichier fr_FR.lang :
    item.tutorial.name=Item Tutoriel
    

    Et voilà, le nom devrait apparaître en jeu !

    Résultat

    Crédits

    Rédaction :

    Correction :

    Inspiration / Aide :

    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



  • Si je peux donner quelques petits conseilles :
     - Dans la class où tu enregistre tous les items, au lieu de réécrire la fonction d'enregistrement en entier :

    GameRegistry.registerItem(NOM, new ResourceLocation("nom_dans_le_registre"));
    

    (d’ailleurs tu t'es trompé ici, maintenant c'est juste "GameRegistry.register")

    et en plus d' ajouter dans la class de l'item le UnlocalizedName (qui pour moi n'a aucune raison d'être différent du RegistryName), j'utilise une fonction qui fait les deux (et qui est plus courte à écrire) :

    private static void registerItem(Item item, String name)
    {
        GameRegistry.register(item.setRegistryName(ModMechaRunicPower.MOD_ID, name).setUnlocalizedName(name));
    }
    

    Tu as ensuite juste à l'appeler dans la fonction registerItems : "registerItem(TUTORIAL, "tutorial_item");"

    - De la même manière, j'ai une fonction pour enregistrer le model :

    private static void registerRender(Item item)
    {
        ModelLoader.setCustomModelResourceLocation(item, 0, new ModelResourceLocation(ModMechaRunicPower.MOD_ID + ":" + item.getUnlocalizedName().substring(5), "inventory"));
    }
    

    Ps : tu as quand même réussi à m'apprendre quelque chose : je ne connaissais pas "ModelLoader.setCustomModelResourceLocation(…)" je passais directement par "Minecraft.getMinecraft().getRenderItem().getItemModelMesher().register(...)". Je sais pas ce qui est le mieux car techniquement, ta méthode fait à un moment appelle à la fonction que j'utilise.

    Sinon, super tuto, c'est sympa de reprend des tuto en 1.11, il n'y avait plus beaucoup de tuto sur les versions récentes depuis quelques temps.



  • Ah mince j'ai pas vu cette erreur, sorry. Va falloir corriger ça.

    Effectivement, cette méthode est plus claire, je vais modifier ça demain, là je suis sur mon téléphone et ce n'est pas pratique.

    Je ne sais pas ce qui est le plus optimisé, et d'ailleurs cette méthode était aussi correcte en 1.8.X.
    Et oui, il y a peu de tutos sur les versions récentes, je vais essayer de mettre à jour ce que je peux ^^ mais il va me falloir du temps :3

    Je corrige tout demain, j'ai peur de faire une bêtise avec mon téléphone.

    Et merci pour ton commentaire, c'est cool de se savoir aidé, parce que j'avoue avoir la pression du rédacteur débutant, je doute beaucoup de ce que j'écris :3

    ÉDIT : Normalement j'ai corrigé la faute avec le registerItem, c'est bon ? Je poste l'optimisation demain, parce que c'est vraiment pas possible sur ce téléphone xD



  • Attention
    A partir de la 1.11.2 le onItemRightClick à  changer
    Pas de grand chose mais maintenant il faut faire

    
    @Override
    public ActionResult <itemstack>onItemRightClick(World world, EntityPlayer player, EnumHand hand) {
        if(!world.isRemote){
                System.out.println("bonjour " + player.getName());
                return new ActionResult(EnumActionResult.SUCCESS, new ItemStack(this));
        }
        else{
                return new ActionResult(EnumActionResult.FAIL, new ItemStack(this));
        }
    }
    
    ```</itemstack>

  • Moddeurs confirmés Rédacteurs

    Effectivement, et je dirais même que cela date de la 1.9, et ne retourne pas new ItemStack(this), mais le stack donné en paramètres de la fonction :

    
    @Override
    public ActionResult <itemstack>onItemRightClick(ItemStack itemStack, World world, EntityPlayer player, EnumHand hand)
    {
    return new ActionResult<itemstack>(EnumActionResult.SUCCESS, itemStack);
    }
    
    

    Pour ce qui est du json des item il me semble que mettre item/generated en parent suffit, pas besoin de remettre le positionnement dans le gui</itemstack></itemstack>



  • Si je fais ce code eclipse me dit que la fonction n'existe pas, je suis allez vérifier sur github mais il font à ma façon


  • Moddeurs confirmés Rédacteurs

    Effectivement, ce que j'avais donné était la fcontion 1.9, en 1.11 faut renvoyer ça :

    
    public ActionResult <itemstack>onItemRightClick(World worldIn, EntityPlayer playerIn, EnumHand handIn)
    {
    return new ActionResult(EnumActionResult.PASS, playerIn.getHeldItem(handIn));
    }
    
    ```</itemstack>


  • Ahem va falloir modifier ça. Pas de soucis, je le ferais quand j'aurais le temps, en ce moment je peux pas trop ^^'



  • J'ai mis à jour le tutoriel, c'est bon comme ça ? 😮



  • Attention c'est seulement à partir de la 1.11.2 xD



  • Je crois que tu as oublier de parler du code à mettre dans le Common Proxy sans lui le Client Proxy ne fonctionne pas 😉



  • Bonjour est-il possible d'utiliser la même classe Item (nommer ItemTutorial dans le tuto) pour plusieurs items ? 
    Si oui comment faire ?



  • Bonjour,
    Il me semble que oui, c'est possible. Cependant, je ne garantis rien, je ne l'ai moi-même jamais expérimenté.
    Quant à comment le faire, il suffit d'utiliser les métadonnées, pour ce faire nous avons à disposition ce tutoriel : https://www.minecraftforgefrance.fr/showthread.php?tid=4383 qui explique comment créer des blocs avec des métadonnées… l'adapter pour les items devrait être faisable.
    Cependant, il ne faut pas oublier que les items avec metadonnées auront les mêmes attributs, leur seule différence sera leur texture. Vous pourrez bien entendu gérer la classe de l'item pour ajouter d'autres différences, mais si vos deux objets ne sont pas identiques, je ne vois pas l'intérêt d'utiliser deux fois la même classe.

    Je tiens également à préciser un autre point : les métadonnées sont utilisées par Minecraft sur les objets pour plusieurs choses : la boussole utilise les métadonnées pour afficher la bonne direction et les outils utilisent les métadonnées pour enregistrer la durabilité restante de l'objet.

    Voici donc mon ultime conseil :
    N'utilisez les métadonnées sur un objet que pour changer la texture de cet objet et non en créer un second.
    Pour en créer un second, il vous suffit juste de créer une nouvelle classe et si vous souhaitez créer un item ressemblant à un item déjà existant (ou que vous avez déjà créé), l'héritage existe pour cela. Je ne recommande donc pas l'utilisation de métadonnées pour créer deux items.

    Pour finir, j'ajouterais que ce que je dis n'est que pure spéculation, je ne suis absolument pas certain de ce que j'avance, je ne l'ai jamais testé et je suis incapable de garantir que cette méthode fonctionnera.



  • Alors oui on peut passer par les metadata, et pas seulement pour la texture vu qu'on peut aussi modifier le nom et certains comportements.
    Mais je crois qu'il voulait dire genre ```java
    public static Item item1 = new ItemTutorial();
    public static Item item2 = new ItemTutorial();

    Et là on utilise la même classe pour deux items, ce qui est totalement possible, à moins que les items aient des effets spécifiques, dans ce cas il vaut mieux avoir plusieurs classes.
    Je précise que par contre qu'il faudra modifier le "NAME" dans "TutorialItems.setItemName(this, NAME)" dans le constructeur de l'item, en mettant par exemple le NAME en paramètre du constructeur.


  • Ah, effectivement. J'avais pas pensé à ce point de vue. My bad, again.



  • @'AymericRed':

    Alors oui on peut passer par les metadata, et pas seulement pour la texture vu qu'on peut aussi modifier le nom et certains comportements.
    Mais je crois qu'il voulait dire genre ```java
    public static Item item1 = new ItemTutorial();
    public static Item item2 = new ItemTutorial();

    Et là on utilise la même classe pour deux items, ce qui est totalement possible, à moins que les items aient des effets spécifiques, dans ce cas il vaut mieux avoir plusieurs classes.
    Je précise que par contre qu'il faudra modifier le "NAME" dans "TutorialItems.setItemName(this, NAME)" dans le constructeur de l'item, en mettant par exemple le NAME en paramètre du constructeur.
    

    merci pour ta réponse ! Juste comment puis-je fare pour mettre name en paramètre du constructeur ?



  • Dans le constructeur de litem, tu mets "String NAME" entre les parenthèses, dans ta classe principale tu auras des erreurs, tu mets "Add arguments to match …" et tu mets entre guillemets le nom de l'item.


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

    Il manque un détail très important, tu as oublié de dire où doit aller le Json du modèle.



  • Le json doit aller dans le dossier assets/modid/models/items



  • bonjour je suis novice dans le developpement de mod et je ne comprend pas comment creer un deuxieme item qui utilise la meme classe item serai t'il possible d'avoir un tuto ecris ou des explication qui me permettrai de parvenir a mais fin ?


Log in to reply