PNJ hostile : IA buffs/soins

Répondre
Partager Rechercher
Bonjour à tous,

Je cherche à créer un comportement permettant à un pnj hostile d'une rencontre de donner un peu plus de fil à retordre aux PJs.

L'idée est qu'il se soignera lorsque ses HP <= 60%, qu'il lancera des buffs lorsqu'il n'aura pas/plus leurs effets sur lui.

J'ai des résultats mais ils ne semblent pas 100% fiables...
Il semble difficile de pouvoir faire en sorte que la créature check "en temps réel" son état et agisse en conséquence.

J'ai tenté l'astuce OnPerceived qui se déclenche assez souvent avec ses 4 stades, pour peu qu'il y ait plusieurs PJ. Mais là encore j'ai quelques bugs (probablement une erreur (ou plusieurs ) dans mon code) :
Dont le plus ennuyeux : Le pnj relance des buffs qu'il a déjà lancé auparavant et dont les effets ne sont pas encore estompés (définitivement, ça sent l'erreur dans le code).

Voici mon script OnPerceived:

Code PHP:

void main()
{

    
object joueur GetLastPerceived();

    
        
    if(
GetLastPerceptionSeen() == TRUE)
    {
       
ClearAllActions(TRUE);    
       
ActionSpeakString("Vu");//String test
       
ExecuteScript("cita_pretre_ia",OBJECT_SELF);
    }
   else if(
GetLastPerceptionHeard() == TRUE)
    {
        
ClearAllActions(TRUE);
        
ActionSpeakString("Entendu");//String test
    //    ExecuteScript("cita_pretre_ia",OBJECT_SELF);
    
}
   else if(
GetLastPerceptionInaudible() == TRUE)
    {
        
ActionSpeakString("Inaudible");//String test
    //    ExecuteScript("cita_pretre_ia",OBJECT_SELF);
    
}
   else if(
GetLastPerceptionVanished() == TRUE)
    {
       
ActionSpeakString("Disparu");//String test
       
ExecuteScript("cita_pretre_ia",OBJECT_SELF);
    }


Puis le script "cita_pretre_ia" qui est appelé dans le OnPerceived :

Code PHP:

#include "NW_I0_GENERIC"

void main()
{
object joueur GetLastPerceived();
int HPpourcent;
int CurrentHP GetCurrentHitPoints();
int MaxHP GetMaxHitPoints();

    if(
CurrentHP<MaxHP)
    {
    
HPpourcent GetPercentageHP(OBJECT_SELF);
    if (
HPpourcent<=60)// Si pourcentage d'HP restant <= 60%
    
{
    
AssignCommand(OBJECT_SELFActionCastSpellAtObject(114OBJECT_SELFMETAMAGIC_QUICKENTRUE20));
    
ActionSpeakString("Je dois me soigner!!");//String pour tests
    
}
    else
    {
    
ActionSpeakString("HP OK");//String pour tests
    
}
    }
    
    if (
GetHasSpellEffect(1024OBJECT_SELF)==FALSE)
    
//Endurance de l'ours de groupe
    
{
    
AssignCommand(OBJECT_SELFActionCastSpellAtObject(1024OBJECT_SELFMETAMAGIC_QUICKENTRUE20));
    
    
//Un heal rapide pour enquiquiner le(s) joueur(s) ^^'
    
AssignCommand(OBJECT_SELFActionCastSpellAtObject(114OBJECT_SELFMETAMAGIC_QUICKENTRUE20PROJECTILE_PATH_TYPE_DEFAULTTRUE));
    
    }
    if (
GetHasSpellEffect(1018OBJECT_SELF)==FALSE)
    
//Protection contre la mort de groupe
    
{
    
AssignCommand(OBJECT_SELFActionCastSpellAtObject(1018OBJECT_SELFMETAMAGIC_QUICKENTRUE20));
    
    }    
    if(
GetHasSpellEffect(42OBJECT_SELF)==FALSE)
    
//Puissance Divine
    
{
    
AssignCommand(OBJECT_SELFActionCastSpellAtObject(42OBJECT_SELFMETAMAGIC_QUICKENTRUE20));

    }
    if(
GetHasFeatEffect(414OBJECT_SELF)==FALSE)
    
//Est-ce qu'on a bouclier divin ?
    
{
    
AssignCommand(OBJECT_SELFActionUseFeat(414OBJECT_SELF));

    }
    if (
GetHasSpellEffect(133OBJECT_SELF)==FALSE)
    
//Prière
    
{
    
AssignCommand(OBJECT_SELFActionCastSpellAtObject(133OBJECT_SELFMETAMAGIC_QUICKENTRUE20));
    
    }
    

Sinon, j'ai fais quelques tests en utilisant le OnUserDefined, mais là encore ce n'est pas probant.
J'ai des résultats aléatoires, avec des rebuffs incohérents et des heals qui sont lancés bien trop tardivement.

J'aimerai avoir vos avis/remarques/commentaires/conseils pour que je puisse mener mon humble projet de novice à bien

Merci d'avance !
Il vaudrait mieux que tu utilises le script récurrent du npc. Tu l'actives quand le prêtre entre en combat et tu le désactive à la fin. Avec ça tu auras une créature qui vérifie son état en "temps réel" (toutes les six secondes).

Pour le stackbuf tu peux essayer de noter les sorts que le prêtre lance pour éviter qu'il ne les relance plus tard, avec peut-être un DelayCommand sur la suppression de la variable une fois la durée terminée... Mais le HasEffect devrait fonctionner. Essaie d'abord avec le heartbeat.
Merci du conseil, j'étais assez "froid" par rapport au OnHearBeat, mais n'avais pas pensé à l'activer/désactiver !

Peut être que mon problème de stackbuff vient du fait que je passe par "assigncommand", ça ne revient pas par hasard à mettre en "file d'attente" une action à faire ?

Je me lance dans l'option script récurrent
J'ai pensé à une possibilité :

Passer par le OnUserDefined, prendre l'évènement :
SetSpawnInCondition(NW_FLAG_ATTACK_EVENT);

Je pense que cela renvoie si le pnj est en situation de combat, non ?

Si la valeur nEvent == 1005 alors on execute un script "X" qui serait un heartbeat personnalisé fonctionnant ainsi :

Si LocalInt en combat == 1

Alors, on exécute le reste du code [Avec un delaycommand de X second (pour rendre la gestion "temps réel" plus fréquente) à la fin.
Sinon, rien ne se passe.

Il faudra que je test ça, mais si vous avez des recommandations et erreurs à éviter sur ce genre de HeartBeat personnalisé, je suis preneur
Oui, AssignCommand met en file d'attente les actions que tu lui donnes en paramètre. Exactement comme si toi en tant que joueur tu lançais huit sorts d'affilée, le premier serait lancé puis les sept autre mis dans la file d'actions.

Du coup tente avec des variables, par exemple:
Code PHP:

// Lance le sort s'il n'est pas déjà noté comme lancé/en cours
void CastIfNeeded(object oint spellint meta 8int hax 1int clvl 20)
{
   
// Vérifie si la valeur de la variable ".spell" existe sur la cible, lance le sort sinon
   
if (!GetLocalInt(o"." IntToString(spell))
   {
      
// Lance le sort
      
AssignCommand(oActionCastSpellAtObject(spellometahaxclvl));
      
// Marque le sort sur le personnage
      
SetLocalInt(o"." IntToString(spell), 1);
   }
}

void main()
{
   
// ...
   
CastIfNeeded(OBJECT_SELF1024); // Mass bear endurance
   
CastIfNeeded(OBJECT_SELF1018); // Mass death ward
   // ...

Faut bien que tu penses l'algo pour éviter que le npc ne mette tout ses sorts d'un coup dans la file d'action... enfin tu verras bien le résultat en jeu.




Edit: Pour le beat au DelayCommand, il m'est déjà arrivé d'avoir des scripts lancés avec DelayCommand qui ne se lançait pas quand je l'avais demandé, voire ne se lançait pas (les deux restent rares). Je n'ai jamais pu trouver l'origine du mal. Tu pourrais avoir un jour des soucis là-dessus, c'est pas dit.
Le script fonctionne bien, placé dans le OnHeartBeat, mais ne se lance que lorsque la créature n'est pas en situation de combat.

Je sèche un peu :/


Code PHP:

#include "NW_I0_GENERIC"

void CastIfNeeded(object oint spellint meta 8int hax 1int clvl 20)
{
   
// Vérifie si la valeur de la variable ".spell" existe sur la cible, lance le sort sinon
   
if (!GetLocalInt(o"." IntToString(spell)))
   {
      
// Lance le sort
      
ActionCastSpellAtObject(spellometahaxclvl);
      
// Marque le sort sur le personnage
      
SetLocalInt(o"." IntToString(spell), 1);      
   }
}

void main()
{
    
object o OBJECT_SELF;
    
int HPpourcent = (GetCurrentHitPoints(OBJECT_SELF)*100)/GetMaxHitPoints(OBJECT_SELF);
    
   
    
   
CastIfNeeded(OBJECT_SELF1024); // Mass bear endurance 
   //Si sort précédent lancé alors... 
   
if(GetLocalInt(o".1024")==1)
   {
   
CastIfNeeded(OBJECT_SELF1018); // Mass death ward      
   
}
   if(
GetLocalInt(o".1018")==1)
   {
   
CastIfNeeded(OBJECT_SELF114); // Heal suprème de groupe    
   
}
   if(
GetLocalInt(o".114")==1)
   {
   
CastIfNeeded(OBJECT_SELF42); // Puissance Divine         
   
}
   if(
GetLocalInt(o".42")==1)
   {
   
CastIfNeeded(OBJECT_SELF133); // Prière
   
}
   
       if (
HPpourcent<=60)// Si pourcentage d'HP restant <= 60%
    
{
    
ActionSpeakString("Je dois me soigner!!");//String pour tests
    
ActionCastSpellAtObject(114OBJECT_SELF8TRUE20);    
    }
    else
    {
    
ActionSpeakString("HP OK");//String pour tests
    
}


Mea Culpa !

Dans le OhB il faut faire un ClearAllActions() juste avant ce qu'on veut qu'il fasse; sinon il ne prendra pas en considération ce code en plein combat

Bon, une toute dernière question car maintenant j'arrive à bien faire ce que je veux, merci à toi Lv4 !

Pour ne pas rendre le OhB de la créature trop lourde, j'ai fais ceci :

(dans le script de base OhB de toute créature, à la fin du code)
Check si on a "X"
Si oui, alors => return; (là je met bien fin au script directement non ? )
Si non, alors lire reste du code.

Lorsque la créature est buffée, le simple check inclu dans le code du OhB ne va pas créer + de lag que ne le fait le script de base ?
Je post le script (pas final, mais fonctionnel) pour ceux que ça intéresserait.

Code PHP:

if(GetHasSpellEffect(1024) && GetHasSpellEffect(1018)
           && 
GetHasSpellEffect(42) && GetHasSpellEffect(133))
          {
      
          
//Si on a déjà tous les buffs, on arrête le script.
          
return; 
          }
        
//Sinon...on arrête ce qu'on est en train de faire.
          
ClearAllActions();
              
//Si on n'a pas Endurance de l'ours 'groupe'
              
if(!GetHasSpellEffect(1024o))
                  {            
                  
AssignCommand(oActionCastSpellAtObject(1024OBJECT_SELF8TRUE20));    
                  }
                
//Sous condition pour éviter le méli-mélo.
                  
if(GetHasSpellEffect(1024o))
                      {
                    
//Etc...
                      
if(!GetHasSpellEffect(1018o))
                          {              
                          
AssignCommand(oActionCastSpellAtObject(1018OBJECT_SELF8TRUE20));      
                          }
                      }
                  if(
GetHasSpellEffect(1018o))
                      {
                      if(!
GetHasSpellEffect(42o))
                          {          
                          
AssignCommand(oActionCastSpellAtObject(42OBJECT_SELF8TRUE20));      
                          }
                      }
                  if(
GetHasSpellEffect(42o))
                      {
                      if(!
GetHasSpellEffect(133o))
                          {
                          
AssignCommand(oActionCastSpellAtObject(133OBJECT_SELF8TRUE20));
                          }
                      } 
Citation :
Publié par El@ndIr
Si oui, alors => return; (là je met bien fin au script directement non ? )

[...]

Lorsque la créature est buffée, le simple check inclu dans le code du OhB ne va pas créer + de lag que ne le fait le script de base ?
Oui pour return, ça stoppe le script.

Faut pas s'en faire tant que ça pour un script récurrent. C'est pas le beat d'une créature particulière -qui de surcroît n'est pas toujours activé- qui va faire planter un module. Un test sur une variable locale est très rapide donc tu n'as pas grand chose à craindre.

Si malgré tout tu veux être au courant des temps tu peux utiliser le plugin profiler, ça te donnera une idée de ce qui bouffe le plus de temps sur ton module (et ça m'étonnerait que ce soit ce beat ^^).
Si je puis me permettre un conseil ou deux, je crois qu'il serait plus pratique pour toi de mettre ton code dans une fonction, dans un script à part (en faire une "bibliothèque"), et ensuite de modifier l'heartbeat par défaut, avec un petit if sur une variable qui si elle existe lance la fonction en question. Tu peux être amener à modifier ce script (le heartbeat) à l'avenir, et je pense que ça sera plus pratique pour toi ainsi.

Un autre point. Plutôt que figer tes sorts à relancer dans le code, pourquoi ne pas les mettre en variable sur le PNJ en question, et regarder ces variables via script. Une simple boucle te permettrait alors de gérer simplement tous les sorts possibles, et les sorts en question seraient dépendant du PNJ, et non du script.
Merci pour tes remarques C.Duk !

En fait, je suis vraiment beginner, du coup j'ai du mal à optimiser mon code c'est vrai ^^
J'essaierai d'optimiser tout ça en m'inspirant du code de Lv4
Répondre

Connectés sur ce fil

 
1 connecté (0 membre et 1 invité) Afficher la liste détaillée des connectés