/*==========================================================================
 *
 *  Copyright (c) 1995 - 1997  Microsoft Corporation.  All Rights Reserved.
 *  Copyright (C) 1994-1995 ATI Technologies Inc. All Rights Reserved.
 *
 *  File:       sprite.c
 *  Content:    sprite manipulation functions
 *
 ***************************************************************************/
#include "foxbear.h"

/*
 * CreateSprite
 */
HSPRITE *CreateSprite (
    USHORT    bitmapCount,
    LONG      x,
    LONG      y,
    USHORT    width,
    USHORT    height,
    USHORT    xmax,
    USHORT    ymax,
    SHORT     as,
    BOOL      active )
{
    HSPRITE *hSprite;
    USHORT   i;

    hSprite = MemAlloc( sizeof (HSPRITE) );
    if( hSprite == NULL )
    {
        ErrorMessage( "hSprite in CreateSprite" );
    }

    hSprite->hSBM = CMemAlloc( bitmapCount, sizeof (HSPRITE_BM) );
    if( hSprite->hSBM == NULL )
    {
        MemFree( hSprite );
        ErrorMessage( "hSprite->hSBM in CreateSprite" );
    }
    
    hSprite->active        = active;
    hSprite->bitmapCount   = bitmapCount;
    hSprite->x             = x;
    hSprite->y             = y;
    hSprite->width         = width;
    hSprite->height        = height;
    hSprite->xv            = 0;
    hSprite->yv            = 0;
    hSprite->xa            = 0;
    hSprite->ya            = 0;
    hSprite->xmax          = xmax;
    hSprite->ymax          = ymax;
    hSprite->absSwitch     = as;
    hSprite->relSwitch     = 0;
    hSprite->switchType    = HOR;
    hSprite->switchForward = TRUE;
    hSprite->switchDone    = FALSE;

    for( i = 0; i < bitmapCount; ++i )
    {
        hSprite->hSBM[i].hBM = NULL;
    }

    return hSprite;

} /* CreateSprite */

/*
 * BitBltSprite
 */
BOOL BitBltSprite (
    HSPRITE   *hSprite,
    GFX_HBM    hBM,
    ACTION     action,
    DIRECTION  direction,
    SHORT      x,
    SHORT      y,
    USHORT     w,
    USHORT     h )
{
    USHORT count;

    if( hSprite == NULL )
    {
        ErrorMessage( "hSprite in BitBltSprite" );
    }

    if( hBM == NULL )
    {
        ErrorMessage( "hBM in BitBltSprite" );
    }

    if( (x >= hSprite->width) || (y >= hSprite->height) )
    {
        ErrorMessage( "x or y in BitBltSprite" );
    }

    count = 0;
    while( hSprite->hSBM[count].hBM != NULL )
    {
        count++;
        if( count >= hSprite->bitmapCount )
        {
            ErrorMessage( "Bitmap overflow in BitBltSprite" );
        }
    }

    hSprite->hSBM[count].hBM       = hBM;
    hSprite->hSBM[count].action    = action;
    hSprite->hSBM[count].direction = direction;
    hSprite->hSBM[count].x         = x;
    hSprite->hSBM[count].y         = y;
    hSprite->hSBM[count].width     = w; 
    hSprite->hSBM[count].height    = h; 

    return TRUE;

} /* BitBltSprite */

/*
 * SetSpriteAction
 */
BOOL SetSpriteAction ( HSPRITE *hSprite, ACTION action, DIRECTION direction )
{
    USHORT c;

    c = 0;

    if( direction == SAME )
    {
        direction = hSprite->currentDirection;
    }

    while( (hSprite->hSBM[c].action != action) || (hSprite->hSBM[c].direction != direction) )
    {
        ++c;
    }

    hSprite->currentAction    = action;
    hSprite->currentDirection = direction;
    hSprite->currentBitmap    = c;
    hSprite->relSwitch        = 0;

    return TRUE;

} /* SetSpriteAction */

/*
 * ChangeSpriteDirection
 */
BOOL ChangeSpriteDirection( HSPRITE *hSprite )
{
    DIRECTION direction;

    if( hSprite->currentDirection == RIGHT )
    {
        direction = LEFT;
    }
    else
    {
        direction = RIGHT;
    }

    SetSpriteAction( hSprite, hSprite->currentAction, direction );

    return TRUE;

} /* ChangeSpriteDirection */

/*
 * GetSpriteAction
 */
ACTION GetSpriteAction( HSPRITE *hSprite )
{
    return hSprite->currentAction;

} /* GetSpriteAction */


/*
 * GetSpriteDirection
 */
DIRECTION GetSpriteDirection( HSPRITE *hSprite )         
{
    return hSprite->currentDirection;

} /* GetSpriteDirection */

/*
 * SetSpriteActive
 */
BOOL SetSpriteActive( HSPRITE *hSprite, BOOL active )
{
    hSprite->active = active;
    
    if( active == FALSE )
    {
        hSprite->xv = 0;
        hSprite->yv = 0;
        hSprite->xa = 0;
        hSprite->ya = 0;
    }

    return TRUE;

} /* SetSpriteActive */

/*
 * GetSpriteActive
 */
BOOL GetSpriteActive( HSPRITE *hSprite )
{
    return hSprite->active;

} /* GetSpriteActive */

/*
 * SetSpriteVelX
 */
BOOL SetSpriteVelX( HSPRITE *hSprite, LONG xv, POSITION position )
{
    if( hSprite->active == FALSE )
    {
        return FALSE;
    }

    if( position == P_ABSOLUTE )
    {
        hSprite->xv = xv;
    }
    else if( position == P_RELATIVE )
    {
        hSprite->xv += xv;
    }

    return TRUE;

} /* SetSpriteVelX */

/*
 * GetSpriteVelX
 */
LONG GetSpriteVelX( HSPRITE *hSprite )
{
    return hSprite->xv;

} /* GetSpriteVelX */

/*
 * SetSpriteVelY
 */
BOOL SetSpriteVelY( HSPRITE *hSprite, LONG  yv, POSITION position )
{
    if( hSprite->active == FALSE )
    {
        return FALSE;
    }

    if( position == P_ABSOLUTE )
    {
        hSprite->yv = yv;
    }
    else if( position == P_RELATIVE )
    {
        hSprite->yv += yv;
    }

    return TRUE;

} /* SetSpriteVelY */

/*
 * GetSpriteVelY
 */
LONG GetSpriteVelY( HSPRITE *hSprite )
{
    return hSprite->yv;

} /* GetSpriteVelY */

/*
 * SetSpriteAccX
 */
BOOL SetSpriteAccX ( HSPRITE *hSprite, LONG xa, POSITION position )
{
    if( position == P_ABSOLUTE )
    {
        hSprite->xa = xa;
    }
    else if( position == P_RELATIVE )
    {
        hSprite->xa += xa;
    }
    return TRUE;

} /* SetSpriteAccX */

/*
 * GetSpriteAccX
 */
LONG GetSpriteAccX( HSPRITE *hSprite )
{
    return hSprite->xa;

} /* GetSpriteAccX */

/*
 * SetSpriteAccY
 */
BOOL SetSpriteAccY ( HSPRITE *hSprite, LONG ya, POSITION position )
{
    if( position == P_ABSOLUTE )
    {
        hSprite->ya = ya;
    }
    else if( position == P_RELATIVE )
    {
        hSprite->ya += ya;
    }
    return TRUE;

} /* SetSpriteAccY */

/*
 * GetSpriteAccY
 */
LONG GetSpriteAccY( HSPRITE *hSprite )
{
    return hSprite->ya;

} /* GetSpriteAccY */

/*
 * SetSpriteX
 */
BOOL SetSpriteX( HSPRITE *hSprite, LONG x, POSITION position )
{
    if( hSprite->active == FALSE )
    {
        return FALSE;
    }

    if( position == P_AUTOMATIC )
    {
        hSprite->xv += hSprite->xa;
        hSprite->x  += hSprite->xv;
    }
    else if( position == P_ABSOLUTE )
    {
        hSprite->x = x;
    }
    else if( position == P_RELATIVE )
    {
        hSprite->x += x;
    }

    if( hSprite->x < 0 )
    {
        hSprite->x += hSprite->xmax << 16;
    }
    else if( hSprite->x >= hSprite->xmax << 16 )
    {
        hSprite->x -= hSprite->xmax << 16;
    }
    return TRUE;

} /* SetSpriteX */

/*
 * GetSpriteX
 */
LONG GetSpriteX( HSPRITE *hSprite )
{
    return hSprite->x;

} /* GetSpriteX */

/*
 * SetSpriteY
 */
BOOL SetSpriteY ( HSPRITE *hSprite, LONG y, POSITION position )
{
    if( hSprite->active == FALSE )
    {
        return FALSE;
    }

    if( position == P_AUTOMATIC )
    {
        hSprite->yv += hSprite->ya;
        hSprite->y  += hSprite->yv;
    }
    else if( position == P_ABSOLUTE )
    {
        hSprite->y = y;
    }
    else if( position == P_RELATIVE )
    {
        hSprite->y += y;
    }

    if( hSprite->y < 0 )
    {
        hSprite->y += hSprite->ymax << 16;
    }
    else if( hSprite->y >= hSprite->ymax << 16 )
    {
        hSprite->y -= hSprite->ymax << 16;
    }

    return TRUE;

} /* SetSpriteY */

/*
 * GetSpriteY
 */
LONG GetSpriteY( HSPRITE *hSprite )
{
    return hSprite->y;

} /* GetSpriteY */

/*
 * SetSpriteSwitch
 */
BOOL SetSpriteSwitch ( HSPRITE *hSprite, LONG absSwitch, POSITION position )               
{
    if( position == P_ABSOLUTE )
    {
        hSprite->absSwitch = absSwitch;
    }
    else if( position == P_RELATIVE )
    {
        hSprite->absSwitch += absSwitch;
    }
    return TRUE;

} /* SetSpriteSwitch */


/*
 * IncrementSpriteSwitch
 */
BOOL IncrementSpriteSwitch ( HSPRITE *hSprite, LONG n )
{
    hSprite->relSwitch += n;
    return TRUE;

} /* IncrementSpriteSwitch */

/*
 * SetSpriteSwitchType
 */
BOOL SetSpriteSwitchType( HSPRITE *hSprite, SWITCHING switchType )
{
    hSprite->switchType = switchType;
    hSprite->relSwitch  = 0;
    return TRUE;

} /* SetSpriteSwitchType */


/*
 * GetSpriteSwitchType
 */
SWITCHING GetSpriteSwitchType ( HSPRITE *hSprite )
{
    return hSprite->switchType;

} /* GetSpriteSwitchType */

/*
 * SetSpriteSwitchForward
 */
BOOL SetSpriteSwitchForward( HSPRITE *hSprite, BOOL switchForward )
{
    hSprite->switchForward = switchForward;

    return TRUE;

} /* SetSpriteSwitchForward */

/*
 * GetSpriteSwitchForward
 */
BOOL GetSpriteSwitchForward( HSPRITE *hSprite )
{
    return hSprite->switchForward;

} /* GetSpriteSwitchForward */

/*
 * SetSpriteSwitchDone
 */
BOOL SetSpriteSwitchDone( HSPRITE *hSprite, BOOL switchDone )
{
    hSprite->switchDone = switchDone;
    return TRUE;

} /* SetSpriteSwitchDone */


/*
 * GetSpriteSwitchDone
 */
BOOL GetSpriteSwitchDone( HSPRITE *hSprite )
{
    return hSprite->switchDone;

} /* GetSpriteSwitchDone */

/*
 * SetSpriteBitmap
 */
BOOL SetSpriteBitmap ( HSPRITE *hSprite, USHORT currentBitmap )
{
    USHORT c;

    c = 0;
    while( (hSprite->currentAction != hSprite->hSBM[c].action) ||
           (hSprite->currentDirection != hSprite->hSBM[c].direction) )
    {
        ++c;
    }
    hSprite->currentBitmap = c + currentBitmap;
    return TRUE;

} /* SetSpriteBitmap */

/*
 * GetSpriteBitmap
 */
USHORT GetSpriteBitmap( HSPRITE *hSprite )
{
    USHORT count;

    count = 0;
    while( (hSprite->currentAction != hSprite->hSBM[count].action) ||
           (hSprite->currentDirection != hSprite->hSBM[count].direction) )
    {
        ++count;
    }
    return hSprite->currentBitmap - count;

} /* GetSpriteBitmap */

/*
 * advanceSpriteBitmap
 */
static BOOL advanceSpriteBitmap( HSPRITE *hSprite )
{
    SHORT       c;
    SHORT       n;
    ACTION      curAct;
    ACTION      act;
    DIRECTION   curDir;
    DIRECTION   dir;

    curAct = hSprite->currentAction;
    curDir = hSprite->currentDirection;

        //
        // See if we're cycling forward or backward though the images.
        //
    if( hSprite->switchForward ) // Are we cycling forward?
    {
        c   = hSprite->currentBitmap + 1;

                // Does the next image exceed the number of images we have?
        if( c >= hSprite->bitmapCount )
                {
                        // if the next image is past the end of the list,
                        // we need to set it to the start of the series.
            SetSpriteBitmap( hSprite, 0 );
                        c   = hSprite->currentBitmap;
        }
                else
                {
                        act = hSprite->hSBM[c].action;
                        dir = hSprite->hSBM[c].direction;

                        // By examining the action and direction fields we can tell
                        // if we've past the current series of images and entered 
                        // another series.
                        if( (curAct != act) || (curDir != dir) )
                        {
                                        SetSpriteBitmap( hSprite, 0 );
                        } 
                        else // We're still in the series, use the next image.
                        {
                                hSprite->currentBitmap = c;
                        }
                }
    }
        else //cycling backwards
        {
        c   = hSprite->currentBitmap - 1;

        if( c < 0 ) // Is the next image past the beginning of the list?
                {
            n = 0;
                        
                        // Find the last bitmap in the series
            while( (n <= hSprite->bitmapCount) &&
                               (curAct == hSprite->hSBM[n].action) &&
                       (curDir == hSprite->hSBM[n].direction) )
                        {
                                ++n;                            
                        }

            hSprite->currentBitmap = n - 1;
        }

                else
                {
                        act = hSprite->hSBM[c].action;
                        dir = hSprite->hSBM[c].direction;
                        // Is the next image past the of the series
                        if( (curAct != act) || (curDir != dir) ) 
                        {
                                n = c + 1;
                                while( (n <= hSprite->bitmapCount) &&
                                           (curAct == hSprite->hSBM[n].action) &&
                                   (curDir == hSprite->hSBM[n].direction) )
                                {
                                        ++n;                            
                                }

                                hSprite->currentBitmap = n - 1;
                        }
                        else  // The next image is fine, use it.
                        {
                                hSprite->currentBitmap = c;
                        }
                }
    }
    return TRUE;

} /* advanceSpriteBitmap */

/*
 * DisplaySprite
 */
BOOL DisplaySprite ( GFX_HBM hBuffer, HSPRITE *hSprite, LONG xPlane )
{
    USHORT      count;
    SHORT       left;
    SHORT       right;
    SHORT       shortx;
    SHORT       shorty;
    SHORT       planex;
    POINT       src;
    RECT        dst;

    if( hSprite->active == FALSE )
    {
        return FALSE;
    }

    count = hSprite->currentBitmap;
    shortx = (SHORT) (hSprite->x >> 16);
    shorty = (SHORT) (hSprite->y >> 16);
    planex = (SHORT) (xPlane >> 16);
    src.x = 0;
    src.y = 0;

    if( shortx < planex - C_SCREEN_W )
    {
        shortx += hSprite->xmax;
    }
    else if( shortx >= planex + C_SCREEN_W )
    {
        shortx -= hSprite->xmax;
    }

    left = shortx - planex;
    
    if( hSprite->currentDirection == RIGHT )
    {
        left += hSprite->hSBM[count].x;
    }
    else
    {
        left += hSprite->width - hSprite->hSBM[count].x - hSprite->hSBM[count].width;
    }

    right = left + hSprite->hSBM[count].width;

    if( left > C_SCREEN_W )
    {
        left = C_SCREEN_W;
    }
    else if( left < 0 )
    {
        src.x = -left;
        left = 0;
    }

    if( right > C_SCREEN_W )
    {
        right = C_SCREEN_W;
    }
    else if( right < 0 )
    {
        right = 0;
    }

    dst.left   = left;
    dst.right  = right;
    dst.top    = shorty + hSprite->hSBM[count].y;
    dst.bottom = dst.top + hSprite->hSBM[count].height;

    gfxBlt(&dst,hSprite->hSBM[count].hBM,&src);

    if( hSprite->switchType == HOR )
    {
        hSprite->relSwitch += abs(hSprite->xv);

        if( hSprite->relSwitch >= hSprite->absSwitch )
        {
            hSprite->relSwitch = 0;
            advanceSpriteBitmap( hSprite );
        }
    }
    else if( hSprite->switchType == VER )
    {
        hSprite->relSwitch += abs(hSprite->yv);

        if( hSprite->relSwitch >= hSprite->absSwitch )
        {
            hSprite->relSwitch = 0;
            advanceSpriteBitmap( hSprite );

            if( GetSpriteBitmap( hSprite ) == 0 )
                {
                SetSpriteSwitchDone( hSprite, TRUE );
            }
        }
    }
    else if( hSprite->switchType == TIMESWITCH )
    {
        hSprite->relSwitch += C_UNIT;

        if( hSprite->relSwitch >= hSprite->absSwitch )
            {
            hSprite->relSwitch = 0;
            advanceSpriteBitmap( hSprite );
            
            if( GetSpriteBitmap( hSprite ) == 0 )
                {
                SetSpriteSwitchDone( hSprite, TRUE );
            }
        }
    }

    return TRUE;

} /* DisplaySprite */

/*
 * DestroySprite
 */
BOOL DestroySprite ( HSPRITE *hSprite )
{
    USHORT i;

    if( hSprite == NULL )
    {
        ErrorMessage( "hSprite in DestroySprite" );
    }

    if( hSprite->hSBM == NULL )
    {
        ErrorMessage( "hSprite->hSBM in DestroySprite" );
    }

    for( i = 0; i < hSprite->bitmapCount; ++i )
    {
        if( !gfxDestroyBitmap( hSprite->hSBM[i].hBM ) )
            {
            ErrorMessage( "gfxDestroyBitmap (hBM) in DestroySprite" );
        }
    }

    MemFree( hSprite->hSBM );
    MemFree( hSprite );

    return TRUE;

} /* DestroySprite */