Le meilleur moyen de créer des sorts ?

LaForêt

Bucheron
11 Septembre 2022
8
0
11
34
Salut,

J'ai besoin de différents points de vues pour ce que je souhaite faire, en gros pour faire simple je souhaite que les joueurs aient des "sorts", certains sorts effectues des dégâts, d'autres soignes, d'autres vont appliquer tel ou tel effet de potion.. Mais quel moyen et le meilleur pour arriver a faire ceci ? Pour l'instant je me dis qu'il faudrait faire un Objet pour chaque type de sort (SpellDamage, SpellHeal, SpellArmor..) mais n'y a t'il pas un meilleur moyen théorique ?

EDIT: Petit précision je ne cherche pas a savoir comment appliquer des effets ou des dégâts ou autres a un joueur mais quel et le meilleur moyen de créer des "sorts" sachant qu'un sort a une définition multiple (sort offensif, défensif, soutient par exemple)
 
Dernière édition:
Bonsoir,

La seule chose que tu veux faire avec un sort c'est l'utiliser, alors c'est pas très compliqué niveau abstraction :
Java:
public interface Spell {
   public void cast(LivingEntity caster);
}

je me dis qu'il faudrait faire un Objet pour chaque type de sort (SpellDamage, SpellHeal, SpellArmor..)
Un sort peut soigner et infliger des dégâts en même temps ;
Java:
public class ExampleSpell implements Spell {
   @Override
   public void cast(LivingEntity caster) {
      // CONSTANTES //
      final double healthCost = 3.0;
      final double range = 5;
      final PotionEffect poison = new PotionEffect(PotionEffectType.POISON, 5, 1);
      final PotionEffect regen = new PotionEffect(PotionEffectType.REGENERATION, 5, 1);
      
      // PRIX //
      if(caster.getHealth() <= healthCost) {
         caster.sendMessage("§cVie insuffisante !");
         return;
      }
      
      caster.setHealth(caster.getHealth() - healthCost);
      
      // ACTIVATION //
      for(Entity nearby : caster.getNearbyEntities(range * 2, range * 2, range * 2)) {
         if((nearby.getLocation().distanceSquared(caster.getLocation()) <= (range * range)) && nearby instanceof LivingEntity) {
            final LivingEntity target = (LivingEntity) nearby;
            
            if(target.getCategory() != EntityCategory.UNDEAD) {
               target.addPotionEffect(poison);
            }
            else {
               target.addPotionEffect(regen);
            }
         }
      }
   }
}

Je te déconseille de mettre trop de fonctions dans Spell car ça va être difficile à abstraire ; par ex. le coût d'un sort peut être en points de vie, mana, mana max, mana actuel, un certain nombre d'items, mana ET points de vie, ..., donc si tu veux afficher la description d'un sort fait plutôt une fonction :
Java:
public interface Spell {
   public String description(LivingEntity caster);
}

Et créer une classe utilitaire pour formater les données à ta place :
Java:
@Override
public String description(LivingEntity caster) {
   return DescriptionBuilder.spell("Exemple")
      .description("Blablabla")
      .costIf(Cost.HP_FLAT, 50, isVampire(caster)) // 50 pv
      .costIf(Cost.MANA_MAX, 5, !isVampire(caster)) // 5% mana max
      .finish();
}

n'y a t'il pas un meilleur moyen théorique ?
Tout dépend de ce que tu veux faire avec tes sorts, tu n'auras pas le même code en fonction de la complexité de ton système de magie.

Cordialement,
ShE3py
 
  • J'aime
Reactions: LaForêt
Bonsoir,

La seule chose que tu veux faire avec un sort c'est l'utiliser, alors c'est pas très compliqué niveau abstraction :
Java:
public interface Spell {
   public void cast(LivingEntity caster);
}


Un sort peut soigner et infliger des dégâts en même temps ;
Java:
public class ExampleSpell implements Spell {
   @Override
   public void cast(LivingEntity caster) {
      // CONSTANTES //
      final double healthCost = 3.0;
      final double range = 5;
      final PotionEffect poison = new PotionEffect(PotionEffectType.POISON, 5, 1);
      final PotionEffect regen = new PotionEffect(PotionEffectType.REGENERATION, 5, 1);
     
      // PRIX //
      if(caster.getHealth() <= healthCost) {
         caster.sendMessage("§cVie insuffisante !");
         return;
      }
     
      caster.setHealth(caster.getHealth() - healthCost);
     
      // ACTIVATION //
      for(Entity nearby : caster.getNearbyEntities(range * 2, range * 2, range * 2)) {
         if((nearby.getLocation().distanceSquared(caster.getLocation()) <= (range * range)) && nearby instanceof LivingEntity) {
            final LivingEntity target = (LivingEntity) nearby;
           
            if(target.getCategory() != EntityCategory.UNDEAD) {
               target.addPotionEffect(poison);
            }
            else {
               target.addPotionEffect(regen);
            }
         }
      }
   }
}

Je te déconseille de mettre trop de fonctions dans Spell car ça va être difficile à abstraire ; par ex. le coût d'un sort peut être en points de vie, mana, mana max, mana actuel, un certain nombre d'items, mana ET points de vie, ..., donc si tu veux afficher la description d'un sort fait plutôt une fonction :
Java:
public interface Spell {
   public String description(LivingEntity caster);
}

Et créer une classe utilitaire pour formater les données à ta place :
Java:
@Override
public String description(LivingEntity caster) {
   return DescriptionBuilder.spell("Exemple")
      .description("Blablabla")
      .costIf(Cost.HP_FLAT, 50, isVampire(caster)) // 50 pv
      .costIf(Cost.MANA_MAX, 5, !isVampire(caster)) // 5% mana max
      .finish();
}


Tout dépend de ce que tu veux faire avec tes sorts, tu n'auras pas le même code en fonction de la complexité de ton système de magie.

Cordialement,
ShE3py
Salut merci pour t'as réponse mais j'ai pas bien compris l'intérêt de l'interface "Spell" ? Admettons que j'ai un sort d'attaque qui s'appelle A et un sort qui soigne qui s'appelle B je vois mal comment bien utiliser l'interface en l'occurrence
 
Le programme sait qu'un joueur peut avoir entre zéro et une infinité de sorts, et que le joueur peut lancer un de ses sorts. Il ne fait aucune assomption en plus, c'est le sort lui-même qui une fois lancé va faire toutes ses vérifications et son activation.

Exemple hors-Minecraft :
Java:
public interface Consumable {
    public void consume(LivingBeing consumer);
}

La seule chose que permet de faire l'interface est de permettre à un autre vivant de consommer un consommable ; qu'il soit rassasié ou qu'il meurt d'intoxication n'est pas le problème de la fonction ; le joueur utilise l'objet, donc il le mange, point.

Exactement la même idée pour Spell ; le joueur veut lancer un sort quelconque, donc il le lance. Après effectivement, si les joueurs ont un kit de sorts fixe ça ne sert à rien :
Java:
public class WhiteMage {
    private final Player player;
    private final CureSpell cureSpell;
    private final ExtraCureSpell extraCureSpell;
}

Sauf que l'interface permet d'utiliser un sort quelconque :
Java:
public static void stealAndCastSpell(Player thief, Player victim) {
    Spell stolen = pickRandomSpell(victim);
    stolen.cast(thief);
}

Après j'ai utilisé une interface, mais une classe abstraite fonctionnerait aussi.

Admettons que j'ai un sort d'attaque qui s'appelle A et un sort qui soigne qui s'appelle B je vois mal comment bien utiliser l'interface en l'occurrence
Dans ce cas-là l'interface ne sert à rien, mais tu n'es pas toujours dans le cas où tu sais ce que tu as comme sort. Si tu veux utiliser un sort quelconque, il faut un moyen de le manipuler sans chercher quel sort concret tu manipules, car par ex. un autre plugin peut servir d'extension à ton plugin et rajouter ses propres Spell.
 
  • J'aime
Reactions: LaForêt