ifndef                  _SPRITES_ASM
_SPRITES_ASM            equ     1

; ------------------------------------------------------
; Includes
                        include Ortho_Matrices.asm

; ------------------------------------------------------
; Constants
SPRITE_ANIM_FORWARD     equ     0
SPRITE_ANIM_PINGPONG    equ     1
SPRITE_ANIM_ONCE        equ     2

; ------------------------------------------------------
; Structures
SPRITE_DATAS            struct
Sprite_Pic_Width        dword   ?
Sprite_Pic_Height       dword   ?
Sprite_Width            dword   ?
Sprite_Height           dword   ?
Sprite_Datas            dword   ?
Texture_Size            dword   ?
Texture                 dword   ?
_Mask                   dword   ?
Texture_Id              dword   ?
Texture_Id2             dword   ?
Color_Key               dword   ?
SPRITE_DATAS            ends
LPSPRITE_DATAS          typedef ptr SPRITE_DATAS

SPRITE                  struct
Datas                   LPSPRITE_DATAS ?
X                       dword   ?
Y                       dword   ?
SPRITE                  ends

SPRITE_RECT             struct
Left                    dd      ?
Top                     dd      ?
_Width                  dd      ?
Height                  dd      ?
SPRITE_RECT             ends

ANIM_SPRITE             struct
Datas                   LPSPRITE_DATAS ?
X                       dword   ?
Y                       dword   ?
Still_Sprite_Nbr        dd      ?
Start_Sprite_Nbr        dd      ?
End_Sprite_Nbr          dd      ?
_Type                   dd      ?
Sprites                 dd      ?
Position                dd      ?
Way                     dd      ?
IsStill                 dd      ?
ANIM_SPRITE             ends

ANIM_GROUP_DATAS        struct
Anim_Ptr                dd      ?
Datas_Ptr               dd      ?
Group_Ptr               dd      ?
ANIM_GROUP_DATAS        ends

ANIM_GROUP              struct
Current_Group           dd      ?
Nbr_Groups              dd      ?
Group_Datas             dd      ?
ANIM_GROUP              ends

ANIM_GROUP_MAIN_DATAS   struct
Still_Idx               dd      ?
First_Idx               dd      ?
Last_Idx                dd      ?
Anim_Type               dd      ?
Group_Table             dd      ?
Picture                 dd      ?
ANIM_GROUP_MAIN_DATAS   ends

; ------------------------------------------------------
; Functions
Destroy_Anim_Sprite     proto   :dword

; ------------------------------------------------------
; Name: Create_Sprite_Datas()
; Desc: Create a 2d bitmap sprite image
Create_Sprite_Datas     proc    uses ebx esi edi Sprite_Struct:dword
                        local   _Real_Width:dword
                        local   _Stride:dword

                        mov     esi, Sprite_Struct

                        mov     eax, [esi + SPRITE_DATAS.Sprite_Pic_Width]
                        shl     eax, 2
                        mov     _Stride, eax

                        ; Alloc the memory for the texture
                        mov     ebx, [esi + SPRITE_DATAS.Texture_Size]
                        shl     ebx, 2                                          ; RGBA
                        mov     _Real_Width, ebx
                        imul    ebx, ebx
                        mov     eax, ALLOCMEM(ebx)
                        .if     eax == FALSE
                                ret
                        .endif
                        mov     [esi + SPRITE_DATAS.Texture], eax

                        ; Memory for the mask
                        mov     ebx, [esi + SPRITE_DATAS.Sprite_Width]
                        mov     eax, [esi + SPRITE_DATAS.Sprite_Height]
                        imul    ebx, eax
                        mov     eax, ALLOCMEM(ebx)
                        .if     eax == FALSE
                                FREEMEM [esi + SPRITE_DATAS.Texture]
                                ret
                        .endif
                        mov     [esi + SPRITE_DATAS._Mask], eax

                        ; Copy the sprite into the memory
                        mov     ecx, [esi + SPRITE_DATAS.Sprite_Datas]
                        mov     eax, [esi + SPRITE_DATAS.Sprite_Pic_Height]
                        sub     eax, [esi + SPRITE_DATAS.Sprite_Height]
                        imul    eax, [esi + SPRITE_DATAS.Sprite_Pic_Width]
                        shl     eax, 2
                        add     ecx, eax
                        mov     ebx, [esi + SPRITE_DATAS._Mask]
                        mov     eax, [esi + SPRITE_DATAS.Sprite_Width]
                        mov     edi, [esi + SPRITE_DATAS.Sprite_Height]
                        dec     edi
                        imul    eax, edi
                        add     ebx, eax
                        mov     edi, [esi + SPRITE_DATAS.Texture]
                        mov     eax, [esi + SPRITE_DATAS.Texture_Size]
                        sub     eax, [esi + SPRITE_DATAS.Sprite_Height]
                        imul    eax, _Real_Width
                        add     edi, eax                                ; Position in the GL texture
                        xor     eax, eax
_Copy_Font_Letter_Y:    push    eax
                                push    ebx
                                push    edi
                                push    ecx
                                xor     edx, edx
_Copy_Font_Letter_X:            push    edx
                                mov     eax, [ecx]
                                add     ecx, 4
                                and     eax, 000ffffffh
                                mov     edx, [esi + SPRITE_DATAS.Color_Key]
                                and     edx, 000ffffffh
                                cmp     edx, eax
                                mov     edx, 1                          ; Mask Color
                                jne     _No_Set_Alpha
                                mov     eax, 0ff000000h                 ; Turn alpha channel on for this color
                                xor     edx, edx                        ; No mask
_No_Set_Alpha:                  xor     eax, 0ff000000h
                                mov     [edi], eax
                                add     edi, 4
                                mov     [ebx], dl                       ; Note the mask is inverted in Y
                                inc     ebx
                                pop     edx
                                inc     edx
                                cmp     edx, [esi + SPRITE_DATAS.Sprite_Width]
                                jne     _Copy_Font_Letter_X
                                pop     ecx
                                pop     edi
                                pop     ebx
                                add     edi, _Real_Width
                                add     ecx, _Stride
                                sub     ebx, [esi + SPRITE_DATAS.Sprite_Width]
                        pop     eax
                        inc     eax
                        cmp     eax, [esi + SPRITE_DATAS.Sprite_Height]
                        jne     _Copy_Font_Letter_Y

                        ; Send the picture to opengl
                        invoke  Create_2D_Texture, [esi + SPRITE_DATAS.Texture], [esi + SPRITE_DATAS.Texture_Size], TRUE, GL_RGBA, GL_RGBA
                        mov     [esi + SPRITE_DATAS.Texture_Id], eax
                        .if     eax == FALSE
                                mov     ebx, FALSE
                                jmp     _Err_sprite
                        .endif

                        mov     ebx, TRUE
_Err_sprite:            ; Remove the temporary letters
                        FREEMEM [esi + SPRITE_DATAS.Texture]
                        mov     eax, ebx
                        ret
Create_Sprite_Datas     endp

; ------------------------------------------------------
; Name: Destroy_Sprite_Datas()
; Desc: Release the allocated sprite resources
Destroy_Sprite_Datas    proc    uses esi Sprite_Struct:dword
                        mov     esi, Sprite_Struct
                        test    esi, esi
                        jz      No_Sprites_Datas
                        invoke  Destroy_Texture, addr [esi + SPRITE_DATAS.Texture_Id]
                        FREEMEM [esi + SPRITE_DATAS._Mask]
No_Sprites_Datas:       ret
Destroy_Sprite_Datas    endp

; ------------------------------------------------------
; Name: Set_Sprite_Pos()
; Desc: Set the position of a sprite
Set_Sprite_Pos          proc    uses esi Sprite_Struct:dword, X:dword, Y:dword
                        mov     esi, Sprite_Struct
                        mov     [esi + SPRITE.X], CMEM(X)
                        mov     [esi + SPRITE.Y], CMEM(Y)
                        ret
Set_Sprite_Pos          endp

; ------------------------------------------------------
; Name: Create_Sprite()
; Desc: Assign a sprite datas to a sprite
Create_Sprite           proc    uses esi Sprite_Struct:dword, Sprite_Datas_Struct:dword 
                        mov     esi, Sprite_Struct
                        mov     [esi + SPRITE.Datas], CMEM(Sprite_Datas_Struct)
                        mov     [esi + SPRITE.X], 0
                        mov     [esi + SPRITE.Y], 0
                        ret
Create_Sprite           endp

; ------------------------------------------------------
; Name: Display_Sprite()
; Desc: Display a sprite
Display_Sprite          proc    uses esi edi Sprite_Struct:dword
                        mov     esi, Sprite_Struct
                        mov     edi, [esi + SPRITE.Datas]
                        invoke  Draw_2D_Quad, INT2FLT([esi + SPRITE.X]), INT2FLT([esi + SPRITE.Y]), INT2FLT([edi + SPRITE_DATAS.Texture_Size]), [edi + SPRITE_DATAS.Texture_Id]
                        ret
Display_Sprite          endp

; ------------------------------------------------------
; Name: Check_Collision()
; Desc: Check if 2 sprites are colliding
Check_Collision         proc    uses ebx esi edi Sprite_Struct1:dword, Sprite_Struct2:dword
                        local   Spr1_X:dword
                        local   Spr1_Y:dword
                        local   Spr2_X:dword
                        local   Spr2_Y:dword
                        local   InterSect_X1:dword
                        local   InterSect_X2:dword
                        local   InterSect_Y1:dword
                        local   InterSect_Y2:dword
                        local   Sprite1_Mask:dword
                        local   Sprite2_Mask:dword
                        local   Sprite1_Width:dword
                        local   Sprite2_Width:dword
                        local   Sprite1_X:dword
                        local   Sprite1_Y:dword
                        local   Sprite2_X:dword
                        local   Sprite2_Y:dword

                        mov     esi, Sprite_Struct1
                        mov     edi, [esi + SPRITE.Datas]
                        mov     eax, Sprite_Struct2
                        mov     ebx, [eax + SPRITE.Datas]

                        mov     ecx, [esi + SPRITE.X]
                        mov     edx, [esi + SPRITE.Y]
                        .if     [eax + SPRITE.X] == ecx && [eax + SPRITE.Y] == edx
                                mov     eax,TRUE
                                ret
                        .endif

                        mov     ecx, [esi + SPRITE.X]
                        add     ecx, [edi + SPRITE_DATAS.Sprite_Width]
                        dec     ecx
                        mov     Spr1_X, ecx

                        mov     ecx, [esi + SPRITE.Y]
                        add     ecx, [edi + SPRITE_DATAS.Sprite_Height]
                        dec     ecx
                        mov     Spr1_Y, ecx
                        
                        mov     ecx, [eax + SPRITE.X]
                        add     ecx, [ebx + SPRITE_DATAS.Sprite_Width]
                        dec     ecx
                        mov     Spr2_X, ecx
                        mov     ecx, [eax + SPRITE.Y]
                        add     ecx, [ebx + SPRITE_DATAS.Sprite_Height]
                        dec     ecx
                        mov     Spr2_Y, ecx
                        
                        mov     ecx, Spr1_X
                        mov     edx, Spr2_X
                        .if     edx < [esi + SPRITE.X] || ecx < [eax + SPRITE.X]
                                xor     eax,eax
                                ret
                        .endif
                        mov     ecx, Spr1_Y
                        mov     edx, Spr2_Y
                        .if     edx < [esi + SPRITE.Y] || ecx < [eax + SPRITE.Y]
                                xor     eax,eax
                                ret
                        .endif

                        mov     ecx, [esi + SPRITE.X]
                        mov     edx, [eax + SPRITE.X]
                        .if     ecx > edx
                                mov     InterSect_X1, ecx
                        .else
                                mov     InterSect_X1, edx
                        .endif

                        mov     ecx, Spr1_X
                        mov     edx, Spr2_X
                        .if     ecx < edx
                                mov     InterSect_X2, ecx
                        .else
                                mov     InterSect_X2, edx
                        .endif

                        mov     ecx, [esi + SPRITE.Y]
                        mov     edx, [eax + SPRITE.Y]
                        .if     ecx > edx
                                mov     InterSect_Y1, ecx
                        .else
                                mov     InterSect_Y1, edx
                        .endif

                        mov     ecx, Spr1_Y
                        mov     edx, Spr2_Y
                        .if     ecx < edx
                                mov     InterSect_Y2, ecx
                        .else
                                mov     InterSect_Y2, edx
                        .endif

                        mov     ecx, [edi + SPRITE_DATAS._Mask]
                        mov     Sprite1_Mask, ecx
                        mov     ecx, [ebx + SPRITE_DATAS._Mask]
                        mov     Sprite2_Mask, ecx

                        mov     ecx, [edi + SPRITE_DATAS.Sprite_Width]
                        mov     Sprite1_Width, ecx
                        mov     ecx, [ebx + SPRITE_DATAS.Sprite_Width]
                        mov     Sprite2_Width, ecx

                        mov     ebx, [esi + SPRITE.X]
                        mov     Sprite1_X, ebx
                        mov     ebx, [esi + SPRITE.Y]
                        mov     Sprite1_Y, ebx
                        mov     ebx, [eax + SPRITE.X]
                        mov     Sprite2_X, ebx
                        mov     ebx, [eax + SPRITE.Y]
                        mov     Sprite2_Y, ebx

                        mov     ebx, InterSect_Y1
                        .while  ebx <= InterSect_Y2
                                mov     ecx, ebx
                                sub     ecx, Sprite2_Y
                                imul    ecx, Sprite2_Width
                                mov     edx, ebx
                                sub     edx, Sprite1_Y
                                imul    edx, Sprite1_Width
                                mov     esi, InterSect_X1
                                .while  esi <= InterSect_X2
                                        mov     eax, esi
                                        mov     edi, esi
                                        sub     eax, Sprite2_X
                                        add     eax, ecx
                                        add     eax, Sprite2_Mask
                                        sub     edi, Sprite1_X
                                        add     edi, edx
                                        add     edi, Sprite1_Mask
                                        mov     al, [eax]
                                        and     al, [edi]
                                        jz      No_Collision
                                        mov     eax, TRUE
                                        ret
No_Collision:                           inc     esi
                                .endw
                                inc     ebx
                        .endw
                        mov     eax,FALSE
                        ret
Check_Collision         endp

; ------------------------------------------------------
; Name: Create_Sprites_Group()
; Desc: Create a group of sprites pictures for animations purposes
Create_Sprites_Group    proc    uses ebx esi edi Group_Struct:dword, Group_Sprite_Datas:dword, Picture:dword
                        local   Group_Nbr_Elements:dword
                        local   Group_Tx_Size:dword
                        local   Group_Key_Color:dword
                        
                        mov     esi, Group_Struct
                        mov     edi, Group_Sprite_Datas
                        mov     edx, Picture
                        lodsd
                        mov     Group_Nbr_Elements, eax
                        lodsd
                        mov     Group_Tx_Size, eax
                        lodsd
                        mov     Group_Key_Color, eax
                        xor     ebx, ebx
                        .while  ebx < Group_Nbr_Elements
                                mov     eax, ebx
                                imul    eax, sizeof SPRITE_RECT
                                lea     ecx, [esi + eax]
                                mov     [edi + SPRITE_DATAS.Sprite_Width], CMEM([ecx + SPRITE_RECT._Width])
                                mov     [edi + SPRITE_DATAS.Sprite_Height], CMEM([ecx + SPRITE_RECT.Height])
                                mov     eax, [ecx + SPRITE_RECT.Top]
                                imul    eax, [edx + BMP.Orig_Width]
                                neg     eax
                                add     eax, [ecx + SPRITE_RECT.Left]
                                shl     eax, 2
                                add     eax, [edx + BMP.Datas]
                                mov     [edi + SPRITE_DATAS.Sprite_Datas], eax
                                mov     [edi + SPRITE_DATAS.Texture_Size], CMEM(Group_Tx_Size)
                                mov     [edi + SPRITE_DATAS.Color_Key], CMEM(Group_Key_Color)
                                mov     [edi + SPRITE_DATAS.Sprite_Pic_Width], CMEM([edx + BMP.Orig_Width])
                                mov     [edi + SPRITE_DATAS.Sprite_Pic_Height], CMEM([edx + BMP.Orig_Height])
                                push    edx
                                invoke  Create_Sprite_Datas, edi
                                pop     edx
                                .if     eax == FALSE
                                        ret
                                .endif
                                add     edi, sizeof SPRITE_DATAS
                                inc     ebx
                        .endw
                        ret
Create_Sprites_Group    endp

; ------------------------------------------------------
; Name: Destroy_Sprites_Group()
; Desc: Free the resource allocated for a group of sprites
Destroy_Sprites_Group   proc    uses ebx esi edi Group_Struct:dword, Group_Sprite_Datas:dword
                        local   Group_Nbr_Elements:dword
                        
                        mov     esi, Group_Struct
                        test    esi, esi
                        jz      @F
                        mov     edi, Group_Sprite_Datas
                        lodsd
                        mov     Group_Nbr_Elements, eax
                        xor     ebx, ebx
                        .while  ebx < Group_Nbr_Elements
                                invoke  Destroy_Sprite_Datas, edi
                                add     edi, sizeof SPRITE_DATAS
                                inc     ebx
                        .endw
@@:                     ret
Destroy_Sprites_Group   endp

; ------------------------------------------------------
; Name: _Create_Animated_Sprite()
; Desc: Fill a SPRITE_ANIM structure
_Create_Animated_Sprite proc    uses edi Anim_Struct:dword, Still_Idx:dword, First_Idx:dword, Last_Idx:dword, Anim_Type:dword, Sprites_Structs:dword
                        mov     edi, Anim_Struct
                        mov     [edi + ANIM_SPRITE.X], 0
                        mov     [edi + ANIM_SPRITE.Y], 0
                        mov     [edi + ANIM_SPRITE.Still_Sprite_Nbr], CMEM(Still_Idx)
                        mov     [edi + ANIM_SPRITE.Start_Sprite_Nbr], CMEM(First_Idx)
                        mov     [edi + ANIM_SPRITE.End_Sprite_Nbr], CMEM(Last_Idx)
                        mov     [edi + ANIM_SPRITE._Type], CMEM(Anim_Type)
                        mov     [edi + ANIM_SPRITE.Sprites], CMEM(Sprites_Structs)
                        mov     [edi + ANIM_SPRITE.Position], CMEM(INT2FLT(Still_Idx))
                        mov     [edi + ANIM_SPRITE.Way], FALSE
                        ret
_Create_Animated_Sprite endp

; ------------------------------------------------------
; Name: _Display_Animated_Sprite()
; Desc: Draw a sprite from a group
_Display_Animated_Sprite proc   uses edi Anim_Struct:dword
                        mov     esi, Anim_Struct
                        mov     ecx, [esi + ANIM_SPRITE.Sprites]
                        mov     eax, FLT2INT([esi + ANIM_SPRITE.Position])
                        cmp     eax, [esi + ANIM_SPRITE.End_Sprite_Nbr]
                        jb      Max_Pos
                        mov     eax, [esi + ANIM_SPRITE.End_Sprite_Nbr]
Max_Pos:                imul    eax, sizeof SPRITE_DATAS
                        add     ecx, eax
                        mov     [esi + ANIM_SPRITE.Datas], ecx
                        invoke  Display_Sprite, Anim_Struct
                        ret
_Display_Animated_Sprite endp

; ------------------------------------------------------
; Name: Run_Sprite_Animation()
; Desc: Perform the given sprite animation
Run_Sprite_Animation    proc    uses esi Anim_Struct:dword, Speed:dword, FrameRate:dword
                        mov     esi, Anim_Struct
                        mov     ecx, [esi + ANIM_SPRITE.Start_Sprite_Nbr]
                        mov     edx, [esi + ANIM_SPRITE.End_Sprite_Nbr]
                        .if     [esi + ANIM_SPRITE._Type] == SPRITE_ANIM_FORWARD
                                inc     edx
                                FCMP    [esi + ANIM_SPRITE.Position], INT2FLT(edx)
                                jae     Reset_Loop
                                fld     dword ptr [esi + ANIM_SPRITE.Position]
                                fld     Speed
                                fmul    FrameRate
                                faddp   st(1), st(0)
                                fstp    dword ptr [esi + ANIM_SPRITE.Position]
                                FCMP    [esi + ANIM_SPRITE.Position], INT2FLT(edx)
                                jb      Bound_Loop
                                mov     [esi + ANIM_SPRITE.Position], CMEM(INT2FLT(ecx))
Bound_Loop:                     ret
Reset_Loop:                     ; Start pos
                                mov     [esi + ANIM_SPRITE.Position], CMEM(INT2FLT(ecx))
                        .elseif [esi + ANIM_SPRITE._Type] == SPRITE_ANIM_PINGPONG
                                .if     [esi + ANIM_SPRITE.Way] == FALSE
                                        FCMP    [esi + ANIM_SPRITE.Position], INT2FLT(edx)
                                        jae     Max_PingPong
                                        fld     dword ptr [esi + ANIM_SPRITE.Position]
                                        fld     Speed
                                        fmul    FrameRate
                                        faddp   st(1), st(0)
                                        fstp    dword ptr [esi + ANIM_SPRITE.Position]
                                        FCMP    [esi + ANIM_SPRITE.Position], INT2FLT(edx)
                                        jae     Max_PingPong
                                        ret
Max_PingPong:                           mov     dword ptr [esi + ANIM_SPRITE.Way], TRUE
                                        mov     [esi + ANIM_SPRITE.Position], CMEM(INT2FLT(edx))
                                .else
                                        FCMP    [esi + ANIM_SPRITE.Position], INT2FLT(ecx)
                                        jbe     Min_PingPong
                                        fld     dword ptr [esi + ANIM_SPRITE.Position]
                                        fld     Speed
                                        fmul    FrameRate
                                        fsubp   st(1), st(0)
                                        fstp    dword ptr [esi + ANIM_SPRITE.Position]
                                        FCMP    [esi + ANIM_SPRITE.Position], INT2FLT(ecx)
                                        jbe     Min_PingPong
                                        ret
Min_PingPong:                           mov     dword ptr [esi + ANIM_SPRITE.Way], FALSE
                                        mov     [esi + ANIM_SPRITE.Position], CMEM(INT2FLT(ecx))
                                .endif
                        .elseif [esi + ANIM_SPRITE._Type] == SPRITE_ANIM_ONCE
                                FCMP    [esi + ANIM_SPRITE.Position], INT2FLT(edx)
                                jae     Max_Once
                                fld     dword ptr [esi + ANIM_SPRITE.Position]
                                fld     Speed
                                fmul    FrameRate
                                faddp   st(1), st(0)
                                fstp    dword ptr [esi + ANIM_SPRITE.Position]
                                FCMP    [esi + ANIM_SPRITE.Position], INT2FLT(edx)
                                jae     Max_Once
                                ret
Max_Once:                       ; End pos
                                mov     [esi + ANIM_SPRITE.Position], CMEM(INT2FLT(edx))
                        .endif 
                        ret
Run_Sprite_Animation    endp

; ------------------------------------------------------
; Name: _Sprite_Stop_Animation()
; Desc: Put the sprite in still position
_Sprite_Stop_Animation  proc    uses esi Anim_Struct:dword
                        mov     esi, Anim_Struct
                        mov     [esi + ANIM_SPRITE.Position], CMEM(INT2FLT([esi + ANIM_SPRITE.Still_Sprite_Nbr]))
                        mov     [esi + ANIM_SPRITE.Way], FALSE
                        ret
_Sprite_Stop_Animation  endp

; ------------------------------------------------------
; Name: Create_Anim_Sprite()
; Desc: Create an animated sprite (public)
Create_Anim_Sprite      proc    uses ebx esi edi Group_Struct:dword, Start_Group:dword
                        local   Nbr_Elements:dword
                        local   AGroup:dword
                        
                        mov     esi, Group_Struct
                        lodsd
                        mov     Nbr_Elements, eax
                        imul    eax, sizeof ANIM_GROUP_DATAS
                        mov     ebx, ALLOCMEM(eax)
                        .if     ebx == NULL
                                xor     eax, eax
                                ret
                        .endif
                        mov     AGroup, ALLOCMEM(sizeof ANIM_GROUP)
                        mov     edi, AGroup
                        .if     edi == NULL
                                FREEMEM ebx
                                xor     eax, eax
                                ret
                        .endif
                        mov     [edi + ANIM_GROUP.Current_Group], CMEM(Start_Group)
                        mov     [edi + ANIM_GROUP.Group_Datas], ebx
                        mov     [edi + ANIM_GROUP.Nbr_Groups], CMEM(Nbr_Elements)
                        mov     edi, [edi + ANIM_GROUP.Group_Datas]
                        xor     ebx, ebx
                        .while  ebx < Nbr_Elements
                                push    ebx
                                
                                mov     eax, [esi + ANIM_GROUP_MAIN_DATAS.Group_Table]
                                mov     eax, [eax]
                                imul    eax, sizeof SPRITE_DATAS
                                mov     ecx, ALLOCMEM(eax)
                                .if     ecx == NULL
                                        invoke  Destroy_Anim_Sprite, AGroup
                                        xor     eax, eax
                                        ret
                                .endif
                                mov     [edi + ANIM_GROUP_DATAS.Datas_Ptr], ecx

                                mov     ebx, ALLOCMEM(sizeof ANIM_SPRITE)
                                .if     ebx == NULL
                                        invoke  Destroy_Anim_Sprite, AGroup
                                        xor     eax, eax
                                        ret
                                .endif
                                mov     [edi + ANIM_GROUP_DATAS.Anim_Ptr], ebx

                                mov     eax, [esi + ANIM_GROUP_MAIN_DATAS.Group_Table]
                                mov     [edi + ANIM_GROUP_DATAS.Group_Ptr], eax
                                invoke  Create_Sprites_Group, eax, [edi + ANIM_GROUP_DATAS.Datas_Ptr], [esi + ANIM_GROUP_MAIN_DATAS.Picture]
                                .if     eax == NULL
                                        invoke  Destroy_Anim_Sprite, AGroup
                                        xor     eax, eax
                                        ret
                                .endif
                                mov     ecx, [edi + ANIM_GROUP_DATAS.Datas_Ptr]
                                invoke  _Create_Animated_Sprite, [edi + ANIM_GROUP_DATAS.Anim_Ptr], [esi + ANIM_GROUP_MAIN_DATAS.Still_Idx], \
                                                                [esi + ANIM_GROUP_MAIN_DATAS.First_Idx], \
                                                                [esi + ANIM_GROUP_MAIN_DATAS.Last_Idx], \
                                                                [esi + ANIM_GROUP_MAIN_DATAS.Anim_Type], ecx
                                ; Number of elements
                                add     edi, sizeof ANIM_GROUP_DATAS
                                add     esi, sizeof ANIM_GROUP_MAIN_DATAS
                                pop     ebx
                                inc     ebx
                        .endw
                        mov     eax, AGroup
                        ret
Create_Anim_Sprite      endp

; ------------------------------------------------------
; Name: Set_Anim_Sprite_Group()
; Desc: Set the current group of an animated sprite
Set_Anim_Sprite_Group   proc    uses edi hGroup:dword, Group_To_Set:dword
                        mov     edi, hGroup
                        mov     [edi + ANIM_GROUP.Current_Group], CMEM(Group_To_Set)
                        ret
Set_Anim_Sprite_Group   endp

; ------------------------------------------------------
; Name: Stop_Anim_Sprite()
; Desc: Stop the current animation of a sprite
Stop_Anim_Sprite        proc    uses ebx esi hGroup:dword
                        mov     esi, hGroup
                        mov     ebx, [esi + ANIM_GROUP.Current_Group]
                        imul    ebx, sizeof ANIM_GROUP_DATAS
                        mov     esi, [esi + ANIM_GROUP.Group_Datas]
                        add     esi, ebx
                        mov     eax, [esi + ANIM_GROUP_DATAS.Anim_Ptr]
                        invoke  _Sprite_Stop_Animation, eax
                        ret
Stop_Anim_Sprite        endp

; ------------------------------------------------------
; Name: Set_Anim_Sprite_Pos()
; Desc: Set the position of an animated sprite (public)
Set_Anim_Sprite_Pos     proc    uses ebx esi hGroup:dword, X:dword, Y:dword
                        mov     esi, hGroup
                        mov     ebx, [esi + ANIM_GROUP.Current_Group]
                        imul    ebx, sizeof ANIM_GROUP_DATAS
                        mov     esi, [esi + ANIM_GROUP.Group_Datas]
                        add     esi, ebx
                        mov     eax, [esi + ANIM_GROUP_DATAS.Anim_Ptr]
                        invoke  Set_Sprite_Pos, eax, X, Y
                        ret
Set_Anim_Sprite_Pos     endp

; ------------------------------------------------------
; Name: Display_Anim_Sprite()
; Desc: Display an animated sprite (public)
Display_Anim_Sprite     proc    uses ebx esi hGroup:dword
                        mov     esi, hGroup
                        mov     ebx, [esi + ANIM_GROUP.Current_Group]
                        imul    ebx, sizeof ANIM_GROUP_DATAS
                        mov     esi, [esi + ANIM_GROUP.Group_Datas]
                        add     esi, ebx
                        mov     eax, [esi + ANIM_GROUP_DATAS.Anim_Ptr]
                        invoke  _Display_Animated_Sprite, eax
                        ret
Display_Anim_Sprite     endp

; ------------------------------------------------------
; Name: Animate_Sprite()
; Desc: Stop the current animation of a group
Animate_Sprite          proc    uses ebx esi hGroup:dword, Speed:dword, Rate:dword
                        mov     esi, hGroup
                        mov     ebx, [esi + ANIM_GROUP.Current_Group]
                        imul    ebx, sizeof ANIM_GROUP_DATAS
                        mov     esi, [esi + ANIM_GROUP.Group_Datas]
                        add     esi, ebx
                        mov     eax, [esi + ANIM_GROUP_DATAS.Anim_Ptr]
                        invoke  Run_Sprite_Animation, eax, Speed, Rate
                        ret
Animate_Sprite          endp

; ------------------------------------------------------
; Name: Destroy_Anim_Sprite()
; Desc: Release all datas of an animated sprite
Destroy_Anim_Sprite     proc    uses ebx esi edi hGroup:dword
                        mov     esi, hGroup
                        test    esi, esi
                        jz      @F
                        mov     eax, [esi + ANIM_GROUP.Nbr_Groups]
                        xor     ebx, ebx
                        .while  ebx < eax
                                push    eax
                                push    ebx
                                imul    ebx, sizeof ANIM_GROUP_DATAS
                                mov     edi, [esi + ANIM_GROUP.Group_Datas]
                                add     edi, ebx
                                mov     eax, [edi + ANIM_GROUP_DATAS.Anim_Ptr]
                                .if     eax != NULL
                                        mov     eax, [eax + ANIM_SPRITE.Sprites]
                                        mov     ebx, [edi + ANIM_GROUP_DATAS.Group_Ptr]
                                        invoke  Destroy_Sprites_Group, ebx, eax
                                        FREEMEM [edi + ANIM_GROUP_DATAS.Anim_Ptr]
                                .endif
                                mov     eax, [edi + ANIM_GROUP_DATAS.Datas_Ptr]
                                .if     eax != NULL
                                        FREEMEM eax
                                .endif
                                pop     ebx
                                pop     eax
                                inc     ebx
                        .endw
                        mov     eax, [esi + ANIM_GROUP.Group_Datas]
                        .if     eax != NULL
                                FREEMEM eax
                        .endif
                        FREEMEM hGroup
@@:                     ret
Destroy_Anim_Sprite     endp

; ------------------------------------------------------
; Name: Sprite_Move()
; Desc: Release all datas of an animated sprite
Sprite_Move             proc    Spr_Pos:real4, Speed:real4, FrameRate:real4, Min:dword, Max:dword
                        mov     ecx, Spr_Pos
                        fld     dword ptr [ecx]
                        fld     Speed
                        fmul    FrameRate
                        faddp   st(1), st(0)
                        fstp    dword ptr [ecx]
                        FCMP    dword ptr [ecx], INT2FLT(Min)
                        jae     Min_Sprite_Left
                        mov     dword ptr [ecx], CMEM(Min)
                        ret
Min_Sprite_Left:        FCMP    dword ptr [ecx], INT2FLT(Max)
                        jbe     Max_Sprite_Right
                        fld     INT2FLT(Max)
                        fstp    dword ptr [ecx]
Max_Sprite_Right:       ret
Sprite_Move             endp

; ------------------------------------------------------
; Name: Get_Current_Anim_Sprite()
; Desc: Set/Return the currently displayed sprite
Get_Current_Anim_Sprite proc    uses ebx esi hGroup:dword
                        mov     esi, hGroup
                        xor     eax, eax
                        .if     esi != NULL
                                mov     esi, hGroup
                                mov     ebx, [esi + ANIM_GROUP.Current_Group]
                                imul    ebx, sizeof ANIM_GROUP_DATAS
                                mov     esi, [esi + ANIM_GROUP.Group_Datas]
                                add     esi, ebx
                                mov     eax, [esi + ANIM_GROUP_DATAS.Anim_Ptr]
        
                                mov     esi, eax
                                mov     ecx, [esi + ANIM_SPRITE.Sprites]
                                mov     eax, FLT2INT([esi + ANIM_SPRITE.Position])
                                imul    eax, sizeof SPRITE_DATAS
                                add     ecx, eax
                                mov     [esi + ANIM_SPRITE.Datas], ecx
                                mov     eax, esi
                        .endif
                        ret
Get_Current_Anim_Sprite endp

endif
