Author Topic: Sprite Library Revisited  (Read 1340 times)

Offline TerryRitchie

  • Semper Fidelis
Sprite Library Revisited
« on: September 06, 2018, 02:44:41 AM »
[UPDATE:] This now contains the new sprite library work in progress.

Original post below

I'm currently in the process of updating my sprite library to take advantage of new features available in QB64 that were not present in 2012 when I wrote the library. I also want to enhance the collision detection routines (and finally get a fully working pixel perfect detection routine as well). One problem when working with sprite sheets is that every sprite needs to be the same size. As an example look below at the sprite sheet I created of a TR3B UFO a few days ago. Each sprite is 266x266 in size, however there is a lot of wasted space on many of them, especially the sprites in the middle. This not only takes up RAM but does not play well with collision detection as two dead spaces can trigger a collision when the images contained within did not actually touch. I purposely designed this sprite sheet to see if I could overcome these issues.

Below is some proof of concept code I wrote that detects the image within each sprite, cuts each individual sprite down to its minimum size, then saves an x,y offset value so the sprites can be successfully lined up later even though they are wildly different in size. This should allow rectangular collision detection to be much more accurate and the memory used by the sprites much less.

Keep in mind, the code is sloppy and will need to be cleaned up but I wanted to get other's opinion on this. Do you think I am on the right track or is there a better way of achieving this that I need to know about? Could my image detection routine be done simpler or in a better manner?

Code: [Select]
'*
'* constants used with SL_FLIP_SPRITE subroutine
'*

CONST SL_NOFLIP = 0
CONST SL_HORIZONTAL = 1
CONST SL_VERTICAL = 2
CONST SL_FLIPBOTH = 3

'*
'* constants used with SL_NEW_SPRITE_SHEET function
'*

CONST SL_SHEETTRANSPARENCY = -1 '       use sheet's transparency info (.PNG)
CONST SL_SETTRANSPARENCY = 0 '          manually set transparency
CONST SL_NOTRANSPARENCY = 1 '           don't use transparency with sheet

'*
'* constants used with SL_NEW_SPRITE function
'*

CONST SL_NOSAVE = 0 '                   sprite will not save background
CONST SL_SAVE = -1 '                    sprite will save background


'*
'* type declarations
'*

TYPE SL_SHEET ' *********************** sprite sheet database ***************************************
    image AS LONG '                     software sprite image
    mask AS LONG '                      software mask image
    spritewidth AS INTEGER '            width of sprite
    spriteheight AS INTEGER '           height of sprite
    collx1 AS INTEGER '                 collision box top left x
    colly1 AS INTEGER '                 collision box top left y
    collx2 AS INTEGER '                 collision box bottom right x
    colly2 AS INTEGER '                 collision box bottom right y
    transparency AS INTEGER '           -1 (TRUE) if sheet uses transparency
END TYPE

TYPE SL_SPRITE ' ********************** sprite database *********************************************
    inuse AS INTEGER '                  this array index in use
    sheet AS INTEGER '                  sheet sprite belongs to
    column AS INTEGER '                 the column on the sheet the sprite resides
    row AS INTEGER '                    the row on the sheet the sprite resides
    sprite AS LONG '                    hardware image
    image AS LONG '                     software image
    mask AS LONG '                      software mask image
    spritewidth AS INTEGER '            width of sprite
    spriteheight AS INTEGER '           height of sprite
    rsprite AS LONG '                   rotated sprite hardware image
    rimage AS LONG '                    rotated sprite software image
    rmask AS LONG '                     rotated sprite mask software image
    rspritewidth AS INTEGER '           rotated sprite image width
    rspriteheight AS INTEGER '          rotated sprite image height
    background AS LONG '                background image behind sprite
    xreal AS SINGLE '                   x location of sprite (center point)
    yreal AS SINGLE '                   y location of sprite (center point)
    xint AS INTEGER '                   x location of sprite on screen INT(xreal) (center point)
    yint AS INTEGER '                   y location of sprite on screen INT(yreal) (center point)
    xactual AS INTEGER '                x location of sprite on screen (upper left x)
    yactual AS INTEGER '                y location of sprite on screen (upper left y)
    restore AS INTEGER '                -1 (true) if sprite restores background
    collx1 AS INTEGER '                 collision box top left x
    colly1 AS INTEGER '                 collision box top left y
    collx2 AS INTEGER '                 collision box bottom right x
    colly2 AS INTEGER '                 collision box bottom right y
    flip AS INTEGER '                   flip horizontally, vertically, or both
    rotation AS SINGLE '                rotation angle of sprite (0 - 359.999)
    transparency AS INTEGER '           -1 (TRUE) if sprite uses transparency
END TYPE

TYPE SL_ROTATE '*********************** precalculated rotation table ********************************
    rwidth AS INTEGER '                 width of rotated sprite
    rheight AS INTEGER '                height of rotated sprite
    px0 AS INTEGER '                    rectangular rotation coordinates
    px1 AS INTEGER
    px2 AS INTEGER
    px3 AS INTEGER
    py0 AS INTEGER
    py1 AS INTEGER
    py2 AS INTEGER
    py3 AS INTEGER
END TYPE

'*
'* defined arrays
'*

REDIM SL_sheet(1, 1, 1) AS SL_SHEET '   master sprite sheet array
REDIM SL_sprite(1) AS SL_SPRITE '       master working sprite array
REDIM SL_rotate(1, 359) AS SL_ROTATE '  precalculated rotation values

'*
'* main code (for testing)
'*

kongsheet = SL_NEW_SPRITE_SHEET("dkong.png", 64, 64, SL_SETTRANSPARENCY, _RGB32(255, 0, 255))
mysprite% = SL_NEW_SPRITE(kongsheet, 1, 1, SL_NOSAVE)
mysprite2% = SL_NEW_SPRITE(kongsheet, 1, 1, SL_NOSAVE)

'SL_FLIP_SPRITE mysprite%, SL_NOFLIP

SCREEN _NEWIMAGE(640, 480, 32)
CLS

DO
    _LIMIT 120
    SL_PUT_SPRITE 128, 128, mysprite2%
    angle = angle + 1: IF angle = 360 THEN angle = 0
    SL_ROTATE_SPRITE mysprite%, angle
    CIRCLE (128, 128), 64, _RGB32(255, 255, 255)
    SL_PUT_SPRITE 128, 128, mysprite%
    _DISPLAY
LOOP




'    ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
SUB SL_ROTATE_SPRITE (handle AS INTEGER, degrees AS INTEGER) '                                                                                                                         SL_ROTATE_SPRITE
    'ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
    '³                    °°°±±±²²²ÛÛÛ COMMAND DESCRIPTION AND USAGE ÛÛÛ²²²±±±°°°                     ³                                °°°±±±²²²ÛÛÛ NOTES ÛÛÛ²²²±±±°°°                                ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                                                                                                ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                            °°°±±±²²²ÛÛÛ KNOWN ISSUES ÛÛÛ²²²±±±°°°                              ³                               °°°±±±²²²ÛÛÛ CREDITS ÛÛÛ²²²±±±°°°                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                                                                                                ³ Portions of this subroutine have code based off of Galleon's RotoZoom subroutine in the QB64  ³
    '³                                                                                                ³ documentation at http://qb64.net/wiki/index.php?title=MAPTRIANGLE (link no longer works)      ³
    '³                                                                                                ³                                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                                                                          °°°±±±²²²ÛÛÛ THEORY OF OPERATION ÛÛÛ²²²±±±°°°                                                                         ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                                                                                                                                                                                                ³
    '³                                                                                                                                                                                                ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                                                                                °°°±±±²²²ÛÛÛ HISTORY ÛÛÛ²²²±±±°°°                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ Date: 09/11/18 by Terry Ritchie                                                                                                                                                                ³
    '³     : Initial writing of code.                                                                                                                                                                 ³
    '³                                                                                                                                                                                                ³
    'ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

    ' declare global variables

    SHARED SL_sprite() AS SL_SPRITE '   master working sprite array
    SHARED SL_rotate() AS SL_ROTATE '   precalculated rotation table

    'declare local variables

    DIM px0 AS INTEGER '                precalculated polar coordinates
    DIM px1 AS INTEGER
    DIM px2 AS INTEGER
    DIM px3 AS INTEGER
    DIM py0 AS INTEGER
    DIM py1 AS INTEGER
    DIM py2 AS INTEGER
    DIM py3 AS INTEGER
    DIM sw AS INTEGER '                 sprite width
    DIM sh AS INTEGER '                 sprite height
    DIM rw AS INTEGER '                 precalculated rotated sprite width
    DIM rh AS INTEGER '                 precalculated rotated sprite height

    ' perform error checks

    IF NOT SL_VALID_SPRITE(handle) THEN '                                                               is this a valid sprite?
        SL_ERROR "SL_ROTATE_SPRITE", 500, "" '                                                          no, report error to programmer
    END IF

    ' correct degree angle if needed (needs to be 0 - 359)

    IF degrees = 360 THEN '                                                                             is it 360?
        degrees = 0 '                                                                                   yes, that's the same as 0
    ELSEIF degrees < 0 THEN '                                                                           is it less than 360?
        DO '                                                                                            yes
            degrees = dgrees + 360 '                                                                    increase by 360
        LOOP UNTIL degrees > 0 '                                                                        until it's in a valid range
    ELSEIF degrees > 360 THEN '                                                                         is it greater than 360?
        DO '                                                                                            yes
            degrees = degrees - 360 '                                                                   decrease by 360
        LOOP UNTIL degrees < 360 '                                                                      until it's in a valid range
    END IF
    SL_sprite(handle).rotation = degrees '                                                              remember degree of rotation
    IF degrees = 0 THEN EXIT SUB '                                                                      no rotation needed, leave
    SL_sprite(handle).rspritewidth = SL_rotate(SL_sprite(handle).sheet, degrees).rwidth '               get precalculated rotated sprite width
    SL_sprite(handle).rspriteheight = SL_rotate(SL_sprite(handle).sheet, degrees).rheight '             get precalculated rotated sprite height
    px0 = SL_rotate(SL_sprite(handle).sheet, degrees).px0 '                                             get precalculated polar coordinates
    px1 = SL_rotate(SL_sprite(handle).sheet, degrees).px1
    px2 = SL_rotate(SL_sprite(handle).sheet, degrees).px2
    px3 = SL_rotate(SL_sprite(handle).sheet, degrees).px3
    py0 = SL_rotate(SL_sprite(handle).sheet, degrees).py0
    py1 = SL_rotate(SL_sprite(handle).sheet, degrees).py1
    py2 = SL_rotate(SL_sprite(handle).sheet, degrees).py2
    py3 = SL_rotate(SL_sprite(handle).sheet, degrees).py3
    rw = SL_rotate(SL_sprite(handle).sheet, degrees).rwidth '                                           get precalculated rotated sprite width
    rh = SL_rotate(SL_sprite(handle).sheet, degrees).rheight '                                          get precalculated rotated sprite height
    sw = SL_sprite(handle).spritewidth - 1 '                                                            get sprite width
    sh = SL_sprite(handle).spriteheight - 1 '                                                           get sprite height
    IF SL_sprite(handle).rimage THEN _FREEIMAGE SL_sprite(handle).rimage '                              free rotated image if it already exists
    SL_sprite(handle).rimage = _NEWIMAGE(rw, rh, 32) '                                                  create rotated image
    _MAPTRIANGLE (0, 0)-(0, sh)-(sw, sh), SL_sprite(handle).image TO _
                 (px0, py0)-(px1, py1)-(px2, py2), SL_sprite(handle).rimage '                           map rotated sprite onto image
    _MAPTRIANGLE (0, 0)-(sw, 0)-(sw, sh), SL_sprite(handle).image TO _
                 (px0, py0)-(px3, py3)-(px2, py2), SL_sprite(handle).rimage
    IF SL_sprite(handle).rsprite THEN _FREEIMAGE SL_sprite(handle).rsprite '                            free rotated hardware sprite if it alreadt exists
    SL_sprite(handle).rsprite = _COPYIMAGE(SL_sprite(handle).rimage, 33) '                              create hardware sprite of rotated image
    IF SL_sprite(handle).transparency THEN '                                                            does this sprite have a mask?
        IF SL_sprite(handle).rmask THEN _FREEIMAGE SL_sprite(handle).rmask '                            yes, free mask image if it already exists
        SL_sprite(handle).rmask = _NEWIMAGE(rw, rh, 32) '                                               created rotated mask image
        _MAPTRIANGLE (0, 0)-(0, sh)-(sw, sh), SL_sprite(handle).mask TO _
                     (px0, py0)-(px1, py1)-(px2, py2), SL_sprite(handle).rmask '                        map rotated mask onto image
        _MAPTRIANGLE (0, 0)-(sw, 0)-(sw, sh), SL_sprite(handle).mask TO _
                     (px0, py0)-(px3, py3)-(px2, py2), SL_sprite(handle).rmask
    END IF

END SUB

'    ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
SUB SL_FLIP_SPRITE (handle AS INTEGER, flip AS INTEGER) '                                                                                                                                SL_FLIP_SPRITE
    'ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
    '³                    °°°±±±²²²ÛÛÛ COMMAND DESCRIPTION AND USAGE ÛÛÛ²²²±±±°°°                     ³                                °°°±±±²²²ÛÛÛ NOTES ÛÛÛ²²²±±±°°°                                ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ Sets a sprite's flipping behavior when drawn to the screen.                                    ³ - Four constants have been created for this subroutine:                                       ³
    '³                                                                                                ³   : SL_NOFLIP     (0) no flipping desired (or reset flip behavior)                            ³
    '³ SL_FLIP_SPRITE mysprite, SL_NOFLIP                                                             ³   : SL_HORIZONTAL (1) flip sprite horizontally                                                ³
    '³                                                                                                ³   : SL_VERTICAL   (2) flip sprite vertically                                                  ³
    '³ input : handle - the sprite to flip.                                                           ³   : SL_FLIPBOTH   (3) flip sprite both horizontally and vertically                            ³
    '³         flip   - the type of flip desired:                                                     ³ - Once a flip behavior has been set it will remain in effect until the behavior is changed.   ³
    '³                  : 0 - no flipping desired (or reset flip behavior)                            ³ - This subroutine will report an error on the following conditions:                           ³
    '³                  : 1 - flip sprite horizontally                                                ³   : An invalid sprite has been requested.                                                     ³
    '³                  : 2 - flip sprite vertically                                                  ³   : An invalid flip behavior.                                                                 ³
    '³                  : 3 - flip sprite both horizontally and vertically                            ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    '³ Sets  : SL_sprite(handle).flip = flip  (sets the flip behavior for later use)                  ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                            °°°±±±²²²ÛÛÛ KNOWN ISSUES ÛÛÛ²²²±±±°°°                              ³                               °°°±±±²²²ÛÛÛ CREDITS ÛÛÛ²²²±±±°°°                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ None                                                                                           ³ None                                                                                          ³
    '³                                                                                                ³                                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                                                                          °°°±±±²²²ÛÛÛ THEORY OF OPERATION ÛÛÛ²²²±±±°°°                                                                         ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ A sprite array setting will be made for use by other library routines.                                                                                                                         ³
    '³                                                                                                                                                                                                ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                                                                                °°°±±±²²²ÛÛÛ HISTORY ÛÛÛ²²²±±±°°°                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ Date: 09/11/18 by Terry Ritchie                                                                                                                                                                ³
    '³     : Initial writing of code.                                                                                                                                                                 ³
    '³                                                                                                                                                                                                ³
    'ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

    ' declare global variables

    SHARED SL_sprite() AS SL_SPRITE '   master working sprite array

    ' perform error checks

    IF NOT SL_VALID_SPRITE(handle) THEN '                                                               is this a valid sprite?
        SL_ERROR "SL_FLIP_SPRITE", 400, "" '                                                            no, report error to programmer
    END IF
    IF flip < 0 OR flip > 3 THEN '                                                                      valid flip behavior requested?
        SL_ERROR "SL_FLIP_SPRITE", 401, "" '                                                            no, report error to programmer
    END IF
    SL_sprite(handle).flip = flip '                                                                     set flipping behavior

END SUB

'    ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
SUB SL_PUT_SPRITE (x AS SINGLE, y AS SINGLE, handle AS INTEGER) '                                                                                                                         SL_PUT_SPRITE
    'ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
    '³                    °°°±±±²²²ÛÛÛ COMMAND DESCRIPTION AND USAGE ÛÛÛ²²²±±±°°°                     ³                                °°°±±±²²²ÛÛÛ NOTES ÛÛÛ²²²±±±°°°                                ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ Places a sprite on the current _DEST (normally screen) at the coordinates provided.            ³ - The x and y values supplied by the programmer can be sent in as single precision to allow   ³
    '³                                                                                                ³   for fine x and y increments. Internally the final x and y values needed for image placement ³
    '³ SL_PUT_SPRITE 100, 100, mysprite%                                                              ³   are converted to integer values.                                                            ³
    '³                                                                                                ³                                                                                               ³
    '³ Input : x      - x location (column) to place sprite.                                          ³                                                                                               ³
    '³         y      - y location (row) to place sprite.                                             ³                                                                                               ³
    '³         handle - the sprite to place on the screen.                                            ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    '³ Sets  :                                                                                        ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    '³ Errors: All reported errors will be in the 300 - 399 range for this function.                  ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                            °°°±±±²²²ÛÛÛ KNOWN ISSUES ÛÛÛ²²²±±±°°°                              ³                               °°°±±±²²²ÛÛÛ CREDITS ÛÛÛ²²²±±±°°°                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                                                                                                ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                                                                          °°°±±±²²²ÛÛÛ THEORY OF OPERATION ÛÛÛ²²²±±±°°°                                                                         ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                                                                                                                                                                                                ³
    '³                                                                                                                                                                                                ³
    '³                                                                                                                                                                                                ³
    '³                                                                                                                                                                                                ³
    '³                                                                                                                                                                                                ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                                                                                °°°±±±²²²ÛÛÛ HISTORY ÛÛÛ²²²±±±°°°                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ Date: 09/10/18 by Terry Ritchie                                                                                                                                                                ³
    '³     : Initial writing of code.                                                                                                                                                                 ³
    '³                                                                                                                                                                                                ³
    'ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

    ' declare global variables

    SHARED SL_sprite() AS SL_SPRITE '   master working sprite array

    ' declare local variables

    DIM xa AS INTEGER '                 actual x location of sprite on screen
    DIM ya AS INTEGER '                 actual y location of sprite on screen
    DIM sw AS INTEGER '                 width of sprite to be drawn
    DIM sh AS INTEGER '                 height of sprite to be drawn
    DIM sprite AS LONG '                the sprite to be drawn

    ' perform error checks

    IF NOT SL_VALID_SPRITE(handle) THEN '                                                               is this a valid sprite?
        SL_ERROR "SL_PUT_SPRITE", 300, "" '                                                             no, report error to programmer
    END IF

    'local variable setup

    SL_sprite(handle).xreal = x '                                                             (SINGLE)  save requested x center location
    SL_sprite(handle).yreal = y '                                                             (SINGLE)  save requested y center location
    SL_sprite(handle).xint = INT(x) '                                                        (INTEGER)  save screen x center location
    SL_sprite(handle).yint = INT(y) '                                                        (INTEGER)  save screen y center location
    IF SL_sprite(handle).rotation THEN '                                                                is sprite rotated?
        sprite = SL_sprite(handle).rsprite '                                                            yes, get rotated sprite image
        sw = SL_sprite(handle).rspritewidth '                                                           get rotated sprite width
        sh = SL_sprite(handle).rspriteheight '                                                          get rotated sprite height
    ELSE '                                                                                              sprite is not rotated
        sprite = SL_sprite(handle).sprite '                                                             get standard sprite image
        sw = SL_sprite(handle).spritewidth '                                                            get standard sprite width
        sh = SL_sprite(handle).spriteheight '                                                           get standard sprite height
    END IF
    xa = SL_sprite(handle).xint - sw \ 2 '                                                              calculate actual screen x location from center
    ya = SL_sprite(handle).yint - sh \ 2 '                                                              calculate actual screen y location from center
    SL_sprite(handle).xactual = xa '                                                         (INTEGER)  save actual screen x location
    SL_sprite(handle).yactual = ya '                                                         (INTEGER)  save actual screen y location

    ' place sprite on the current destination

    SELECT CASE SL_sprite(handle).flip '                                                                which flipping style is selected?
        CASE 0 '                                                                  (constant SL_NOFLIP)  normal, no flipping
            _PUTIMAGE (xa, ya), sprite '                                                                draw normal sprite
        CASE 1 '                                                              (constant SL_HORIZONTAL)  flip horizontally
            _PUTIMAGE (xa + sw - 1, ya)-(xa, ya + sh - 1), sprite '                                     draw horizontally flipped sprite
        CASE 2 '                                                                (constant SL_VERTICAL)  flip vertically
            _PUTIMAGE (xa, ya + sh - 1)-(xa + sw - 1, ya), sprite '                                     draw vertically flipped sprite
        CASE 3 '                                                                (constant SL_FLIPBOTH)  flip vertically and horizontally
            _PUTIMAGE (xa + sw - 1, ya + sh - 1)-(xa, ya), sprite '                                     draw horizontally and vertically flipped sprite
    END SELECT

END SUB

'    ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
FUNCTION SL_NEW_SPRITE (sheet AS INTEGER, column AS INTEGER, row AS INTEGER, restores AS INTEGER) '                                                                                       SL_NEW_SPRITE
    'ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
    '³                    °°°±±±²²²ÛÛÛ COMMAND DESCRIPTION AND USAGE ÛÛÛ²²²±±±°°°                     ³                                °°°±±±²²²ÛÛÛ NOTES ÛÛÛ²²²±±±°°°                                ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ Creates a pointer to a sprite contained in the SL_sprite database array.                       ³ - Two constants have been created for use with this function:                                 ³
    '³                                                                                                ³   : SL_NOSAVE ( 0) do not save the background image behind sprite between calls.              ³
    '³ mysprite% = SL_NEW_SPRITE(mysheet%, 2, 3, SL_NOSAVE)                                           ³   : SL_SAVE   (-1) save the background image behind sprite between calls.                     ³
    '³                                                                                                ³ - The function will report an error on the following conditions:                              ³
    '³ Input : sheet    - the sprite sheet the sprite resides on.                                     ³   : An invalid sprite sheet has been selected.                                                ³
    '³         column   - the column in the sprite sheet the sprite resides in.                       ³   : An invalid row or column value within a sprite sheet has been requested.                  ³
    '³         row      - the row in the sprite sheet the sprite resides in.                          ³   : An invalid background image restoration method has been requested.                        ³
    '³         restores - background saving behavior                                                  ³                                                                                               ³
    '³                    :  0 - don't restore background between calls.                              ³                                                                                               ³
    '³                    : -1 - restore the background between calls.                                ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    '³ Output: an integer value greater than zero (0) that acts as a handle pointing to the sheet and ³                                                                                               ³
    '³         location on the sheet where a sprite resides.                                          ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    '³ Sets  : SL_sprite(x).inuse        = -1       (TRUE, this sprite index is in use)               ³                                                                                               ³
    '³         SL_sprite(x).sheet        = sheet    (the sprite sheet where this sprite resides)      ³                                                                                               ³
    '³         SL_sprite(x).column       = column   (column within sprite sheet where sprite resides) ³                                                                                               ³
    '³         SL_sprite(x).row          = row      (row within sprite sheet where sprite resides)    ³                                                                                               ³
    '³         SL_sprite(x).restore      = restores (background save behavior of sprite)              ³                                                                                               ³
    '³         SL_sprite(x).spritewidth             (sprite width copied from sprite sheet)           ³                                                                                               ³
    '³         SL_sprite(x).spriteheight            (sprite height copied from sprite sheet)          ³                                                                                               ³
    '³         SL_sprite(x).collx1                  (upper left collision box x copied from sheet)    ³                                                                                               ³
    '³         SL_sprite(x).colly1                  (upper left collision box y copied from sheet)    ³                                                                                               ³
    '³         SL_sprite(x).collx2                  (lower right collision box x copied from sheet)   ³                                                                                               ³
    '³         SL_sprite(x).colly2                  (lower right collision box y copied from sheet)   ³                                                                                               ³
    '³         SL_sprite(x).sprite                  (hardware sprite image created from sheet image)  ³                                                                                               ³
    '³         SL_sprite(x).image                   (software sprite image copied from sheet image)   ³                                                                                               ³
    '³         SL_sprite(x).mask                    (software mask image copied from sheet image)     ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    '³         all other SL_sprite(x).* subvariables reset to their initial value                     ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    '³ Errors: All reported errors will be in the 200 - 299 range for this function.                  ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                            °°°±±±²²²ÛÛÛ KNOWN ISSUES ÛÛÛ²²²±±±°°°                              ³                               °°°±±±²²²ÛÛÛ CREDITS ÛÛÛ²²²±±±°°°                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ None                                                                                           ³ None                                                                                          ³
    '³                                                                                                ³                                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                                                                          °°°±±±²²²ÛÛÛ THEORY OF OPERATION ÛÛÛ²²²±±±°°°                                                                         ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ The sprite array database is scanned for a free index and once found that index number becomes the handle (pointer) the the sprite being created. The new index contains further pointers to   ³
    '³ where the requested sprite is located in the sprite sheet array database; the sheet number, the row, and the column the sprite resides in within the sheet. Information from the sprite sheet  ³
    '³ is then copied over (see Sets: above) from the sheet array to the sprite array. A hardware image of the sprite is then created from the software image contained in the sprite sheet.          ³
    '³                                                                                                                                                                                                ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                                                                                °°°±±±²²²ÛÛÛ HISTORY ÛÛÛ²²²±±±°°°                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ Date: 09/10/18 by Terry Ritchie                                                                                                                                                                ³
    '³     : Initial writing of code.                                                                                                                                                                 ³
    '³                                                                                                                                                                                                ³
    'ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

    ' declare global variables

    SHARED SL_sheet() AS SL_SHEET '     master sprite sheet array
    SHARED SL_sprite() AS SL_SPRITE '   master working sprite array

    ' declare local variables

    DIM handle AS INTEGER '             handle (pointer) number of new sprite

    ' perform error checks

    IF sheet > UBOUND(SL_sheet) OR SL_sheet(sheet, 0, 0).image <> -1 THEN '                             valid sprite sheet requested?
        SL_ERROR "SL_NEW_SPRITE", 200, "" '                                                             no, report error to programmer
    END IF
    IF column > UBOUND(SL_sheet, 2) OR row > UBOUND(SL_sheet, 3) OR row < 1 OR column < 1 THEN '        valid row and column requested?
        SL_ERROR "SL_NEW_SPRITE", 201, "" '                                                             no, report error to programmer
    END IF
    IF ABS(restores) > 1 THEN '                                                                         valid background restoration behavior requested?
        SL_ERROR "SL_NEW_SPRITE", 202, "" '                                                             no, report error to programmer
    END IF

    ' local variable setup

    handle = 0 '                                                                                        initialize handle value

    ' increase sprite array's size if needed

    DO '                                                                                                look for next available handle
        handle = handle + 1 '                                                                           increment to next handle value
    LOOP UNTIL (NOT SL_sprite(handle).inuse) OR handle = UBOUND(SL_sprite) '                            stop looking when valid handle found
    IF SL_sprite(handle).inuse THEN '                                                                   is the last array element in use?
        handle = handle + 1 '                                                                           yes, increment to next handle value
        REDIM _PRESERVE SL_sprite(handle) AS SL_SPRITE '                                                increase the size of the sprite array
    END IF

    ' populate sprite array

    SL_sprite(handle).inuse = -1 '                                                              (TRUE)  mark array index as in use
    SL_sprite(handle).sheet = sheet '                                                                   point to sheet where sprite resides              *
    SL_sprite(handle).column = column '                                                                 point to column on sheet where sprite located    * these still needed?
    SL_sprite(handle).row = row '                                                                       point to row on sheet where sprite located       *
    SL_sprite(handle).restore = restores '                             (constants SL_SAVE & SL_NOSAVE)  background restore behavior of sprite
    SL_sprite(handle).rsprite = 0 '                                                                     no rotated hardware image yet
    SL_sprite(handle).rimage = 0 '                                                                      no rotated software image yet
    SL_sprite(handle).rmask = 0 '                                                                       no rotated software mask image yet
    SL_sprite(handle).background = 0 '                                                                  no background image saved yet
    SL_sprite(handle).xreal = 0 '                                                                       reset x location of sprite (center x)
    SL_sprite(handle).yreal = 0 '                                                                       reset y location of sprite (center y)
    SL_sprite(handle).xint = 0 '                                                                        reset x location of sprite on screen INT(xreal) (center x)
    SL_sprite(handle).yint = 0 '                                                                        reset y location of sprite on screen INT(yreal) (center y)
    SL_sprite(handle).xactual = 0 '                                                                     reset x location of sprite on screen (upper left x)
    SL_sprite(handle).yactual = 0 '                                                                     reset y location of sprite on screen (upper left y)
    SL_sprite(handle).collx1 = SL_sheet(sheet, column, row).collx1 '                                    get sprite's collision box boundaries
    SL_sprite(handle).colly1 = SL_sheet(sheet, column, row).colly1
    SL_sprite(handle).collx2 = SL_sheet(sheet, column, row).collx2
    SL_sprite(handle).colly2 = SL_sheet(sheet, column, row).colly2
    SL_sprite(handle).spritewidth = SL_sheet(sheet, column, row).spritewidth '                          get width of sprite
    SL_sprite(handle).spriteheight = SL_sheet(sheet, column, row).spriteheight '                        get height of sprite
    SL_sprite(handle).sprite = _COPYIMAGE(SL_sheet(sheet, column, row).image, 33) '                     create hardware sprite image
    SL_sprite(handle).image = _COPYIMAGE(SL_sheet(sheet, column, row).image, 32) '                      copy software sprite image from sheet
    IF SL_sheet(sheet, column, row).transparency THEN '                                                 does this sprite use transparency?
        SL_sprite(handle).mask = _COPYIMAGE(SL_sheet(sheet, column, row).mask, 32) '                    yes, copy software sprite mask image from sheet
        SL_sprite(handle).transparency = -1 '                                                   (TRUE)  remember this sprite has a transparency layer
    ELSE '                                                                                              no transparency
        SL_sprite(handle).mask = 0 '                                                                    no mask will be brought in
        SL_sprite(handle).transparency = 0 '                                                   (FALSE)  remember this sprite has no transparency layer
    END IF
    SL_sprite(handle).flip = 0 '                                                                        no sprite flipping
    SL_sprite(handle).rotation = 0 '                                                                    no sprite rotation angle

    SL_NEW_SPRITE = handle

END FUNCTION

'    ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
FUNCTION SL_NEW_SPRITE_SHEET (filename AS STRING, spritewidth AS INTEGER, spriteheight AS INTEGER, transparency AS INTEGER, transcolor AS _UNSIGNED LONG) '                         SL_NEW_SPRITE_SHEET
    'ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
    '³                    °°°±±±²²²ÛÛÛ COMMAND DESCRIPTION AND USAGE ÛÛÛ²²²±±±°°°                     ³                                °°°±±±²²²ÛÛÛ NOTES ÛÛÛ²²²±±±°°°                                ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ Loads a sprite sheet's sprites into memory and stores them in the sprite sheet array. Three    ³ - The software image mask will only be created if the sprite sheet contains a transparency    ³
    '³ sprite images are created for each sprite; a hardware image for displaying, a software image   ³   layer (alpha channel) either built-in or user defined.                                      ³
    '³ for manipulating, and a software mask image for pixel collision detection.                     ³ - Three constants have been created for use with this function:                               ³
    '³                                                                                                ³   : SL_SHEETTRANSPARENCY (-1) to have the function use built-in sprite sheet's alpha layer.   ³
    '³ mysheet% = SL_NEW_SPRITE_SHEET("sprites.png", 64, 96, SL_SETTRANSPARENCY, _RGB32(255, 0, 255)) ³   : SL_SETTRANSPARENCY   ( 0) to have the function use the programmer supplied alpha value.   ³
    '³                                                                                                ³   : SL_NOTRANSPARENCY    ( 1) to have the function ignore alpha channel completely.           ³
    '³ Input : filename     - the name of the sprite sheet image file to load in.                     ³ - The function will report an error on the following conditions:                              ³
    '³         spritewidth  - the width of every sprite contained on the sprite sheet.                ³   : A filename is supplied that does not exist.                                               ³
    '³         spriteheight - the height of every sprite contained on the sprite sheet.               ³   : A sprite width or height that is less than one (1).                                       ³
    '³         transparency - the type of transparency to apply to the sprite sheet and sprites:      ³   : An invalid transparency type value. Valid types are from negative one (-1) to one (1).    ³
    '³                         : -1 - use the sprite sheet's built-in alpha channel (PNG files).      ³   : The sprite sheet does not contain at least one row and column of sprites.                 ³
    '³                         :  0 - use the programmer assigned alpha channel value.                ³ - The programmer supplied alpha channel value will be ignored if transparency is set to a     ³
    '³                         :  1 - this sheet does not have any transparency included.             ³   value of negative one (-1).                                                                 ³
    '³         transcolor   - programmer assigned alpha channel value for the sprite sheet.           ³ - When transparency is set to one (1) the programmer supplied transcolor is used to identify  ³
    '³                                                                                                ³   the background color used in the sprite sheet. This is still needed to find the collision   ³
    '³ Output: An integer value greater than zero (0) that acts as a handle pointing to the sheet     ³   box boundaries for collision detection.                                                     ³
    '³         that contains the sprites.                                                             ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    '³ Sets  : SL_sheet(x, 0, 0).image        = -1      (TRUE, this sheet index is in use)            ³                                                                                               ³
    '³         SL_sheet(x, 0, 0).spritewidth  = columns (the number indexes in the 2nd dimension of   ³                                                                                               ³
    '³                                                   sheet array)                                 ³                                                                                               ³
    '³         SL_sheet(x, 0, 0).spriteheight = rows    (the number of indexes in the 3rd dimension   ³                                                                                               ³
    '³                                                   of sheet array)                              ³                                                                                               ³
    '³         SL_sheet(x, c, r).image                  software image copied from sprite sheet       ³                                                                                               ³
    '³         SL_sheet(x, c, r).spritewidth            width of sprite in pixels                     ³                                                                                               ³
    '³         SL_sheet(x, c, r).spriteheight           height of sprite in pixels                    ³                                                                                               ³
    '³         SL_sheet(x, c, r).mask                   sprite mask (image black, background white)   ³                                                                                               ³
    '³         SL_sheet(x, c, r).collx1                 collision box upper left x                    ³                                                                                               ³
    '³         SL_sheet(x, c, r).colly1                 collision box upper left y                    ³                                                                                               ³
    '³         SL_sheet(x, c, r).collx2                 collision box lower right x                   ³                                                                                               ³
    '³         SL_sheet(x, c, r).colly2                 collision box lower right y                   ³                                                                                               ³
    '³           * c = column  r = row                                                                ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    '³ Errors: All reported errors will be in the 100 - 199 range for this function.                  ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                            °°°±±±²²²ÛÛÛ KNOWN ISSUES ÛÛÛ²²²±±±°°°                              ³                               °°°±±±²²²ÛÛÛ CREDITS ÛÛÛ²²²±±±°°°                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ Need to incorporate Steve's PNG transparency layer identifier instead of mine.                 ³ Portions of this subroutine have code based off of Galleon's RotoZoom subroutine in the QB64  ³
    '³                                                                                                ³ documentation at http://qb64.net/wiki/index.php?title=MAPTRIANGLE (link no longer works)      ³
    '³                                                                                                ³                                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                                                                          °°°±±±²²²ÛÛÛ THEORY OF OPERATION ÛÛÛ²²²±±±°°°                                                                         ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ Once the sprite sheet is loaded it is scanned for an alpha channel if the programmer requests to use the sheet's transparency layer. The width and height of the sheet are retrieved and then  ³
    '³ divided by the sprite dimensions provided by the programmer to determine how many columns and rows of sprites exist. A three dimensional sheet array is created where the first dimension is   ³
    '³ the sheet number (handle/pointer), the second and thirs dimensions are then related to the sprite column and row locations on the sheet. Each sprite is copied from the sprite sheet into the  ³
    '³ sprite sheet array. Each sprite is scanned to determine the absolute image size within the sprite (background/transparency areas are ignored) to determine the smallest possible collision box ³
    '³ needed for collision detection. Finally, the sprite is scanned again to create an image mask for pixel perfect detection.                                                                      ³
    '³                                                                                                                                                                                                ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                                                                                °°°±±±²²²ÛÛÛ HISTORY ÛÛÛ²²²±±±°°°                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ Date: 09/09/18 by Terry Ritchie                                                                                                                                                                ³
    '³     : Initial writing of code.                                                                                                                                                                 ³
    '³                                                                                                                                                                                                ³
    'ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

    ' declare global variables

    SHARED SL_sheet() AS SL_SHEET '    master sprite sheet array
    SHARED SL_rotate() AS SL_ROTATE '  precalculated rotation table

    ' declare local variables

    DIM handle AS INTEGER '             handle (pointer) number of new sprite sheet
    DIM x AS INTEGER '                  generic counter to cycle through sheet sprite columns
    DIM y AS INTEGER '                  generic counter to cycle through sheet sprite rows
    DIM x1 AS INTEGER '                 generic counter to cycle though sprite for collision boundaries and mask creation
    DIM y1 AS INTEGER '                 generic ocunter to cycle though sprite for collision boundaries and mask creation
    DIM osource AS LONG '               original source image before this function was called
    DIM odest AS LONG '                 original destination image before this function was called
    DIM pixel AS _UNSIGNED LONG '       pixel color at each coordinate in sprite sheet
    DIM alpha AS _UNSIGNED LONG '       alpha level of current pixel
    DIM top AS INTEGER '                upper boundary of sprite image
    DIM bottom AS INTEGER '             lower boundary of sprite image
    DIM left AS INTEGER '               left boundary of sprite image
    DIM right AS INTEGER '              right boundary of sprite image
    DIM sheetimage AS LONG '            sprite sheet image
    DIM sheetwidth AS INTEGER '         width of sprite sheet in pixels
    DIM sheetheight AS INTEGER '        height of sprite sheet in pixels
    DIM rows AS INTEGER '               number of sprite rows contained on sheet
    DIM columns AS INTEGER '            number of sprite columns contained on sheet
    DIM clearcolor AS _UNSIGNED LONG '  transcolor passed in will be modified
    DIM tempsprite AS LONG '            temporary sprite for _CLEARCOLOR detection

    DIM px(3) AS SINGLE '               polar x coordinates of maptriangle
    DIM py(3) AS SINGLE '               polar y coordinates of maptriangle
    DIM sinr AS SINGLE
    DIM cosr AS SINGLE
    DIM bx1 AS INTEGER
    DIM bx2 AS INTEGER
    DIM by1 AS INTEGER
    DIM by2 AS INTEGER

    ' perform error checks

    IF NOT _FILEEXISTS(filename) THEN '                                                                 does the sprite sheet exist?
        SL_ERROR "SL_NEW_SPRITE_SHEET", 100, filename '                                                 no, report error to programmer
    END IF
    IF ABS(transparency) > 1 THEN '                                                                     valid transparency setting?
        SL_ERROR "SL_NEW_SPRITE_SHEET", 101, "" '                                                       no, report error to programmer
    END IF
    IF spritewidth < 1 OR spriteheight < 1 THEN '                                                       valid sprite width/height supplied?
        SL_ERROR "SL_NEW_SPRITE_SHEET", 102, "" '                                                       no, report error to programmer
    END IF
    IF transparency = -1 AND UCASE$(RIGHT$(filename, 4)) <> ".PNG" THEN '                               wrong file type for transparency?
        SL_ERROR "SL_NEW_SPRITE_SHEET", 103, UCASE$(RIGHT$(filename, 4)) '                              yes, report error to programmer
    END IF

    ' local variable setup

    sheetimage = _LOADIMAGE(filename, 32) '                                                             load sprite sheet file
    sheetwidth = _WIDTH(sheetimage) '                                                                   get width of sheet
    sheetheight = _HEIGHT(sheetimage) '                                                                 get height of sheet
    columns = sheetwidth \ spritewidth '                                                                get number of whole columns of sprites
    rows = sheetheight \ spriteheight '                                                                 get number of whole rows of sprites
    IF columns < 1 OR rows < 1 THEN '                                                                   at least one sprite column and row on sheet?
        SL_ERROR "SL_NEW_SPRITE_SHEET", 104, "" '                                                       no, report error to programmer
    END IF
    osource = _SOURCE '                                                                                 remember current source image
    odest = _DEST '                                                                                     remember current destination image
    handle = 0 '                                                                                        initialize handle value
    clearcolor = transcolor '                                                                           get background/transparent color passed in

    ' increase sheet array's 1st dimension if needed to create a new sprite sheet

    DO '                                                                                                look for the next available handle
        handle = handle + 1 '                                                                           increment the handle value
    LOOP UNTIL (NOT SL_sheet(handle, 0, 0).image) OR handle = UBOUND(SL_sheet) '                       stop looking when valid handle value found
    IF SL_sheet(handle, 0, 0).image = -1 THEN '                                                        is the last array element in use?
        handle = handle + 1 '                                                                           yes, increment the handle value
        REDIM _PRESERVE SL_sheet(handle, UBOUND(SL_sheet, 2), UBOUND(SL_sheet, 3)) AS SL_SHEET '        create new sheet in sprite array
        REDIM _PRESERVE SL_rotate(handle, UBOUND(SL_rotate, 2)) AS SL_ROTATE
    END IF

    ' increase sheet array's 2nd and 3rd dimensions if needed to match number of rows and columns

    IF columns > UBOUND(SL_sheet, 2) THEN '                                                             more columns in this sheet than others?
        REDIM _PRESERVE SL_sheet(handle, columns, UBOUND(SL_sheet, 3)) AS SL_SHEET '                    yes, increase the array's 2nd dimension to match
    END IF
    IF rows > UBOUND(SL_sheet, 3) THEN '                                                                more rows in this sheet than others?
        REDIM _PRESERVE SL_sheet(handle, UBOUND(SL_sheet, 2), rows) AS SL_SHEET '                       yes, increase the array's 3rd dimension to match
    END IF

    ' the variables in SL_sheet(x, 0, 0) will serve a dual purpose
    ' SL_sheet(x, 0, 0).image will contain either -1 (true) or 0 (false) to indicate the first dimension of the array is in use.
    ' SL_sheet(x, 0, 0).spritewidth will contain the number of columns contained in the sheet (the array's 2nd dimension)
    ' SL_sheet(x, 0, 0).spriteheight will contain the number of rows contained in the sheet (the array's 3rd dimension)

    SL_sheet(handle, 0, 0).image = -1 '                                                         (TRUE)  mark as in use
    SL_sheet(handle, 0, 0).spritewidth = columns '                                                      remember number of columns in sheet
    SL_sheet(handle, 0, 0).spriteheight = rows '                                                        remember number of rows in sheet

    ' identify transparency of sprite sheet if requested

    IF transparency = -1 THEN '                                        (constant SL_SHEETTRANSPARENCY)  sheet have alpha channel?
        x = 0 '                                                                                         yes, start at upper left x of sheet
        y = 0 '                                                                                         start at upper left y of sheet
        alpha = 255 '                                                                                   assume no alpha channel
        _SOURCE sheetimage '                                                                            set sprite sheet image as source image
        DO '                                                                                            start looping through the sheet's pixels
            pixel = POINT(x, y) '                                                                       get the pixel's color attributes
            alpha = _ALPHA32(pixel) '                                                                   get the alpha level (0 - 255)
            IF alpha = 0 THEN EXIT DO '                                                                 if it is transparent then leave the loop
            x = x + 1 '                                                                                 move right one pixel
            IF x > sheetwidth THEN '                                                                    have we gone off the sheet?
                x = 0 '                                                                                 yes, reset back to the left beginning
                y = y + 1 '                                                                             move down one pixel
            END IF
        LOOP UNTIL y > sheetheight '                                                                    don't stop until the entire sheet has been checked
        IF alpha = 0 THEN '                                                                             did we find a transparent pixel?
            tempsprite = _NEWIMAGE(1, 1, 32) '                                                          yes, create a temporary image         * why did I have to do
            _CLEARCOLOR pixel, tempsprite '                                                             set pixel found as transparent        * this hack to get
            clearcolor = _CLEARCOLOR(tempsprite) '                                                      get the transparent color from image  * clearcolor to come out
            _FREEIMAGE tempsprite '                                                                     temporary image no longer needed      * to the right value?
        ELSE '                                                                                          no transparency found within sheet
            transparency = 1 '                                                                          set sheet to having no alpha channel
        END IF
    ELSEIF transparency = 0 THEN '                                       (constant SL_SETTRANSPARENCY)  manually set alpha channel?
        _CLEARCOLOR clearcolor, sheetimage '                                                            yes, set color as transparent
        clearcolor = _CLEARCOLOR(sheetimage) '                                                          get the transparent color ************* again, why this hack?
    END IF

    ' load sprites from sheet and place into sprite array

    FOR x = 1 TO columns '                                                                              cycle through the sheet's columns
        FOR y = 1 TO rows '                                                                             cycle through the sheet's rows
            SL_sheet(handle, x, y).image = _NEWIMAGE(spritewidth, spriteheight, 32) '                   create software sprite image
            IF transparency < 1 THEN '                                                                  should a mask be created?
                SL_sheet(handle, x, y).mask = _NEWIMAGE(spritewidth, spriteheight, 32) '                yes, create software sprite mask image
                _DEST SL_sheet(handle, x, y).mask '                                                     write to the mask image
            ELSE
                SL_sheet(handle, x, y).transparency = 0 '                                      (FALSE)  set sprite as having no transparency
            END IF
            _PUTIMAGE , sheetimage, SL_sheet(handle, x, y).image,_
                 ((x - 1) * spritewidth, (y - 1) * spriteheight)-_
                 ((x - 1) * spritewidth + spritewidth - 1, (y - 1) * spriteheight + spriteheight - 1) ' copy sprite from sheet and place in sprite image

            ' precalculate collision boundaries and update sprite mask if needed

            _SOURCE SL_sheet(handle, x, y).image '                                                      work from the software sprite image
            top = spriteheight '                                                                        set initial collision boundary markers
            left = spritewidth
            bottom = 0
            right = 0
            FOR x1 = 0 TO spritewidth - 1 '                                                             cycle through the width of sprite
                FOR y1 = 0 TO spriteheight - 1 '                                                        cycle through the height of sprite
                    IF POINT(x1, y1) <> clearcolor THEN '                                               is this pixel a transparent/background color?
                        IF x1 < left THEN left = x1 '                                                   no, save position if left-most pixel
                        IF y1 < top THEN top = y1 '                                                     save position if top-most pixel
                        IF x1 > right THEN right = x1 '                                                 save position if right-most pixel
                        IF y1 > bottom THEN bottom = y1 '                                               save position if bbottom-most pixel
                    END IF
                    IF transparency < 1 THEN '                                                          update software sprite mask?
                        IF POINT(x1, y1) = clearcolor THEN '                                            yes, is this pixel a transparent/background color?
                            PSET (x1, y1), _RGB32(255, 255, 255) '                                      yes, set as white on the mask image
                        END IF
                    END IF
                NEXT y1
            NEXT x1
            SL_sheet(handle, x, y).collx1 = left '                                                      collision box top left x
            SL_sheet(handle, x, y).colly1 = top '                                                       collision box top left y
            SL_sheet(handle, x, y).collx2 = right '                                                     collision box bottom right x
            SL_sheet(handle, x, y).colly2 = bottom '                                                    collision box bottom right y
            SL_sheet(handle, x, y).spritewidth = spritewidth '                                          remember sprite width
            SL_sheet(handle, x, y).spriteheight = spriteheight '                                        remember sprite height
        NEXT y
    NEXT x
    _FREEIMAGE sheetimage '                                                                             sprite sheet image no longer needed

    ' create precalculated rotation table for the sprite sheet (0 to 359 degrees)

    FOR x = 1 TO 359
        px(0) = -spritewidth / 2 '                                                                      upper left  x polar coordinate of sprite
        py(0) = -spriteheight / 2 '                                                                     upper left  y polar coordinate of sprite
        px(1) = px(0) '                                                                                 lower left  x polar coordinate of sprite
        py(1) = spriteheight / 2 '                                                                      lower left  y polar coordinate of sprite
        px(2) = spritewidth / 2 '                                                                       lower right x polar coordinate of sprite
        py(2) = py(1) '                                                                                 lower right y polar coordinate of sprite
        px(3) = px(2) '                                                                                 upper right x polar coordinate of sprite
        py(3) = py(0) '                                                                                 upper right y polar coordinate of sprite
        sinr = SIN(-x / 57.2957795131) '                                                                calculate the sin of rotation
        cosr = COS(-x / 57.2957795131) '                                                                calculate the cosine of rotation
        bx1 = 0 '                                                                                       upper left x boundary of sprite
        by1 = 0 '                                                                                       upper left y boundary of sprite
        bx2 = 0 '                                                                                       lower right x boundary of sprite
        by2 = 0 '                                                                                       lower right y boundary of sprite
        FOR y = 0 TO 3 '                                                                                cycle through all four polar coordinates
            x2 = (px(y) * cosr + sinr * py(y)) '                                                        compute new polar coordinate location
            y2 = (py(y) * cosr - px(y) * sinr) '                                                        compute new polar coordinate location
            px(y) = x2 '                                                                                save the new polar coordinate
            py(y) = y2 '                                                                                save the new polar coordinate
            IF px(y) < bx1 THEN bx1 = px(y) '                                                           save lowest  x value seen \  NOTE: use for
            IF px(y) > bx2 THEN bx2 = px(y) '                                                           save highest x value seen  \ background image         <--------------------- LOOK
            IF py(y) < by1 THEN by1 = py(y) '                                                           save lowest  y value seen  / rectangle coordinates
            IF py(y) > by2 THEN by2 = py(y) '                                                           save highest y value seen /
        NEXT y
        SL_rotate(handle, x).rwidth = bx2 - bx1 + 1 '                                                   calculate width of rotated sprite
        SL_rotate(handle, x).rheight = by2 - by1 + 1 '                                                  calculate height of rotated sprite
        SL_rotate(handle, x).px0 = px(0) + ((bx2 - bx1 + 1) / 2) '                                      calculate triangular coordinates
        SL_rotate(handle, x).px1 = px(1) + ((bx2 - bx1 + 1) / 2)
        SL_rotate(handle, x).px2 = px(2) + ((bx2 - bx1 + 1) / 2)
        SL_rotate(handle, x).px3 = px(3) + ((bx2 - bx1 + 1) / 2)
        SL_rotate(handle, x).py0 = py(0) + ((by2 - by1 + 1) / 2)
        SL_rotate(handle, x).py1 = py(1) + ((by2 - by1 + 1) / 2)
        SL_rotate(handle, x).py2 = py(2) + ((by2 - by1 + 1) / 2)
        SL_rotate(handle, x).py3 = py(3) + ((by2 - by1 + 1) / 2)
    NEXT x
    _SOURCE osource '                                                                                   return source to current
    _DEST odest '                                                                                       return destination to current
    SL_NEW_SPRITE_SHEET = handle '                                                                      return the handle number pointing to this sheet

END FUNCTION

'    ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
SUB SL_ERROR (routine AS STRING, errno AS INTEGER, info AS STRING) '                                                                                                                           SL_ERROR
    'ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
    '³                    °°°±±±²²²ÛÛÛ COMMAND DESCRIPTION AND USAGE ÛÛÛ²²²±±±°°°                     ³                                °°°±±±²²²ÛÛÛ NOTES ÛÛÛ²²²±±±°°°                                ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ Reports a Sprite Library error to the programmer. Used internally only.                        ³ - A copy of this library has been provided that has all of the error checks removed. Once     ³
    '³                                                                                                ³   you are sure no errors exist you would then include that library for your final             ³
    '³ Input : routine - the function/subroutine the error occurred in                                ³   compilation. The error checking routines do take some processing away from your code so     ³
    '³                                                                                                ³   performance will improve by removing them.                                                  ³
    '³         errno   - the error number associated with the error                                   ³                                                                                               ³
    '³                   100 - sprite does not exist                                                  ³                                                                                               ³
    '³                   101 - sprite is not in use                                                   ³                                                                                               ³
    '³                   102 - sprite can't be hidden                                                 ³                                                                                               ³
    '³                   103 - invalid zoom value                                                     ³                                                                                               ³
    '³                   104 - invalid rotation angle                                                 ³                                                                                               ³
    '³                   105 - invalid flipping behavior                                              ³                                                                                               ³
    '³                   106 - sheet does not exist                                                   ³                                                                                               ³
    '³                   107 - sheet is not in use                                                    ³                                                                                               ³
    '³                   108 - invalid transparency setting                                           ³                                                                                               ³
    '³                   109 - invalid sprite width/height                                            ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    '³         info    - any information that need to be conveyed with the error                      ³                                                                                               ³
    '³                   such as a file name                                                          ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                            °°°±±±²²²ÛÛÛ KNOWN ISSUES ÛÛÛ²²²±±±°°°                              ³                               °°°±±±²²²ÛÛÛ CREDITS ÛÛÛ²²²±±±°°°                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ none                                                                                           ³ This routine was created in response to a request from QB64 member pitt                       ³
    '³                                                                                                ³ http://www.qb64.net/forum/index.php?topic=7281.0 (link no longer works)                       ³
    '³                                                                                                ³                                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                                                                          °°°±±±²²²ÛÛÛ THEORY OF OPERATION ÛÛÛ²²²±±±°°°                                                                         ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ A pure text screen is shown, the font reset to default, and _AUTODISPLAY enabled. This forces the code out of any graphics screen it may currently be in. The error is then displayed to the   ³
    '³ programmer based on the values passed in. The program is then forced to terminate.                                                                                                             ³
    '³                                                                                                                                                                                                ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                                                                                °°°±±±²²²ÛÛÛ HISTORY ÛÛÛ²²²±±±°°°                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ Date: 09/09/18 by Terry Ritchie                                                                                                                                                                ³
    '³     : Initial writing of code.                                                                                                                                                                 ³
    '³                                                                                                                                                                                                ³
    'ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

    SCREEN 0, 0, 0, 0 '                                                             go to a pure text screen
    _FONT 16 '                                                                      set the standard screen 0 font
    IF _FULLSCREEN THEN _FULLSCREEN _OFF '                                          turn off full screen if on
    _AUTODISPLAY '                                                                  auto update the display
    CLS '                                                                           clear the screen
    COLOR 10, 0
    PRINT "                   **************************************" '             print error header
    PRINT "                   ** Sprite Library Error Encountered **"
    PRINT "                   **************************************"
    PRINT
    COLOR 15, 0
    PRINT " "; routine;
    COLOR 7, 0
    PRINT " has reported error";
    COLOR 30, 0
    PRINT STR$(errno)
    COLOR 7, 0
    PRINT
    SELECT CASE errno '                                                             which error number is being reported?
        CASE 100
            PRINT "- "; CHR$(34); info; CHR$(34); " sprite sheet does not exist"
            PRINT "- check path or spelling"
        CASE 101
            PRINT "- invalid transparency setting supplied - valid settings are"
            PRINT "- : -1 (constant SL_SHEETTRANSPARENCY)"
            PRINT "- :  0 (constant SL_SETTRANSPARENCY)"
            PRINT "- :  1 (constant SL_NOTRANSPARENCY)"
        CASE 102
            PRINT "- sprite width and height must be greater than zero"
        CASE 103
            PRINT "- selecting to use a sheet's transparency only works with .PNG files"
            PRINT "- the function was passed a "; info; " file."
        CASE 104
            PRINT "- there must be at least one column and one row of sprites on sheet"
        CASE 200
            PRINT "- the specified sprite sheet is not in use or does not exist"
        CASE 201
            PRINT "- invalid row or column selected for specified sprite sheet"
        CASE 202
            PRINT "- background restore behavior for a sprite can only be 0 (FALSE) or -1 (TRUE)"
        CASE 300, 400, 500
            PRINT "- the requested sprite does not exist"
    END SELECT
    COLOR 12, 0
    PRINT
    PRINT " See sprite library doumentation for further explanation."
    COLOR 7, 0
    DO: LOOP UNTIL INKEY$ = "" '                                                    clear the keyboard buffer
    END '                                                                           end the program

END SUB


'    ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
FUNCTION SL_VALID_SPRITE (handle AS INTEGER) '                                                                                                                                         SL_VALID_SPRITE
    'ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
    '³                    °°°±±±²²²ÛÛÛ COMMAND DESCRIPTION AND USAGE ÛÛÛ²²²±±±°°°                     ³                                °°°±±±²²²ÛÛÛ NOTES ÛÛÛ²²²±±±°°°                                ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ Reports on the validity of a sprite handle (pointer).                                          ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    '³ valid% = SL_VALID_SPRITE(mysprite%)                                                            ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    '³ Input : handle - the handle (pointer) of the sprite being examined.                            ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    '³ Output: an integer value of zero (0) (FALSE) or negative 1 (-1) (TRUE)                         ³                                                                                               ³
    '³                                                                                                ³                                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                            °°°±±±²²²ÛÛÛ KNOWN ISSUES ÛÛÛ²²²±±±°°°                              ³                               °°°±±±²²²ÛÛÛ CREDITS ÛÛÛ²²²±±±°°°                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ None                                                                                           ³ None                                                                                          ³
    '³                                                                                                ³                                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                                                                          °°°±±±²²²ÛÛÛ THEORY OF OPERATION ÛÛÛ²²²±±±°°°                                                                         ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ The sprite database is examined at the index the handle points to. If .inuse is -1 (TRUE) then the sprite handle is valid.                                                                     ³
    '³                                                                                                                                                                                                ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³                                                                                °°°±±±²²²ÛÛÛ HISTORY ÛÛÛ²²²±±±°°°                                                                               ³
    'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
    '³ Date: 09/11/18 by Terry Ritchie                                                                                                                                                                ³
    '³     : Initial writing of code.                                                                                                                                                                 ³
    '³                                                                                                                                                                                                ³
    'ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

    ' declare global variables

    SHARED SL_sprite() AS SL_SPRITE '   master working sprite array

    IF handle > UBOUND(SL_SPRITE) OR (NOT SL_sprite(handle).inuse) THEN '                                is this a valid sprite handle?
        SL_VALID_SPRITE = 0 '                                                                   (FALSE)  no, return 0
    ELSE '                                                                                               yes, it is valid
        SL_VALID_SPRITE = -1 '                                                                   (TRUE)  return -1
    END IF

END FUNCTION



'    ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

'ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
'³                    °°°±±±²²²ÛÛÛ COMMAND DESCRIPTION AND USAGE ÛÛÛ²²²±±±°°°                     ³                                °°°±±±²²²ÛÛÛ NOTES ÛÛÛ²²²±±±°°°                                ³
'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
'³                                                                                                ³                                                                                               ³
'³                                                                                                ³                                                                                               ³
'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
'³                            °°°±±±²²²ÛÛÛ KNOWN ISSUES ÛÛÛ²²²±±±°°°                              ³                               °°°±±±²²²ÛÛÛ CREDITS ÛÛÛ²²²±±±°°°                               ³
'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
'³                                                                                                ³                                                                                               ³
'³                                                                                                ³                                                                                               ³
'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
'³                                                                          °°°±±±²²²ÛÛÛ THEORY OF OPERATION ÛÛÛ²²²±±±°°°                                                                         ³
'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
'³                                                                                                                                                                                                ³
'³                                                                                                                                                                                                ³
'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
'³                                                                                °°°±±±²²²ÛÛÛ HISTORY ÛÛÛ²²²±±±°°°                                                                               ³
'ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
'³                                                                                                                                                                                                ³
'³                                                                                                                                                                                                ³
'ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
« Last Edit: September 12, 2018, 02:03:22 AM by TerryRitchie »
g=c800:5 fixes everything

Offline SMcNeill

  • QB64 Developer
Re: Sprite Library Revisited
« Reply #1 on: September 06, 2018, 03:01:03 AM »
Why not use a data file to go along with each Sprite sheet? 

MySprite.jpg would be the name of the sheet, MySprite.NFO could be the name of a simple file containing pixel coordinates for each Sprite in the sheet.  Seems the easiest way to me for variable size sprites.

Offline TerryRitchie

  • Semper Fidelis
Re: Sprite Library Revisited
« Reply #2 on: September 06, 2018, 03:06:51 AM »
Yeah, in the game industry that's called a sprite atlas and in an earlier version of my code I actually wrote the coordinates out to a file as you suggested. However, I want the library to contain as much of the inner workings as possible with the programmer shielded from all the details.

Standard practice is to have all sprites the same size and ordered on the sheet. Having the programmer supply an atlas would be a pain in the ascii for him/her, especially since they are using a sprite library to avoid learning how to actually manipulate sprites.

Make sense? I get what your saying though.

One other thing too, manually trying to center up different sized sprites is way harder than it sounds.
« Last Edit: September 06, 2018, 03:09:05 AM by TerryRitchie »
g=c800:5 fixes everything

Offline SMcNeill

  • QB64 Developer
Re: Sprite Library Revisited
« Reply #3 on: September 06, 2018, 09:11:18 AM »
Trick would be to have a program which calculates the Sprite dimensions for you, and then places the individual sprites onto a sheet and preserve the coordinates. 

Steps I'd imagine would be:

Sprite editor to make a Sprite (or MS Paint/whatever tool the user wants).  A lot of these editors set us a default size for sheet creation (a lot of the RPGmaker sprites are 64x64 or 128x128).

A QB64 program to clear color background (black or white, as I've seen both used as a standard background), and then a simple detection routine to minimize size as much as possible.  (A little 32x32 fireball would have the blank edges stripped from it.)  Resave via SaveImage to the new size in BMP or PNG format.

An INFORM program where the user can load sprites and drag them into place on a Sprite sheet, to manually minimize overall size, or to set a specific order/layout....  OR...   A program which automatically assembles the sheet for us, from the edited files.

Either way, preserving the Atlas would be an automated task and wouldn't be anything the end-user would ever have to worry about, with the added feature of saving load/runtime inside the game, as all the calculations have already been done previously, and not need to happen on demand, in game.

*********************
*********************

One personal thing I'd like to see added to your library is the ability to SetSpritePath. 

"TN,P1,N1,P1,TE,P1,E1......RA"

A simple string script which tells the Sprite to try and Turn North (load north facing spite, if you have a directional set), Pause 1 turn, North 1, Pause 1 Turn, Turn East, Pause 1 Turn, East 1.... RepeatAll.

PRESTO!  Simple patrol path instantly added!

Add simple options for NP (no path; just wander), CH (chase hero, using pathfinding as discussed previously), and PB (play brick; don't move).

It'd be the start of a simple scripting system which the user could add/expand on later with future options (such as "LC123" so if the user Left Clicks on the Sprite, the program performs event 123 which might be a Popup saying, "Hi Hero!  How's your Mama today?").

It'd take the Sprite Library from just being a library to draw/display sprites, to becoming a much more active Game Engine.   ;)

Re: Sprite Library Revisited
« Reply #4 on: September 06, 2018, 11:14:31 AM »
I was wondering how you might handle rotation (with collision) but I see you are doing a sprite for every occasion! Wow, that's allot of sprites.

Petr had some discussion with pixel perfect collision here: https://www.qb64.org/forum/index.php?topic=261.0
As I recall he was entangling images pretty well (without collision), but again wow! that's allot of calculation.


« Last Edit: September 06, 2018, 11:15:43 AM by bplus »
B = B + ...
QB64 x 64 v1.2 2018 0228/86 git b30af92
QB64 v1.2 20180228/86 git 6fde149
QB64 v1.2 [dev build]_d84bb00

Offline Petr

  • I am instructed.
Re: Sprite Library Revisited
« Reply #5 on: September 06, 2018, 11:16:11 AM »
Hi,

What you are looking for is the collision detection between N-angles. No matter how big the picture is, the subtlety of its borders is essential. This means that a finer entry that contains more points will also be very demanding on the processor. Making the mask out of the picture is not a problem, then the outer points (image border)  (each N point according to the detector sensitivity setting). The distance between these points will be given by the character's step. For example: If the characters move in the game with a 20 pixel pitch, it is enough to evaluate the collision detection every twentieth point of the circuit.


That's what I did somehow in writing collision detection. Then I realized another thing. To speed up the operation, it is possible to clarify where there is a collision. If the body is to be detected only to the left and right, it is not necessary for the field to contain the coordinates of the bottom and the ceiling. It is possible that MLINE could well serve to detect a collision between 2 points. In essence, it only sends the points through which the line passes. Just rewrite it - not use PSET, but this output send as array outputs...

Next option is ELLIPSE collision detection.

This source create image borders for collision detection:

rewrite on line 25 image name to your image name first
Code: [Select]

TYPE Borders '                                          this is "struct" for array Borders. This array conatains borders between image and background. It is my first vesion for collision detection.
    X AS INTEGER '     X coordinate for point on border between two different colors (that is why you need one colored background for writing borders)
    Y AS INTEGER '     Y coordinate for point on border between two different color
    Clr AS LONG '      color on border, it is read, but then is not used, you need it not, only for drawing colored circuit in original colors
    maxX AS INTEGER '  use index 0 only and return maximal picture X size (real width there are calculated valid pixels only)
    minX AS INTEGER '  use index 0 only and return real image start on oxis X  (for use as quadratic collision detect with MinY and MaxY)
    MaxY AS INTEGER '  use index 0 only and return real height
    MinY AS INTEGER '  use index 0 only and return real start on axis Y
END TYPE

TYPE Brd '            this is next level - "compressed" array type Borders - contains not all color borders, but just StartX, EndX (for every Y) and StartY and EndY (for every X) (just circuit)
    Xmin AS INTEGER ' if picture height is 100, there is 100 records for Xmin. Its X on left, its image start X
    Xmax AS INTEGER ' if picture height is 100, there is 100 records for Xmax. Its X from right, last image point
    Y AS INTEGER '    Y it is "help" value, need for draw content but not need for detect function
    Ymin AS INTEGER ' if picture WIDTH is 100, there is 100 records for Ymin. Its Y from above, its image start Y
    Ymax AS INTEGER ' if picture WIDTH is 100, there is 100 records for Ymax. Its Y from bottom, its image end Y
    X AS INTEGER '    the same as Y
END TYPE
REDIM S(0) AS Brd

my& = _NEWIMAGE(800, 600, 32)
SCREEN my&
PRINT "1] program load picture to memory"
imag& = _LOADIMAGE("z.jpg", 32) '                       load example picture (downloaded from google)
PRINT "2] function Filter& set image background (R input, G input, B input, R output, G output, B output)"
image& = filter&(imag&, 150, 150, 150, 255, 255, 255) ' this is exemplary function. Downgrade background colors in set range to set RGB32 value: First 3* 255 are RGB inputs, next 3*230 are RGB outputs
'                                                       This muss be set correctly for fine work! For use write program arrays outputs to text file, bacause its then always valid for this one picture
'                                                       Array T contains all colors borders information, array S contains just external borders information
SLEEP
SCREEN image&: SLEEP '                                  'It depends a lot on the correct setting of this function if the source image does not contain clear boundaries for proper border detection.
SCREEN my&
REDIM T(0) AS Borders
_SOURCE image&
_DEST 0
PRINT "3] SUB Border create array named 'T' that contains X, Y about all colors borders and show it:"
Border T(), image&, _RGB32(255, 255, 255) '            function based on ONE color in background. Write image borders to array, writed for 256 colors and or truecolor, but tested for truecolor only
'                                                      parameter in function is background color in source image
SLEEP
FOR u = LBOUND(T) TO UBOUND(T)
    PSET (T(u).X, 50 + T(u).Y + 80)
NEXT u
SLEEP
PRINT "4] SUB Compress reduce the input array T by writing only outer points to array S"
compress T(), S(), 25 'if in game - character - step  size is bigger than 25, muss this step be enought for collis detect...
SLEEP


PRINT "5] This is compressed (array S) output (PSET) on left, LINE for < > method is right" '    program show you detected and to array writed borders
FOR T = LBOUND(s) TO UBOUND(s)

    IF S(T).Xmin AND S(T).Xmax THEN LINE (300 + S(T).Xmin, 370 + S(T).Y)-(300 + S(T).Xmax, 370 + S(T).Y)
    IF S(T).Ymin AND S(T).Ymax THEN LINE (300 + S(T).X, 370 + S(T).Ymax)-(300 + S(T).X, 370 + S(T).Ymin)


    IF S(T).Xmin THEN PSET (S(T).Xmin, S(T).Y + 370)
    IF S(T).Xmax THEN PSET (S(T).Xmax, S(T).Y + 370)
    IF S(T).Ymin THEN PSET (S(T).X, S(T).Ymin + 370)
    IF S(T).Ymax THEN PSET (S(T).X, S(T).Ymax + 370)


NEXT
PRINT "Array size in records (S):"; UBOUND(s); " and size array T contains all color borders information:"; UBOUND(T)




SUB Border (array() AS Borders, Source AS LONG, Background_Color AS _UNSIGNED LONG) 'own border detection sub
    _DEST Source&
    minX = _WIDTH(Source&)
    minY = _HEIGHT(Source&)
    DIM M AS _MEM '32 bytova promenna                                                                         '  MEM values are 32 BYTES long,
    M = _MEMIMAGE(Source&) '                                                                                     M is pointer to memory with image content
    SELECT CASE _PIXELSIZE(Source&) '                                                                           select if image is 256 colored, 8 bit (1 byte for pixel) or 32 bit colored (4 byte for pixel)
        CASE 0: END 'not writed for text mode yet                                                                0 is for text mode, then this screen contains cells 8 * 16 pixels, one cell has value one BYTE
        CASE 1: '    for 256 colors image
            REDIM Value AS _UNSIGNED _BYTE, OldValue AS _UNSIGNED _BYTE '                                       because i need read value directly from memory, muss set correct type first - basicaly are
            FOR y = 1 TO _HEIGHT(Source&) - 1 '                                                                 all varibles set to SINGLE (4 byte long)
                FOR x = 1 TO _WIDTH(Source&) - 1
                    OldValue = Value
                    _MEMGET M, M.OFFSET + IN&(x, y), Value '                                                    is the same as POINT but very  more faster!  _MEMGET is as POINT for read color value,
                    '                                                                                           _MEMPUT is the same as POINT for writing color value. In use with mem muss be correct type and offset set.
                    IF Value = Background_Color AND OldValue <> Background_Color OR Value <> Background_Color AND OldValue = Background_Color THEN
                        GOSUB rozsah '                                                                          subprogram continuously compares values for MinX, MaxX, MinY and MaxY
                        i = i + 1
                        REDIM _PRESERVE array(i) AS Borders '                                                   this command increases the field value without losing the field contents // to i size
                        array(i).X = x
                        array(i).Y = y
                        array(i).Clr = Value
                    END IF
                    IF y > 0 THEN _MEMGET M, M.OFFSET + IN&(x, y - 1), Value '                                  condition for preveting MEMORY REGION OUT OF RANGE and read current color value on X, Y
                    IF Value = Background_Color AND OldValue <> Background_Color OR Value <> Background_Color AND OldValue = Background_Color THEN
                        i = i + 1 'This condition: If current color is the same as background and previous color is different than background or above, write this coordinates to array. Easy trick.
                        REDIM _PRESERVE array(i) AS Borders '                                                   this command increases the field value without losing the field contents // to i size
                        array(i).X = x
                        array(i).Y = y '                                                            program line 117 control colors in row, program line 126 control colors in column
                        array(i).Clr = Value
                    END IF
            NEXT x, y

        CASE 4: 'for 32 bit screen                                                                ' this block is the same for truecolor (4 byte blocks)
            REDIM Value4 AS LONG, OldValue4 AS LONG '                                               program lines 142 and 153 control and writing borders to array
            FOR y = 0 TO _HEIGHT(Source&) - 4
                FOR x = 0 TO _WIDTH(Source&) - 4
                    OldValue4& = Value4&
                    _MEMGET M, M.OFFSET + IN&(x, y), Value4&
                    IF Value4& = Background_Color AND OldValue4& <> Background_Color OR Value4& <> Background_Color AND OldValue4& = Background_Color THEN
                        GOSUB rozsah
                        i = i + 1
                        REDIM _PRESERVE array(i) AS Borders
                        array(i).X = x
                        array(i).Y = y
                        array(i).Clr = Value4&

                    END IF

                    IF y > 0 THEN _MEMGET M, M.OFFSET + IN&(x, y - 1), Value4&

                    IF Value4& = Background_Color AND OldValue4& <> Background_Color OR Value4& <> Background_Color AND OldValue4& = Background_Color THEN
                        i = i + 1
                        REDIM _PRESERVE array(i) AS Borders
                        array(i).X = x
                        array(i).Y = y
                        array(i).Clr = Value4&

                    END IF
                    nic:
            NEXT x, y
    END SELECT
    _DEST 0
    array(0).minX = minX '                                                                           to zero position in array are writed image width and height for
    array(0).maxX = maxX '                                                                           possibillity quadric collision detection
    array(0).MinY = minY
    array(0).MaxY = maxY

    EXIT SUB
    rozsah:
    IF minX > x AND x > 0 THEN minX = x
    IF minY > y AND y > 0 THEN minY = y
    IF maxX < x THEN maxX = x
    IF maxY < y THEN maxY = y
    RETURN
END SUB

FUNCTION IN& (x AS INTEGER, y AS INTEGER)
    IN& = _PIXELSIZE(_SOURCE) * ((_WIDTH * y) + x) '                                                  function return offset for MEM functions. Copyed from Steve McNeill
END FUNCTION

FUNCTION filter& (image AS LONG, Ri, Gi, Bi, Ro, Go, Bo) '                                            Function has the task of making the background of the specified color and reducing the
    DIM f AS _MEM '                                                                                   background color unevenness to make the best possible image border detection
    f = _MEMIMAGE(image&)
    '   filter& = _MEMNEW(_WIDTH(image&) * _HEIGHT(image&) * _PIXELSIZE(image&)) '                    Here is one bug for developers. Uncomment and give C++ compilation error. I know why, so just for info. :-D With love :-D
    filter& = _NEWIMAGE(_WIDTH(image&), _HEIGHT(image&), 32)
    DIM GG AS _MEM
    GG = _MEMIMAGE(filter&)
    _SOURCE image&
    _DEST image& '                                                                                    next ask for developers. Comment DEST on this line and start it. You give HALF picture. Why?
    SELECT CASE _PIXELSIZE(image&)
        CASE 4
            DIM clr AS LONG
            choice& = _RGBA32(Ro, Go, Bo, 255)
            FOR y = 0 TO _HEIGHT(image&) - 4
                FOR x = 0 TO _WIDTH(image&) - 4
                    _MEMGET f, f.OFFSET + IN&(x, y), clr&

                    R = _RED32(clr&)
                    G = _GREEN32(clr&)
                    B = _BLUE32(clr&)
                    A = _ALPHA32(clr&)
                    IF R > Ri AND G > Gi AND B > Bi THEN _MEMPUT GG, GG.OFFSET + IN&(x, y), choice& ELSE _MEMPUT GG, GG.OFFSET + IN&(x, y), clr&
    NEXT x, y: END SELECT
    _MEMFREE f
    _MEMFREE GG
    _FREEIMAGE image&
END FUNCTION

SUB compress (array() AS Borders, outArray() AS Brd, stp) 'z pole vybere jen nejvetsi a nejmensi X pro JEDNO Y, bude to mit vystup X1, X2, Y, pres line to udela obrys

    FOR C = LBOUND(array) TO UBOUND(array)
        FOR D = LBOUND(array) TO UBOUND(array)
            IF array(D).Y = ly THEN
                IF array(D).X > 0 AND array(D).Y > 0 THEN
                    IF max < array(D).X THEN max = array(D).X
                    IF min > array(D).X THEN min = array(D).X
                END IF
            END IF
        NEXT D
        IF min < 65535 AND min > 0 THEN
            REDIM _PRESERVE outArray(i) AS Brd

            outArray(i).Y = ly
            outArray(i).Xmin = min
            outArray(i).Xmax = max
            i = i + 1
        END IF
        min = 65535: max = 0: ly = ly + 1
    NEXT C

    min = 65535: max = 0: ly = 0
    FOR C = LBOUND(array) TO UBOUND(array)
        FOR D = LBOUND(array) TO UBOUND(array)
            IF array(D).X = ly THEN
                IF array(D).X > 0 AND array(D).Y > 0 THEN
                    IF max < array(D).Y THEN max = array(D).Y
                    IF min > array(D).Y THEN min = array(D).Y
                END IF
            END IF
        NEXT D
        IF min < 65535 AND min > 0 THEN
            REDIM _PRESERVE outArray(i) AS Brd
            '    LINE (ly, min)-(ly, max)
            outArray(i).X = ly
            outArray(i).Ymin = min
            outArray(i).Ymax = max
            i = i + 1
        END IF
        min = 65535: max = 0: ly = ly + 1
    NEXT C
    IF stp > 1 THEN 'performs a reduction of records according to a specified number of STP steps. This is useful for speeding up the program if the basic
        '            move of the pictures is not 1 but more, so  then it is necessary for all the pictures to move in the same step and it is not necessary to write all
        '            the collision coordinates.

        'create copy array outArray AS BRD
        REDIM Arrcopy(0) AS Brd
        ii = 0
        FOR copy = LBOUND(outarray) TO UBOUND(outarray) STEP stp
            ii = ii + 1
            REDIM _PRESERVE Arrcopy(ii) AS Brd
            Arrcopy(ii) = outArray(copy)
        NEXT copy
        REDIM outArray(0) AS Brd

        FOR CopyBack = LBOUND(arrcopy) TO UBOUND(arrcopy)
            REDIM _PRESERVE outArray(CopyBack) AS Brd
            outArray(CopyBack) = Arrcopy(CopyBack)
        NEXT
        ERASE Arrcopy
    END IF
END SUB


this code NOT DETECT collision, but do borders for collision detection. (i have it not writed yet). I you have this points in memory, then is possible using (after upgrading sub outputs to array) this MLINE sub:

Code: [Select]

SCREEN 13

MLine 0, 320, 200, 0
SLEEP 2
LINE (0, 320)-(200, 0), 14
SLEEP







SUB MLine (x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER)
    '------------------------------------------------------
    'in line solution for X:
    IF x1 < x2 AND y1 = y2 THEN
        FOR K = x1 TO x2
            PSET (x1 + (K - x1), y1)
        NEXT K
        EXIT SUB
    END IF

    IF x1 > x2 AND y1 = y2 THEN
        FOR K = x1 TO x2 STEP -1
            PSET (x1 + (K - x1), y1)
        NEXT K
        EXIT SUB
    END IF
    '-------------------------------------------------------
    'in line solution for Y:
    IF x1 = x2 AND y1 < y2 THEN
        FOR K = y1 TO y2
            PSET (x1, y1 + (K - y1))
        NEXT K
        EXIT SUB
    END IF

    IF x1 = x2 AND y1 > y2 THEN
        FOR K = y1 TO y2 STEP -1
            PSET (x1, y1 + (K - y1))
        NEXT K
        EXIT SUB
    END IF


    '--------------------------------------------------- radkova reseni otestovana a v poradku
    D = SQR((x1 - x2) ^ 2 + (y1 - y2) ^ 2)

    ky = ABS(y1 - y2) / D
    kx = ABS(x1 - x2) / D

    IF x1 > x2 THEN kx = kx * -1
    IF y1 > y2 THEN ky = ky * -1

    'solution for X1 <> X2, Y1 <> Y2
    outX = x1: outY = y1

    FOR K = 0 TO D
        PSET (outX, outY)
        outX = outX + kx
        outY = outY + ky
    NEXT K
END SUB

or much easyer. If is picture simetric, use this collision detection function:

Code: [Select]
'elipse collision
SCREEN 13
LET CenterX = 160: LET CenterY = 100: LET A = 40: LET B = 20

FOR F = 0 TO _PI(2) STEP .01
    PSET (CenterX + SIN(F) * A, CenterY + COS(F) * B)
NEXT F

DO
    WHILE _MOUSEINPUT: WEND
    IF Ellipse(_MOUSEX, _MOUSEY, CenterX, CenterY, A, B) THEN LOCATE 1, 1: PRINT "collision" ELSE LOCATE 1, 1: PRINT SPACE$(9)
LOOP UNTIL _KEYHIT = 27
END


FUNCTION Ellipse (X&, Y&, CX&, CY&, A&, B&)
    XY## = (((X& - CX&) ^ 2) / A& ^ 2) + (((Y& - CY&) ^ 2) / B& ^ 2)
    IF XY## <= 1.1 THEN Ellipse = 1 ELSE Ellipse = 0
END FUNCTION



Offline TerryRitchie

  • Semper Fidelis
Re: Sprite Library Revisited
« Reply #6 on: September 06, 2018, 12:46:34 PM »
I was wondering how you might handle rotation (with collision) but I see you are doing a sprite for every occasion! Wow, that's allot of sprites.

Yes, that's called "cheater" 3D. I designed the ship using a 3D designer program then output an image for each rotation of 10 degrees. This was how Donkey Kong Country was done in 1994 to get the illusion of having 3D characters while using a 2D engine. It's painstaking work to create sheets like this but simple and effective when wanting the illusion of 3D. Most overhead games of years ago (Starcraft) were done using this method as well.

Doom used a similar method to achieve 3D in a 2D environment. I've attached the Archvile sprite sheet to show how much work was put into just one character. The two John's used clay models and a 3D scanner to create their sprites.
g=c800:5 fixes everything

Offline TerryRitchie

  • Semper Fidelis
Re: Sprite Library Revisited
« Reply #7 on: September 06, 2018, 12:51:26 PM »
It'd take the Sprite Library from just being a library to draw/display sprites, to becoming a much more active Game Engine.   ;)

All good points! This is why I wanted to bounce this off the forum before I got too heavy into this. So many good ideas. I can already envision the code in my head for many of the ideas you presented here.
g=c800:5 fixes everything

Offline TerryRitchie

  • Semper Fidelis
Re: Sprite Library Revisited
« Reply #8 on: September 06, 2018, 01:21:00 PM »
Next option is ELLIPSE collision detection.

I like your ellipse detection. My library currently uses circle detection. It never occurred to me to use a ratio between two radii with Pythagoras to achieve elliptical detection. Nice!
g=c800:5 fixes everything

Offline Petr

  • I am instructed.
Re: Sprite Library Revisited
« Reply #9 on: September 06, 2018, 01:24:04 PM »
Hi again, for  your ask - how do image detection better - for some images is need polygon image scan. It is for cases, if is square image not possible to use as on this:

Offline TerryRitchie

  • Semper Fidelis
Re: Sprite Library Revisited
« Reply #10 on: September 06, 2018, 01:28:58 PM »
I played around with the possibility of using a polygon technique years ago. However, the processing needed to do this slowed everything to a crawl when put into a loop. If memory serves I wasn't able to get over 10-15 FPS because of all the time needed to check each part of the polygon.

I tried making the routine a bit smarter by detecting where the actual collision was taking place in order not to have to check the entire polygon structure of both sprites. But I could never get it to work quite right.

That elliptical collision has me thinking. A few well placed ellipses of different sizes throughout a sprite could be a close representation of a polygon detection method. I could easily write a program that allows the programmer to place the different sized ellipses onto a sprite, covering most if not all of the image. Writing code to automagically do that would take some work though.
« Last Edit: September 06, 2018, 01:32:46 PM by TerryRitchie »
g=c800:5 fixes everything

Offline Petr

  • I am instructed.
Re: Sprite Library Revisited
« Reply #11 on: September 06, 2018, 01:46:36 PM »
I would deal with this particular case by manually defining the points for each frame. This is then loaded as a _NEWIMAGE set for each. OR, if it's about saving memory, well, it can be done by dragging the picture the way it is and no longer splitting into single frames, but using pre-defined polygons. But I think, that more practical is if  picture will be cut into single frames because then the disk image can be released from memory and the all chopped parts  are together  maximal  the same size as the source image. And then the putimage will be used and speed will not be a problem.

i try do something  in this way.

Offline TerryRitchie

  • Semper Fidelis
Re: Sprite Library Revisited
« Reply #12 on: September 07, 2018, 12:54:49 PM »
I optimized the code in the original post above. Image detection is much simpler and the code now generates a sprite mask for use in pixel-perfect detection.

It also generates a hardware image for viewing now.
« Last Edit: September 07, 2018, 01:15:57 PM by TerryRitchie »
g=c800:5 fixes everything

Offline TerryRitchie

  • Semper Fidelis
Re: Sprite Library Revisited
« Reply #13 on: September 11, 2018, 03:18:35 AM »
Petr - your N-Angles code is intriguing. Sorry I didn't comment on it earlier. I have it saved and will be looking it over to see how I may utilize it.

I'm happy to report that I'm chugging along on the new version of the sprite library (I updated the code in the original post above if anyone is interested in seeing it). I want to make sure it's as compatible as possible with Linux and MacOS as well. I can handle the Linux end of things, but when it comes time I'll need you MacOS users to help me out. My last Powerbook died on me a few years back.

I'm taking a whole new approach to the library that will make it easier to incorporate the ideas that Steve and others have put forth. What I have now is already magnitudes faster than the old library. QB64 coding is all coming back to me like boomerang! The new QB64 version, with its new features, and new IDE make this so much more enjoyable. Really great job to all the contributors to it over the years.
g=c800:5 fixes everything

Offline TerryRitchie

  • Semper Fidelis
Re: Sprite Library Revisited
« Reply #14 on: September 12, 2018, 02:12:50 AM »
Super exciting and productive day today! My goal was to incorporate sprite rotation by the end of the day. I finished sprite rotation and flipping!

The old sprite library was very slow to perform sprite rotation due to the fact that sprites were not preloaded. This meant that every time a sprite needed to be rotated, the image was grabbed from the sheet, calculations were done to get the triangular coordinates for _MAPTRIANGLE and then the mapped image would be placed to the screen. This happened each and every time you rotated a sprite. Keeping a sprite rotating 360 degrees took a LOT of horsepower.

What I did today was create code that precalculates all of the variables needed for sprite rotation when the sheet is initially loaded and places the values in a rotational lookup table. Now keeping a sprite rotating 360 degrees is just a simple matter of plugging the known values into _MAPTRIANGLE. Boom! Instant rotation. And wow is it fast, because of the precalculated figures and the use of hardware images.

I updated the code in the original post above along with adding the sprite sheet (dkong.png)  for the test code contained in it.

Oh, one more thing ... _MAPTRIANGLE is a beast! LOL, sorry, that command was not my friend today.
« Last Edit: September 12, 2018, 02:15:32 AM by TerryRitchie »
g=c800:5 fixes everything