PureBasic:Realiser un RPG2D/Interface, inventaire et boutiques
PureBasic:Realiser_un_RPG2D
<< Précédent | Sommaire | Suivant >>
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).
[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:
- on initialise le tableau de joueurs
- on initialise l'inventaire
- on charge les objets du jeu
- 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:
- utiliser une police graphique
- 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



