Skip to content

L'utilisation d'un item sur un bloc

Bienvenue dans cours de modding de Minecraft avec Forge 1.20.1. Jusqu'à présent, nous avons appris à manipuler les aliments, à créer des armes puissantes, et à gérer la synchronisation entre le client et le serveur lors d'un clic droit dans le vide (méthode use).

Aujourd'hui, nous allons franchir une étape cruciale : interagir directement avec les blocs de notre monde. Qu'il s'agisse de transformer de la terre en un bloc magique, d'allumer un feu personnalisé ou de récolter une ressource spécifique, tout repose sur une seule et unique méthode système : useOn.


1. Comprendre le concept de useOn

Dans Minecraft, lorsque vous faites un clic droit avec un objet à la main, le jeu doit d'abord déterminer si vous visez un élément physique de l'environnement ou si vous cliquez simplement "dans le vide".

  • La méthode use(...) : Se déclenche de manière globale lorsque le joueur utilise l'objet, principalement sans bloc ciblé (comme manger ou bander un arc).

  • La méthode useOn(...) : Ne s'exécute que si et seulement si le curseur (crosshair) du joueur pointe directement un bloc à portée de main au moment du clic droit. C'est le comportement typique de la houe sur la terre ou du briquet sur l'obsidienne.

Analogie pour débutant : Le Rapport de Police

Imaginez que la méthode useOn soit un officier de police appelé sur une scène d'action. Minecraft ne vous donne pas les variables en vrac. À la place, il vous tend une mallette fermée contenant un rapport complet.

Cette mallette, c'est l'objet UseOnContext (contexte d'utilisation). À l'intérieur, vous y trouverez toutes les preuves : qui a cliqué, sur quel bloc, de quel côté du bloc, et dans quel monde !


2. Décortiquer l'objet UseOnContext

Pour exploiter la méthode useOn, nous devons inspecter les méthodes d'extraction du contexte UseOnContext (souvent abrégé en ctx ou context dans le code).

Méthode d'extractionType de retourDescription / Utilité
context.getLevel()LevelRécupère le monde actuel. Crucial pour faire notre vérification de sécurité level.isClientSide.
context.getClickedPos()BlockPosDonne les coordonnées exactes (X, Y, Z) du bloc sur lequel le joueur a cliqué.
context.getPlayer()PlayerRécupère l'entité du joueur qui a effectué l'action (peut être null dans certains cas rares d'automatisation).
context.getItemInHand()ItemStackReprésente l'exemplaire précis de l'objet actuellement tenu (utile pour réduire sa durabilité ou consommer l'item).
context.getClickedFace()DirectionIndique sur quelle face du bloc le clic a eu lieu (le dessus UP, le dessous DOWN, le Nord, le Sud, etc.).

3. Structure et Implémentation du Code

Créons un exemple concret : un outil personnalisé appelé BatonAlchimiqueItem. Lorsqu'on fait un clic droit sur un bloc de LOG (Bûche de chêne), il le transforme instantanément en bloc d'OR, joue un son de transformation et applique des dégâts d'usure à notre objet.

java
package com.monmod.items;

import net.minecraft.core.BlockPos;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.*;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;

public class BatonAlchimiqueItem extends Item {
    public BatonAlchimiqueItem(Properties properties) {
        super(properties);
    }

    @Override
    public InteractionResult useOn(UseOnContext context) {
        // 1. Extraire les données indispensables de la mallette "context"
        Level level = context.getLevel();
        BlockPos positionCliquee = context.getClickedPos();
        Player joueur = context.getPlayer();

        // 2. Déterminer quel type de bloc a été touché
        BlockState blocTouche = level.getBlockState(positionCliquee);

        // Sécurité : S'assurer qu'un joueur valide a fait l'action
        if (joueur == null) {
            return InteractionResult.PASS;
        }

        // 3. Condition : Est-ce du bois de chêne (Bûche)?
        if (blocTouche.is(Blocks.OAK_LOG)) {
            // 4. Gestion de la barrière asynchrone (Cours 9) : Actions physiques côté Serveur
            if (!level.isClientSide) {
                // Remplacer le bloc par un bloc d'or
                level.setBlockAndUpdate(positionCliquee, Blocks.GOLD_BLOCK.defaultBlockState());
                
                // Jouer un effet sonore à cet endroit précis
                level.playSound(null, positionCliquee, SoundEvents.AMETHYST_BLOCK_CHIME,
                        SoundSource.BLOCKS, 1.0F, 1.0F);
                
                // Appliquer 1 point de dégât de durabilité à l'item tenu
                context.getItemInHand().hurtAndBreak(1, joueur, (p) -> p.broadcastBreakEvent(context.getHand()));
            }
            // On informe Minecraft que l'action a réussi (bloque l'animation par défaut)
            return InteractionResult.SUCCESS;
        }

        // Si on clique sur autre chose qu'une bûche de chêne, on laisse passer sans rien faire
        return InteractionResult.PASS;
    }
}

Le Piège Absolu : Ne pas modifier le monde côté Client ! Si vous oubliez la condition if (!level.isClientSide) avant d'appeler la méthode level.setBlockAndUpdate(...), le bloc d'or apparaîtra visuellement sur l'écran du joueur, mais il sera totalement invisible et inexistant pour le serveur. Dès que le joueur tentera de marcher dessus ou de recharger sa partie, le bloc réapparaîtra magiquement sous forme de bûche de chêne. C'est le fameux bug du "bloc fantôme".


4. Les valeurs de retour de InteractionResult

À la fin de votre méthode useOn, vous devez impérativement renvoyer un état à Minecraft pour lui dire comment s'est déroulée l'interaction:

  • InteractionResult.SUCCESS : L'action s'est bien déroulée. Le jeu joue l'animation de balancement du bras (swing) et arrête de chercher d'autres interactions.

  • InteractionResult.CONSUME : Semblable à SUCCESS, souvent utilisé côté serveur pour valider la consommation d'une ressource.

  • InteractionResult.PASS : Rien ne s'est passé. Le jeu va continuer à chercher si le bloc lui-même possède une action prioritaire (comme ouvrir un coffre).

  • InteractionResult.FAIL : L'action a explicitement échoué. Annule toute autre tentative d'interaction.


Exercice Pratique Obligatoire

Pour valider la compréhension de ce cours, implémentez l'exercice suivant:

Objectif : Le Bâton de Jardinier Fertile

  1. Créez un item nommé BatonJardinierItem.

  2. Lorsque le joueur fait un clic droit sur un bloc de Terre standard (Blocks.DIRT):

  • Le bloc doit se transformer instantanément en Bloc d'Herbe (Blocks.GRASS_BLOCK).

  • Un effet de particules de type Bone Meal doit apparaître (Astuce : utilisez level.levelEvent(2005, positionCliquee, 0);).

  1. Si le joueur fait un clic droit sur la face supérieure (UP) d'un bloc d'Herbe:
  • Faites apparaître un bloc de Pissenlit (Blocks.DANDELION) juste au-dessus du bloc ciblé (utilisez la méthode positionCliquee.above()).

  • Attention à vérifier que le bloc au-dessus est bien de l'air !