IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Utilisation de DirectX sous Delphi : Asphyre 4.1


précédentsommaire

V. Les entrées utilisateurs

V-A. Introduction

Maintenant que vous savez afficher des vues 2D et 3D à couper le souffle (allons, pas de fausse modestie), il reste une chose importante: pouvoir faire interagir la scène avec le joueur.
Pour cela, il existe trois interfaces principales disponibles avec DirectX :

  • le clavier ;
  • la souris ;
  • les manettes (joystick, joypad, etc.).

Comme d'habitude, nous allons voir leur fonctionnement tout au long de l'écriture d'un programme simple. Celui-ci devra afficher un carré blanc. L'utilisateur pourra déplacer ce carré à l'aide du clavier et d'une des manettes. Évidemment, il pourra sélectionner la manette directement pendant le « jeu ».
Afin d'aller plus, loin, on affichera quelques données sur les manettes afin d'identifier plus facilement la manette courante.

Maintenant que vous êtes familier avec l'affichage avec Asphyre, nous ne rentrerons pas dans les détails de l'affichage.

Le projet de départ que nous utiliserons ne fait pour l'instant qu'afficher un fond noir ainsi que le nombre de FPS.

V-B. Les entrées utilisateur dans Asphyre

Comme toujours, Asphyre propose des classes déjà toutes faites. Il nous incombe tout de même de les initialiser. Tout se passe dans les unités suivantes :

  • AsphyreInputs.pas : déclare la variable AsphyreInput de type TAsphyreInput qui centralise les entrées utilisateurs ;
  • AsphyreKeyboard.pas : unité relative au clavier ;
  • AsphyreJoystick.pas : unité relative aux manettes ;
  • AsphyreMouse.pas : unité relative à la souris ;
  • DirectInput.pas : unité contenant toutes les constantes ainsi que les implémentations de DirectX.

Ajoutons donc ces unités à la clause uses de notre projet.

Pour commencer, ajoutons également ces variables privées nécessaires à la suite du programme :

  • OriginRectPos : TPoint : coordonnées de l'origine du carré ;
  • DispRectPos : TPoint : coordonnées finales du carré.

Voyons maintenant l'initialisation :

 
Sélectionnez
//Initialisation de AsphyreInput
AsphyreInput.Initialize;
AsphyreInput.WindowHandle := Handle;
//Initialisation des interfaces
AsphyreInput.Joysticks.Initialize;
AsphyreInput.Keyboard.Initialize;

N'oubliez surtout pas d'assigner la propriété WindowHandle !

La finalisation se fait aussi simplement :

 
Sélectionnez
AsphyreInput.Finalise;

à noter que la finalisation de AsphyreInput déclenche aussi la finalisation de Joysticks, Keyboard et Mouse. Cette simple ligne suffit donc.

L'affichage du carré se fera de cette façon dans le callback de rendu :

 
Sélectionnez
procedure TMainFrm.RenderCallback(Sender: TAsphyreDevice; Tag: TObject);
const
  RectSize = 40;
var
  p: TPoint4;
  cl: TColor4;
begin
  //Affichage du nombre de FPS
  Sender.SysFonts.Font['s/arial'].TextOut('FPS : ' + IntToStr(Timer.FrameRate),
    10, 10, $99009900);
  with Sender.Canvas do
  begin
    //Spécification des coordonnées du carré
    p := Point4(DispRectPos.X, DispRectPos.Y,
                DispRectPos.X + RectSize, DispRectPos.Y,
                DispRectPos.X + RectSize, DispRectPos.Y + RectSize,
                DispRectPos.X, DispRectPos.Y + RectSize);

    //Affichage du carré
    FillQuad(p, clWhite4, fxuBlend);
  end;
end;

Vous devriez avoir ceci :

Image non disponible

V-C. Le clavier

Tachons maintenant de déplacer ce carré blanc lorsque l'on appuie sur les flèches du pavé directionnel.
L'idéal est de tout regrouper ces opérations dans OnProcessEvent.
La première chose à faire est de mettre à jour les informations en appelant Update :

 
Sélectionnez
if AsphyreInput.Keyboard.Update then

Si l'Update s'est correctement déroulé, on peut passer à la suite.
La détection des touches sous DirectX ne se passe pas de la même façon que la VCL. En effet, dans la VCL, vous avez un événement OnKeyPress (ou Down et Up) qui passe la touche appuyée en paramètre. Sous DirectX, c'est à vous de tester si chaque touche qui vous intéresse est pressée ou non.
Pour cela, la propriété Key de AsphyreInput.Keyboard nous permet de savoir facilement si la touche spécifiée est pressée ou non de cette façon :

 
Sélectionnez
procedure TMainFrm.ProcessEvent(Sender: TObject);
const
  DecValue = 40;
var
  Decalage: TPoint;
begin
  //Mise à zéro du décalage
  Decalage := Point(0, 0);

  with AsphyreInput.Keyboard do
  begin
    //Appui sur droite ou gauche
    if Key[DIK_RIGHT] then
      Decalage.X := DecValue else
    if Key[DIK_LEFT] then
      Decalage.X := -DecValue;

    //Appui sur bas ou haut
    if Key[DIK_DOWN] then
      Decalage.Y := DecValue else
    if Key[DIK_UP] then
      Decalage.Y := -DecValue;

    //Appui sur Echap pour fermer l'application
    if Key[DIK_ESCAPE] then
      Close;
  end;

  //Assignation de la position du carré
  DispRectPos.X := OriginRectPos.X + Decalage.X;
  DispRectPos.Y := OriginRectPos.Y + Decalage.Y;
end;

Les constantes DIK_[Touche] se trouvent dans l'unité DirectInput.pas. Ce code est simple : suivant la direction pressée, le décalage est assigné. Vous aurez aussi remarqué que nous vérifions l'état de la touche Echap afin de fermer l'application. Son interception dans OnKeyPress est donc inutile ici!
Les deux dernières lignes calculent la position finale du carré.

Et c'est tout !
Exécutez votre programme et essayez d'appuyer sur les touches du pavé directionnel, le carré se déplacera et reviendra à sa place lorsque vous lâchez la touche.

Il existe également des propriétés KeyPressed et KeyReleased, mais comme stipulé en commentaire dans l'unité AsphyreKeyboard.pas, elles sont tributaires des appels à Update. Donc si vous utilisez ces méthodes, votre programme peut perdre des événements du clavier.

Il vaut donc mieux utiliser la propriété Key.

V-D. Les joysticks

Afin d'afficher les données de chaque joystick (pour peu que vous en ayez plusieurs), nous avons besoin d'un record ainsi que d'un tableau dynamique contenant ces records.
Voici la structure :

 
Sélectionnez
TJoyInfos = record
  Axis: integer;
  Buttons: integer;
  POVs: integer;
end;

Cette structure contiendra les capacités de chacun des joystick branché.

Nous aurons également des variables privées suivantes :

  • CurrentJoystick : integer : indice du joystick courant ;
  • JoyPos : TPoint : amplitude du déplacement du joystick pour l'affichage ;
  • Joys : array or TJoyInfos : contient les données de chaque joystick ;
  • JoyDown : boolean : si le bouton est appuyé ou non.

Ajoutons donc une fonction qui va récupérer les informations des joysticks :

 
Sélectionnez
function TMainFrm.GetJoyInfos(const JoyIndex: integer): TJoyInfos;
begin
  with AsphyreInput.Joysticks[JoyIndex].DeviceCaps do
  begin
    //Nombre d'axes
    Result.Axis := dwAxes;
    //Nombre de boutons
    Result.Buttons := dwButtons;
    //Nombre de POVs
    Result.POVs := dwPOVs;
  end;
end;

Les capacités du joystick se trouvent dans la propriété DeviceCaps.

Vous pouvez également afficher le nom du joystick en utilisant la méthode GetDeviceInfo de AsphyreJoystick.InputDevice, mais je n'ai pas réussi à la faire fonctionner: soit elle ne marche pas soit je m'y suis pris comme un pied.

Bref, si vous avez la solution à ce problème, je suis preneur.

Étoffons un peu l'initialisation afin de récupérer toutes ces données :

 
Sélectionnez
  //Initialisation de AsphyreInput
  AsphyreInput.Initialize;
  AsphyreInput.WindowHandle := Handle;

  //Initialisation des interfaces
  AsphyreInput.Joysticks.Initialize;
  AsphyreInput.Keyboard.Initialize;

  //Initialise le joystick courant
  CurrentJoystick := 0;

  //Spécifie la longueur du tableau
  SetLength(Joys, AsphyreInput.Joysticks.Count);

  //Initialise chaque information de joystick
  for I := 0 to AsphyreInput.Joysticks.Count - 1 do
    Joys[I] := GetJoyInfos(i);

  //Initialisation de l'état appuyé à faux
  JoyDown := false;

Ajoutons également la libération du tableau dans OnDestroy :

 
Sélectionnez
procedure TMainFrm.FormDestroy(Sender: TObject);
begin
  //Vidage du tableau    
  SetLength(Joys, 0);

  //Finalisation de AsphyreInput
  AsphyreInput.Finalize;

  //Finalisation de DirectX
  Devices.Finalize;
end;

Afin de faciliter l'écriture du code, nous allons également séparer l'affichage des informations des joysticks :

 
Sélectionnez
procedure TMainFrm.DrawJoyInfos(Sender: TAsphyreDevice);
var
  i: integer;
  Color: Cardinal;
begin
  //Y a-t-il des joysticks?
  if AsphyreInput.Joysticks.Count > 0 then
  begin
    //Affichage du nombre de joystick(s) et des amplitudes de déplacement
    Sender.SysFonts.Font['s/arial'].TextOut(
      Format('Joysticks : %u (X=%d, Y=%d)',
        [AsphyreInput.Joysticks.Count, JoyPos.X, JoyPos.Y]),
      10, 25, $FF777777);
    //Affichage des données de chaque joystick
    with Sender.Canvas do
    for i := 0 to AsphyreInput.Joysticks.Count - 1 do
    begin
      //Si le joystick est le joystick courant
      if i = CurrentJoystick then
      begin
        //Aficher un cercle rouge en face du texte
        FillCircle(10, 51 + i * 20, 5, 10, clRed4, fxuBlend);
        //Couleur du texte en blanc
        Color := $FFFFFFFF;
      end else
        //Couleur du texte en gris
        Color := $FF777777;

      //Affichage du texte
      Sender.SysFonts.Font['s/arial'].TextOut(
        Format('%d :  %d axe(s),  %d bouton(s),  %d POV(s)',
          [i + 1, Joys[i].Axis, Joys[i].Buttons, Joys[i].POVs]),
        20, 45 + i * 20, Color);
    end;
  end else
    //S'il n'y a pas de joystick
    Sender.SysFonts.Font['s/arial'].TextOut('Aucun joystick détecté.',
      10, 25, $FF777777);
end;

Ajoutons cette procédure dans le callback de rendu :

 
Sélectionnez
procedure TMainFrm.RenderCallback(Sender: TAsphyreDevice; Tag: TObject);
const
  RectSize = 40;
var
  p: TPoint4;
  cl: TColor4;
begin
  //Affichage du nombre de FPS
  Sender.SysFonts.Font['s/arial'].TextOut('FPS : ' + IntToStr(Timer.FrameRate),
    10, 10, $99009900);

  //Affichage des données des joysticks
  DrawJoyInfos(Sender);
  
  with Sender.Canvas do
  begin
    //Spécification des coordonnées du carré
    p := Point4(DispRectPos.X, DispRectPos.Y,
                DispRectPos.X + RectSize, DispRectPos.Y,
                DispRectPos.X + RectSize, DispRectPos.Y + RectSize,
                DispRectPos.X, DispRectPos.Y + RectSize);

    //Spécification de la couleur suivant que l'état appuyé soit vrai ou faux
    if JoyDown then
      cl := clRed4
    else
      cl := clWhite4;

    //Affichage du carré
    FillQuad(p, cl, fxuBlend);
  end;
end;

Vous devriez avoir quelque chose comme ceci :

Image non disponible

Écrivons maintenant le code pour utiliser les données des joysticks.
Contrairement aux souris et clavier, il est courant d'avoir plusieurs joysticks/pads branchés en même temps sur l'ordinateur. De plus, le joystick que l'utilisateur voudrait utiliser n'est pas forcément le premier branché !
Vous devez donc prendre compte de tous ces paramètres lorsque que vous codez un jeu.
Heureusement, Asphyre gère ce problème de façon très simple. En effet, chaque joystick a un indice différent (de 0 à n-1); il est donc très facile d'utiliser le bon joystick.
Regardons le code pour notre exemple :

 
Sélectionnez
const
  JoyDeadZone = 3000;
...
  //Vérification de la présence d'un joystick
  if AsphyreInput.Joysticks.Count > 0 then
  begin
    //Mise à jour des données des joysticks
    if AsphyreInput.Joysticks.Update then
    with AsphyreInput.Joysticks[CurrentJoystick].JoyState do
    begin
      //Si l'amplitude est supérieure à la deadzone alors effectuer le déplacement

      //en X
      if Abs(lX) > JoyDeadZone then
        Decalage.X := lX div 200;

      //en Y
      if Abs(lY) > JoyDeadZone then
        Decalage.Y := lY div 200;

      //Pour l'affichage des données
      JoyPos := Point(lX, lY);
    end;
  end;

Vous aurez remarqué cette constante JoyDeadZone. C'est une constante assez importante puisque c'est elle qui définit la zone morte (dead zone) de votre joystick. En effet, si le joystick est sensible, à peine vous aurez bougé le manche que l'affichage réagira. C'est une bonne chose me direz-vous. Je vous répondrais oui, sauf qu'un joystick est rarement centré pile au centre lorsqu'il est au repos. Vous aurez donc des mouvements parasites que vous pourrez supprimer avec cette zone morte. Elle définit l'amplitude minimale à prendre en compte.
Par exemple, pour éviter les mouvements parasites avec mon pad, j'ai dû paramétrer la deadzone à 3000…

Nous testerons donc pour chaque direction si l'amplitude est supérieure à la deadzone. Nous diviserons l'amplitude par 200 afin d'éviter que le carré sorte complètement de la zone d'affichage (l'amplitude d'un joystick allant de -32768 à 32767).
La dernière ligne ne sert qu'à assigner JoyPos à l'amplitude actuelle afin de l'afficher.

Vous pouvez d'ores et déjà exécuter votre code. Vous verrez que si vous agissez sur votre premier joystick (dans l'ordre de la liste des Contrôleurs de jeu du Panneau de configuration de Windows) le carré bougera dans le sens que vous lui donnez.

La dernière chose à faire est de permettre de spécifier le joystick utilisé. Pour cela, l'utilisateur devra taper sur le pavé numérique du clavier le numéro du joystick.
Nous devons donc ajouter les tests des touches 1 à 9 (oui voyons grand !) afin de donner à CurrentJoystick l'indice du joystick :

 
Sélectionnez
  if AsphyreInput.Keyboard.Update then
  with AsphyreInput.Keyboard do
  begin
    //Appui de 1 à 9 sur le pavé numérique pour spécifier le joystick utilisé
    if Key[DIK_NUMPAD1] then CurrentJoystick :=
      Min(0, AsphyreInput.Joysticks.Count - 1) else
    if Key[DIK_NUMPAD2] then CurrentJoystick :=
      Min(1, AsphyreInput.Joysticks.Count - 1) else
    if Key[DIK_NUMPAD3] then CurrentJoystick :=
      Min(2, AsphyreInput.Joysticks.Count - 1) else
    if Key[DIK_NUMPAD4] then CurrentJoystick :=
      Min(3, AsphyreInput.Joysticks.Count - 1) else
    if Key[DIK_NUMPAD5] then CurrentJoystick :=
      Min(4, AsphyreInput.Joysticks.Count - 1) else
    if Key[DIK_NUMPAD6] then CurrentJoystick :=
      Min(5, AsphyreInput.Joysticks.Count - 1) else
    if Key[DIK_NUMPAD7] then CurrentJoystick :=
      Min(6, AsphyreInput.Joysticks.Count - 1) else
    if Key[DIK_NUMPAD8] then CurrentJoystick :=
      Min(7, AsphyreInput.Joysticks.Count - 1) else
    if Key[DIK_NUMPAD9] then CurrentJoystick :=
      Min(8, AsphyreInput.Joysticks.Count - 1);
  end;

C'est long, mais c'est indispensable. Notez que nous testons à chaque fois si l'indice ne dépasse pas le nombre de joysticks ! Mais pourquoi le faire à chaque fois et pas une bonne fois pour toutes à la fin ? La raison en est très simple : on utilise CurrentJoystick dans le callback de rendu afin de mettre en valeur le joystick utilisé. Or, si on ne paramètre cette variable qu'à la fin, pendant un court instant, elle aura une valeur potentiellement hors de l'étendue (supérieure au nombre de joysticks). Eh bien ce court instant a suffi à faire planter systématiquement lorsque j'ai voulu faire comme ceci : le callback de rendu faisait appel à cette variable au moment où elle n'était pas encore vérifiée.
C'est pour cette raison que j'ai volontairement utilisé cette méthode qui n'assignera jamais CurrentJoystick à autre chose qu'un indice valable.

Ainsi, l'utilisateur pourra choisir le joystick utilisé :

Image non disponible
Programme avec le joystick n°2 (indice 1) sélectionné

Il ne reste qu'une chose : Détecter l'appui du bouton.
Pour cela, une ligne suffit :

 
Sélectionnez
if AsphyreInput.Joysticks.Update then
with AsphyreInput.Joysticks[CurrentJoystick].JoyState do
begin
  //Etat baissé si le bouton 0 est pressé
  JoyDown := rgbButtons[0] > 0;
end;

La propriété rgbButtons recense tous les boutons du contrôleur. Ils sont identifiés par leur indice.

Vous pouvez trouver très facilement l'indice de chacun des boutons de votre contrôleur. Pour cela, rendez-vous dans la boite de dialogue Contrôleur de jeu du Panneau de configuration de Windows. Sélectionnez le contrôleur et cliquez sur Propriétés. Vous devrez avoir la liste de tous les boutons numérotés. Cliquez sur le bouton que vous voulez utiliser, repérez son numéro et retranchez 1 à ce numéro : vous avez l'indice !

Vous remarquerez aussi une particularité : la propriété rgbButtons[Index] n'est pas de type booléen. Il est donc tout à fait possible d'utiliser des boutons avec une amplitude. Dans notre cas, le bouton est du type on/off, nous testerons donc simplement si sa valeur est 0 (relâché) ou supérieur à 0 (appuyé).

Vous pouvez exécuter votre programme, lorsque vous appuyez sur le bouton, le carré deviendra rouge :

Image non disponible
Le bouton est enfoncé

V-E. La souris

Passons maintenant à la dernière interface disponible: la souris. Son utilisation sous Asphyre ressemble beaucoup à celle du joystick. À ceci près, que l'on n'a pas une valeur d'amplitude pour les déplacements, mais une différence (delta).
Premièrement, il faut initialiser le gestionnaire de souris. Nous avons maintenant l'habitude, cela se fait comme ceci :

 
Sélectionnez
  //Initialisation de AsphyreInput
  AsphyreInput.Initialize;
  AsphyreInput.WindowHandle := Handle;

  //Initialisation des interfaces
  AsphyreInput.Joysticks.Initialize;
  AsphyreInput.Keyboard.Initialize;
  AsphyreInput.Mouse.Initialize;

Comme son nom l'indique, la dernière ligne initialise le gestionnaire de souris. Nous pouvons dès lors utiliser la souris avec Asphyre.
Lorsque l'utilisateur appuiera sur le bouton gauche de la souris, cela aura le même effet que le bouton du joystick : le carré devient rouge et redevient blanc lorsque le bouton est lâché. Ajoutons une variable privée MouseDown : boolean afin de ne pas interférer avec la variable JoyDown pour le joystick. Cette variable MouseDown sera initialisée à false dans OnCreate
Modifions donc notre procédure ProcessEvent afin d'utiliser la souris :

 
Sélectionnez
  //Mise à jour des données de la souris
  if AsphyreInput.Mouse.Update then
  with AsphyreInput.Mouse do
  begin
    //Assignation des valeurs des mouvements
    Decalage.X := DeltaX * 2;
    Decalage.Y := DeltaY * 2;

    //Si le bouton gauche est enfoncé
    if Pressed[0] then
      MouseDown := true;
    //S'il est relâché
    if Released[0] then
      MouseDown := false;
  end;

La question qui se pose ici est de savoir où placer cette portion de code par rapport aux autres gestionnaires que nous avons écrits précédemment (clavier et joysticks). Tel que nous l'avons écrit, le dernier des périphériques donnera sa valeur à Decalage.X et Decalage.Y. C'est donc à vous de savoir quel périphérique sera prioritaire par rapport aux autres. En général, les contrôleurs d'un jeu ne sont pas aussi redondants et on doit choisir entre le couple clavier/souris ou le joystick par exemple pour les jeux tirés des consoles. Ce problème se pose donc uniquement dans notre exemple. J'ai personnellement placé cette gestion de la souris en premier.
Rien ne vous empêche également de cumuler tous ces contrôleurs en ajoutant à chaque fois la valeur de Decalage.X et Decalage.Y. Bref, vous l'aurez compris, il est très facile de gérer les contrôleurs de façon adaptée à vos besoins.

Regardons le code plus en détail. On commence comme d'habitude par faire un Update. Cette opération est indispensable sinon, les buffers ne sont pas mis à jour et aucun mouvement n'est détecté !
Les lignes suivantes utilisent DeltaX et DeltaY qui sont comme je vous le disais les deux différences (et non amplitudes) de déplacement de la souris. Dans notre exemple volontairement simple, nous utiliserons directement la valeur des deltas pour notre position.
La multiplication par 2 ne sert qu'à avoir une amplitude de mouvement plus grand.

Passons aux lignes du dessous. Elles gèrent l'état du bouton 0 (le bouton gauche) appuyé ou relâché.
Comme vous le constatez, le système est différent du joystick. Pour celui-ci, nous devions tester si la valeur d'un bouton est supérieure à zéro. Si oui, le bouton est pressé et dès que la valeur repasse à 0, le bouton vient d'être relâché.
Ici, il faut tester lorsque le bouton est appuyé (Pressed) et lorsqu'il est relâché (Released). C'est pour cette raison que je n'ai pas écrit :

 
Sélectionnez
    MouseDown := Pressed[0];

Si vous essayez ce code, vous verrez le cube rouge qu'une fraction de seconde : Simplement au moment où le bouton est pressé et non pendant que le bouton est pressé ! La nuance est subtile.
À noter qu'il existe également la propriété DeltaWheel de Mouse qui, comme son nom l'indique permet d'utiliser la molette.

Pour finir, ajoutons la prise en compte de la nouvelle variable MouseDown dans le callback de rendu :

 
Sélectionnez
procedure TMainFrm.RenderCallback(Sender: TAsphyreDevice; Tag: TObject);
const
  RectSize = 40;
var
  p: TPoint4;
  cl: TColor4;
begin
  //Affichage du nombre de FPS
  Sender.SysFonts.Font['s/arial'].TextOut('FPS : ' + IntToStr(Timer.FrameRate),
    10, 10, $99009900);

  //Affichage des données des joysticks
  DrawJoyInfos(Sender);

  with Sender.Canvas do
  begin
    //Spécification des coordonnées du carré
    p := Point4(DispRectPos.X, DispRectPos.Y,
                DispRectPos.X + RectSize, DispRectPos.Y,
                DispRectPos.X + RectSize, DispRectPos.Y + RectSize,
                DispRectPos.X, DispRectPos.Y + RectSize);

    //Spécification de la couleur suivant que l'état appuyé soit vrai ou faux
    if JoyDown or MouseDown then
      cl := clRed4
    else
      cl := clWhite4;

    //Affichage du carré
    FillQuad(p, cl, fxuBlend);
  end;
end;

Vous pouvez exécuter votre programme et voir votre carré blanc trembler suivant la vitesse de déplacement de votre souris.

V-F. Téléchargement du programme d'exemple

Vous pouvez télécharger le programme d'exemple ici: Exemple2D.zip

V-G. Conclusion

Voilà, dorénavant, vous êtes prêt à vous lancer dans la création d'un jeu avec votre EDI préféré ! N'hésitez pas à me faire part de vos créations, je me ferai une joie de les ajouter en téléchargement à la suite de ce tutoriel !
Bon dev à tous !


précédentsommaire

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2008 Pierre RODRIGUEZ. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.