Le fichier FGD

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

Introduction : qu'est ce que c'est ?

FGD signifie Game Definition File. C'est un fichier texte qui est chargé par Hammer pour qu'il « sache » quelles entités votre mod est capable de gérer. Ca paraît bien compliqué à cause de mes explications, mais en fait c'est très simple.

http://www.game-lab.com/images/tuts/hl1_make_fgd/FGD.gif

Voici comment est traduit le FGD dans Hammer

FGD : syntaxe pour une entité

La syntaxe est la suivante :

@TypeDEntité <paramètres> = nom_entité : "Nom de l'entité"
[
    <variables>
]

Dans l'ordre :

FGD: syntaxe pour une variable

La syntaxe pour déclarer une variable est la suivante :

nom_variable(type_variable) : "Description de la variable" < : valeur_par_defaut > []

Ca paraît un peu compliqué. Les éléments entre <> indiquent qu'ils sont optionnels.

Un exemple :

@PointClass base(Targetname) size(-16 -16 -16, 16 16 16) = player_loadsaved : "Load Auto-Saved game"
[
    duration(string) : "Fade Duration (seconds)" : "2"
    holdtime(string) : "Hold Fade (seconds)" : "0"
    renderamt(integer) : "Fade Alpha" : 255
    rendercolor(color255) : "Fade Color (R G B)" : "0 0 0"
    messagetime(string) : "Show Message delay" : "0"
    message(string) : "Message To Display" : ""
    loadtime(string) : "Reload delay" : "0"
]

Vous voyez donc variable(type) : "Description" : "Valeur par défaut". Sachez que la valeur par défaut n'est pas nécessaire.

FGD : syntaxe pour une variable choices/flags

La syntaxe est la même à la base, sauf qu'on rajoute des éléments entre les [] :

nom_variable(type_variable) : "Description de la variable" < : valeur_par_defaut >
[
    valeur1 : "Description de la valeur 1"
    valeur2 : "Description de la valeur 2"
]

valeur1, valeur2, ... sont les différentes valeurs que peut prendre la variable, et "Description de la valeur 1" sont les descriptions correspondantes.

Voici un exemple d'entité utilisant une variable de type choices et une variable spawnflags de type flags :

@SolidClass base(RenderFields, Targetname) = momentary_rot_button : "Direct wheel control"
[
    target(target_destination) : "Targetted object"
    speed(integer) : "Speed" : 50
    master(string) : "Master"
    sounds(choices) : "Sounds" : 0 =
    [
        0: "None"
        1: "Big zap & Warmup"
        2: "Access Denied"
        3: "Access Granted"
        4: "Quick Combolock"
        5: "Power Deadbolt 1"
        6: "Power Deadbolt 2"
        7: "Plunger"
        8: "Small zap"
        9: "Keycard Sound"
        21: "Squeaky"
        22: "Squeaky Pneumatic"
        23: "Ratchet Groan"
        24: "Clean Ratchet"
        25: "Gas Clunk"
    ]
    distance(integer) : "Distance (deg)" : 90
    returnspeed(integer) : "Auto-return speed" : 0
    spawnflags(flags) =
    [
        1: "Door Hack" : 0
        2: "Not useable" : 0
        16: "Auto Return" : 0
        64: "X Axis" : 0
        128: "Y Axis" : 0
    ]
    _minlight(integer) : "_minlight"
    angles(string) : "Pitch Yaw Roll (Y Z X)" : "0 0 0"
]

Pour les flags, la syntaxe des valeurs que peut prendre la variable est différente. C'est forcément des puissances de 2, car c'est du binaire. Allez voir le tutorial à ce sujet.
La syntaxe est : valeur: "Description" : coché avec coché soit 0 soit 1, suivant si la case est précochée ou non.

Côté code

Du côté du code, je vais supposer que vous avez déjà une classe qui va gérer votre entité.
Ajoutez dans la déclaration de celle-ci la ligne :

//...
void    KeyValue( KeyValueData *pkvd );
//...

Un peu plus loin vous allez mettre le contenu de cette fonction :

//
// KeyValue
//
void CMaClasse::KeyValue( KeyValueData *pkvd )
{
    if( FStrEq( pkvd->szKeyName, "variable1" ) )
    {
        strcpy( m_pszVariable1, pkvd->szValue );
        pkvd->fHandled = 1;
    }
    else if( FStrEq( pkvd->szKeyName, "variable2" ) )
    {
        m_iVariable2 = atoi( pkvd->szValue );
        pkvd->fHandled = 1;
    }
    else
    {
        CBaseEntity::KeyValue( pkvd );
    }
}

La fonction aura certainement cette allure. En fait, pendant le chargement de la map, le moteur va appeler cette fonction autant de fois qu'il y a de variables définies pour votre entité dans votre FGD.
Le paramètre est de type KeyValueData. Regardons de plus près :

typedef struct KeyValueData_s
{
    char    *szClassName;    // in: entity classname
    char    *szKeyName;      // in: name of key
    char    *szValue;        // in: value of key
    long    fHandled;        // out: DLL sets to true if key-value pair was understood

} KeyValueData;

szClassName est le nom de l'entité (ex: monster_barney)
szKeyName est le nom de la variable
szValue est la valeur de la variable (utilisez atoi() ou atof() pour vos conversions)
fHandled indique si la variable a été prise en compte ou non.

Revenons à la fonction KeyValue. Dedans, on teste pour savoir de quelle variable il s'agit ; pour cela on a une série de else if( FStrEq( pkvd->szKeyName, "nom_de_la_variable" ) ) { ... }.
Ensuite, on enregistre la valeur de cette variable. Dans mon exemple j'ai mis que le contenu de m_pszVariable1 serait stocké dans une variable de type char* (en utilisant strcpy()), et que celui de m_iVariable2 serait stocké dans une variable de type int (en convertissant avec atoi()). Il faut bien sûr avoir déclaré dans votre classe les deux variables m_pszVariable1 et m_iVariable2 correspondantes.
Ensuite, il faut mettre pkvd->fHandled = 1 pour indiquer qu'on a bien enregistré cette variable.
En cas d'échec, ou si vous ne traitez pas une certaine variable, on renvoie pkvd à CBaseEntity. Si votre classe n'est pas dérivée de CBaseEntity, renvoyez pkvd à la classe mère de votre classe (si vous n'avez rien compris à cette phrase, laissez CBaseEntity).

Conclusion

Voilà, vous avez pu ajouter une entité dans la liste disponible dans Hammer, vous avez pu définir des variables que les mappeurs peuvent modifier, et vous avez appris comment récupérer les valeurs entrées par les mappeurs dans le code.

Sachez simplement que le fichier FGD n'est pas sensible à la casse, c'est à dire que Flags et flags sont interprétés de la même manière. Sachez aussi que vous pouvez insérer autant de nouvelles lignes que vous voulez, pour plus de liberté, et que vous pouvez ajouter des commentaires comme en C/C++ (même syntaxe). Si vous avez des remarques ou des ajouts à faire à ce tutorial, n'hésitez pas à envoyer un petit mail :)

Petit plus : si vous utilisez EditPlus pour faire vos fichiers texte, voici un fichier template qui dira à EditPlus de quelle couleur mettre les mots, pour plus de clarté (merci FireStorm) :

Le fichier template FGD pour EditPlus

  1. Placez ce fichier fgd.stx dans le répertoire d'editplus.
  2. Lancez EditPlus, et faites tools->preferences.
  3. Allez dans la partie files & syntax, cliquez sur add, un nouveau type de fichier apparaît.
  4. Remplissez les champs suivants :
    • Description : Half-Life FGD
    • File extensions : fgd
    • Syntax File : [Le fichier STX]
    • Associate in Explorer : oui (double-clic sur un fgd = ouverture avec EditPlus)
  5. Cliquez sur l'onglet Syntax Colors et choisissez les couleurs que vous voulez.
  6. Cliquez sur ok et ouvrez vos FGD :)