Utiliser les events avec une arme

Écrit le 03/07/2003 par DukeNukem
Dernière mise à jour : 30/01/2006

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 animations 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 :

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ù on appellera notre event.

Si vous avez bien suivi le tuto sur "comment créer une nouvelle arme", supprimer toute la partie entre les slashs dans PrimaryAttack(), là où l'ont 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à 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 animation 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 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 éjecte 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. Certaines fonctions ressemblent assez à celles de la mp.dll (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 !