Plugin Résolu [JAVA] Faire une action lors d'un impact avec une flèche

Kenda

Architecte en herbe
16 Juillet 2016
300
1
2
125
32
www.youtube.com
Bonjour,
Je développement un petit plugin, et j'aimerai faire des actions différentes avec des flèches.
Par exemple, si je tire la flèche "Explosive", lors d'un impact, elle doit faire une explosion.
Si je tire la flèche "téléportation", alors je me fais tp
etc...
J'ai chercher avec l'event "EntityShootBowEvent" et "ArrowHitEvent" , mais rien. Je n'arrive pas à récupéré le nom de la flèche à l'impact.
Quelqu'un aurait-il une petite aide ? Merci.
 

ShE3py

Enbogueuse
Support
26 Septembre 2015
4 422
193
489
247
21
Mìlhüsa
Bonsoir,

Tu peux récupérer l'UUID de la flèche EntityShootBowEvent#getProjectile() et le stocker dans une List<UUID>/Map<UUID, V>, puis mettre ta logique dans ArrowHitEvent.

À noter qu'une flèche peut tomber plusieurs fois, et qu'il faut probablement désactiver l'effet si la flèche est parée par un bouclier.

Cordialement,
ShE3py
 

Kenda

Architecte en herbe
16 Juillet 2016
300
1
2
125
32
www.youtube.com
Mais ducoup, comment que je peux verifier les UUID des flèches ? par exemple que la flèche "Teleport" soit différente d'une autre flèche ?
Car oui, les UUID sont unique, mais je peux pas vraiment les comparé ? Sauf si je met un boolean pour bloquer l'utilisation je suppose
 

ShE3py

Enbogueuse
Support
26 Septembre 2015
4 422
193
489
247
21
Mìlhüsa
Un truc comme ça ?
Java:
public class MyEventHandler implements EventListener {
    private final Map<UUID, ImpactEffect> effects = new HashMap<>();
 
    @EventHandler
    public void onEntityShootBow(EntityShootBowEvent e) {
       this.effects.put(e.getProjectile().getUniqueId(), ImpactEffect.TELEPORT); // ou `new ImpactEffect.Explode(...)`
    }
 
    @EventHandler
    public void onProjectileHit(ProjectileHitEvent e) {
       final UUID key = e.getEntity().getUniqueId();
       final ImpactEffect effect = this.effects.getOrDefault(key, ImpactEffect.NONE); // `ImpactEffect.NONE` nous évite de gérer les `null`
     
       if(effect.onHit(e)) {
          this.effects.remove(key, effect);
       }
    }
}

Implémentation (en programmation) fonctionnelle d'ImpactEffect :
Java:
@FunctionalInterface
public interface ImpactEffect {
    /**
     * @return {@code true} si le projectile doit perdre son effet, {@code false} si le projectile doit garder son effet.
     */
    // L'accès d'une méthode est forcément `public` dans les interfaces, donc le mot-clef est redondant.
    boolean onHit(ProjectileHitEvent e);
 
    /**
     * Téléporte le joueur à l'endroit du projectile si celui-ci a percuté un bloc.
     */
    // @FunctionalInterface nous permet de directement définir l'unique méthode de l'interface via une lambda.
    // De la même façon, l'accès d'un champ est forcément `public static final`, donc les mots-clefs sont redondants.
    ImpactEffect TELEPORT = e -> {
       Block block = e.getHitBlock();
       BlockFace face = e.getHitBlockFace();
     
       if(e.getEntity().getShooter() instanceof Entity shooter && block != null) {
          return shooter.teleport(block.getRelative(face != null ? face : BlockFace.UP).getLocation());
       }
       else {
          return false;
       }
    };
 
    /**
     * Créer une explosion à l'endroit du projectile.
     */
    // Idem, l'accès d'une classe interne d'une interface est forcément `public static`.
    final class Explode implements ImpactEffect {
       /**
        * La puissance de l'explosion, où {@code 4} correspond à une TNT.
        */
       public final float power;
       public final boolean setFire, breakBlocks;
     
       public Explode(float power, boolean setFire, boolean breakBlocks) {
          this.power = power;
          this.setFire = setFire;
          this.breakBlocks = breakBlocks;
       }
     
       /**
        * Créer une explosion détruisant les blocs.
        */
       public Explode(float power, boolean setFire) {
          this(power, setFire, true);
       }
     
       /**
        * Créer une explosion détruisant les blocs, mais ne mettant pas le feu.
        */
       public Explode(float power) {
          this(power, false);
       }
     
       @Override
       public boolean onHit(ProjectileHitEvent e) {
          Projectile projectile = e.getEntity();
          ProjectileSource shooter = projectile.getShooter();
       
          Location origin = projectile.getLocation();
          Entity source = (shooter instanceof Entity) ? (Entity) shooter : null;
       
          return projectile.getWorld().createExplosion(origin.getX(), origin.getY(), origin.getZ(), this.power, this.setFire, this.breakBlocks, source);
       }
    }
 
    /**
     * N'effectue aucune action à l'impact.
     */
    ImpactEffect NONE = e -> true;
 
    /**
     * Combine plusieurs {@linkplain ImpactEffect}.
     */
    final class Combine implements ImpactEffect {
       private final ImpactEffect[] effects;
     
       public Combine(ImpactEffect[] effects) {
          this.effects = effects;
       }
     
       public Combine(Iterable<ImpactEffect> effects) {
          this(Iterables.toArray(effects, ImpactEffect.class));
       }
     
       @Override
       public boolean onHit(ProjectileHitEvent e) {
          boolean loseEffect = false;
       
          for(ImpactEffect effect : this.effects) {
             loseEffect |= effect.onHit(e);
          }
       
          return loseEffect;
       }
    }
}
 

Kenda

Architecte en herbe
16 Juillet 2016
300
1
2
125
32
www.youtube.com
Ducoup, je dois crée une class "ImpactEffect" qui serait les impacts de la flèche ? si je comprend bien
 

ShE3py

Enbogueuse
Support
26 Septembre 2015
4 422
193
489
247
21
Mìlhüsa
Bah tu as plusieurs choix ;
  • Map<UUID, Interface> où tu mets la logique de l'effet dans une méthode de l'interface ;

  • Map<UUID, Énumération> où tu mets la logique de l'effet dans une méthode de l'énumération ;

  • Map<UUID, Énumération> où tu mets la logique de l'effet dans l'@EventHandler (tu switch sur l'enum) ;

  • Plusieurs List<UUID> (une par effet), où tu mets la logique de l'effet dans l'@EventHandler
    Java:
    if(this.teleportEffects.contains(uuid)) ... else if(this.explodeEffects.contains(uuid)) ...
Le problème de l'énumération est que tu ne peux pas avoir de paramètres, style toutes les explosions seront les mêmes (ou du moins, tu auras plusieurs explosions).

Tu peux voir la version avec l'interface fonctionnelle comme une fonction :
Java:
private boolean sayHello(ProjectileHitEvent e) {
    e.getEntity().sendMessage("Hello!");
    return true;
}

@EventHandler
public void onEntityShootBow(EntityShootBowEvent e) {
    ImpactEffect effect = this::sayHello;
 
    this.effects.put(e.getProjectile().getUniqueId(), effect);
}

Tu peux d'ailleurs remplacer les champs constants par une fonction :
Diff:
 @FunctionalInterface
 public interface ImpactEffect {
     /**
      * @return {@code true} si le projectile doit perdre son effet, {@code false} si le projectile doit garder son effet.
      */
     // L'accès d'une méthode est forcément `public` dans les interfaces, donc le mot-clef est redondant.
     boolean onHit(ProjectileHitEvent e);
   
     /**
      * Téléporte le joueur à l'endroit du projectile si celui-ci a percuté un bloc.
      */
-    ImpactEffect TELEPORT = e -> {
+    static boolean teleport(ProjectileHitEvent e) {
         Block block = e.getHitBlock();
         BlockFace face = e.getHitBlockFace();
       
         if(e.getEntity().getShooter() instanceof Entity shooter && block != null) {
             return shooter.teleport(block.getRelative(face != null ? face : BlockFace.UP).getLocation());
         }
         else {
             return false;
         }
     };
 }
Java:
ImpactEffect teleport = ImpactEffect::teleport;
Donc tu délègues le travail de gestion de l'évènement à n'importe quelle fonction que tu veux.
 
Dernière édition:

Kenda

Architecte en herbe
16 Juillet 2016
300
1
2
125
32
www.youtube.com
Ouaip, mais ducoup, si j'ai une flèche avec le nom "Téléportation", comment je peux bind une classe avec cette flèche précisement ?
Genre la flèche avec le nom "Téléportation", doit fait l'effet de TP, si le nom c'est "Explosion" alors la flèche doit exploser
 

ShE3py

Enbogueuse
Support
26 Septembre 2015
4 422
193
489
247
21
Mìlhüsa
Bah avec des if ?

Java:
@EventHandler
public void onEntityShootBow(EntityShootBowEvent e) {
    ImpactEffect effect = ImpactEffect.NONE;
   
    if(/* joueur a la classe mage */) {
       effect = ImpactEffect.TELEPORT;
    }
    else if(/* l'arc a pour nom « bombardier » */) {
       effect = new ImpactEffect.Explode(4);
    }
   
    this.effects.put(e.getProjectile().getUniqueId(), effect);
}
 

ShE3py

Enbogueuse
Support
26 Septembre 2015
4 422
193
489
247
21
Mìlhüsa
Haaaaaa, tu parlais de l'item flèche, je pensais que tu parlais de l'entité. Avec EntityShootBowEvent#getConsumable() ?

Java:
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) // nous sommes Big Brothers ; on ne veut que être sûrs que le projectile sera créé.
public void onEntityShootBow(EntityShootBowEvent e) {
    ImpactEffect effect = ImpactEffect.NONE;
   
    ItemStack consumable = e.getConsumable();
   
    ItemMeta meta;
    if(consumable != null && consumable.hasItemMeta() && (meta = consumable.getItemMeta()).hasDisplayName() && meta.getDisplayName().equals("Vroum vroum")) {
       effect = new ImpactEffect.Explode(4);
    }
   
    this.effects.put(e.getProjectile().getUniqueId(), effect);
}