Page 1 sur 1

Snake..ssssssss+pathfinding

Publié : ven. 10/août/2007 22:06
par Huitbit
Bonjour,

Voilà le premier méchant que je vais mettre dans mon jeu (voir http://www.purebasic.fr/french/viewtopic.php?t=6950)

Image


Image
(sprite nécessaire pour le fonctionnement du code ci-dessous)

[EDIT] légère modification de corps_decalage_x et corps_decalage_y pour éviter des changements de cases trop brusques de la queue(ça évite également les problèmes de repérage dans le tableau 32*24). Maintenant, un maillon de 16*16 est confondu avec la tête.

Code : Tout sélectionner

;auteur Huitbit
;test dragon fer
;pb v4.02
;***************************************************
Enumeration
  #spr_dragon_fer
  #spr_ecran
  #spr_souris
  #spr_cible
EndEnumeration
Structure donnees_dragon
  x.w
  y.w
  dx.b
  dy.b
  numero_decision.b
  spr_colonne.w
  spr_ligne.w
EndStructure
Structure decision_dragon
  x.w
  y.w
  dx.b
  dy.b
EndStructure

Dim carte.b(31, 23)
Dim dragon.donnees_dragon(40)
NewList decision.decision_dragon()

tete.donnees_dragon
;dragon initial
;tête
tete_dl=4
tete\x=320
tete\y=320
tete\dx=2
tete\dy=0
tete_spr_colonne=0
tete_spr_ligne=0
;corps
taille_dragon=10
For i=0 To taille_dragon
  dragon(i)\x=tete\x-16*taille_dragon+16*i+corps_decalage_x
  dragon(i)\y=320+corps_decalage_y
  dragon(i)\dx=tete_dl
  dragon(i)\dy=0
  dragon(i)\spr_colonne=0
  dragon(i)\spr_ligne=32
Next i

corps_decalage_x=-16
corps_decalage_y=8

x_cible=320
y_cible=320
;*********************************************************
InitSprite()
InitKeyboard()
InitMouse()
OpenWindow(0,0,0,1024,768,"Test_dragon, CLIQUER DU DROIT POUR QUITTER...CLIQUER DU GAUCHE POUR PLACER LA CASE D'ARRIVEE DU DRAGON ",#PB_Window_ScreenCentered|#PB_Window_SystemMenu  )
OpenWindowedScreen(WindowID(0),0,0,1024,768,1,0,0)
UsePNGImageDecoder()
;planche d'images
LoadSprite(#spr_dragon_fer,"dragon_fer.png")
;-fond d'écran
;{
CreateSprite(#spr_ecran,1024,768)
StartDrawing(SpriteOutput(#spr_ecran));dessin d'un damier 32*32+grille16*16 pour les tests
Box(0,0,1024,768,RGB(255,255,255));fond blanc
For i=0 To 31
  For j=0 To 23
    If (i+j)%2=0 
      Box(i*32,j*32,32,32,RGB(128,128,128));cases grises
    EndIf
  Next j
Next i
For i=1 To 63
  Line(i*16,0,0,768,RGB(0,0,255))
Next i
For i=1 To 47
  Line(0,i*16,1024,0,RGB(0,0,255))
Next i
StopDrawing()
;}
CreateSprite(#spr_souris,32,32)
StartDrawing(SpriteOutput(#spr_souris))
Circle(16,16,16,RGB(255,255,0))
StopDrawing()

CreateSprite(#spr_cible,32,32)
StartDrawing(SpriteOutput(#spr_cible))
Box(0,0,32,32,RGB(255,0,255))
StopDrawing()

;********************************************************************
;BOUCLE PRINCIPALE*****************************************
;********************************************************************
Repeat
  ;gestion des évènements de la fenêtre
  ;{
  Repeat
    Event = WindowEvent()      
    If  Event = #PB_Event_CloseWindow
      End 
    EndIf
  Until Event = 0  
  ;}
  
  ExamineMouse()
  x_souris=32*Int(MouseX()/32)
  y_souris=32*Int(MouseY()/32)
  If  MouseButton(#PB_MouseButton_Right )
    End
  EndIf
  
  If  MouseButton(#PB_MouseButton_Left)
    carte( x_cible/32, y_cible/32)=0
    x_cible=x_souris
    y_cible=y_souris
    carte( x_cible/32, y_cible/32)=#spr_cible
    cible_atteinte=0
  EndIf
  
  ;déplacement automatique vers la cible 
  If tete\x%32=0  And tete\y%32=0
    
    ;prise de décision pour aller à droite
    ;{
    If tete\x<x_cible And tete\y=y_cible 
      If tete\dx<>tete_dl
        tete\dx=tete_dl
        tete\dy=0
        tete_spr_colonne=0
        tete_spr_ligne=0
        corps_decalage_x=0
        corps_decalage_y=8
        AddElement(decision())
        decision()\x=tete\x
        decision()\y=tete\y
        decision()\dx=tete\dx
        decision()\dy=tete\dy
      EndIf
      
    EndIf
    ;}
    
    ;prise de décision pour aller à gauche
    ;{
    If tete\x>x_cible And  tete\y=y_cible  
      If tete\dx<>-tete_dl
        tete\dx=-tete_dl
        tete\dy=0
        tete_spr_colonne=64
        tete_spr_ligne=0
        corps_decalage_x=16
        corps_decalage_y=8
        AddElement(decision())
        decision()\x=tete\x
        decision()\y=tete\y
        decision()\dx=tete\dx
        decision()\dy=tete\dy
      EndIf
      
    EndIf
    ;}
    
    ;prise de décision pour aller en bas
    ;{
    If tete\y<y_cible 
      If tete\dy<>tete_dl
        tete\dx=0
        tete\dy=tete_dl
        tete_spr_colonne=32
        tete_spr_ligne=0
        corps_decalage_x=8
        corps_decalage_y=0
        AddElement(decision())
        decision()\x=tete\x
        decision()\y=tete\y
        decision()\dx=tete\dx
        decision()\dy=tete\dy
      EndIf
    EndIf
    ;}
    
    ;prise de décision pour aller en haut
    ;{
    If tete\y>y_cible 
      If tete\dy<>-tete_dl
        tete\dx=0
        tete\dy=-tete_dl
        tete_spr_colonne=96
        tete_spr_ligne=0
        corps_decalage_x=8
        corps_decalage_y=16
        AddElement(decision())
        decision()\x=tete\x
        decision()\y=tete\y
        decision()\dx=tete\dx
        decision()\dy=tete\dy
      EndIf
      
    EndIf
    ;}
    
    ;si la cible est atteinte
    ;{
    If tete\x=x_cible And tete\y=y_cible
      cible_atteinte=1
      tete\dx=0
      tete\dy=0
    EndIf
    ;}
    
  EndIf
  
  For i=0 To taille_dragon
    
    ForEach decision()
      
      If dragon(i)\x=decision()\x And dragon(i)\y=decision()\y And dragon(i)\numero_decision=ListIndex(decision())
        dragon(i)\dx=decision()\dx
        dragon(i)\dy=decision()\dy
        dragon(i)\numero_decision=dragon(i)\numero_decision+1
      EndIf
      
    Next
    
  Next i
  
  ;mise à jour du compteur de décision quand la queue est passée
  If dragon(0)\numero_decision=1
    
    FirstElement(decision())
    DeleteElement(decision())
    For i=0 To taille_dragon
      dragon(i)\numero_decision=dragon(i)\numero_decision-1
    Next i
    
  EndIf
  
  
  
  If ElapsedMilliseconds()-chrono>5
    chrono=ElapsedMilliseconds()
    tete\x=tete\x+tete\dx
    tete\y=tete\y+tete\dy
    If cible_atteinte=0
      For i=0 To taille_dragon
        dragon(i)\x=dragon(i)\x+dragon(i)\dx
        dragon(i)\y=dragon(i)\y+dragon(i)\dy
      Next i
    EndIf
  EndIf
  
  ;affichage des sprites
  ;{
  DisplaySprite(#spr_ecran,0,0)
  For i=0 To 31
    For j=0 To 23
      If   carte(i,j)=#spr_cible
        DisplaySprite(#spr_cible,i*32,j*32)
      EndIf
    Next j
  Next i
  For i=0 To taille_dragon
    ClipSprite(#spr_dragon_fer,dragon(i)\spr_colonne,dragon(i)\spr_ligne,16,16)
    DisplaySprite(#spr_dragon_fer,dragon(i)\x+corps_decalage_x,dragon(i)\y+corps_decalage_y)
  Next i
  
  ClipSprite(#spr_dragon_fer,tete_spr_colonne,tete_spr_ligne,32,32)
  DisplaySprite(#spr_dragon_fer,tete\x,tete\y)
  
  DisplaySprite(#spr_souris,x_souris,y_souris)
  StartDrawing(ScreenOutput())
  ForEach decision()
    DrawText(decision()\x,decision()\y,Str(ListIndex(decision()))+";"+Str(decision()\dx)+","+Str(decision()\dy))
  Next  
  StopDrawing()
  ;}
  
  Delay(15)  
  ;calcul du fps
  ;{
  If Second < ElapsedMilliseconds() 
    Second = ElapsedMilliseconds()+1000
    fps = Frame_Counter 
    Frame_Counter = 0 
  Else 
    Frame_Counter + 1 
  EndIf     
  StartDrawing(ScreenOutput()) 
  DrawText(0,0,"FPS : "+Str(fps)) 
  StopDrawing() 
  ;}
  FlipBuffers()
ForEver

; IDE Options = PureBasic v4.02 (Windows - x86)
; CursorPosition = 56
; FirstLine = 30
; Folding = -
Ce n'est pas le même type de snake que dans mon jeu.
Dans mon jeu, j'ai copié le style de déplacement du jeu MSX SUPER SNAKE des années 80 (c'est à dire, seuls deux des éléments du serpent sont animés), ici TOUS les éléments sont animés.
La "tête" indique (liste chaînée "décision") les changements de direction au reste du corps.

La GROSSE difficulté a été de gérer une tête de 32*32 avec un corps à éléments 16*16.
J'ai utilisé la liste chaînée "décision()" car au début, le serpent suivait une trace sur un tableau mais avec ce système, dès qu'il y avait un "croisement", les éléments perdaient une information et se superposaient!

Pour le dragon on peut prendre une liste chaînée à la place du tableau "dragon(i)".


J'ai appelé ce post SNAKES car j'aimerais bien regrouper différentes méthodes pour faire des snakes (si ça intéresse quelqu'un, je peux mettre la version basique du dragon vert de mon jeu).
Si vous en avez déjà fait ça serait bien de laissssser une trace sur ce poste :P .
Chaque méthode a ses avantages :wink:


Hasta la vista!

PS: j'ai laissé le quadrillage et les infos pour que tout le monde puisse comprendre le fonctionnement :1;4,0 sur une case signifie (changement de direction n°1,dx=4,dy=0)

L'IA est un peu simpliste mais je vais pas sortir tout de suite le pathfinding :lol: :lol:

Publié : ven. 10/août/2007 23:15
par Anonyme
tu es très productif ! une vrai usine, c'est super en tout cas de partagé :D !

Publié : sam. 11/août/2007 0:45
par Ar-S
Bravo pour tes codes !

Doit on obligatoirement utiliser si peu de couleurs pour les sprites ou peut on utiliser un vrai bonne grosse palette ?

Publié : sam. 11/août/2007 3:15
par Huitbit
Merci!
"Partager, c'est un devoir pour tout apprenti schtroumpfeur digne de ce nom!"
Anonyme XVIII ième siècle
Doit on obligatoirement utiliser si peu de couleurs pour les sprites ou peut on utiliser un vrai bonne grosse palette ?
Aucune contre-indication
Il suffit d'une tête de 32*32 dans quatre directions et d'un maillon de 16*16 et c'est bon (pour les maillons, tu peux en mettre plusieurs et les affecter avec un random, ça donnera un peu plus de style!)

En fait, je fais mes sprites pixel par pixel avec paint!
Alors avec 16 couleurs, j'hésite moins quand je dois choisir une couleur (et...je suis un peu resté bloqué aux années 80 :roll:)

Hasta la vista!

Publié : sam. 11/août/2007 9:13
par Ar-S
Ah le 16 c je connaissais bien, même le 256 avec Deluxe Paint :)

Quand j'aurai 5 min j'essayerai de vous faire qques dessins, pourquoi pas des maillons en 3D :) on verra, en tout cas ce jour je suis de mariage alors je vous souhaite à tous un bon week end.

Ajout du pathfinding...+[EDIT]

Publié : sam. 18/août/2007 14:08
par Huitbit
Hug!

J'ai adapté mon pathfinding (recherche dans quatre directions au lieu de huit, utilisation des macros) au programme précédent(pour le pathfinding voir : http://www.purebasic.fr/french/viewtopic.php?t=6428 ou http://www.purebasic.fr/french/viewtopic.php?t=6468)

Cette fois-ci, comme me l'avait conseillé Thyphoon, j'utilise Allocatememory pour sauvegarder le chemin dans la procedure pathfinding.
La tête du dragon fonctionne comme une tête de lecture et "lit" le chemin tout en laissant des informations au reste du corps.
On peut changer la cible à n'importe quel moment à condition que les coordonnées de la tête soient divisibles par 32 (contraintes dues au jeu!).

Le dragon trouve toujours un chemin même si parfois il se ballade un peu avant de trouver ! :lol: (faudra améliorer le calcul de l'heuristique H dans le pathfinding :? )
[EDIT] -rajout de la recherche du chemin le plus proche(j'ai joué sur F et H dans le pathfinding, je cherche le dernier F valide le plus proche de la cible)
J'ai augmenté le nombre de rochers pour faire apparaître des cases inaccessibles
-correction d'un bug qui trainait :roll: (quand on choisissait deux fois de suite la même cible, le dragon "perdait" tête!


Les parties du programmes nécessaires pour les tests sont là mais sous forme de commentaires (damier, affichage des coordonnées, affichage de la liste des décisions)

Image

Image
(sprite nécessaire pour le fonctionnement du code ci-dessous)

Code : Tout sélectionner

;auteur Huitbit
;test dragon fer_pathfinding
;pb v4.02
;***************************************************
;-déclarations 
;{
Enumeration
  #spr_dragon_fer
  #spr_ecran
  #spr_roche
  #spr_souris
  #spr_cible
  #spr_chemin
  #liste_open 
  #liste_closed 
  #liste_mur 
  #liste_chemin 
EndEnumeration

Structure donnees_dragon
  x.w
  y.w
  dx.b
  dy.b
  numero_decision.b
  spr_colonne.w
  spr_ligne.w
EndStructure

Structure decision_dragon
  x.w
  y.w
  dx.b
  dy.b
EndStructure

Structure infos_carte
  id.w
  parent.w
  f.w
  g.w
  liste.b
EndStructure

Structure liste
  id.w 
  f.w 
EndStructure 

Global Dim carte.infos_carte(31, 23)
Global Dim liste_ouverte.liste(767) 
Dim dragon.donnees_dragon(40)
NewList decision.decision_dragon()
Global position_premier_element.w , x_cible.b, y_cible.b 
Global *memoryID_x.l,*memoryID_y.l, position_sauvegarde.w

;adresses pour la mémorisation des coordonnées du dragon
*memoryID_x=AllocateMemory(1000)
*memoryID_y=AllocateMemory(1000)

;}

;-initalisation
;{
tete.donnees_dragon
;dragon initial
;tête
tete_dl=2
tete\x=320
tete\y=320
tete\dx=0
tete\dy=0
tete_spr_colonne=0
tete_spr_ligne=0
;corps
taille_dragon=10
For i=0 To taille_dragon
  dragon(i)\x=tete\x-16*taille_dragon+16*i+corps_decalage_x
  dragon(i)\y=320+corps_decalage_y
  dragon(i)\dx=tete_dl
  dragon(i)\dy=0
  dragon(i)\spr_colonne=0
  dragon(i)\spr_ligne=32
Next i
corps_decalage_x=-16
corps_decalage_y=8
cible_atteinte=1

  ;ID des cases 
For i=0 To 31
  For j=0 To 23
    carte(i,j)\id=ID_case   
    ID_case+1      
  Next j 
Next i
 ;}
  
Macro preparer_pathfinding() 
For i=0 To 31
  For j=0 To 23
    If carte(i,j)\liste<>#liste_mur 
      carte(i,j)\liste=0  
      carte(i,j)\f=0 
      carte(i,j)\g=0 
      carte(i,j)\parent=0 
    EndIf        
  Next j
Next i
For i=1 To 767
  liste_ouverte(i)\f=0 
Next i 
EndMacro 

  ;explorations des quatre cases adjacentes
Macro exploration_case_adjacente(i,j) 
If carte(x_temp+i,y_temp+j)\liste=0 
  carte(x_temp+i,y_temp+j)\parent=carte(x_temp,y_temp)\id 
  carte(x_temp+i,y_temp+j)\g=carte(x_temp,y_temp)\g+1
  carte(x_temp+i,y_temp+j)\f=manhattan(x_temp+i,y_temp+j)+carte(x_temp+i,y_temp+j)\g 
  carte(x_temp+i,y_temp+j)\liste=#liste_open 
  position=position+1 
  liste_ouverte(position)\f=carte(x_temp+i,y_temp+j)\f 
  liste_ouverte(position)\id=carte(x_temp+i,y_temp+j)\id 
  ajout_dans_liste_ouverte(position) 
ElseIf carte(x_temp+i,y_temp+j)\liste=#liste_open 
  If carte(x_temp+i,y_temp+j)\g > carte(x_temp,y_temp)\g + 1
    carte(x_temp+i,y_temp+j)\parent=carte(x_temp,y_temp)\id 
    carte(x_temp+i,y_temp+j)\g=carte(x_temp,y_temp)\g+1
    carte(x_temp+i,y_temp+j)\f=manhattan(x_temp+i,y_temp+j)+carte(x_temp+i,y_temp+j)\g 
  EndIf  
EndIf        
EndMacro

  ;calcul de H************** 
Macro manhattan(x,y) 
(Abs(x-x_arrivee)+Abs(y-y_arrivee))
EndMacro

;construction du tas**************** 
Macro ajout_dans_liste_ouverte(p) 
k.w=p 
f_a_placer.w =liste_ouverte(p)\f 
id.w=liste_ouverte(p)\id 
While k>=(position_premier_element+1)   And f_a_placer< liste_ouverte(Int(k*0.5))\f 
  liste_ouverte(k)\f=liste_ouverte(Int(k*0.5))\f 
  liste_ouverte(k)\id=liste_ouverte(Int(k*0.5))\id  
  k=Int(k*0.5) 
Wend 
liste_ouverte(k)\f=f_a_placer 
liste_ouverte(k)\id=id  
EndMacro

;remaniement du tas après suppression de la racine************************ 
Macro reformer_tas(p) 
travail.b=0; 0 : en cours, 1 : effectué 
k.w=position_premier_element  
f_a_replacer.w= liste_ouverte(position_premier_element)\f 
id.w=liste_ouverte(position_premier_element)\id 
While travail=0 And 2*k<=p 
  If 2*k =p 
    indice_grand.w=p 
  Else 
    If liste_ouverte(2*k)\f<=liste_ouverte(2*k+1)\f 
      indice_grand=2*k 
    Else 
      indice_grand=2*k+1 
    EndIf 
  EndIf    
  If f_a_replacer>liste_ouverte(indice_grand)\f 
    liste_ouverte(k)\f=liste_ouverte(indice_grand)\f    
    liste_ouverte(k)\id=liste_ouverte(indice_grand)\id 
    k=indice_grand 
  Else 
    travail=1 
  EndIf 
Wend 
liste_ouverte(k)\f=f_a_replacer 
liste_ouverte(k)\id=id  
EndMacro


Macro choix_du_sprite_tete()
If tete\dx=tete_dl
  tete_spr_colonne=0
  tete_spr_ligne=0
  corps_decalage_x=0
  corps_decalage_y=8
ElseIf  tete\dx=-tete_dl
  tete_spr_colonne=64
  tete_spr_ligne=0
  corps_decalage_x=16
  corps_decalage_y=8
ElseIf tete\dy=tete_dl
  tete_spr_colonne=32
  tete_spr_ligne=0
  corps_decalage_x=8
  corps_decalage_y=0
ElseIf  tete\dy=-tete_dl
  tete_spr_colonne=96
  tete_spr_ligne=0
  corps_decalage_x=8
  corps_decalage_y=16
EndIf
EndMacro

  ;recherche du chemin******************************* 
Procedure pathfinding(x.b,y.b,x_arrivee.b,y_arrivee.b) 
  f_min.w=manhattan(x,y) 
  id_f_min.w=carte(x,y)\id 
  h_min.w=f_min
  
  ;étape n°1 ajout de la case de départ à la liste ouverte 
  liste_ouverte(1)\id=carte(x,y)\id 
  liste_ouverte(1)\f=manhattan(x,y) 
  carte(x,y)\liste=#liste_open 
  
  ;étape n°2 boucle de recherche 
  position.w=1 
  position_premier_element=1 
  
  Repeat 
    
    ;étape n°2.a choix case en cours    
    x_temp.b=liste_ouverte(position_premier_element)\id/24   ;x=id / y_max
    y_temp.b=liste_ouverte(position_premier_element)\id%24 ;y=id % y_max
    
    ;étape n°2.b passage de la case en cours à la liste fermée 
    carte(x_temp,y_temp)\liste=#liste_closed 
    liste_ouverte(position_premier_element)\f=0        
    position_premier_element=position_premier_element+1    
    Swap liste_ouverte(position_premier_element)\f,liste_ouverte(position)\f 
    Swap liste_ouverte(position_premier_element)\id,liste_ouverte(position)\id 
    reformer_tas(position) 
    
    
    ;étape n°2.c  exploration des quatre cases adjacentes      
    exploration_case_adjacente(0,-1)
    exploration_case_adjacente(1,0)
    exploration_case_adjacente(0,1)
    exploration_case_adjacente(-1,0)
    
    ;sauvegarde du f_min de la carte au cas où il n'y ait pas de chemin possible 
    If  carte(x_temp,y_temp)\f>=f_min   And   h_min>manhattan(x_temp,y_temp)
      f_min=carte(x_temp,y_temp)\f 
      id_f_min=carte(x_temp,y_temp)\id   
      h_min=manhattan(x_temp,y_temp)
    EndIf 
    
  Until     carte(x_arrivee,y_arrivee)\liste=#liste_closed Or liste_ouverte(position_premier_element)\f=0  
  
  ;3. enregistrement du chemin sous la forme d'une chaîne de caractères 
  ;3.a. cas où il y a un chemin 
  If x_temp=x_arrivee And y_temp=y_arrivee
    carte(x_temp,y_temp)\liste=#liste_chemin 
    PokeB( *memoryID_x,carte(x_temp,y_temp)\id/24)
    PokeB( *memoryID_y,carte(x_temp,y_temp)\id%24) 
    position_sauvegarde=0
    Repeat  
      carte(x_temp,y_temp)\liste=#liste_chemin 
      position_sauvegarde=position_sauvegarde+1
      PokeB( *memoryID_x+position_sauvegarde,carte(x_temp,y_temp)\parent/24)
      PokeB( *memoryID_y+position_sauvegarde,carte(x_temp,y_temp)\parent%24) 
      x_parent.b=carte(x_temp,y_temp)\parent/24
      y_parent.b=carte(x_temp,y_temp)\parent%24
      x_temp=x_parent 
      y_temp=y_parent    
    Until  carte(x_temp,y_temp)\parent=0  
    
    ;3.b. chemin le plus proche
  Else 
    x_temp=id_f_min/24
    y_temp=id_f_min%24 
    x_cible=x_temp
    y_cible=y_temp
    carte(x_temp,y_temp)\liste=#liste_chemin 
    PokeB( *memoryID_x,carte(x_temp,y_temp)\id/24)
    PokeB( *memoryID_y,carte(x_temp,y_temp)\id%24) 
    position_sauvegarde=0 
    Repeat  
      carte(x_temp,y_temp)\liste=#liste_chemin 
      position_sauvegarde=position_sauvegarde+1
      PokeB( *memoryID_x+position_sauvegarde,carte(x_temp,y_temp)\parent/24)
      PokeB( *memoryID_y+position_sauvegarde,carte(x_temp,y_temp)\parent%24) 
      x_parent=carte(x_temp,y_temp)\parent/24
      y_parent=carte(x_temp,y_temp)\parent %24
      x_temp=x_parent 
      y_temp=y_parent    
    Until  carte(x_temp,y_temp)\parent=0  
    
    
  EndIf 
  
  
EndProcedure


;*********************************************************
InitSprite()
InitKeyboard()
InitMouse()
OpenWindow(0,0,0,1024,768,"Test_dragon, CLIQUER DU DROIT POUR QUITTER...CLIQUER DU GAUCHE POUR PLACER LA CASE D'ARRIVEE DU DRAGON ",#PB_Window_ScreenCentered|#PB_Window_SystemMenu  )
OpenWindowedScreen(WindowID(0),0,0,1024,768,1,0,0)
UsePNGImageDecoder()
;-planche d'images et sprites
;{
LoadSprite(#spr_dragon_fer,"dragon_fer.png")

CreateSprite(#spr_roche,32,32)
StartDrawing(SpriteOutput(#spr_roche))
Circle(16,16,16,RGB(190, 145, 65))
StopDrawing()

CreateSprite(#spr_souris,32,32)
StartDrawing(SpriteOutput(#spr_souris))
Box(0,0,32,32,RGB(0,0,255))
Circle(16,16,16,RGB(255,255,0))
StopDrawing()

CreateSprite(#spr_cible,32,32)
StartDrawing(SpriteOutput(#spr_cible))
Box(0,0,32,32,RGB(255,0,255))
StopDrawing()

CreateSprite(#spr_chemin,32,32)
StartDrawing(SpriteOutput(#spr_chemin))
Circle(16,16,8,RGB(0,0,100))
StopDrawing()
;}
;-fond d'écran
;{
CreateSprite(#spr_ecran,1024,768)
; StartDrawing(SpriteOutput(#spr_ecran));dessin d'un damier 32*32+grille16*16 pour les tests
; Box(0,0,1024,768,RGB(255,255,255));fond blanc
; For i=0 To 31
;   For j=0 To 23
;     If (i+j)%2=0 
;       Box(i*32,j*32,32,32,RGB(128,128,128));cases grises
;     EndIf
;   Next j
; Next i
; For i=1 To 63
;   Line(i*16,0,0,768,RGB(100,100,100))
; Next i
; For i=1 To 47
;   Line(0,i*16,1024,0,RGB(100,100,100))
; Next i
; StopDrawing()

UseBuffer(#spr_ecran)
For i=0 To 31
  For j=0  To 23
    If Random(3)=1 Or (i=0 Or i=31 Or j=0 Or j=23)
      DisplaySprite(#spr_roche,i*32,j*32)
      carte(i,j)\liste=  #liste_mur
    Else
      carte(i,j)\liste=0
    EndIf
  Next j
Next i
UseBuffer(-1)
;}

;position initiale du pointeur de la souris
MouseLocate(352,320)

;********************************************************************
;BOUCLE PRINCIPALE*****************************************
;********************************************************************
Repeat
  ;-gestion des évènements de la fenêtre
  ;{
  Repeat
    Event = WindowEvent()      
    If  Event = #PB_Event_CloseWindow
      End 
    EndIf
  Until Event = 0  
  ;}
  
  ;-gestion de la souris
  ;{
  ExamineMouse()
  x_souris=Int(MouseX()/32)
  y_souris=Int(MouseY()/32)
  If  MouseButton(#PB_MouseButton_Right )
    End
  EndIf
  
  If  MouseButton(#PB_MouseButton_Left) And carte( x_souris, y_souris)\liste<>#liste_mur  And (tete\x%32=0 And tete\y%32=0)  
    If ElapsedMilliseconds()-date_click_gauche>200
      date_click_gauche=ElapsedMilliseconds() 
      cible_atteinte=0
      x_cible=x_souris
      y_cible=y_souris
      preparer_pathfinding() 
      pathfinding(tete\x/32,tete\y/32,x_cible, y_cible)
      If tete\x<>x_cible*32 And tete\y<>y_cible*32
        tete\x=PeekB(*memoryID_x+position_sauvegarde)*32
        tete\y=PeekB(*memoryID_y+position_sauvegarde)*32
      EndIf
      tete\dx=(PeekB(*memoryID_x+position_sauvegarde-1)-PeekB(*memoryID_x+position_sauvegarde))*tete_dl
      tete\dy=(PeekB(*memoryID_y+position_sauvegarde-1)-PeekB(*memoryID_y+position_sauvegarde))*tete_dl
      
      AddElement(decision())
      decision()\x=tete\x
      decision()\y=tete\y
      decision()\dx=tete\dx
      decision()\dy=tete\dy
      
      choix_du_sprite_tete()
      
    EndIf
  EndIf
  ;}
  
  ;-la tête suit le chemin enregistré et les informations pour le reste du corps
  ;{
  If tete\x=PeekB(*memoryID_x+position_sauvegarde)*32 And tete\y=PeekB(*memoryID_y+position_sauvegarde)*32 And position_sauvegarde>0
    dx_decision=(PeekB(*memoryID_x+position_sauvegarde-1)-PeekB(*memoryID_x+position_sauvegarde))*tete_dl
    dy_decision=(PeekB(*memoryID_y+position_sauvegarde-1)-PeekB(*memoryID_y+position_sauvegarde))*tete_dl
    If dx_decision<>tete\dx Or dy_decision<>tete\dy
      tete\dx=dx_decision
      tete\dy=dy_decision
      
      AddElement(decision())
      decision()\x=tete\x
      decision()\y=tete\y
      decision()\dx=tete\dx
      decision()\dy=tete\dy
      
      choix_du_sprite_tete()
      
    EndIf    
    position_sauvegarde=position_sauvegarde-1
  EndIf
  ;}
  ;     
  ;-si la cible est atteinte
  ;{
  If tete\x=x_cible*32 And tete\y=y_cible*32
    cible_atteinte=1
  EndIf
  ;}
  
  ;-lecture des informations laissées par la tête par le corps
  ;{
  For i=0 To taille_dragon
    
    ForEach decision()
      
      If dragon(i)\x=decision()\x And dragon(i)\y=decision()\y And dragon(i)\numero_decision=ListIndex(decision())
        dragon(i)\dx=decision()\dx
        dragon(i)\dy=decision()\dy
        dragon(i)\numero_decision=dragon(i)\numero_decision+1
      EndIf
      
    Next
    
  Next i
  ;}
  
  ;-mise à jour du compteur de décision quand la queue est passée
  ;{
  If dragon(0)\numero_decision=1
    
    FirstElement(decision())
    DeleteElement(decision())
    For i=0 To taille_dragon
      dragon(i)\numero_decision=dragon(i)\numero_decision-1
    Next i
    
  EndIf
  ;}
  
  ;-animation du dragon
  ;{
  If ElapsedMilliseconds()-chrono>10
    chrono=ElapsedMilliseconds()
    If cible_atteinte=0
      tete\x=tete\x+tete\dx
      tete\y=tete\y+tete\dy
      For i=0 To taille_dragon
        dragon(i)\x=dragon(i)\x+dragon(i)\dx
        dragon(i)\y=dragon(i)\y+dragon(i)\dy
      Next i
    EndIf
  EndIf
  ;}
  
  ;-affichage des sprites
  ;{
  DisplaySprite(#spr_ecran,0,0)
  For i=0 To 31
    For j=0 To 23
      If   carte(i,j)\liste=#liste_chemin
        DisplaySprite(#spr_chemin,i*32,j*32)
      EndIf
    Next j
  Next i
  
  For i=0 To taille_dragon
    ClipSprite(#spr_dragon_fer,dragon(i)\spr_colonne,dragon(i)\spr_ligne,16,16)
    DisplaySprite(#spr_dragon_fer,dragon(i)\x+corps_decalage_x,dragon(i)\y+corps_decalage_y)
  Next i
  
  ClipSprite(#spr_dragon_fer,tete_spr_colonne,tete_spr_ligne,32,32)
  DisplaySprite(#spr_dragon_fer,tete\x,tete\y)
  
  DisplaySprite(#spr_souris,x_souris*32,y_souris*32)
  
  ;  StartDrawing(ScreenOutput())
  ;     For i=0 To 31
  ;     For j=0 To 23
  ;       If   carte(i,j)\f<>0
  ;         DrawText(i*32,j*32,Str(carte(i,j)\f))
  ;       EndIf
  ;     Next j
  ;   Next i
  
  ;     ForEach decision()
  ;       DrawText(decision()\x,decision()\y,Str(ListIndex(decision()))+";"+Str(decision()\dx)+","+Str(decision()\dy))
  ;     Next  
  ;  DrawText(100,0,"x="+Str(carte(x_souris,y_souris)\id/24)+">>y="+Str(carte(x_souris,y_souris)\id%24)+">>ID="+Str(carte(x_souris,y_souris)\id)+">>type="+Str(carte(x_souris,y_souris)\liste))
  ; StopDrawing()
  ;}
  
  Delay(15)  
  ;calcul du fps
  ;{
  If Second < ElapsedMilliseconds() 
    Second = ElapsedMilliseconds()+1000
    fps = Frame_Counter 
    Frame_Counter = 0 
  Else 
    Frame_Counter + 1 
  EndIf     
  StartDrawing(ScreenOutput()) 
  DrawText(0,0,"FPS : "+Str(fps)) 
  StopDrawing() 
  ;}
  FlipBuffers()
ForEver

; IDE Options = PureBasic v4.02 (Windows - x86)
; CursorPosition = 374
; FirstLine = 184
; Folding = PgY-
Hasta la vista!