PureBasic:Realiser un RPG2D/Interface, inventaire et boutiques


PureBasic:Realiser_un_RPG2D

<< Précédent | Sommaire | Suivant >>



Prérequis

Difficultée estimée de cette étape: moyenne


Pré-requis:

  • Bonne connaissance du PureBasic (allocations dynamiques, pointeurs et gestion des entrées/sorties)
  • Avoir compris le fonctionnement du système d'événements


Dans cet article, nous allons gérer les statistiques des joueurs et leur inventaire. Couplé à la création d'un menu et de fonctions permettant d'afficher une boîte de dialogue, un choix et bien plus encore, nous allons pouvoir créer de A à Z les événements associés aux boutiques dans notre jeu (auberge et boutique d'achat d'objets).


Sommaire

[modifier] Rpg2d Generator: un générateur de fichiers .map, .sprite et .event

[modifier] Conception de l'outil

Cet outil utilise les étapes précédentes afin de générer les fichiers nécessaires au fonctionnement d'un niveau.

Il inclut les fichiers dont est constitué le projet, initialise les modules, charge les données statiques personnalisables et écrit les fichiers.

Le but est fournir un utilitaire permettant de créer rapidement les niveaux de notre jeu.

Procedure main()
 	map.s_map
  	libsprite.s_lib_sprite
  	libevent.s_lib_event

	OpenConsole()
	PrintN("RPG 2D .:. Generateur de fichiers ")
	PrintN("Games Creators Network - http://www.games-creators.org/")
	PrintN("Ce generateur permet de generer facilement les fichiers:")
	PrintN("o .event")
	PrintN("o .map")
	PrintN("o .sprite")
	PrintN("")
	PrintN("")  
	PrintN("* SPRITES:")
	PrintN("- initialisation")
	InitSprites(@libsprite)
	PrintN("- chargement des donnees statiques")
	StaticSprite(@map,@libsprite,@libevent)
	PrintN("- sauvegarde");
	SaveSprites(@libsprite,#SPRITE_IMG,#SPRITEFILE)
	
	PrintN("* MAP:")
	PrintN("- génération aleatoire");
	RandomizeMap(@map,#MAP_SIZE_X,#MAP_SIZE_Y,#CHIPSET)
	StaticMap(@map,@libsprite,@libevent)
	PrintN("- sauvegarde des donnees")
	SaveMap(@map, #MAPFILE, #MAP_MUSIQUE, #CHIPSET)
	
	PrintN("* EVENEMENTS:")
	PrintN("- initialisation")
	InitLibEvent(@libevent,@map,@libsprite)
	PrintN("- chargement des donnees statiques")
	StaticEvent(@map,@libsprite,@libevent)
	PrintN("- sauvegarde des donnees")
	SaveEvent(@libevent,@map,@libsprite,#EVENT_SON,#EVENTFILE)
	PrintN("")
	PrintN("")  
	PrintN("Generation des donnees realisee avec succes...")
	CloseWindow(0)
	Input()     ; Attend que la touche [Entrée] soit appuyée 
	CloseConsole()
EndProcedure


Attention: ce type d'application est très pratique car il permet sans effort de générer les fichiers nécessaires mais des erreurs de segmentation peuvent apparaître si les données statiques sont mauvaises. De plus, après un changement dans les fonctions de chargement et de sauvegarde, il peut devenir nécessaire de regénérer certains fichiers.


[modifier] Comment utiliser cet outil?

Pour utiliser cet outil, il faut modifier les constantes et les fonctions dans la zone de configuration.

;/* *****************
;** ** CONFIG ZONE **
;** ***************** */
;/*
;** Configurez ce qui suit afin de personnaliser les
;** fichiers générés.
;**/

;/*
;** Indiquez ici le nom des fichiers qui vont être
;** générés.
;**
;** Rappel: pour une carte complète, utiliser le
;**         même nom avec des extensions différentes
;*/

#MAPFILE = "map\generator.map"
#SPRITEFILE = "map\generator.sprite"
#EVENTFILE = "map\generator.event"

;/* Chipset utilisé par la carte */
#CHIPSET = "tiles\mchip0.bmp"
;/* Taille de la carte */
#MAP_SIZE_X = 100
#MAP_SIZE_Y = 100
;/* Musique de la carte
;** <!> réenregistrer la carte écrase cette donnée
;*/
#MAP_MUSIQUE = "musique\Opening1.mid"

;/*
;** Modification de la carte "à la main"/
;** Lors de l'appel à cette fonction, map
;** contient déjà une carte aléatoire de
;** taille MAP_SIZE_X*MAP_SIZE_Y
;*/
Procedure StaticMap(*map.s_map, *libsprite.s_lib_sprite, *libevent.s_lib_event)
EndProcedure

;/*
;** La planche utilisée par *tous* vos sprites
;*/
#SPRITE_IMG = "sprites\player.bmp"

;/*
;** Votre bibliothèque de sprite doit être
;** générée ici.
;*/
Procedure StaticSprite(*map.s_map, *libsprite.s_lib_sprite, *libevent.s_lib_event)
EndProcedure

#EVENT_SON = "son\tornade.wav"

Procedure StaticEvent(*map.s_map, *libsprite.s_lib_sprite, *libevent.s_lib_event)
EndProcedure

;/* *****************
;** ** FIN         **
;** ***************** */

Pour peupler ces fonctions, reportez-vous aux étapes précédentes et reprenez les fonctions chargeant des données statiques en les modifiant.


N'oubliez pas d'allouer toutes les ressources et de mettre à jour les compteurs (incrémenter n_sprite lorsque vous ajoutez un sprite par exemple).


[modifier] La gestion des joueurs

[modifier] Ajout des fichiers player.pb et player.pbi

Ajoutez les fichiers player.c et player.h au projet:

player.pb

;** player.pb
;**
;** Gestion du joueur
;**
;** Rôle:
;** - gestion des stats relatives aux joueurs
;** - gestion de l'inventaire

XIncludeFile "player.pbi"

player.pbi

;** player.pbi
;**
;** Gestion du joueur
;**
;** Rôle:
;** - gestion des stats relatives aux joueurs
;** - gestion de l'inventaire

[modifier] Gestion des statistiques des joueurs et de l'inventaire

Avant de concevoir les boutiques et notre menu, nous devons pouvoir gérer les joueurs et leur inventaire. Pour cela, nous allons concevoir un type dans player.pbi qui va contenir toutes les données.

#MAX_JOUEURS = 4
#MAX_ITEMS = 200

;e_statut_joueur
Enumeration 
    #STATUT_NORMAL
    #STATUT_POISON
    #STATUT_SOMMEIL
EndEnumeration

Structure s_stat_player
    joueurActif.b

    level.l
    statut.l
   
    hp.l 
    hpmax.l
    mp.l
    mpmax.l
   
    vitesse.l
   
    force.l
    dexterite.l
    precision.l
    intelligence.l
   
    equipement_tete.l
    equipement_corps.l
    equipement_arme.l
   
    surf.surface
EndStructure

;e_item_type
Enumeration 
    #ITEM_OBJET
    #ITEM_ARMURE_TETE
    #ITEM_ARMURE_CORPS
    #ITEM_ARME
    #ITEM_QUETE
EndEnumeration

Structure s_item
    nom.s
    icone.surface
    type.l
    son.l
    quantite.b
EndStructure

Structure s_player
    joueurs.s_stat_player[4] ; MAX_JOUEURS
    inventaire.s_item[200]   ; MAX_ITEMS
    argent.l
EndStructure

Afin de simplifier les code et comme le nombre de joueurs et d'objets ne varie pas ou très peu durant une partie, ce type est totalement statique.

Nous définissons au début un nombre maximum d'objets et de joueurs puis on alloue un tableau contenant toutes les caractéristiques de ces derniers.

Une variable est également ajoutée pour stocker l'argent de l'équipe.


Le tableau joueurs contient les statistiques de tous les membres du groupe. Même si un joueur ne fait pas parti actuellement de l'équipe, on est contraint de sauvegarder ces donneés durant toute la partie, c'est donc le booléen joueurActif qui indique s'il faut prendre ou pas ce personnage en considération.

Les trois variables gérant l'équipement du joueur equipement_tete, equipement_corps et equipement_arme contiennent -1 si le joueur n'est pas équipé ou l'id de l'objet dont il est équipé. Cet id se réfère au tableau d'inventaire.

Le tableau d'inventaire contient tous les objets du jeu. Pour gérer l'inventaire de l'équipe, il suffit alors d'affecter à chaque type d'objet une quantité. Ainsi afficher l'inventaire de l'équipe revient à parcourir ce tableau en ne gardant que les objets en quantité non-nulle.

Les objets sont caractérisés par un nom, une icône (une petite image symbolisant l'objet), un type et un son lorsqu'on les utilise.

Le type de l'objet peut être:

  • objet: les "consommables" (potions principalement)
  • armures pour la tête (casques)
  • armures pour le corps
  • armes (épée, arc, hache, etc.)
  • objets de quête (carte, clés, objets uniques)


Idée d'amélioration: modifiez le jeu afin de rendre les objets de quête invendables.


[modifier] Initialisation et libération des joueurs

L'initialisation du système se fait en plusieurs étapes:

  1. on initialise le tableau de joueurs
  2. on initialise l'inventaire
  3. on charge les objets du jeu
  4. on charge les joueurs par défaut

La libération du système consiste principalement à libérer les objets: la chaîne contenant leur nom et leur icône.

Procedure InitPlayer(*player.s_player)
    Protected i

    For i=0 To #MAX_JOUEURS-1
        InitStatPlayer(*player\joueurs[i])
        InitInventaire(*player)
    Next i 
    
    ; Init joueur 0 
    *player\joueurs[0]\joueurActif=1
    *player\joueurs[0]\Hp=80        ;  FIXEE: en attendant d'implanter les combats... */
    *player\joueurs[0]\hpmax=100
    *player\joueurs[0]\mp=0
    *player\joueurs[0]\mpmax=20
    
    *player\joueurs[0]\vitesse=3
    
    *player\joueurs[0]\force=1
    *player\joueurs[0]\dexterite=1
    *player\joueurs[0]\precision=1
    *player\joueurs[0]\intelligence=1
    LoadSurface(*player\joueurs[0]\surf,"battlers\1.bmp")
    TransparentSpriteColor(*player\joueurs[0]\surf\id,255,255,255)
    *player\joueurs[0]\equipement_tete=1
    *player\joueurs[0]\equipement_corps=2
    *player\joueurs[0]\equipement_arme=4
    
    ; Init joueur 
    *player\argent=100
    
EndProcedure

Procedure InitStatPlayer(*stat.s_stat_player)
    
    *stat\joueurActif=0
    
    *stat\level=1
    *stat\statut=#STATUT_NORMAL
    
    *stat\Hp=0
    *stat\hpmax=0
    *stat\mp=0
    *stat\mpmax=0
    
    *stat\vitesse=0
    
    *stat\force=0
    *stat\dexterite=0
    *stat\precision=0
    *stat\intelligence=0
    
    *stat\equipement_tete=-1
    *stat\equipement_corps=-1
    *stat\equipement_arme=-1
    
    *stat\surf\id=#Null
EndProcedure

Procedure InitItem(*item.s_item)
    *item\nom=#NULL$
    *item\icone\id=#Null
    *item\type=#ITEM_OBJET
    *item\son=#Null
    *item\quantite=0
EndProcedure

Procedure InitInventaire(*player.s_player)
    
    Protected i
    For i=0 To #MAX_ITEMS-1
        InitItem(*player\inventaire[i])
    Next i    
    ; item 0: potion de santé 
    *player\inventaire[0]\nom="Petite potion de santé"
    LoadSurface(*player\inventaire[0]\icone,"icons\potion.bmp")
    TransparentSpriteColor(*player\inventaire[0]\icone\id,239,243,255)
    *player\inventaire[0]\type=#ITEM_OBJET
    *player\inventaire[0]\son=LoadSound(#PB_Any,"son\heal.wav")
    *player\inventaire[0]\quantite=5
    
    ; item 1: casque  
    *player\inventaire[1]\nom="Casque en fer"
    LoadSurface(*player\inventaire[1]\icone,"icons\casque.bmp")
    TransparentSpriteColor(*player\inventaire[1]\icone\id,239,243,255)
    *player\inventaire[1]\type=#ITEM_ARMURE_TETE
    *player\inventaire[1]\son=#Null
    *player\inventaire[1]\quantite=1
    
    ; item 2: armure   
    *player\inventaire[2]\nom="Armure moyenne"
    LoadSurface(*player\inventaire[2]\icone,"icons\armure.bmp")
    TransparentSpriteColor(*player\inventaire[2]\icone\id,239,243,255)
    *player\inventaire[2]\type=#ITEM_ARMURE_CORPS
    *player\inventaire[2]\son=#Null
    *player\inventaire[2]\quantite=1
    
    ; item 3: bouclier   
    *player\inventaire[3]\nom="Bouclier en fer"
    LoadSurface(*player\inventaire[3]\icone,"icons\bouclier.bmp")
    TransparentSpriteColor(*player\inventaire[3]\icone\id,239,243,255)
    *player\inventaire[3]\type=#ITEM_ARME
    *player\inventaire[3]\son=#Null
    *player\inventaire[3]\quantite=1
    
    ; item 4: épée   
    *player\inventaire[4]\nom="Epée de mauvaise qualité"
    LoadSurface(*player\inventaire[4]\icone,"icons\epee.bmp")
    TransparentSpriteColor(*player\inventaire[4]\icone\id,239,243,255)
    *player\inventaire[4]\type=#ITEM_ARME
    *player\inventaire[4]\son=#Null
    *player\inventaire[4]\quantite=1
    
    ; item 5: arc   
    *player\inventaire[5]\nom="Arc court"
    LoadSurface(*player\inventaire[5]\icone,"icons\arc.bmp")
    TransparentSpriteColor(*player\inventaire[5]\icone\id,239,243,255)
    *player\inventaire[5]\type=#ITEM_ARME
    *player\inventaire[5]\son=#Null
    *player\inventaire[5]\quantite=1
EndProcedure

Procedure FreePlayer(*player.s_player)
    
    Protected  i
    ; libération de l'inventaire 
    For i=0 To #MAX_ITEMS-1
        *player\inventaire[i]\nom=#NULL$
        If IsSprite(*player\inventaire[i]\icone\id)
            FreeSprite(*player\inventaire[i]\icone\id)
        EndIf    
        If IsSound(*player\inventaire[i]\son)
            FreeSound(*player\inventaire[i]\son)
        EndIf    
    Next 
EndProcedure

Remarque : J'ai ajouté une fonction LoadSurface dans le fichier main.pbi() de façon à initialiser les données nécessaires lors du chargement d'un sprite.

Procedure LoadSurface(*surf, Fichier.s)
    Protected *Temp.surface
    *Temp=*surf
    *Temp\id=LoadSprite(#PB_Any,Fichier)
    If *Temp\id 
        *Temp\w=SpriteWidth(*Temp\id)
        *Temp\h=SpriteHeight(*Temp\id)
    Else
        MessageRequester("Erreur","Impossible de charger le sprite " + Fichier,0)
        End
    EndIf
EndProcedure 


[modifier] Action associée à chaque objet

Les objets consommables produisent un effet sur les statistiques des personnages (regain de HP, augmentation de la vitesse...). Ces effets sont gérés par la fonction DoEvent()

Procedure DoItem(*player.s_player,itemId.l,son.l)
    Select itemId
        Case 0
            If *player\inventaire[0]\quantite>0 And *player\joueurs[0]\Hp<*player\joueurs[0]\hpmax
                PlaySound(*player\inventaire[0]\son)
                *player\joueurs[0]\Hp + 20
                If *player\joueurs[0]\Hp>*player\joueurs[0]\hpmax
                    *player\joueurs[0]\Hp=*player\joueurs[0]\hpmax
                EndIf    
                *player\inventaire[0]\quantite - 1
            EndIf    
    EndSelect
EndProcedure


[modifier] L'interface graphique

L'interface graphique fait jouer un élément supplémentaire que nous ne gérions pas auparavant. Il s'agit du texte.

Pour gérer du texte, deux choix s'offrent à nous:

  1. utiliser une police graphique
  2. utiliser la bibliothèque Font


Le principe de la police graphique est de stocker dans une image le dessin des lettres 'a', 'b', 'c'... et de les afficher en copiant cette image sur la surface associée à l'écran.

Cette méthode est intéressante mais présente plusieurs inconvénients: la gestion des langues étrangères s'avère difficile par exemple. Il faut encore convertir une police standard en police graphique et concevoir ce système.


Pour faciliter le travail, on peut à l'inverse s'orienter versFont: cette bibliothèque permet d'ouvrir des fichiers TrueType et de les utiliser directement pour afficher une chaîne de caractère à l'écran.

[modifier] Ajout des fichiers gui.pb et gui.pbi

Ajoutez les fichiers gui.pb et gui.pbi au projet:

gui.pb

;** gui.pb
;**
;** Gestion du GUI, des menus, des dialogues...
;**
;** Rôle:
;** - dialogue
;** - menus
;** - GUI (barre de vie par exemple)

XincludeFile "gui.pbi"

player.pbi

;** gui.pbi
;**
;** Gestion du GUI, des menus, des dialogues...
;**
;** Rôle:
;** - dialogue
;** - menus
;** - GUI (barre de vie par exemple)

[modifier] La structure s_gui

Afin de simplifier l'affichage de l'interface, un type structuré s_gui doit être créé dans gui.pbi. Il contient un pointeur vers la fonte utilisée et vers les surfaces contenant les images utiles pour le rendu des dialogues et des menus.

Remarque : Pour gérer la transparence des boites de dialogues , j'avais commencé par utiliser DisplayTranslucideSprite() .ça rendait très bien sur mon ordinateur , mais en faisant des tests sur une machine moins performante , je me suis aperçu que cette fonction faisait ramer le jeu , alors je me suis rabattu sur les sprites 3D .
Ils sont beaucoup plus rapides , et offrent plus de possibilités , comme les rotations les zooms , etc. Par rapport à la version originale j'ai ajouté deux champs dans la structure s_gui fond pour afficher le fond transparent des boites de dialogues , et curseur pour afficher un curseur transparent dans les menus.

Structure s_gui
    font.l
    surf.surface     
    fond.surface       ;Pour le sprite3D gérant la transparence
    menu.surface
    curseur.surface    ;Pour le sprite3D gérant la transparence
EndStructure
  • surf pointe vers l'images contenant les fonds colorés et les bordures.
  • menu pointe vers l'images contenant l'image de fond sur lequel nous allons dessiner le menu.
  • fond pointe vers le sprite 3D contenant le fond transparent des boites de dialogues
  • curseur pointe vers le sprite3D contenant le curseur transparent des menus.
Procedure InitGui(*gui.s_gui)
    *gui\surf\id=LoadSprite(#PB_Any,"img\gui.bmp",#PB_Sprite_Texture)
    TransparentSpriteColor(*gui\surf\id,0,0,0)
    *gui\menu\id=LoadSprite(#PB_Any,"img\menu.bmp")
    TransparentSpriteColor(*gui\menu\id,0,0,0)
    
    ;On génère ici le sprite3D qui sera utilisé comme fond translucide 
    ClipSprite(*gui\surf\id,0,0,400,150)
    CopieS=CreateSprite(#PB_Any,400,150,#PB_Sprite_Texture)         
    UseBuffer(copieS)                                                
    DisplaySprite(*gui\surf\id,0,0)
    UseBuffer(-1)
    *gui\fond\id=CreateSprite3D(#PB_Any,copieS)
    
    ;On génère ici le sprite3D qui sera utilisé comme curseur translucide 
    ClipSprite(*gui\surf\id,0,300,76,32)
    CopieM=CreateSprite(#PB_Any,400,150,#PB_Sprite_Texture)
    UseBuffer(copieM)
    DisplaySprite(*gui\surf\id,0,0)
    UseBuffer(-1)
    *gui\curseur\id=CreateSprite3D(#PB_Any,copieM)
    Start3D()
	    TransformSprite3D(*gui\curseur\id,0,0,500,0,500,150,0,150)
    Stop3D()  
                
    ; chargement de la police utilisée dans le jeu */
    *gui\font=LoadFont(#PB_Any,"carolingia", 16,#PB_Font_HighQuality)
    
    If *gui\font=0
        MessageRequester("Erreur","Impossible de charger la police de caractères carolingia.ttf", 0)
    EndIf    
    
EndProcedure

Procedure FreeGui(*gui.s_gui)
    FreeSprite(*gui\surf\id)
    *gui\surf\id=#Null
    FreeSprite(*gui\menu\id)
    *gui\menu\id=#Null
    CloseFont(*gui\font)
    *gui\font=#Null
EndProcedure


[modifier] Afficher un dialogue

Il faut désormais s'attacher à écrire la fonction qui va "gérer" le jeu durant un dialogue. Il s'agit d'une boucle de jeu légèrement diffèrente à celle qui est présente dans la fonction Main().

Procedure DrawDialog(*map.s_map,*libsprite.s_lib_sprite, *gui.s_gui,dialog.s,color.l)
 
    Protected src.rectangle, dest.rectangle
    Protected isdone, i, CopieEcran , hdc
 
    i=1
    isdone=0

    While isdone=0
    	ClearScreen(0,0,0)
 		
	HandleMap(*map)
 	HandleSprites(0, *libsprite, *map)
    
        If ExamineKeyboard()
     
            If KeyboardReleased(#PB_Key_Space)
                isdone = 1
            EndIf    
        
            If KeyboardReleased(#PB_Key_Escape)
                isdone = 1
            EndIf    
            
        EndIf
    
	; affiche le fond
    	dest\x=200
    	dest\y=400
    	    
    	Start3D() 
    	   DisplaySprite3D(*gui\fond\id,dest\x,dest\y,150)
	Stop3D() 
    	
	; affichage fond 
    	src\x=0
    	src\y=150
    	src\w=400
    	src\h=150
       
    	ClipSprite(*gui\surf\id,src\x,src\y,src\w,src\h)
    	DisplayTransparentSprite(*gui\surf\id,dest\x,dest\y) 
        
    	dest\y + 4 
    	dest\x=210
    
        hdc = StartDrawing(ScreenOutput())
        If hdc 
            DrawingMode(1)                          
            DrawingFont(UseFont(*gui\font))  
            FrontColor(Red(color),Green(color),Blue(color))

            ;Pour récupérer la hauteur des caractères 
            GetTextExtentPoint32_(hdc,@dialog,Len(dialog),Size.size) ; Seulement pour windows !
            i=1            
            While i < Len(dialog) 

                Lettre$=Mid(dialog,i,1)
                
                If  Lettre$=Chr(10)
                    dest\y + Size\cy
                    dest\x=210
                ElseIf Lettre$="\" 
                    dest\y + Size\cy
                    dest\x=210
                    i + 1
                Else
                    Locate(dest\x,dest\y)
                    DrawText(Lettre$)
                    dest\x + TextLength(Lettre$)
                EndIf   
                i + 1
            Wend   

            StopDrawing()
        EndIf   
        
        FlipBuffers()
  
    Wend    
      
EndProcedure

Le but de cette fonction est d'afficher un fond légèrement transparent et d'écrire caractère par caractère le texte dessus.

Si l'utilisateur appuie sur la touche espace, la boucle de jeu est rompue.

Cette fonction retourne une valeur: si elle vaut zéro, le programme continue sinon cela signifie que le jeu est en train de se fermer.


[modifier] Afficher un choix


La fonction affichant un choix procède de la même logique, cependant afin de pouvoir gérer un nombre de choix infini, une liste chainée n_input() devra être renseignée avant d'appeler la fonction DrawInput.

La déclaration de la liste chainée se trouve dans le fichier event.pbi

;Liste chainée pour la fonction gui->DrawInput() 
NewList  n_Input.s()
Procedure.l DrawInput(*map.s_map,*libsprite.s_lib_sprite,*gui.s_gui,color.l,dialog.s)

    Protected src.rectangle,dest.rectangle
    Protected isdone, i, selection, advance,n_input
   
    isdone=0
    selection=0
    n_input=CountList(n_input())
	
    While isdone=0
	ClearScreen(0,0,0)
		
	; affiche la map et les sprites
        HandleMap(*map)
        HandleSprites(0, *libsprite, *map)
        
        If ExamineKeyboard()

            If KeyboardReleased(#PB_Key_Up)
                If selection>0
                    selection - 1
                EndIf
            EndIf
            If KeyboardReleased(#PB_Key_Down)
                If selection<n_input-1
                    selection + 1
                EndIf
            EndIf
            If KeyboardReleased(#PB_Key_Space)
                isdone = 1
            EndIf   
            
        EndIf
    	
	; affiche le fond
    	dest\x=200
    	dest\y=400
    	    
    	Start3D() 
    	  DisplaySprite3D(*gui\fond\id,dest\x,dest\y,150)
	Stop3D() 
    	
	; affichage cadre  
    	src\x=0
    	src\y=150
    	src\w=400
    	src\h=150
       
    	ClipSprite(*gui\surf\id,src\x,src\y,src\w,src\h)
    	DisplayTransparentSprite(*gui\surf\id,dest\x,dest\y) 
        
        ; affiche les textes
        hdc = StartDrawing(ScreenOutput())
        If hdc 
            DrawingMode(1)                          
            DrawingFont(UseFont(*gui\font))  
            FrontColor(Red(color),Green(color),Blue(color))
            
            ;Pour récupérer la hauteur des caractères 
            GetTextExtentPoint32_(hdc,@dialog,Len(dialog),Size.size) ; Seulement pour windows !
          
            dest\y + 4 
            dest\x + 5
            i=1
            
            While i < Len(dialog) 
                
                Lettre$=Mid(dialog,i,1)
                
                If Lettre$="\"
                    dest\y + Size\cy
                    dest\x=205
                    i + 1
                Else
                    Locate(dest\x,dest\y)
                    DrawText(Lettre$)
                    dest\x + TextLength(Lettre$)
                EndIf   
                i + 1
            Wend  
            
            ; affichage des choix 
            dest\x=220
            dest\y + 10
            
            ForEach n_Input()
                        
                dest\y + Size\cy
                Locate(dest\x,dest\y)
                DrawText(n_Input())
                
                ;Affiche le curseur
                If Selection = ListIndex(n_input())
	           Box(210,dest\y,8,size\cy,RGB(155,155,155))
                EndIf
            Next
            StopDrawing()
        EndIf
        
        FlipBuffers()
             
    Wend    

    ProcedureReturn selection
EndProcedure
 


[modifier] Le menu

[modifier] Ecran principal

Les fonctions qui suivent sont relativement simples: il s'agit de manipulation d'une boucle de jeu sur laquelle on affiche du texte et des sprites selon le contexte.

Procedure.l Menu(*player.s_player,*gui.s_gui)
     
    Protected dest.rectangle
    Protected isdone, res, selection, i, son
    
    son = LoadSound(#PB_Any,"son\clic.wav")
    
    selection=0
    isdone=0
    res=0
 
    While isdone=0

        If ExamineKeyboard()
            
            If KeyboardReleased(#PB_Key_Escape)
            
                isdone = 1
                PlaySound(son)
                
            ElseIf KeyboardReleased(#PB_Key_Up)
            
                selection - 1
                PlaySound(son)
                
            ElseIf KeyboardReleased(#PB_Key_Down)
                selection + 1
                PlaySound(son)
            EndIf
            
            If selection<0
                selection=3
            EndIf    
            If selection>3
                selection=0
            EndIf 
            
            If KeyboardReleased(#PB_Key_Space)
                PlaySound(son)
                
                Select selection
            
                    Case 0
                        ; inventaire 
                        res=MenuInventaire(*gui,*player, selection, son)
                        isdone=res

                    Case 1
                        ; équiper 
                        res=MenuEquiper(*gui,*player, selection, son)
                        isdone=res

                    Case 2
                        ; magie */

                    Case 3
                        ; quitter */
                        res=1
                        isdone=1

                EndSelect
            EndIf

        EndIf
 
        DisplaySprite(*gui\menu\id,0,0)

        MenuBlitLeftPanel(*gui,*player,selection)
 
        ; affichage infos sur le panneau droit 
        
        For i=0 To #MAX_JOUEURS-1
            If *player\joueurs[i]\joueurActif

                dest\x=220
                dest\y=120+i*(*player\joueurs[i]\surf\h+10)
                DisplayTransparentSprite(*player\joueurs[i]\surf\id,dest\x,dest\y)
                
                StartDrawing(ScreenOutput())
                    DrawingMode(1)                          
                    DrawingFont(UseFont(*gui\font))  
                    FrontColor(255,255,255)
                    
                    dest\x=220+*player\joueurs[i]\surf\w+10
                    dest\y=120+i*(*player\joueurs[i]\surf\h+10)
                    Locate(dest\x,dest\y)
                    DrawText("Hp : " + Str(*player\joueurs[i]\Hp) + " / " + Str(*player\joueurs[i]\hpmax))
                    
                    dest\x=450+*player\joueurs[i]\surf\w+10
                    dest\y=120+i*(*player\joueurs[i]\surf\h+10)
                    Locate(dest\x,dest\y)
                    DrawText("Mp : " + Str(*player\joueurs[i]\mp) + " / " + Str(*player\joueurs[i]\mpmax))          
                    
                    dest\x=220+*player\joueurs[i]\surf\w+10
                    dest\y=170+i*(*player\joueurs[i]\surf\h+10)
                    Locate(dest\x,dest\y)
                    DrawText("Force :         " + Str(*player\joueurs[i]\force))
                    
                    dest\x=450+*player\joueurs[i]\surf\w+10
                    dest\y=170+i*(*player\joueurs[i]\surf\h+10)
                    Locate(dest\x,dest\y)
                    DrawText("Dextérité :    " + Str(*player\joueurs[i]\dexterite))          
                    
                    dest\x=220+*player\joueurs[i]\surf\w+10
                    dest\y=190+i*(*player\joueurs[i]\surf\h+10)
                    Locate(dest\x,dest\y)
                    DrawText("Précision :    " + Str(*player\joueurs[i]\precision))
                    
                    dest\x=450+*player\joueurs[i]\surf\w+10
                    dest\y=190+i*(*player\joueurs[i]\surf\h+10)
                    Locate(dest\x,dest\y)
                    DrawText("Intelligence : " + Str(*player\joueurs[i]\intelligence))
               StopDrawing()     
            
           EndIf    
        Next i
        
        FlipBuffers()

    Wend
    FreeSound(son)
    ProcedureReturn res
EndProcedure
 


Cette fonction affiche le panneau à gauche comprenant les actions principales dans le menu:

  • Inventaire
  • Equiper
  • Magie (pas encore créé!)
  • Quitter

Elle affiche également l'argent gagné par le joueur.

Procedure MenuBlitLeftPanel(*gui.s_gui,*player.s_player,selection.b)
    
    Static alpha ,alpha_sens
    Protected src.rectangle, dest.rectangle, color
    
    StartDrawing(ScreenOutput())
	DrawingMode(1)                          
    DrawingFont(UseFont(*gui\font))  
    FrontColor(255,255,255)
    
    dest\x=32
    dest\y=32
    Locate(dest\x,dest\y)
    DrawText("Inventaire") 
    
    dest\y=64
    Locate(dest\x,dest\y)
    DrawText("Equiper") 

    dest\y=96
    Locate(dest\x,dest\y)
    DrawText("Magie")  
    
    dest\y=128
    Locate(dest\x,dest\y)
    DrawText("Quitter")  

    ; panneau gauche bas 
    dest\x=28;
    dest\y=480;
    Locate(dest\x,dest\y)
    DrawText("Argent: " + Str(*player\argent))  
	
    StopDrawing()
          
    If alpha_sens=0
    	alpha + 1
        If alpha>=175
    	   alpha_sens=1
	EndIf    
    Else 
        alpha - 1
	If alpha<=125
	   alpha_sens=0
    	EndIf
    EndIf
	
   dest\x=28
   dest\y=24+selection*32
   Start3D()
   DisplaySprite3D(*gui\curseur\id,dest\x,dest\y,alpha)
  Stop3D() 

 EndProcedure


[modifier] Affichage de l'inventaire

L'affichage de l'inventaire suit exactement le même principe que la fonction précédente.

Cette fonction affiche l'inventaire du joueur qui correspond au type d'objet sélectionné dans la zone en haut à droite du menu.

Procedure.l MenuInventaire(*gui.s_gui,*player.s_player,selectiong.l,son.l)
    Protected src.rectangle,dest.rectangle
    Protected isdone,res,selection,selitem,item, i, n_item
    
    isdone=0
    res=0
    selection=0
    selitem=0
    
    While isdone=0
        
        If ExamineKeyboard()
                    
            If KeyboardReleased(#PB_Key_Escape)
            
                PlaySound(son)
                isdone = 1
                
            ElseIf KeyboardReleased(#PB_Key_Left)
                selection - 1
                selitem=0
                PlaySound(son)
                
            ElseIf KeyboardReleased(#PB_Key_Right)
            
                selection + 1
                selitem=0
                PlaySound(son)
                
            EndIf
            
            If KeyboardReleased(#PB_Key_Up)
                selitem - 1
                PlaySound(son)
            ElseIf KeyboardReleased(#PB_Key_Down)
                selitem + 1
                PlaySound(son)
            EndIf
            
            If selection<0
                selection=3
            ElseIf selection>3
                selection=0
            EndIf
            
            If selitem<0
                selitem=0
            EndIf
            
            If selitem>=n_item And n_item>0
                selitem=n_item-1
            EndIf
            
            If n_item>0 And KeyboardReleased(#PB_Key_Space) 
                ; recherche de l'item sélectionné dans le tableau 
                item=0
                i=0
                While item<selitem And i<#MAX_ITEMS
                    ;Attention on a 5 types, mais 4 sélections , les armures tetes et corps sont ensembles ! réécrire ce test 
                    If *player\inventaire[i]\nom<>#NULL$ And *player\inventaire[i]\quantite>0 And selection=*player\inventaire[i]\type 
                        item + 1
                    EndIf
                    i + 1
                Wend
                DoItem(*player, i, son)
            EndIf
         
        EndIf   
        
        DisplaySprite(*gui\menu\id,0,0)
    
        ; affichage partie haute 
        MenuBlitTopPanel(*gui,selection)
        ; affichage partie gauche 
        MenuBlitLeftPanel(*gui,*player,selectiong)
        ; affichages des items dans la catégorie sélectionnée 
        n_item=0
        dest\x=0
        dest\y=100
        For i=0 To #MAX_ITEMS-1
            Selection0=(selection=0) And (*player\inventaire[i]\type=#ITEM_OBJET)
            selection1=(selection=1) And ((*player\inventaire[i]\type=#ITEM_ARMURE_TETE) Or (*player\inventaire[i]\type=#ITEM_ARMURE_CORPS))
            Selection2=(selection=2) And (*player\inventaire[i]\type=#ITEM_ARME)
            Selection3=(selection=3) And (*player\inventaire[i]\type=#ITEM_QUETE)
            If *player\inventaire[i]\nom<>#NULL$ And *player\inventaire[i]\quantite>0  And (Selection0 Or selection1 Or Selection2 Or Selection3) 
                dest\x=220
                dest\y + 32
                ; affichage icône 
                DisplayTransparentSprite(*player\inventaire[i]\icone\id,dest\x,dest\y)
                ; affichage texte (nom + qtité 
                dest\x=250
                dest\y + 5
                StartDrawing(ScreenOutput())
                    DrawingMode(1)                          
                    DrawingFont(UseFont(*gui\font))  
                    FrontColor(255,255,255)
                    Locate(dest\x,dest\y)
                    DrawText(*player\inventaire[i]\nom + " ( x " + Str(*player\inventaire[i]\quantite) + " )") 
                StopDrawing()
                dest\y - 5
                n_item + 1
            EndIf
        Next i
        ; affichage du rectangle de sélection */
        If n_item>0
            src\x=0
            src\y=300
            src\w=24
            dest\w=24
            src\h=24
            dest\h=24
            dest\x=220
            dest\y=132+selitem*32
            
            StartDrawing(ScreenOutput())
                DrawingMode(4)
                Box(dest\x,dest\y,src\w,src\h,RGB(255,0,0))
            StopDrawing()
        EndIf
        FlipBuffers()
    Wend
    ProcedureReturn res  
EndProcedure

La fonction ci-dessous affiche la zone en haut à droite du menu qui contient le type d'objet actuellmeent affiché.

Procedure MenuBlitTopPanel(*gui.s_gui,selection.b)

    Protected src.rectangle, dest.rectangle
    Static alpha, alpha_sens 
	 
    StartDrawing(ScreenOutput())
    DrawingMode(1)                          
    DrawingFont(UseFont(*gui\font))  
    FrontColor(255,255,255)
        
    dest\x=250
    dest\y=40
    Locate(dest\x,dest\y)
    DrawText("Objets") 
        
    dest\x=350
    Locate(dest\x,dest\y)
    DrawText("Armures") 
                
    dest\x=450
    Locate(dest\x,dest\y)
    DrawText("Armes") 
        
    dest\x=550
    Locate(dest\x,dest\y)
    DrawText("Obj. quêtes") 
    StopDrawing()
              
    If alpha_sens=0
        alpha + 1
        If alpha>=175
            alpha_sens=1
        EndIf    
    Else 
        alpha - 1
        If alpha<=125
            alpha_sens=0
        EndIf
    EndIf
 

    dest\x=245+selection*100
    dest\y=32
            
    Start3D()
	DisplaySprite3D(*gui\curseur\id,dest\x,dest\y,alpha)
    Stop3D()         

EndProcedure

[modifier] L'équipement

L'équipement est une fonction relativement similaire aux deux précédentes.

Procedure.l MenuEquiper(*gui.s_gui,*player.s_player,selectiong.l,son.l)
    Protected src.rectangle, dest.rectangle
    Protected surf.surface
    Protected isdone, res, selection, i 
    Static  alpha ,alpha_sens
       
    isdone=0
    res=0
    selection=0

    While isdone=0
 
        If ExamineKeyboard()
 

            If KeyboardReleased(#PB_Key_Escape)
                PlaySound(son)
                isdone = 1
            EndIf
            If KeyboardReleased(#PB_Key_Left)
                PlaySound(son)
                Select selection

                    Case 0
                        i=*player\joueurs[0]\equipement_tete-1
                        If i<0
                            i=#MAX_ITEMS-1
                        EndIf    
                        While i<>*player\joueurs[0]\equipement_tete
                            If *player\inventaire[i]\type=#ITEM_ARMURE_TETE And *player\inventaire[i]\quantite>0
                                *player\joueurs[0]\equipement_tete=i
                            Else 
                                i - 1
                                If i<0
                                    *player\joueurs[0]\equipement_tete=-1
                                    i=-1
                                EndIf
                            EndIf
                        Wend    

                    Case 1
                        i=*player\joueurs[0]\equipement_corps-1
                        If i<0
                            i=#MAX_ITEMS-1
                        EndIf    
                        While i<>*player\joueurs[0]\equipement_corps

                            If *player\inventaire[i]\type=#ITEM_ARMURE_CORPS And *player\inventaire[i]\quantite>0
                                *player\joueurs[0]\equipement_corps=i
                            Else 
                                i - 1
                                If i<0
                                    *player\joueurs[0]\equipement_corps=-1
                                    i=-1
                                EndIf
                            EndIf
                        Wend
                        
                    Case 2
                        i=*player\joueurs[0]\equipement_arme-1
                        If i<0
                            i=#MAX_ITEMS-1

                        EndIf    
                        While i<>*player\joueurs[0]\equipement_arme
                            If *player\inventaire[i]\type=#ITEM_ARME And *player\inventaire[i]\quantite>0
                                *player\joueurs[0]\equipement_arme=i
                            Else 
                                i - 1
                                If i<0
                                    *player\joueurs[0]\equipement_arme=-1
                                    i=-1
                                EndIf
                            EndIf   
                        Wend

                EndSelect
            EndIf
            If KeyboardReleased(#PB_Key_Right)

                PlaySound(son)
                Select selection
                    Case 0
                        i=*player\joueurs[0]\equipement_tete + 1
                        If i>=#MAX_ITEMS
                            i=0
                        EndIf    
                        While i<>*player\joueurs[0]\equipement_tete
                            If *player\inventaire[i]\type=#ITEM_ARMURE_TETE And *player\inventaire[i]\quantite>0
                                *player\joueurs[0]\equipement_tete=i
                            Else 
                                i + 1
                                If i>=#MAX_ITEMS
                                    *player\joueurs[0]\equipement_tete=-1
                                    i=-1
                                EndIf
                            EndIf
                        Wend

                    Case 1
                        i=*player\joueurs[0]\equipement_corps + 1
                        If i>=#MAX_ITEMS
                            i=0
                        EndIf  
                        While  i<>*player\joueurs[0]\equipement_corps
                            If *player\inventaire[i]\type=#ITEM_ARMURE_CORPS And *player\inventaire[i]\quantite>0
                                *player\joueurs[0]\equipement_corps=i
                            Else 
                                i + 1
                                If i>=#MAX_ITEMS
                                    *player\joueurs[0]\equipement_corps=-1
                                    i=-1
                                EndIf
                            EndIf
                        Wend
                    Case 2
                        i=*player\joueurs[0]\equipement_arme + 1
                        If i>=#MAX_ITEMS
                            i=0
                        EndIf  
                        While i<>*player\joueurs[0]\equipement_arme
                            If *player\inventaire[i]\type=#ITEM_ARME And *player\inventaire[i]\quantite>0
                                *player\joueurs[0]\equipement_arme=i
                            Else 
                                i + 1
                                If i>=#MAX_ITEMS
                                    *player\joueurs[0]\equipement_arme=-1
                                    i=-1
                                EndIf
                            EndIf
                        Wend
                        

                EndSelect
            EndIf
            
            If KeyboardReleased(#PB_Key_Up)
                selection - 1
                PlaySound(son)
            EndIf
            If KeyboardReleased(#PB_Key_Down)
                selection + 1
                PlaySound(son)
            EndIf

            If selection<0
                selection=2
            ElseIf selection>2
                selection=0
            EndIf    


        EndIf
        
        DisplaySprite(*gui\menu\id,0,0)

        ; affichage partie gauche 
        MenuBlitLeftPanel(*gui,*player,selectiong)
        ; affichages équipement + items équipe 
        dest\x=220
        dest\y=132
        StartDrawing(ScreenOutput())
        DrawingMode(1)                          
        DrawingFont(UseFont(*gui\font))  
        FrontColor(255,255,255)
        Locate(dest\x,dest\y)
        If *player\joueurs[0]\equipement_tete=-1
            DrawText("Eqp. tête             " + "non-équipé") 
        Else
            DrawText("Eqp. tête             " + *player\inventaire[*player\joueurs[0]\equipement_tete]\nom ) 
        EndIf  
        
        dest\y + 32
        Locate(dest\x,dest\y)
        If *player\joueurs[0]\equipement_corps=-1
            DrawText("Eqp. corps             " + "non-équipé") 
        Else
            DrawText("Eqp. corps             " + *player\inventaire[*player\joueurs[0]\equipement_corps]\nom ) 
        EndIf 
        
        dest\y + 32
        Locate(dest\x,dest\y)
        If *player\joueurs[0]\equipement_arme=-1
            DrawText("Arme                   " + "non-équipé") 
        Else
            DrawText("Arme                   " + *player\inventaire[*player\joueurs[0]\equipement_arme]\nom ) 
        EndIf 
  
        StopDrawing()
         
        ; affichage du rect de sélection 
            If alpha_sens=0
        alpha + 1
        If alpha>=175
            alpha_sens=1
        EndIf    
    Else 
        alpha - 1
        If alpha<=125
            alpha_sens=0
        EndIf
    EndIf
        dest\x=218
        dest\y=125+selection*32
        Start3D()
	   DisplaySprite3D(*gui\curseur\id,dest\x,dest\y,alpha)
	Stop3D() 
        
        FlipBuffers()
    Wend
    ProcedureReturn res
EndProcedure

[modifier] Modification des événements

Pour terminer cette étape, il faut lier les fonctions que nous avons conçues au système d'événement réalisé précédemment.

[modifier] Ajout de nos nouveaux événements

Comme explique précédemment, le système d'événement est évolutif, il va être très simple de gérer nos trois événements.

Voici une méthode qui est utilisable pour n'importe quel ajout d'événement.

Tout d'abord, modifier les types et ajouter la structure contenant les paramètres de notre événement.

; Ajout du type d'événement au type somme
;   <!> opération déjà effectuée à l'étape 6
;
;Type d'évènement : e_event_type
Enumeration
    #EVENT_NULL
    #EVENT_TELEPORT
    #EVENT_DIALOG   
    #EVENT_BATTLE
    #EVENT_SHOP
    #EVENT_INN
EndEnumeration


; Modification des structures stockant les paramètres
;   des événements.
;

Structure s_event_param_dialog 
  dialog.s
EndStructure

Structure s_event_param_shop 
    n_item.l
    *itemId.long
    *coutItem.long
    *prixVente.long
EndStructure

Structure s_event_param_inn 
    cout.l
EndStructure


Modifier ReadEvent() et WriteEvent().

Procedure ReadEvent(*event.s_event)
    Protected *Ptparam_dialog.s_event_param_dialog,*Ptparam_telport.s_event_param_teleport
    Protected *Ptparam_shop.s_event_param_shop,*Ptparam_inn.s_event_param_inn
    Protected *Ptr.long
    
    InitEvent(*event)
     
    *event\type=ReadLong()
    If *event\type=#EVENT_NULL
        ProcedureReturn
    EndIf 
    *event\onaction=ReadLong()
    *event\proba=ReadByte()
    *event\player_anim=ReadLong()
    *event\is_unique=ReadByte()
    
    FichierSon.s=Space(256)
    ReadData(@FichierSon,63)
    *event\sound=LoadSound(#PB_Any,FichierSon)
     
    Select *event\type
     
    Case #EVENT_NULL
     
    Case #EVENT_TELEPORT
        
        *event\param=AllocateMemory(SizeOf(s_event_param_teleport))
        *Ptparam_telport=*event\param
        
        Fichier.s=Space(256)
        ReadData(@Fichier,63)
        
        If Len(Trim(Fichier))=0
            *Ptparam_telport\filename=#NULL$
        Else
            *Ptparam_telport\filename=Fichier
        EndIf 
        
        *Ptparam_telport\startX=ReadLong()
        *Ptparam_telport\startY=ReadLong()
     
    Case #EVENT_DIALOG
        *event\param=AllocateMemory(SizeOf(s_event_param_dialog))
        *Ptparam_dialog=*event\param
        
        dialogue.s=Space(256)
        ReadData(@dialogue,255)
        
        *Ptparam_dialog\dialog=dialogue
     
    Case #EVENT_BATTLE
     
    Case #EVENT_SHOP
        *event\param=AllocateMemory(SizeOf(s_event_param_shop))
        *Ptparam_shop=*event\param
        *Ptparam_shop\n_item=ReadLong()
        *Ptparam_shop\itemId=AllocateMemory(*Ptparam_shop\n_item*SizeOf(LONG))
        *Ptparam_shop\coutItem=AllocateMemory(*Ptparam_shop\n_item*SizeOf(LONG))
        
        For i=0 To *Ptparam_shop\n_item-1
            *Ptr=*Ptparam_shop\itemId + i * SizeOf(LONG)
            *Ptr\l=ReadLong()
            *Ptr=*Ptparam_shop\coutItem + i * SizeOf(LONG)
            *Ptr\l=ReadLong()
		Next i
        
        *Ptparam_shop\prixVente=AllocateMemory(#MAX_ITEMS*SizeOf(LONG))
        For i=0 To #MAX_ITEMS-1
            *Ptr=*Ptparam_shop\prixVente + i * SizeOf(LONG)
            *Ptr\l=ReadLong()
        Next
    Case #EVENT_INN
        *event\param=AllocateMemory(SizeOf(s_event_param_inn))
        *Ptparam_inn=*event\param
        *Ptparam_inn\cout=ReadLong()
    
    EndSelect
EndProcedure
Procedure WriteEvent(*event.s_event,bg_sound.s)
	Protected *Ptparam_dialog.s_event_param_dialog,*Ptparam_telport.s_event_param_teleport
    Protected *Ptparam_shop.s_event_param_shop,*Ptparam_inn.s_event_param_inn
    Protected *Ptr.long
    
    *buffer=AllocateMemory(256)
    
    WriteLong(*event\type)
    If *event\type=#EVENT_NULL
        ProcedureReturn
    EndIf 
    
    WriteLong(*event\onaction)
    WriteByte(*event\proba)
    WriteLong(*event\player_anim)
    WriteByte(*event\is_unique)
    
    WriteData(@bg_sound,Len(bg_sound))
    WriteData(*buffer,63-Len(bg_sound))
    
    Select *event\type
     
    Case #EVENT_NULL
     
    Case #EVENT_TELEPORT
    	*Ptparam_telport=*event\param
    	If *Ptparam_telport\filename<>#NULL$
    		WriteData(*Ptparam_telport\filename,Len(*Ptparam_telport\filename))
	  	EndIf  
    	WriteData(*buffer,63-Len(*Ptparam_telport\filename))
    	WriteLong(*Ptparam_telport\startX)
    	WriteLong(*Ptparam_telport\startY)
     
    Case #EVENT_DIALOG
    	*Ptparam_dialog=*event\param
    	If *Ptparam_dialog\dialog<>#NULL$
	    	WriteData(*Ptparam_dialog\dialog,Len(*Ptparam_dialog\dialog))
    	EndIf  
    	WriteData(*buffer,255-Len(*Ptparam_dialog\dialog))

    Case #EVENT_BATTLE
     
    Case #EVENT_SHOP
        *Ptparam_shop=*event\param
        WriteLong(*Ptparam_shop\n_item)
        For i=0 To *Ptparam_shop\n_item-1
            *Ptr=*Ptparam_shop\itemId + i * SizeOf(LONG)
            WriteLong(*Ptr\l)
            *Ptr=*Ptparam_shop\coutItem + i * SizeOf(LONG)
            WriteLong(*Ptr\l)
		Next i	
        For i=0 To #MAX_ITEMS-1
            *Ptr=*Ptparam_shop\prixVente + i * SizeOf(LONG)
            WriteLong(*Ptr\l)
        Next i        
     
    Case #EVENT_INN
        *Ptparam_inn=*event\param
        WriteLong(*Ptparam_inn\cout)
        
    EndSelect
EndProcedure


Ecrire la fonction DoEventXXX() (où XXX est le nom de notre événement).
Ces fonctions sont réalisées dans les étapes suivantes.


Modifier DoEvent()

Procedure.l DoEvent(*event.s_event,*libevent.s_lib_event,*map.s_map,*libsprite.s_lib_sprite,*player.s_player,*gui.s_gui)
    Protected *Ptparam_shop.s_event_param_shop,*Ptparam_inn.s_event_param_inn
    
    If *event=#Null
        ProcedureReturn 0
    EndIf
    
    If *event\type=#EVENT_NULL
        ProcedureReturn 0
    EndIf
    
    
    If *event\sound
        PlaySound(*event\sound)
    EndIf        
    Select *event\type
        
        Case #EVENT_TELEPORT
            DoEventTeleport(*event, *libevent, *map, *libsprite)
            ProcedureReturn 1
            
        Case #EVENT_DIALOG
            DoEventDialog(*event, *gui, *map, *libsprite)
            
        Case #EVENT_BATTLE
            DoEventBattle(*event, *libevent, *map, *libsprite)
            
        Case #EVENT_SHOP
            DoEventShop(*event.s_event, *map.s_map, *libsprite.s_lib_sprite, *player.s_player, *gui.s_gui)
            
        Case #EVENT_INN
            DoEventInn(*event.s_event,*map.s_map,*libsprite.s_lib_sprite, *player.s_player, *gui.s_gui)
            
            
    EndSelect    
    
    If *event\is_unique
        
        ; suppr. de l'événement 
        Select *event\type
            
            Case #EVENT_NULL
                
            Case #EVENT_TELEPORT
                FreeMemory(*event\param)
                
            Case #EVENT_DIALOG
                FreeMemory(*event\param)
                
            Case #EVENT_BATTLE
                FreeMemory(*event\param)
                
            Case #EVENT_SHOP
                *Ptparam_shop=*event\param
                FreeMemory(*Ptparam_shop\itemId)
                FreeMemory(*Ptparam_shop\coutItem)
                FreeMemory(*Ptparam_shop\prixVente)
                FreeMemory(*event\param)
                
            Case #EVENT_INN
                FreeMemory(*event\param)
                
        EndSelect
        *event\type=#EVENT_NULL
        *event\onaction=#EVENT_ACTION_AUTO
    EndIf
    
    ProcedureReturn 0
    
EndProcedure


[modifier] L'événement dialog

Reprenons la fonction DoEventDialog():

Procedure DoEventDialog(*event.s_event,*gui.s_gui,*map.s_map,*libsprite.s_lib_sprite)
    
    Protected *param.s_event_param_dialog
    Protected color
    
    color=RGB(255,255,255)
    
    *param=*event\param
    
    DrawDialog(*map,*libsprite, *gui, *param\dialog,color)
    
EndProcedure


[modifier] L'événement shop

Avant de programmer la fonction DoEventShop(), revenons à notre fichier gui.pb quelques instants afin de concevoir les deux boîtes de dialogues un peu spéciales nécessaires à l'achat et à la vente d'objets.

Nous allons débuter par la fonction générant une boîte de dialogue pour l'achat d'objets. Le tableau itemId est un tableau de n_item éléments qui contient la liste des id des objets vendus dans la boutique. Le tableau coutItem est un tableau de n_item éléments qui contient la liste des coûts des objets vendus dans la boutique.

Procedure.l DrawShopAchat(*map.s_map,*libsprite.s_lib_sprite,*player.s_player,*gui.s_gui,color.l,dialog.s,n_item.l, *itemId.long, *coutItem.long)
	Protected  src.rectangle, dest.rectangle
	Protected  isdone, i, selection, res , *PtrCoutitem.long, *PtritemID.long

  	isdone=0
  	selection=0
  	res=0

	While isdone=0
		ClearScreen(0,0,0)
		
		HandleMap(*map)
 		HandleSprites(0, *libsprite, *map)
  		
		  If ExamineKeyboard()
  		
		  	If KeyboardReleased(#PB_Key_Up)
 				
				 If selection>0
				 	selection - 1
	 			EndIf
			 		
            ElseIf KeyboardReleased(#PB_Key_Down)
 				
				 If selection<n_item-1
 					selection + 1
				EndIf
					
            ElseIf KeyboardReleased(#PB_Key_Space)
 				
				*PtrCoutitem=*Coutitem + Selection * SizeOf(long)
				*PtritemID=*itemId + Selection * SizeOf(long)
 				
				If *player\argent>=*PtrCoutitem\l
 
 					*player\argent - *PtrCoutitem\l
 					*player\inventaire[*PtritemID\l]\quantite + 1
 
 					DrawDialog(*map, *libsprite, *gui, "Merci!", color)
 				Else 

 					DrawDialog(*map, *libsprite, *gui, "Vous n'avez pas assez d'argent\npour acheter cet objet.", color)
				EndIf
				
			ElseIf KeyboardReleased(#PB_Key_Escape)
 				isdone = 1
			EndIf

		EndIf
				
 		; affiche le fond
    	dest\x=200
    	dest\y=400
    	    
    	Start3D() 
    		DisplaySprite3D(*gui\fond\id,dest\x,dest\y,150)
	    Stop3D() 
    	
	    ; affichage fond 
    	src\x=0
    	src\y=150
    	src\w=400
    	src\h=150
       
    	ClipSprite(*gui\surf\id,src\x,src\y,src\w,src\h)
    	DisplayTransparentSprite(*gui\surf\id,dest\x,dest\y) 

 
		; affichage texte 

        hdc = StartDrawing(ScreenOutput())
        If hdc 
            DrawingMode(1)                          
            DrawingFont(UseFont(*gui\font))  
            FrontColor(Red(color),Green(color),Blue(color))
            ;Pour récupérer la hauteur des caractères 
            GetTextExtentPoint32_(hdc,@dialog,Len(dialog),Size.size) ; Seulement pour windows !
         
            ; affichage texte 
            dest\y + 4 
            dest\x + 5
            
            i=1            
            While i < Len(dialog) 

                Lettre$=Mid(dialog,i,1)
                
                If  Lettre$=Chr(10)
                    dest\y + Size\cy
                    dest\x=210
                ElseIf Lettre$="\" 
                    dest\y + Size\cy
                    dest\x=210
                    i + 1
                Else
                    Locate(dest\x,dest\y)
                    DrawText(Lettre$)
                    dest\x + TextLength(Lettre$)
                EndIf   
                i + 1
            Wend  
            
    		; affichage de l'item sélectioné 
    			
      		; Génération choix 
  			*PtrCoutitem=*Coutitem + Selection * SizeOf(long)
			*PtritemID=*itemId + Selection * SizeOf(long)
			dest\x=252
        	dest\y + 50
    		Locate(dest\x,dest\y)
            DrawText(*player\inventaire[*PtritemID\l]\nom + " " + Str(*ptrcoutItem\l))
              
   
            StopDrawing()
         EndIf   

		dest\x - 32

		DisplayTransparentSprite(*player\inventaire[*ptritemID\l]\icone\id,dest\x,dest\y)
    
		FlipBuffers()

    Wend
    
    ProcedureReturn res
EndProcedure

Dans la fonction affichant une boîte de dialogue pour vendre des objets, le tableau prixVente contient MAX_ITEMS éléments, il s'agit des prix de vente de tous les objets du jeu dans cette boutique.

Procedure.l DrawShopVente(*map.s_map,*libsprite.s_lib_sprite, *player.s_player,*gui.s_gui,color.l,dialog.s, *prixVente.long)
	Protected  src.rectangle, dest.rectangle
	Protected  isdone, i, selection, res, item, n_item,fond1
	Protected  itemId.l ,coutItem.l,*Ptr.long
 
	; Calcul du nombre d'items 
 	n_item=0
 	For i=0 To #MAX_ITEMS-1
 		If *player\inventaire[i]\nom<>#NULL$ And *player\inventaire[i]\quantite>0
 			n_item + 1
		EndIf	 
	Next i	
	If n_item=0
		DrawDialog(*map, *libsprite, *gui, "Vous n'avez aucun objet à vendre.", color)
		ProcedureReturn 0
	EndIf

	; Création des tableaux d'objets 
	Dim itemID(n_item)
	Dim Coutitem(n_item)

 	item=0
	For i=0 To #MAX_ITEMS-1
 		If *player\inventaire[i]\nom<>#NULL$ And *player\inventaire[i]\quantite>0
 			*ptr=*prixvente + i * SizeOf(long)
			itemID(item)=i
 			Coutitem(item)=*ptr\l
			item + 1
		EndIf	 
	Next i

	isdone=0
	selection=0
	res=0
		
    
 	While isdone=0
	 	ClearScreen(0,0,0)
		
		HandleMap(*map)
 		HandleSprites(0, *libsprite, *map)
		
		If ExamineKeyboard()

 			If KeyboardReleased(#PB_Key_Up)
 			
			 	If selection>0
 					selection - 1
				EndIf	
			
			ElseIf KeyboardReleased(#PB_Key_Down)
 			
			 	If selection<n_item-1
 					selection + 1
				EndIf	
			
			ElseIf KeyboardReleased(#PB_Key_Space)
 			
			 	*player\argent + Coutitem(selection)
 				*player\inventaire[itemID(selection)]\quantite - 1
 				If *player\inventaire[itemID(selection)]\quantite=0
 					isdone = 2
					res = 2
				EndIf
				;HandleMap(*map)
 				;HandleSprites(0, *libsprite, *map)
 				DrawDialog(*map, *libsprite, *gui, "Merci!", color)
			
			ElseIf KeyboardReleased(#PB_Key_Escape)
 			
			 		isdone = 1
			EndIf	
 		EndIf
 		 
		; affiche le fond
    	dest\x=200
    	dest\y=400
    	    
    	Start3D() 
    		DisplaySprite3D(*gui\fond\id,dest\x,dest\y,150)
	    Stop3D() 
    	
	    ; affichage fond 
    	src\x=0
    	src\y=150
    	src\w=400
    	src\h=150
       
    	ClipSprite(*gui\surf\id,src\x,src\y,src\w,src\h)
    	DisplayTransparentSprite(*gui\surf\id,dest\x,dest\y) 
 	
		; affichage texte 
		hdc = StartDrawing(ScreenOutput())
        If hdc 
            DrawingMode(1)                          
            DrawingFont(UseFont(*gui\font))  
            FrontColor(Red(color),Green(color),Blue(color))
            
            ;Pour récupérer la hauteur des caractères 
            GetTextExtentPoint32_(hdc,@dialog,Len(dialog),Size.size) ; Seulement pour windows !
            
            dest\y + (3*size\cy)/4
 			dest\x + 5
 
             i=1            
            While i < Len(dialog) 

                Lettre$=Mid(dialog,i,1)
                
                If  Lettre$=Chr(10)
                    dest\y + Size\cy
                    dest\x=210
                ElseIf Lettre$="\" 
                    dest\y + Size\cy
                    dest\x=210
                    i + 1
                Else
                    Locate(dest\x,dest\y)
                    DrawText(Lettre$)
                    dest\x + TextLength(Lettre$)
                EndIf   
                i + 1
            Wend  
 
			; affichage de l'item sélectioné 
			dest\x=252
			dest\y + 50
			Locate(dest\x,dest\y)
			DrawText(*player\inventaire[itemId(selection)]\nom + " " + Str(coutItem(Selection)))
			
			StopDrawing()
		EndIf

		dest\x - 32

		DisplayTransparentSprite(*player\inventaire[itemID(selection)]\icone\id,dest\x,dest\y)
 
		;l'équipement a-t-il été vendu? */
		 For i=0 To #MAX_JOUEURS-1
		 	If *player\joueurs[i]\joueurActif
	 			If *player\joueurs[i]\equipement_arme>-1
		    		If *player\inventaire[*player\joueurs[i]\equipement_arme]\quantite=0
		         		*player\joueurs[i]\equipement_arme=-1
		         	EndIf	
			    EndIf  
    			If *player\joueurs[i]\equipement_tete>-1   
		        	If *player\inventaire[*player\joueurs[i]\equipement_tete]\quantite=0
		          		*player\joueurs[i]\equipement_tete=-1
		          	EndIf	
			    EndIf   
    			If *player\joueurs[i]\equipement_corps>-1     
		        	If *player\inventaire[*player\joueurs[i]\equipement_corps]\quantite=0
		            	*player\joueurs[i]\equipement_corps=-1
	            	EndIf	
		        EndIf    
		    EndIf
		Next i 
		
		FlipBuffers()

	Wend
 	ProcedureReturn res
EndProcedure
 


Il ne reste plus qu'à écrire la fonction DoEventShop() (fichier event.pb):

Procedure DoEventShop(*event.s_event, *map.s_map, *libsprite.s_lib_sprite, *player.s_player, *gui.s_gui)
    
    Protected *param.s_event_param_shop
	Protected res,color
    color=RGB(255,255,255)
    *param=*event\param
    ;Initialise la liste chainée
    ClearList(n_Input())
    AddElement(n_Input())
    n_Input()="Acheter"
    AddElement(n_Input())
    n_Input()="Vendre" 
    AddElement(n_Input())
    n_Input()="Rien pour le moment" 
      
    res=-1
    While res<>2
        res=DrawInput(*map, *libsprite, *gui, color, "Bonjour, que puis-je faire pour vous?")
        If res=0 ; Achat 
            DrawShopAchat(*map, *libsprite, *player, *gui, color, "Quel objet souhaitez-vous acquérir?", *param\n_item, *param\itemId, *param\coutItem)
        ElseIf res=1    ; Vente 
       
            While DrawShopVente(*map,*libsprite,*player,*gui,color, "Quel objet souhaitez-vous vendre?", *param\prixVente)=2

             Wend
        EndIf
    Wend
    HandleMap(*map)
    HandleSprites(0, *libsprite, *map)
    DrawDialog(*map.s_map,*libsprite.s_lib_sprite,*gui, "Merci de votre visite.", color)
    Delay(100)
                
EndProcedure


[modifier] L'événement inn

Pour concevoir l'événement associé à une auberge, il faut réutiliser la fonction affichant un dialogue et celle affichant un choix. En les combinant, on peut facilement obtenir le résultat voulu.

Procedure DoEventInn(*event.s_event,*map.s_map,*libsprite.s_lib_sprite, *player.s_player, *gui.s_gui)
    Protected  *param.s_event_param_inn
    Protected i, color
    
    Color=RGB(255,255,255)
    *param=*event\param
    
    ;Initialise la liste chainée
    ClearList(n_Input())
    ;Ajoute les choix possibles
    AddElement(n_Input())
    n_Input()="J'aimerais me reposer"
    AddElement(n_Input())
    n_Input()="Rien pour le moment"
        
    If DrawInput(*map, *libsprite, *gui, color, "Bonjour, que puis-je faire pour vous?")=0
        If *player\argent>=*param\cout
            *player\argent - *param\cout
            For i=0 To #MAX_JOUEURS-1
                *player\joueurs[i]\Hp=*player\joueurs[i]\hpmax
                *player\joueurs[i]\mp=*player\joueurs[i]\mpmax
            Next i  
            DrawDialog(*map, *libsprite,*gui, "Vous êtes reposé!\n\n  Merci d'être venu!", color)
        Else
            DrawDialog(*map,*libsprite,*gui, "Je suis désolé,\n mais vous n'avez pas assez d'argent.", color)
        EndIf    
    Else 
        DrawDialog(*map,*libsprite,*gui, "Repassez quand vous le souhaitez.", color)
    EndIf

EndProcedure

[modifier] Modification de la fonction main()

rocedure Main()
    
    player.s_player
    map.s_map
    libsprite.s_lib_sprite
    libevent.s_lib_event 
    gui.s_gui
    
    InitGui(@gui)
    InitPlayer(@player)
    LoadMap(@map, "map\generator.map")
    
    ;** Les sprites **
    ;InitSprites(@libsprite)
    ;SpriteStaticData(@libsprite)
    ;SaveSprites(@libsprite, "sprites\player.bmp", "map\01.sprite")
    LoadSprites(@libsprite,"map\generator.sprite")
    
    ;** Les évènements **
    ;InitLibEvent(@libevent,@map,@libsprite)
    ;EventStaticData(@libevent,@map,@libsprite)
    ;SaveEvent(@libevent,@map,@libsprite,"son\tornade.wav","map\01.event")
	loadEvent(@libevent,@map,@libsprite,"map\generator.event")
    
    Repeat
     
        FlipBuffers()
        ClearScreen(0,0,0)
        If ExamineKeyboard()
        	If KeyboardReleased(#PB_Key_Escape)
            	quit=Menu(@player, @gui)    
        	EndIf  
	    EndIf      
        MapEvent(@map)
        SpriteEvent(@map, @libsprite)
        Event(@libevent, @map, @libsprite, @player, @gui)
        
        HandleMap(@map)
        HandleSprites(0,@libsprite, @map)
        HandleEvent(@libevent, @map, @libsprite,@player,@gui) 
    Until quit
    
    FreeLibEvent(@libevent, @map, @libsprite) 
    FreeMap(@map)
    FreeSprites(@libsprite)
    FreePlayer(@player)
    FreeGui(@gui)
    
EndProcedure


[modifier] De la différence entre les fonctions KeyboardReleased() et KeyboardPushed()

Si vous étudiez attentivement le code qui suit en le comparant aux étapes précédentes, on peut s'apercevoir que certaines touches qui étaient testées avec un événement du type KeyboardPushed sont désormais associées à un événement du type KeyboardReleased. Le but est d'éviter que l'interface réagisse trop vite: en effet ces deux événements se comportent de façon très différente:

  • un événement KeyboardPushed est généré tant que la touche reste enfoncée
  • un événement KeyboardReleased unique est généré quand la touche est relâchée.

Ainsi, avec un événement de type KeyboardPushed, on peut facilement valider trois ou quatre menus en une fraction de seconde! L'interface devient alors quasiment incontrôlable.

A l'inverse avec un KeyboardReleased, l'interface ne bouge que lorsque la touche est relâchée ce qui évite ce problème.


[modifier] Démo 5: Menus, inventaire et boutiques

Démo 5: menus, inventaire et boutiques


Démo 5: menus, inventaire et boutiques