[English]

NEWS
INTRODUCTION
TERMINOLOGIE
LE CODE SOURCE
LES BUGS
TUTORIAUX
DOWNLOADS
L'AUTEUR

  LES MODS: AJOUTER UNE COMMANDE DANS LA CONSOLE
(fonction "KickPlayer" écrite par Christoffer "Eldritch" Lundberg,
concept de base donné par Astro Muppet)


Nous allons commencer ces tutoriaux de modding avec quelque chose que les joueurs aiment habituellement manipuler: les commandes de la console.

1) Pour quoi sont utilisées les commandes d'AvP ?

Les commandes intégrées à la console d'AvP peuvent vous permettre de faire ce que la plupart des jeux 3D font:
  • Concernant les joueurs, vous pouvez modifier le gameplay en général, en utilisant des cheats, ou en changeant directement votre configuration (en bindant une touche à une commande, par exemple, pour faire des raccourcis). Normalement, vous n'avez pas besoin de jouer avec cela à moins d'être un hardcore gamer (comme dans la communauté Quake III).
  • Concernant les programmeurs, vous pouvez altérer les valeurs par défaut des variables du jeu, rendant le gameplay différent. L'utilisation principale de cela est de tester vos mods: vous créez des commandes pour voir votre personnage en mode fil de fer, par exemple, afin de déboguer votre travail (actuellement une commande existante), et cela constitue le but premier d'une console.
Les deux aspects seront couverts dans les tutoriaux, car je considère qu'ils sont tout deux intéressants, d'un point de vue programmation.

Donc s'il vous plaît, pas de "t'es un sale gars, parce que les gents vont tricher avec ces tuts". Je sais que les cheats puent, mais ils font partie intégrante des jeux.

2) Comment faire des commandes ?

Créer des commandes est vraiment simple dans AvP. Cela peut être fait en 3 étapes:
a) Vous créez l'interface de la commande, (c-à-d son nom, son texte d'aide associé, la fonction à exécuter en appelant cette commande et l'indicateur d'une commande de type cheat).
b) Vous créez le prototype de la fonction pour laisser le compilateur trouver les fichiers utilisés.
c) Vous créez la fonction (ce que la commande fera dans AvP).
Maintenant, pour créer des commandes, vous devez trouver une place adaptée dans les sources.
  • Vous pouvez trouver d'intéressantes commandes primaires dans le fichier CConvars.cpp, mais si vous voulez ajouter les vôtres dedans, vous devriez seulement mettre vos commandes de débogage ici, car cela a été fait pour les modes Debug et Win32 Release d'AvP. (Il est recommandé de compiler les sources avec le mode Release For Fox).
  • Le fichier Davehook.cpp est réservé pour l'architecture du jeu (contient des commandes console basiques).
  • Dans Gamevars.cpp, vous pouvez trouver 3 commandes pour supporter le morphing des espèces pendant le jeu (réservé pour le débogage, vous ne devriez pas mettre les vôtres ici...)
  • Finallement, le dernier et le plus intéressant fichier pour le modding est Gamecmds.cpp, qui contient la majeure partie des commandes concernant le gameplay. Il s'agit du bon endroit à modifier, donc celui que nous utiliserons.
3) Un exemple: la commande "KICK"

Pour illustrer ce tutorial, je vais vous montrer comment faire une commande "KICK".
Pour ceux qui ne le savent pas, son but est de déconnecter un joueur pendant un jeu en réseau. De cette manière, vous pouvez éviter les tricheurs. Commençons...

a) L'interface

Dans Gamecmds.cpp, ajoutez ceci à la liste de commandes (préférablement à la fin de la liste):
ConsoleCommand::Make
(
"KICK", // C'est la commande à tapper dans la console
"Kick a player.", // Le texte d'aide affiché dans la console
KickPlayer // La fonction associée avec la commande
);
b) Le prototype

Vous avez juste à ajouter ce qui suit près des autres prototypes (toujours dans Gamecmds.cpp):
extern void KickPlayer(int Index);
Cela indiquera au compilateur de trouver la fonction "KickPlayer" dans un autre fichier.

c) La fonction

Maintenant, vous allez créer la fonction dans le fichier Pldnet.c (support multiplayer). Pour cela, vous devez créer un prototype près des autres:
void KickPlayer(int Index);
Ensuite vous pouvez ajouter ce qui suit à la fin du fichier:
HRESULT KickThisPlayer(LPDIRECTPLAY4 lpDP2A, DPID DP_PlayerId) // Ma petite fonction ;)
{
// Créons un message de jeu (voir la note 1)
NETMESSAGEHEADER *headerPtr;
int headerSize = sizeof(NETMESSAGEHEADER);

headerPtr = (NETMESSAGEHEADER *) endSendBuffer;
endSendBuffer += headerSize;

// Initialise le type de message (termine le jeu d'un joueur)
headerPtr->type = (unsigned char) NetMT_EndGame;

// Maintenant, envoi d'un message au joueur cible (voir la note 2 pour des détails)
return DpExtSend(lpDP2A, AVPDPNetID, DP_PlayerId, DPSEND_GUARANTEED, &sendBuffer, headerSize);
}

void KickPlayer(int Index) // La fonction liée à la commande KICK
{
DPID DP_PlayerId;
HRESULT result;
char msg[100];

// On kick le joueur d'un jeu en réseau
if (AvP.Network == I_No_Network)
{
NewOnScreenMessage("Who are you trying to kick? The AI?");
return;
}
if ((Index < 0) || (Index >= NET_MAXPLAYERS))
{
NewOnScreenMessage("Invalid player number");
return;
}

// On obtient l'id du joueur (voir la note 3)
DP_PlayerId = netGameData.playerData[Index].playerId;

if (!DP_PlayerId)
{
NewOnScreenMessage("Invalid player number");
return;
}
if (glpDP == NULL)
{
NewOnScreenMessage("ERROR: glpDP is non-existant!");
return;
}

// Et le joueur fait BOOM !
if(AvP.Network==I_Host)
{
result = KickThisPlayer(glpDP,DP_PlayerId);

if(result == DP_OK)
{
// Informe les autres joueurs que vous avez kické quelqu'un...
sprintf(msg,"%s (#%d) has been kicked!", netGameData.playerData[Index].name, Index);
NewOnScreenMessage(msg);

sprintf(msg,"%s has been kicked!", netGameData.playerData[Index].name);
AddNetMsg_ChatBroadcast(msg, FALSE);
}
}
else
NewOnScreenMessage("You're a client, dude !");
}
Okay, donc qu'est-ce que tout cela signifie ?

Premièrement, nous avons la fonction "KickThisPlayer" qui s'occupe de la partie technique du processus de kick, en utilisant DirectX (voir les notes pour cela). Cette fonction est le véritable noyau de la commande console.

Ensuite, nous avons la seconde fonction: il s'agit de celle que nous avons déclaré à l'étape a). Le processus est vraiment simple: vous affichez la console dans le jeu, puis vous entrez "kick x", où x est l'id du joueur (voir les notes).

Ce que vous avez vu ici est l'utilisation de 2 fonctions, mais vous pouvez très bien n'en utiliser qu'une ou plus pour achever vos idées.

3) Un petit bonus

Kicker un joueur à partir de son id est bien, mais connaître l'id des joueurs est mieux !
Voici pourquoi il y a ce petit bonus: vous afficherez l'id de tous les joueurs pendant le jeu en réseau en tant qu'hôte (en pressant la touche TAB). Je ne publierai pas le listing complet, donc suivez correctement ce qui suit:
  • Localisez la fonction "DoMultiplayerEndGameScreen" dans Pldnet.c
  • Ajoutez le code en bleu:
    for(i=0;i<NET_MAXPLAYERS;i++)
    {
    if(netGameData.playerData[i].playerId)
    {
    RenderStringVertically(netGameData.playerData[i].name,x,y,0xffffffff);
    x+=20;
    }
    }

    if(AvP.Network==I_Host)
    RenderStringVertically("ID",x,y,0xffffffff);
    pour afficher le titre.
  • Finallement, faites de même avec cela:
    x=120;
    for(j=0;j<NET_MAXPLAYERS;j++)
    {
    if(netGameData.playerData[j].playerId)
    {
    sprintf(text,"%d",netGameData.playerData[i].playerFrags[j]);
    if(i==j)
    RenderStringCentred(text,x,y,0xffff0000);
    else
    RenderStringCentred(text,x,y,0xff00ff00);
    x+=20;
    }
    }
    if(AvP.Network==I_Host)
    {
    sprintf(text, "%d", i);
    RenderStringCentred(text,x,y,0xffffffff);
    }
    pour afficher les id.
A ce point vous êtes prêts pour faire vos propres commandes console.
Maintenant, voyez les notes si vous êtes encore plus curieux...

Note 1:
  • quand vous utilisez DirectX pour gérer des réseaux, vous utilisez l'interface DirectPlay. Ce que vous devez comprendre est que DirecPlay utilise des messages pour faire communiquer les pc entre eux.
  • Il y a 2 types de messages: les messages systèmes & les messages du jeu.
  • Les messages systèmes sont gérés par DirecPlay, mais le jeu peut les utiliser (ex.: le message "DPSYS_HOST" dans AvP signifie que vous devenez l'hôte). Les messages du jeu sont spécifiques au jeu (ex.: dans AvP, le message "NetMT_StartGame" démarrer une partie).
Note 2:
  • pour simuler un message de kick, qui n'a pas été codé dans AvP, j'ai juste dû envoyer un message au client avec cette fonction.
  • lpDP2A est l'objet DirectPlay d'AvP.
  • AVPDPNetID est l'id de la source du message (dans ce cas, l'hôte).
  • DP_PlayerId est l'id de la destination (dans ce cas, le client à kicker).
  • DPSEND_GUARANTEED signifie que nous sommes sûrs que le client recevra le message (non sûr par défaut).
  • &sendBuffer est l'adresse du buffer contenant le message à envoyer au client.
  • headerSize est la taille de ce buffer.
Note 3:
  • dans DirectPlay, tous les joueurs sont identifiés par un unique id qui est un nombre.
  • Dans AvP, votre id est toujours AVPDPNetID et pour les autres joueurs, cela dépend dans quel ordre ils se sont connectés à votre session.