Source:Réaliser un RPG en PureBasic/Combats, fondus enchaînés et coffre/sprite.pb


;** sprite.pb
;**
;** Gestion des sprites
;**
;** Rôle:
;** - initialisation et libération des sprites.
;** - affichage des sprites
;** - Gestion des événements associés au sprites

 
XIncludeFile "sprite.pbi"

Procedure InitSprites(*libsprite.s_lib_sprite)
  *libsprite\n_sprite = 0
  *libsprite\sprites = #Null
EndProcedure

Procedure FreeSprites(*libsprite.s_lib_sprite)
  Define.l i, anim
  Define.s_sprite *Ptsprite
  Define.s_anim   *Ptanim
  Define.s_frame  *Ptframe
    
  For i = 0 To *libsprite\n_sprite - 1
      
    *Ptsprite = *libsprite\sprites + i * SizeOf(s_sprite)
    
    If IsSprite(*Ptsprite\surf\Id) : FreeSprite(*Ptsprite\surf\Id) : EndIf
    
    For anim = 0 To *Ptsprite\n_anim - 1
      *Ptanim = *Ptsprite\anim + anim * SizeOf(s_anim)
      FreeMemory(*Ptanim\frame)
    Next anim
    
    FreeMemory(*Ptsprite\anim)
      
  Next i
  
  FreeMemory(*libsprite\sprites)
  
  *libsprite\n_sprite = 0   
    
EndProcedure

Procedure SpriteStaticData(*libsprite.s_lib_sprite)
  Define.l MemSprite
  Define.s_sprite *Ptsprite
  Define.s_anim   *Ptanim
  Define.s_frame  *Ptframe
  
  *libsprite\n_sprite = 2
  *libsprite\sprites  = AllocateMemory(*libsprite\n_sprite * SizeOf(s_sprite))
  
  ;Sprite 0: player 
  *Ptsprite=*libsprite\sprites
  With *Ptsprite
  \x             = 256
  \y             = 86
  \offsetG       = 5
  \offsetD       = 15 
  \offsetH       = 20
  \offsetB       = 3 
  \surf\id = LoadSprite(#PB_Any, "sprites\player.bmp")
  TransparentSpriteColor(\surf\Id, #Black)
  \width         = 24
  \height        = 32
  \n_anim        = 4
  \cur_anim      = #ANIMATION_BAS
  \ispaused_anim = 1
  \anim          = AllocateMemory(\n_anim * SizeOf(s_anim))
  EndWith
  
  ; anim 0: haut 
  *Ptanim = *Ptsprite\anim
  With *Ptanim
  \cur_frame          = 0
  \n_frame            = 3
  \delaybetweenframes = 100
  \lastfrmupdate      = ElapsedMilliseconds()
  \frame              = AllocateMemory(\n_frame * SizeOf(s_frame))
  EndWith
  
  With *Ptframe    
  *Ptframe = *Ptanim\frame
  \x = 144
  \y = 0
  *Ptframe + SizeOf(s_frame)
  \x = 168
  \y = 0
  *Ptframe + SizeOf(s_frame)
  \x = 192
  \y = 0
  EndWith
  
  ; anim 1: droite 
  *Ptanim + SizeOf(s_anim)
  With *Ptanim
  \cur_frame          = 0
  \n_frame            = 3
  \delaybetweenframes = 100
  \lastfrmupdate      = ElapsedMilliseconds()
  \frame              = AllocateMemory(\n_frame * SizeOf(s_frame))
  EndWith
     
  With *Ptframe
  *Ptframe = *Ptanim\frame
  \x = 144
  \y = 32
  *Ptframe + SizeOf(s_frame)
  \x = 168
  \y = 32
  *Ptframe + SizeOf(s_frame)
  \x = 192
  \y = 32
  EndWith
  
  ;anim 2: bas 
  *Ptanim + SizeOf(s_anim)
  With *Ptanim
  \cur_frame          = 0
  \n_frame            = 3
  \delaybetweenframes = 100
  \lastfrmupdate      = ElapsedMilliseconds()
  \frame              = AllocateMemory(\n_frame * SizeOf(s_frame))
  EndWith
  
  With *Ptframe
  *Ptframe = *Ptanim\frame
  \x = 144
  \y = 64
  *Ptframe + SizeOf(s_frame)
  \x = 168
  \y = 64
  *Ptframe + SizeOf(s_frame)
  \x = 192
  \y = 64
  EndWith
  
  ; anim 3: gauche 
  *Ptanim + SizeOf(s_anim)
  With *Ptanim
  \cur_frame          = 0
  \n_frame            = 3
  \delaybetweenframes = 100
  \lastfrmupdate      = ElapsedMilliseconds()
  \frame              = AllocateMemory(\n_frame * SizeOf(s_frame))
  EndWith
  
  With *Ptframe
  *Ptframe = *Ptanim\frame
  \x = 144
  \y = 96
  *Ptframe + SizeOf(s_frame)
  \x = 168
  \y = 96
  *Ptframe + SizeOf(s_frame)
  \x = 192
  \y = 96
  EndWith
   
  ;Sprite 1: pnj
  MemSprite = *Ptsprite\surf\id
  *Ptsprite + SizeOf(s_sprite)
  With *Ptsprite    
  \x             = 100
  \y             = 128
  \offsetG       = 5
  \offsetD       = 15 ; 15
  \offsetH       = 20
  \offsetB       = 3  ; 10
  \surf\id       = MemSprite ; utilise le même sprite que le sprite 0
  \width         = 24
  \height        = 32
  \n_anim        = 4 
  \cur_anim      = #ANIMATION_BAS
  \ispaused_anim = 0
  \anim          = AllocateMemory(\n_anim * SizeOf(s_anim))
  EndWith
  
  ;anim 0: haut 
  *Ptanim=*Ptsprite\anim
  With *Ptanim
  \cur_frame          = 0
  \n_frame            = 3
  \delaybetweenframes = 100
  \lastfrmupdate      = ElapsedMilliseconds()
  \frame              = AllocateMemory(\n_frame * SizeOf(s_frame))
  EndWith
  
  With *Ptframe
  *Ptframe=*Ptanim\frame
  \x = 0
  \y = 0
  *Ptframe + SizeOf(s_frame)
  \x = 24
  \y = 0
  *Ptframe + SizeOf(s_frame)
  \x = 48
  \y = 0
  EndWith
  
  ;anim 1: droite 
  *Ptanim + SizeOf(s_anim)
  With *Ptanim
  \cur_frame          = 0
  \n_frame            = 3
  \delaybetweenframes = 100
  \lastfrmupdate      = ElapsedMilliseconds()
  \frame              = AllocateMemory(\n_frame * SizeOf(s_frame))
  EndWith
  
  With *Ptframe
  *Ptframe = *Ptanim\frame
  \x = 0
  \y = 32
  *Ptframe + SizeOf(s_frame)
  \x = 24
  \y = 32
  *Ptframe + SizeOf(s_frame)
  \x = 48
  \y = 32
  EndWith
  
  ;anim 2: bas
  *Ptanim + SizeOf(s_anim)
  With *Ptanim
  \cur_frame          = 0
  \n_frame            = 3
  \delaybetweenframes = 100
  \lastfrmupdate      = ElapsedMilliseconds()
  \frame              = AllocateMemory(\n_frame * SizeOf(s_frame))
  EndWith
  
  With *Ptframe
  *Ptframe = *Ptanim\frame
  \x = 0
  \y = 64
  *Ptframe + SizeOf(s_frame)
  \x = 24
  \y = 64
  *Ptframe + SizeOf(s_frame)
  \x = 48
  \y = 64
  EndWith
  
  ;anim 3: gauche 
  *Ptanim + SizeOf(s_anim)
  With *Ptanim
  \cur_frame          = 0
  \n_frame            = 3
  \delaybetweenframes = 100
  \lastfrmupdate      = ElapsedMilliseconds()
  \frame              = AllocateMemory(\n_frame * SizeOf(s_frame))
  EndWith
  
  With *Ptframe
  *Ptframe=*Ptanim\frame
  \x = 0
  \y = 96
  *Ptframe + SizeOf(s_frame)
  \x = 24
  \y = 96
  *Ptframe + SizeOf(s_frame)
  \x = 48
  \y = 96
  EndWith
EndProcedure

Procedure SaveSprites(*libsprite.s_lib_sprite, charset.s, filename.s)
  Define *buffer
  Define.s_sprite *Ptsprite
  Define.s_anim   *Ptanim
  Define.s_frame  *Ptframe
  Define.l i, anim, frm
  
  *buffer = AllocateMemory(256)
  
  If *libsprite = 0 : ProcedureReturn : EndIf    
  If OpenFile(0, filename) = 0 : ProcedureReturn : EndIf

  WriteLong(0, *libsprite\n_sprite)
  
  For i = 0 To *libsprite\n_sprite - 1
      
    *Ptsprite = *libsprite\sprites + i * SizeOf(s_sprite)
    
    With *Ptsprite
    WriteLong(0, \x)
    WriteLong(0, \y)
    WriteLong(0, \width)
    WriteLong(0, \height)
    WriteLong(0, \n_anim)
    WriteLong(0, \cur_anim)
    WriteByte(0, \ispaused_anim)
    WriteLong(0, \offsetG)
    WriteLong(0, \offsetD)
    WriteLong(0, \offsetH)
    WriteLong(0, \offsetB)
    EndWith
    WriteData(0, @charset,Len(charset))
    WriteData(0, *buffer, 255 - Len(charset))
    
    For anim = 0 To *Ptsprite\n_anim - 1
      *Ptanim=*Ptsprite\anim + anim * SizeOf(s_anim)
      With *Ptanim
      WriteLong(0, \n_frame)
      WriteLong(0, \cur_frame)
      WriteLong(0, \lastfrmupdate)
      WriteLong(0, \delaybetweenframes)
      EndWith
      
      For frm=0 To *Ptanim\n_frame-1
        *Ptframe = *Ptanim\frame + frm * SizeOf(s_frame)
        WriteLong(0, *Ptframe\x)
        WriteLong(0, *Ptframe\y)
      Next frm

    Next anim     

  Next i
  
  CloseFile(0)
EndProcedure      

Procedure LoadSprites(*libsprite.s_lib_sprite, filename.s)
    
  Define.s FichierCharset
  Define.l i, anim, frm
  Define.s_sprite *Ptsprite
  Define.s_anim   *Ptanim
  Define.s_frame  *Ptframe
  
  If *libsprite = 0 : ProcedureReturn : EndIf   
  If ReadFile(0, filename) = 0 : ProcedureReturn : EndIf
  
  *libsprite\n_sprite = ReadLong(0)
  *libsprite\sprites = AllocateMemory(*libsprite\n_sprite * SizeOf(s_sprite))
  
  For i = 0 To *libsprite\n_sprite - 1
        
    *Ptsprite=*libsprite\sprites + i * SizeOf(s_sprite)
    With *Ptsprite
    \x             = ReadLong(0)
    \y             = ReadLong(0) 
    \width         = ReadLong(0)
    \height        = ReadLong(0)
    \n_anim        = ReadLong(0)
    \cur_anim      = ReadLong(0)
    \ispaused_anim = ReadByte(0)
    \offsetG       = ReadLong(0)
    \offsetD       = ReadLong(0)
    \offsetH       = ReadLong(0)
    \offsetB       = ReadLong(0)
    EndWith
    
    FichierCharset = Space(256)
    ReadData(0, @FichierCharset, 255)
    
    *Ptsprite\surf\Id = LoadSprite(#PB_Any, FichierCharset)
    
    If *Ptsprite\surf\Id = 0
      MessageRequester("Erreur", "Impossible de charger " + FichierCharset, 0)
      End
    EndIf    
      
    ;Change la couleur transparente du sprite
    TransparentSpriteColor(*Ptsprite\surf\Id, #Black)
    
    *Ptsprite\anim = AllocateMemory(*Ptsprite\n_anim * SizeOf(s_anim))
    
    For anim = 0 To *Ptsprite\n_anim - 1
        
      *Ptanim = *Ptsprite\anim + anim * SizeOf(s_anim)
      With *Ptanim
      \n_frame            = ReadLong(0)
      \cur_frame          = ReadLong(0)
      \lastfrmupdate      = ReadLong(0)
      \delaybetweenframes = ReadLong(0)
      \frame=AllocateMemory(\n_frame * SizeOf(s_frame))
      EndWith
      
      For frm = 0 To *Ptanim\n_frame - 1
          
        *Ptframe = *Ptanim\frame + frm * SizeOf(s_frame)
        
        *Ptframe\x = ReadLong(0)
        *Ptframe\y = ReadLong(0)
          
      Next frm
    Next anim
  Next i
  
  CloseFile(0)
    
EndProcedure

Procedure HandleSprites(ispaused.l,*libsprite.s_lib_sprite,*map.s_map)
    
  Define.s_sprite *Ptsprite
  Define.l i, oldx, oldy, anim 
  
  ;centre la vue sur le joueur 
  *Ptsprite = *libsprite\sprites
  
  If (*Ptsprite\x < (800 / 2))
    *map\offsetX = 0     
  Else
    *map\offsetX=*Ptsprite\x-(800/2)
    If *map\offsetX > *map\width * #TILE_WIDTH - 800 
      *map\offsetX = *map\width * #TILE_WIDTH - 800
    EndIf 
  EndIf
  
  
  If *Ptsprite\y < (600 / 2)
    *map\offsetY = 0     
  Else
    *map\offsetY = *Ptsprite\y - (600 / 2)
    If *map\offsetY > *map\height * #TILE_HEIGHT - 600 
      *map\offsetY = *map\height * #TILE_HEIGHT - 600
    EndIf
  EndIf    
  
  ;gestion du joueur 
  HandleSprite(*Ptsprite, *map)      
   
  ;gestion des pnj 
  For i = 1 To *libsprite\n_sprite - 1
      
    *Ptsprite = *libsprite\sprites + i * SizeOf(s_sprite)
    
    If ispaused = 0 And *Ptsprite\ispaused_anim = 0
        
      oldx = *Ptsprite\x
      oldy = *Ptsprite\y
      
      ;on fait se déplacer le pnj en continuant dans sa direction... 
      Select *Ptsprite\cur_anim
          
        Case #ANIMATION_DROITE
          SpriteMove(*Ptsprite, *Ptsprite\x + 1, *Ptsprite\y, *map)
            
        Case #ANIMATION_GAUCHE
          If *Ptsprite\x>0
            SpriteMove(*Ptsprite, *Ptsprite\x - 1, *Ptsprite\y, *map)
          EndIf   
        Case #ANIMATION_HAUT
          If *Ptsprite\y>0
            SpriteMove(*Ptsprite, *Ptsprite\x, *Ptsprite\y - 1, *map)
          EndIf    
        Case #ANIMATION_BAS
          SpriteMove(*Ptsprite, *Ptsprite\x, *Ptsprite\y + 1, *map)
              
      EndSelect
      
      If oldx = *Ptsprite\x And oldy = *Ptsprite\y
        anim = *Ptsprite\cur_anim
        While anim = *Ptsprite\cur_anim
          anim=Random(*Ptsprite\n_anim - 1)
        Wend 
        SpriteChangeAnim(*Ptsprite, anim, 0)
      EndIf
        
    EndIf  
    
    HandleSprite(*Ptsprite, *map)
  Next
EndProcedure

Procedure HandleSprite(*sprite.s_sprite, *map.s_map)
  Define.rectangle src, dest 
  Define.s_sprite *Ptsprite
  Define.s_anim *Ptanim
  Define.s_frame *Ptframe
  
  *Ptanim = *sprite\anim + *sprite\cur_anim * SizeOf(s_anim) 
  
  If *sprite\ispaused_anim = 0
    If ElapsedMilliseconds() - *Ptanim\lastfrmupdate > *Ptanim\delaybetweenframes
      *Ptanim\cur_frame = (*Ptanim\cur_frame + 1) % *Ptanim\n_frame
      *Ptanim\lastfrmupdate = ElapsedMilliseconds()
    EndIf
  EndIf   
  
  *Ptframe = *Ptanim\frame + *Ptanim\cur_frame * SizeOf(s_frame)
  
  src\x  = *Ptframe\x
  src\y  = *Ptframe\y
  src\w  = *sprite\width
  src\h  = *sprite\height
  
  dest\x = *sprite\x - *map\offsetX
  dest\y = *sprite\y - *map\offsetY
  dest\w = *sprite\width
  dest\h = *sprite\height
  
  ;teste si le sprite est à l'écran... si ce n'est pas le cas, inutile de continuer! 
  If dest\x < -dest\w : ProcedureReturn : EndIf
  If dest\y < -dest\h : ProcedureReturn : EndIf
  
  If dest\x<0
    src\x  - dest\x
    src\w  + dest\x
    dest\w + dest\x
    dest\x = 0
  EndIf  
  
  If dest\y < 0
    src\y  - dest\y
    src\h  + dest\y
    dest\h + dest\h
    dest\y = 0
  EndIf
      
  ClipSprite(*sprite\surf\Id, src\x, src\y,src\w, src\h)
  DisplayTransparentSprite(*sprite\surf\Id,dest\x,dest\y)
  
EndProcedure
 
Procedure SpriteChangeAnim(*sprite.s_sprite,anim.l,ispaused.b)
  Define.s_anim *Ptanim
  *sprite\cur_anim      = anim
  *sprite\ispaused_anim = ispaused
  *Ptanim               = *sprite\anim + *sprite\cur_anim * SizeOf(s_anim)   
  *Ptanim\cur_frame     = 0
  *Ptanim\lastfrmupdate = ElapsedMilliseconds()
EndProcedure

Procedure SpriteMove(*sprite.s_sprite, x, y, *map.s_map)
  Define.l hgx,hgy, hdx,hdy, bgx,bgy, bdx,bdy
  Define.l T1, T2, T3, T4
  
  ; calcul du tile sur lequel est situé chaque bord du sprite 
  hgx = (x + *sprite\offsetG) / #TILE_WIDTH
  hgy = (y + *sprite\offsetH) / #TILE_HEIGHT
  
  hdx = (x + #TILE_WIDTH - *sprite\offsetD) / #TILE_WIDTH
  hdy = (y + *sprite\offsetH) / #TILE_HEIGHT
  
  bgx = (x + *sprite\offsetG) / #TILE_WIDTH
  bgy = (y + #TILE_HEIGHT - *sprite\offsetB) / #TILE_HEIGHT
  
  bdx = (x + #TILE_WIDTH -  *sprite\offsetD) / #TILE_WIDTH
  bdy = (y + #TILE_HEIGHT - *sprite\offsetB) / #TILE_HEIGHT
  
  T1 = DataMap(GET_TILE(hgx, hgy, *map))   
  T2 = DataMap(GET_TILE(hdx, hdy, *map)) 
  T3 = DataMap(GET_TILE(bgx, bgy, *map)) 
  T4 = DataMap(GET_TILE(bdx, bdy, *map)) 
  
  
  
  If chipset(T1)\Collision = 0 And chipset(T2)\Collision = 0 And chipset(T3)\Collision = 0 And chipset(T4)\Collision = 0
    
    If x > *map\width * #TILE_WIDTH - *sprite\width 
      x = *map\width * #TILE_WIDTH - *sprite\width
    EndIf
    
    If y > *map\height * #TILE_HEIGHT - *sprite\height
        y = *map\height * #TILE_HEIGHT - *sprite\height
    EndIf
    
    *sprite\x = x
    *sprite\y = y
      
  EndIf
EndProcedure
            
Procedure SpriteEvent(*map.s_map,*libsprite.s_lib_sprite)
    
  Define.s_sprite *Ptsprite
  
  *Ptsprite = *libsprite\sprites
  *Ptsprite\ispaused_anim = 1
   
  If KeyboardPushed(#PB_Key_Right)
      
    SpriteMove(*Ptsprite, *Ptsprite\x + 1, *Ptsprite\y, *map)
    If *Ptsprite\cur_anim <> #ANIMATION_DROITE
        SpriteChangeAnim(*Ptsprite, #ANIMATION_DROITE, 0)
    Else
        *Ptsprite\ispaused_anim = 0
    EndIf
      
  ElseIf KeyboardPushed(#PB_Key_Left)
      
    If *Ptsprite\x > 0
        SpriteMove(*Ptsprite, *Ptsprite\x - 1, *Ptsprite\y, *map)
    EndIf    
    If *Ptsprite\cur_anim<>#ANIMATION_GAUCHE
        SpriteChangeAnim(*Ptsprite, #ANIMATION_GAUCHE, 0)
    Else
        *Ptsprite\ispaused_anim = 0
    EndIf
      
  ElseIf KeyboardPushed(#PB_Key_Down)
      
    SpriteMove(*Ptsprite, *Ptsprite\x, *Ptsprite\y + 1, *map)
    If *Ptsprite\cur_anim<>#ANIMATION_BAS
        SpriteChangeAnim(*Ptsprite, #ANIMATION_BAS, 0)
    Else
        *Ptsprite\ispaused_anim = 0
    EndIf
      
  ElseIf KeyboardPushed(#PB_Key_Up)
      
    If*Ptsprite\y > 0
        SpriteMove(*Ptsprite, *Ptsprite\x, *Ptsprite\y-1, *map)
    EndIf    
    If *Ptsprite\cur_anim <> #ANIMATION_HAUT
        SpriteChangeAnim(*Ptsprite, #ANIMATION_HAUT, 0)
    Else
        *Ptsprite\ispaused_anim = 0
    EndIf
      
  EndIf
         
EndProcedure