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

Ce tutorial est basé sur celui sur la création d'une nouvelle arme. Nous allons voir ici comment intégrer les events dans le code de notre arme pour économiser de la bande passante et diminuer le ping (en théorie).

Mais que va-t-on faire avec ces events ? Et bien on va faire faire certaines actions anciennement codées dans la dll serveur à la DLL client. Quelles actions ? Toutes celles qui sont de l'ordre de « l'esthétique », c'est à dire jouer les animation du modèle, dessiner les impacts de balles et éjecter les douilles.

Côté serveur

On va d'abord commencer par la DLL serveur. Retournez à votre définition de classe (dans weapons.h normalement) pour y ajouter une variable membre privée :

class CMonArme : public CBaseWeapon
{
  // ...

private:
  unsigned short m_usMonArme;  // events
};

Cette variable servira à stocker un index pour l'event. Maintenant retournez dans le fichier de votre arme et rajoutez cette instruction dans la fonction Precache() :

// event
m_usMonArme = PRECACHE_EVENT (1, "events/monarme.sc");

Voilà notre event est précaché, maintenant on peut passer à la fonction PrimaryAttack(), là où l'on appellera notre event. Si vous avez bien suivis le tutorial sur comment créer une nouvelle arme, supprimez toute la partie entre les slashs dans PrimaryAttack(), là où l'on fait appel à SendWeaponAnim(), EMIT_SOUND(), EjectBrass() et DecalGunshot() pour la remplacer par ça :

  ///////////////////////////////////////////////////////////////////////////////
  Vector vecSrc = m_pPlayer->GetGunPosition ();
  Vector vecAiming = m_pPlayer->GetAutoaimVector (AUTOAIM_5DEGREES);
  Vector vecDir = m_pPlayer->FireBulletsPlayer (1,
                                                vecSrc,
                                                vecAiming,
                                                VECTOR_CONE_3DEGREES,
                                                8192,
                                                BULLET_PLAYER_MONARME,
                                                0,
                                                0,
                                                m_pPlayer->pev,
                                                m_pPlayer->random_seed);

  // on appelle l'event chez la client dll
  PLAYBACK_EVENT_FULL (0,                     // drapeaux d'état
                       m_pPlayer->edict (),   // *pInvoker
                       m_usMonArme,           // index event
                       0.0,                   // délai avant action de l'event
                       (float *)&g_vecZero,   // origine
                       (float *)&g_vecZero,   // angles
                       vecDir.x,              // paramètre #1
                       vecDir.y,              // paramètre #2
                       0, 0, 0, 0 );          // Autres paramètres inutilisés

  ///////////////////////////////////////////////////////////////////////////////

Ici on appelle toujours FireBulletsPlayer() pour appliquer les dégâts à la cible puis on appelle directement l'event avec PLAYBACK_EVENT_FULL(). Tout le reste va donc se passer côté client. Vous pouvez compiler, c'est tout ce qu'il y'a à faire côté serveur.

Côté client

Bon, là il va y avoir un petit plus de boulot. Commencez par aller dans hl_events.cpp et dans l'extern C ajoutez notre nouvelle fonction event à la suite des autres :

void EV_MonArmeFire (struct event_args_s *args);

Puis un peu plus bas dans la fonction Game_HookEvents(), on va relier l'event à cette nouvelle fonction :

gEngfuncs.pfnHookEvent ("events/monarme.sc", EV_MonArmeFire);

Maintenant dans ev_hldm.cpp on va redéclarer notre fonction dans l'extern C au début du fichier :

void EV_MonArmeFire (struct event_args_s *args);

Allez dans ev_hldm.h et dans le premier enum (celui de Bullet normalement) ajoutez cette ligne :

BULLET_PLAYER_MONARME,

Descendez à la fin du fichier et placez avant les quatre déclarations de fonction la liste des animations du modèle « view » de votre arme :

enum monarme_e
  {
    MA_IDLE1 = 0,
    MA_IDLE2,
    MA_IDLE3,
    MA_SHOOT,
    MA_SHOOT_EMPTY,
    MA_RELOAD,
    MA_DRAW,
    MA_HOLSTER,
  };

Retournez dans ev_hldm.cpp et allez dans la fonction EV_HLDM_DecalGunshot(). Cherchez le switch de iBulletType et ajoutez cette ligne juste avant le cas default :

case BULLET_PLAYER_MONARME:

Descendez un peu jusqu'à la fonction EV_HLDM_FireBullets(). Une fois dedans, trouvez le switch de iBulletType et ajoutez ce bloc de code :

case BULLET_PLAYER_MONARME:
  EV_HLDM_PlayTextureSound (idx, &tr, vecSrc, vecEnd, iBulletType);
  EV_HLDM_DecalGunshot (&tr, iBulletType);
  break;

Voilà. Il ne reste plus qu'à définir notre fonction EV_MonArmeFire() :

// --------------------------------------------------------------------------
// EV_MonArmeFire
//
// Event du tir de Mon Arme.
// --------------------------------------------------------------------------

void
EV_MonArmeFire (struct event_args_s *args)
{
  int idx;
  vec3_t origin;
  vec3_t angles;
  vec3_t velocity;

  vec3_t ShellVelocity;
  vec3_t ShellOrigin;
  int shell;
  vec3_t vecSrc, vecAiming;
  vec3_t up, right, forward;

  idx = args->entindex;
  VectorCopy (args->origin, origin);
  VectorCopy (args->angles, angles);
  VectorCopy (args->velocity, velocity);

  AngleVectors (angles, forward, right, up);

  // modèle de la douille
  shell = gEngfuncs.pEventAPI->EV_FindModelIndex ("models/shell.mdl");

  if (EV_IsLocal (idx))
    {
      // flash
      EV_MuzzleFlash ();

      // on joue l'animation du modèle "view" de l'arme
      gEngfuncs.pEventAPI->EV_WeaponAnimation (MA_SHOOT, 2);

      // l'angle de vue est décalé du à la force du tir (pour le réalisme ;))
      V_PunchAxis (0, -2.0);
    }

  // on récupère les infos sur la douille (origine, vitesse, ...)
  EV_GetDefaultShellInfo (args, origin, velocity, ShellVelocity, ShellOrigin,
                          forward, right, up, 20, -12, 4 );

  // on ejecte la douille
  EV_EjectBrass (ShellOrigin, ShellVelocity, angles[YAW], shell, TE_BOUNCE_SHELL); 

  // on joue un son de tir
  gEngfuncs.pEventAPI->EV_PlaySound (idx, origin, CHAN_WEAPON, "weapons/monarme_fire.wav",
                                     gEngfuncs.pfnRandomFloat (0.92, 1.0), ATTN_NORM, 0,
                                     98 + gEngfuncs.pfnRandomLong (0, 3));

  EV_GetGunPosition (args, vecSrc, origin);
  VectorCopy (forward, vecAiming);

  // on dessine un decal d'impact contre le mur
  EV_HLDM_FireBullets (idx, forward, right, up, 1, vecSrc, vecAiming, 8192,
                       BULLET_PLAYER_MONARME, 0, 0, args->fparam1, args->fparam2);
}

Dans cette fonction, on récupère les paramètres de l'event envoyés depuis la DLL serveur. Certaine fonctions ressemblent assez à celles de la DLL serveur (comme EV_HLDM_FireBullets() ou EV_EjectBrass()). EV_GetDefaultShellInfo() calcule les vecteurs ShellVelocity et ShellOrigin à notre place.

Conclusion

Voilà c'est déjà terminé ! Finalement c'était pas bien long à convertir le code serveur en event. Si vous avez un SecondaryAttack(), vous pouvez très bien créer un second event pour cette fonction. N'oubliez pas de créer un fichier monarme.sc dans le dossier event de votre mod !

Creative Commons Logo Contrat Creative Commons

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