Attention : cet article a été écrit pour le SDK 2.2 d'Half-Life 1 ! Il se peut qu'il ne soit pas entièrement compatible avec des versions antérieures ou supérieures du Kit.

Introduction

Vous avez vu dans le tutorial sur comment créer un nouveau monstre, que chaque entité possédait une fonction Classify() permettant de classer cette entité parmis un des quinze types possibles. Nous allons voir ici comment est utilisée cette fonction pour définir les relations entres NPC ou joueur, et comment personnaliser ces relations.

La fonction IRelationship()

La fonction CBaseMonster::IRelationship() retourne une valeur d'un tableau de int, qui est une sorte de table des relations, avec les attitude de chaque type de CLASS_XXX envers les autres CLASS_XXX. La fonction ressemble à ceci :

int
CBaseMonster::IRelationship (CBaseEntity *pTarget)
{
  static int iEnemy[14][14] =
    {            //   NONE   MACH   PLYR    HPASS    HMIL    AMIL    APASS   ...
    /*NONE*/        { R_NO  ,R_NO   ,R_NO   ,R_NO   ,R_NO   ,R_NO   ,R_NO   ,...    },
    /*MACHINE*/     { R_NO  ,R_NO   ,R_DL   ,R_DL   ,R_NO   ,R_DL   ,R_DL   ,...    },
    /*PLAYER*/      { R_NO  ,R_DL   ,R_NO   ,R_NO   ,R_DL   ,R_DL   ,R_DL   ,...    },
    /*HUMANPASSIVE*/{ R_NO  ,R_NO   ,R_AL   ,R_AL   ,R_HT   ,R_FR   ,R_NO   ,...    },
    /*HUMANMILITAR*/{ R_NO  ,R_NO   ,R_HT   ,R_DL   ,R_NO   ,R_HT   ,R_DL   ,...    },
    /*ALIENMILITAR*/{ R_NO  ,R_DL   ,R_HT   ,R_DL   ,R_HT   ,R_NO   ,R_NO   ,...    },
    /*ALIENPASSIVE*/{ R_NO  ,R_NO   ,R_NO   ,R_NO   ,R_NO   ,R_NO   ,R_NO   ,...    },
    /* ... */       { ...   ,...    ,...    ,...    ,...    ,...    ,...    ,...    },
    };

  return iEnemy[Classify ()][pTarget->Classify ()];
}

Je n'ai pas recopié toute la fonction pour des raisons de commodité (et puis à vrai dire on s'en moque un peu). La fonction retourne donc une des macros suivantes, en fonction de ce que retourne la fonction Classify() de cette entitée et ce que retourne la fonction Classify() de l'autre entité :

Macro Valeur Signification Description
R_AL -2 ALLY Allié avec le monstre
R_FR -1 FEAR Aura peur, s'enfuira en présence du monstre
R_NO  0 NONE Ignorance
R_DL  1 DISLIKE Attaquera
R_HT  2 HATE Attaquera en priorité
R_NM  3 NEMESIS Pire ennemi, attaquera quoi qui se passe, priorité maximale

Seul le barnacle ne figure pas dans la table car il est un peu spécial.

Maintenant comment cette fonction est elle utilisée ? Et bien à certains endroits du code, comme les fonctions CBaseMonster::Look(), CBaseMonster::BestVisibleEnemy() ou CBaseMonster::GetEnemy() qui testent la valeur renvoyée avec le Classify() d'une autre entité. Par exemple :

// un NPC quelquonque
CBaseMonster *pMonster;

if (IRelationship (pMonster) == R_FR)
{
  // faire quelquechose...
}

C'est aussi avec cette fonction qu'on active des bits de conditions (bits_COND_XXX), avec un switch par exemple (voir fonction CBaseMonster::Look())...

Personnaliser les relations d'un monstre

La fonction CBaseMonster::IRelationship() reste cependant une fonction de base. L'héritage et la surcharge de classes nous permet de personnaliser cette table des relations pour chaque monstre.

On va prendre un exemple : le leech, l'espère de vermisseau qui vie dans la flotte et qui attaque le joueur. Si l'on prend CLeech::Classify() (leech.cpp), on obtient CLASS_INSECT. Et maintenant, si l'on regarde le tableau iEnemy (de IRelationship()) on voit que les CLASS_INSECT en présence de CLASS_PLAYER, ça renvoie R_FR (FEAR). Pour que le leech attaque le joueur, on a donc surchargé la fonction IRelationship() à la classe CLeech :

int
CLeech::IRelationship (CBaseEntity *pTarget)
{
  if (pTarget->IsPlayer ())
    return R_DL;

  return CBaseMonster::IRelationship (pTarget);
}

Ici, si pTarget (qui est l'entité confronté avec le monstre) est le joueur, IRelationship() retourne la macro R_DL (DISLIKE). Sinon, on retombe sur la fonction de CBaseMonster. Ainsi, nous avons créé une variante de CLASS_INSECT uniquement pour CLeech.

On peut s'amuser aussi à changer celui du Garg. Ajoutez la déclaration de fonction dans la classe CGargantua (gargantua.cpp) :

public:
  int IRelationship (CBaseEntity *pTarget);

puis juste après la définition de classe par exemple,

int
CGargantua::IRelationship (CBaseEntity *pTarget)
{
  if (pTarget->IsPlayer ())
    return R_FR; // a peur du joueur !
  else if (FClassnameIs (pTarget->pev, "monster_headcrab"))
    return R_HT; // deteste les headcrabs

  return CBaseMonster::IRelationship (pTarget);
}

Ainsi lorsque le Garg verra le joueur, il en aura une peur bleue et lorsqu'il rencontrera des headcrabq, il s'énervera dessus. On a utilisé ici, la fonction FClassenameIs() pour tester si pTarget est un headcrab. Si oui, on modifie la relation en R_HT (HATE) avec le headcrab.

Table des relations

Voici un tableau un peu plus propre des relations entres NPC/Joueur :

TARGET / MONSTER (0) NONE (1) MACHINE (2) PLAYER (3) HUMAN PASSIVE (4) HUMAN MILITARY (5) ALIEN MILITARY (6) ALIEN PASSIVE
NONE NONE NONE NONE NONE NONE NONE NONE
MACHINE NONE NONE DISLIKE DISLIKE NONE DISLIKE DISLIKE
PLAYER NONE DISLIKE NONE NONE DISLIKE DISLIKE DISLIKE
HUMAN PASSIVE NONE NONE ALLY ALLY HATE FEAR NONE
HUMAN MILITARY NONE NONE HATE DISLIKE NONE HATE DISLIKE
ALIEN MILITARY NONE DISLIKE HATE DISLIKE HATE NONE NONE
ALIEN PASSIVE NONE NONE NONE NONE NONE NONE NONE
ALIEN MONSTER NONE DISLIKE DISLIKE DISLIKE DISLIKE NONE NONE
ALIEN PREY NONE NONE DISLIKE DISLIKE DISLIKE NONE NONE
ALIEN PREDATOR NONE NONE DISLIKE DISLIKE DISLIKE NONE NONE
INSECT FEAR FEAR FEAR FEAR FEAR NONE FEAR
PLAYER ALLY NONE DISLIKE ALLY ALLY DISLIKE DISLIKE DISLIKE
PLAYER BIO WEAPON NONE NONE DISLIKE DISLIKE DISLIKE DISLIKE DISLIKE
ALIEN BIO WEAPON NONE NONE DISLIKE DISLIKE DISLIKE ALLY NONE

TARGET / MONSTER (7) ALIEN MONSTER (8) ALIEN PREY (9) ALIEN PREDATOR (10) INSECT (11) PLAYER ALLY (12) PLAYER BIO WEAPON (13) ALIEN BIO WEAPON
NONE NONE NONE NONE NONE NONE NONE NONE
MACHINE DISLIKE DISLIKE DISLIKE NONE DISLIKE DISLIKE DISLIKE
PLAYER DISLIKE DISLIKE DISLIKE NONE NONE DISLIKE DISLIKE
HUMAN PASSIVE HATE DISLIKE FEAR NONE ALLY NONE NONE
HUMAN MILITARY DISLIKE DISLIKE DISLIKE NONE HATE NONE NONE
ALIEN MILITARY NONE NONE NONE NONE DISLIKE NONE NONE
ALIEN PASSIVE NONE NONE NONE NONE NONE NONE NONE
ALIEN MONSTER NONE NONE NONE NONE DISLIKE NONE NONE
ALIEN PREY NONE NONE FEAR NONE DISLIKE NONE NONE
ALIEN PREDATOR NONE HATE DISLIKE NONE DISLIKE NONE NONE
INSECT FEAR FEAR FEAR NONE FEAR NONE NONE
PLAYER ALLY DISLIKE DISLIKE DISLIKE NONE NONE NONE NONE
PLAYER BIO WEAPON DISLIKE DISLIKE DISLIKE NONE DISLIKE NONE DISLIKE
ALIEN BIO WEAPON DISLIKE DISLIKE NONE NONE DISLIKE DISLIKE NONE
Creative Commons Logo Contrat Creative Commons

Cet article est mis à disposition sous un contrat Creative Commons (licence CC-BY-ND).