Coder un système de classe

Écrit le 03/07/2003 par douanier007
Dernière mise à jour : 06/02/2006

Introduction

Pour coder un système de classe, il faut créer un menu (dernière partie) et coder la partie serveur qui changera la classe du joueur.

Partie server

Voilà le code à ajouter dans le SDK côté serveur.

Dans bg_public.h, juste après :

typedef enum {
    TEAM_FREE,
    TEAM_RED,
    TEAM_BLUE,
    TEAM_SPECTATOR,

    TEAM_NUM_TEAMS
} team_t;

ajoutez ça :

typedef enum {
    CLASSE_SOLDAT,
    CLASSE_CHASSEUR,
    CLASSE_JEDI,
    CLASSE_WAIT,

    CLASSE_NUM_CLASSE
} classe_t;

ensuite, dans g_local.h ajoutez juste après ça :

team_t    sessionTeam;

cette définition :

classe_t    sessionClasse; // la classe courante;

dans g_cmds.c maintenant :
juste avant ça :

/*
=================
SetTeam
=================
*/
void SetTeam( gentity_t *ent, char *s ) {

vous ajoutez ça :

/*
=================
SetClasse douanier007
=================
*/
void SetClasse( gentity_t *ent, char *s ) {

    int classe,oldclasse;
    gclient_t                *client;
    int                          clientNum;
    spectatorState_t    specState;
    int                          specClient;
    char                      classename[50];

    client = ent->client;

    clientNum = client - level.clients;
    specClient = 0;
    specState = SPECTATOR_NOT;

    if ( !Q_stricmp( s, "jedi" )) {
        classe = CLASSE_JEDI;
        strcpy(classename,"jedi");
    }
    else if ( !Q_stricmp( s, "chasseur" ) ) {
        classe = CLASSE_CHASSEUR;
        strcpy(classename,"Chasseur de prime");
    }
    else  if ( !Q_stricmp( s, "soldat" )) {
        classe = CLASSE_SOLDAT;
        strcpy(classename,"soldat");
    }
    else {
        classe = CLASSE_SOLDAT;
        strcpy(classename,"soldat");
    }

    oldclasse = client->sess.sessionClasse;
    if ( classe == oldclasse ) {
        return; //on ne change pas de classe pour la même classe
    }

    //on le tue et on vire flags etc
    ent->flags &= ~FL_GODMODE;
    ent->client->ps.stats[STAT_HEALTH] = ent->health = 0; player_die (ent, ent, ent, 100000, MOD_SUICIDE);

    client->sess.sessionClasse = classe;
    client->sess.spectatorState = specState;
    client->sess.spectatorClient = specClient;

    Com_Printf("Vous êtes désormais un %s ",classename);//on affiche la nouvelle classe

    ClientUserinfoChanged( clientNum );
    ClientBegin( clientNum, qfalse );
}

Cette fonction permet de changer la classe d'un joueur. On lance la commande avec en paramètres le nom de la classe et *ent un pointeur sur le joueur.

Ben il faut ajouter ce qui va suivre toujours dans le même fichier. Moi je l'ai mis juste au-dessus de :

/*
=================
Cmd_Team_f
=================
*/
void Cmd_Team_f( gentity_t *ent ) {

mais ça a pas beaucoup d'importance faîtes juste attention a mettre le tout en dessous de la fonction SetClasse. Bon alors mettez ça :

/*
=================
Cmd_Classe_f
=================
*/
void Cmd_Classe_f( gentity_t *ent ) {

    char      s[MAX_TOKEN_CHARS];
    trap_Argv( 1, s, sizeof( s ) );

    SetClasse( ent, s );
}

toujours dans le même fichier, rajoutez après

    else if (Q_stricmp (cmd, "team") == 0)
        Cmd_Team_f (ent);

    //Nouveau code:
    else if (Q_stricmp (cmd, "classe") == 0)
        Cmd_Classe_f (ent);

Donc à chaque fois que la commande « classe » sera tapée dans la console, Cmd_Classe_f sera lancé qui lancera a son tour SetClasse avec en paramètre ce qui suis la commande (ex : « jedi » qui indique que la classe sera changée pour jedi)

Ben il manque pas grand chose. Le système de classe est terminé côté serveur. On va rajouter un choix des armes/forces/model en fonction de la classe.

On commence par ajouter ça dans g_client.c :

    if (client->sess.sessionTeam == TEAM_RED)
    {
        if (client->sess.sessionClasse == CLASSE_JEDI)
        {
            Q_strncpyz( model,"reborn", sizeof( model ) );
        }
        else if (client->sess.sessionClasse == CLASSE_SOLDAT)
        {
            Q_strncpyz( model,"stormtrooper" , sizeof( model ) );
        }
        else if (client->sess.sessionClasse == CLASSE_CHASSEUR)
        {
            Q_strncpyz( model,"kyle", sizeof( model ) );
        }
    }
    if (client->sess.sessionTeam == TEAM_BLUE)
    {
        if (client->sess.sessionClasse == CLASSE_JEDI)
        {
            Q_strncpyz( model,"jeditrainer", sizeof( model ) );
        }
        else if (client->sess.sessionClasse == CLASSE_SOLDAT)
        {
            Q_strncpyz( model,"rebel", sizeof( model ) );
        }
        else if (client->sess.sessionClasse == CLASSE_CHASSEUR)
        {
            Q_strncpyz( model,"rodian", sizeof( model ) );
        }
    }

mettez le juste après ça :

    Q_strncpyz( model, Info_ValueForKey (userinfo, "team_model"), sizeof( model ) );
    //Q_strncpyz( headModel, Info_ValueForKey (userinfo, "team_headmodel"), sizeof( headModel ) );

(voyez au passage que Lucas nous prépare des surprise pour les prochaines versions du SDK :p)
bon ça ça va marcher dans le cas d�un mod en teamplay pour le free for all et dérivé, ajoutez après :

    Q_strncpyz( model, Info_ValueForKey (userinfo, "model"), sizeof( model ) );
    //Q_strncpyz( headModel, Info_ValueForKey (userinfo, "headmodel"), sizeof( headModel ) );

ce bout de code :

    if (client->sess.sessionClasse == CLASSE_JEDI)
    {
        Q_strncpyz( model,"jeditrainer", sizeof( model ) );
    }
    else if (client->sess.sessionClasse == CLASSE_SOLDAT)
    {
        Q_strncpyz( model,"rebel", sizeof( model ) );
    }
    else if (client->sess.sessionClasse == CLASSE_CHASSEUR)
    {
        Q_strncpyz( model,"rodian", sizeof( model ) );
    }

(évidement vous pouvez changer les models notez que le nom du model correspond au répertoire dans lequel il se trouve. Jetez un coup d'½il dans les .pk3 de lucas.)

dans la fonction ClientConnect qui se trouve tjrs dans le même fichier rajoutez ça :

    client->sess.sessionClasse = CLASSE_SOLDAT;

Ensuite on va rajouter ça en bas de la fonction ClientSpawn (toujours dans le même fichier) :

    //si le joueur est un jedi
    if ( client->sess.sessionClasse == CLASSE_JEDI )
    {
        client->ps.speed = 250 ;
        client->ps.stats[STAT_ARMOR] = 0;
        client->ps.trueNonJedi = qfalse;
        client->ps.trueJedi = qtrue;
        //make sure they only use the saber
        client->ps.weapon = WP_SABER;
        client->ps.stats[STAT_WEAPONS] = (1 << WP_SABER);

        if (g_gametype.integer >= GT_TEAM) //rules teamplay
        {
            if (client->sess.sessionTeam == TEAM_RED) //imperial ! == gros meuchant =)
            {
                //on lui file les pouvoirs obscurs
                i = 0;
                while (i <= NUM_FORCE_POWERS)
                {
                    if (forcePowerDarkLight[i] != FORCE_LIGHTSIDE)
                    {
                        client->ps.fd.forcePowerBaseLevel[i] = client->ps.fd.forcePowerLevel[i];
                        client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_3;
                        client->ps.fd.forcePowersKnown |= (1 << i);
                    }

                    i++;
                }

                client->ps.fd.forcePowerLevel[FP_PUSH] = FORCE_LEVEL_2;
                client->ps.fd.forcePowerLevel[FP_PULL] = FORCE_LEVEL_2;
            }
            else if (client->sess.sessionTeam == TEAM_BLUE) //rebel ! == gentillou !
            {
                i = 0;
                while (i <= NUM_FORCE_POWERS)
                {
                    if (forcePowerDarkLight[i] != FORCE_DARKSIDE)
                    {
                        client->ps.fd.forcePowerBaseLevel[i] = client->ps.fd.forcePowerLevel[i];
                        client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_3;
                        client->ps.fd.forcePowersKnown |= (1 << i);
                    }

                    i++;
                }
            }
        }
        else if (g_gametype.integer < GT_TEAM) //si la partie est de type "ffa"
        {
            i = 0;
            while (i <= NUM_FORCE_POWERS)
            {
                client->ps.fd.forcePowerBaseLevel[i] = client->ps.fd.forcePowerLevel[i];
                client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_3;
                client->ps.fd.forcePowersKnown |= (1 << i);
                i++;
            }
        }

        client->ps.fd.forcePowerBaseLevel[FP_LEVITATION] = client->ps.fd.forcePowerLevel[FP_LEVITATION];
        client->ps.fd.forcePowerLevel[FP_LEVITATION] = FORCE_LEVEL_3;
        client->ps.fd.forcePowersKnown |= (1 << FP_LEVITATION);
    }

    //maintenant dans le cas d'un soldat
    else if (client->sess.sessionClasse ==CLASSE_SOLDAT)
    {
        client->ps.speed = 330 ;
        client->ps.stats[STAT_ARMOR] = 200;
        client->ps.trueNonJedi = qtrue;
        client->ps.trueJedi = qfalse;
        client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_SABER); // pas de sabre

        if (g_gametype.integer >= GT_TEAM) //rules teamplay
        {
            if (client->sess.sessionTeam == TEAM_RED) //imper
            {
                client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BLASTER );
                client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_REPEATER );
                client->ps.weapon = WP_BLASTER ;
                client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_BRYAR_PISTOL);
                client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_FLECHETTE );
            }
            else if (client->sess.sessionTeam == TEAM_BLUE) //rebel
            {
                client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BRYAR_PISTOL );
                client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_FLECHETTE );
                client->ps.weapon = WP_BRYAR_PISTOL ;
                client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_BLASTER);
                client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_REPEATER );
            }
            //commun aux deux teams
            client->ps.stats[STAT_WEAPONS] |= (1 << WP_THERMAL);
            client->ps.stats[STAT_WEAPONS] |= (1 << WP_STUN_BATON);
        }
        else  //si non team play !
        {
            client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BLASTER );
            client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_REPEATER );
            client->ps.weapon = WP_BLASTER ;
            client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BRYAR_PISTOL );
            client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_FLECHETTE );
        }
    }

    //bon maintenant les chasseurs de prime
    else if (client->sess.sessionClasse ==CLASSE_CHASSEUR) //chasseur de prime
    {
        client->ps.speed = 220 ;
        client->ps.stats[STAT_ARMOR] = 50;
        client->ps.trueNonJedi = qtrue;
        client->ps.trueJedi = qfalse;
        client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_SABER); // pas de sabre
        client->ps.stats[STAT_HOLDABLE_ITEMS] |= ( 1 << HI_BINOCULARS );
        client->ps.stats[STAT_HOLDABLE_ITEM] = BG_GetItemIndexByTag(HI_BINOCULARS, IT_HOLDABLE);
        client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BLASTER );
        client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BOWCASTER );
        client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_ROCKET_LAUNCHER );
        client->ps.stats[STAT_WEAPONS] |= (1 << WP_DISRUPTOR);
        client->ps.stats[STAT_WEAPONS] |= (1 << WP_THERMAL);
        client->ps.ammo[AMMO_ROCKETS] = ammoData[AMMO_ROCKETS].max*0.75;
        client->ps.ammo[AMMO_TRIPMINE] = ammoData[AMMO_TRIPMINE].max/2;
        client->ps.ammo[AMMO_DETPACK] = ammoData[AMMO_DETPACK].max/2;
        client->ps.stats[STAT_WEAPONS] |= (1 << WP_DET_PACK);
        client->ps.stats[STAT_WEAPONS] |= (1 << WP_TRIP_MINE);
    }

    //on met des munitions ça peut servir

    client->ps.ammo[AMMO_POWERCELL] = ammoData[AMMO_POWERCELL].max/2;
    client->ps.ammo[AMMO_BLASTER] = ammoData[AMMO_BLASTER].max/2;
    client->ps.ammo[AMMO_METAL_BOLTS] = ammoData[AMMO_METAL_BOLTS].max/2;
    client->ps.ammo[AMMO_THERMAL] = ammoData[AMMO_THERMAL].max/3;

La partie graphique

Ben c'est pas la peine que je vous explique comment faire précisément (j'en serais incapable en fait). Recherchez dans les .pk3 de Lucas les fichiers ingame.menu et ingame_join.menu. C'est à l'intérieur de ces fichiers que sont définis les menus.

À cette adresse vous trouverez le tut codé et fini avec les .menu que j'ai modifiés. Fouillez dedans je peux pas vous dire mieux. Reprenez les exemples que j'y ai ajouté et faites de même pour les vôtres. Sachez tout de même deux choses : dans mon exemple de menu, pour le voir il faut appuyer sur echap pendant le jeu et il se trouve dans la « barre d'outil » en haut. Autre chose, la communication entre le serveur qui gère les changements de classe et le menu se fait via commande console grâce a la commande exec.

Je vous mets quand même un exemple pour pas faire trop fumiste :p

action
{
    play                          "sound/interface/button1.wav" ;
    exec                          "cmd classe jedi" ;
    uiScript                      closeingame
}

Ben en fait on y comprend pas grand chose là alors allez vite fouiller dans ces fichiers.

Si toutefois vous n'y arrivez toujours pas, maillez-moi à cette adresse : douanier007 -- free.fr je me ferai un plaisir de vous aider.