/**********************************************************************
 *
 * File :     gunbit.c
 *
 * Abstract : The gun and explosion bit handler. This module handles
 *            the movement and display of the gun and the bits that
 *            appear when an object is struck or explodes.
 *
 **********************************************************************
 *
 * This file is a product of Criterion Software Ltd.
 *
 * This file is provided as is with no warranties of any kind and is
 * provided without any obligation on Criterion Software Ltd. or
 * Canon Inc. to assist in its use or modification.
 *
 * Criterion Software Ltd. will not, under any
 * circumstances, be liable for any lost revenue or other damages arising
 * from the use of this file.
 *
 * Copyright (c) 1995 Criterion Software Ltd.
 * All Rights Reserved.
 *
 * RenderWare is a trademark of Canon Inc.
 *
 ************************************************************************/

/*--- Include Files ---*/

#include "global.h"     /* Application general includes */

/*--- Macros and Magic Number definitions */

#define GUN_UP              -0.26   /* The gun position when it is fully
                                       up (ie the normal firing position */
                                       
#define GUN_REMOVE          -0.5    /* The gun is removed once it drops
                                       below this value */

#define GUN_FORWARD         1.0     /* distance gun is in front of the 
                                       camera */
#define GUN_SPEED           0.05    /* When the gun is rising and falling
                                       it moves at this rate */
#define GUNSIGHT_POS        6.0     /* The distance that the gun is drawn
                                       in front of the camera */
#define EXPLOSION_HEIGHT    0.1     /* The start height for the bits of
                                       explosion */

#define BIT_BOUNCE          (-1.0/2.0)  /* The change in velocity of a bit 
                                           when it hits the ground */

/* Bit Definitions */

/*  define number of random matrices to create for random bit orientation 
 *  Note: MAX_BIT_ROTATIONS must be n^2 to allow BIT_ROT_MASK to be used as a 
 *  selector
 */

#define MAX_BIT_ROTATIONS   8   /* Number of different bit rotations */
#define BIT_ROT_MASK        (MAX_BIT_ROTATIONS - 1)

#define EXP_VEL             0.05    /* The maximum velocity of an 
                                       exploding bit */
#define BIT_SIZE            0.025   /* The size of a bit */

#define SPARK_BITS          1   /* no of bits created for a spark */
#define TROPHY_BITS         2   /* no of bits created when the trophy is hit */
#define HIT_BITS            5   /* no of bits created when a rat is hit */
#define EXPLOSION_BITS      20  /* no of bits created when a rat explodes */

/*--- Structure Definitions ---*/

/*  Bit: A bit is one of the small particles that can be observed when the gun 
 *  fires at a wall or the trophy (a spark) or when a Rat explodes. Bits are 
 *  created in blocks. The size of the block depends on the type of bit being 
 *  created.
 */ 

typedef struct 
{
    Object oObj;            /* Bit's are objects just like everything else */
    int *npCount;           /* Reference counter. When this is zero all Bits                                
                               in the current group have been destroyed and 
                               the global structure can also be destroyed */
    int nPos;               /* identifies which of the Random bit rotation 
                               matrices this Bit is using */
    RwV3d vVel;             /* the velocity of the Bit */
} Bit;


/*  All Bits: A single global variable of this type is created and used as 
 *  a global pool for the Bit Data
 */

typedef struct 
{
    RwClump *cpExplode;                     /* The clump for the Explosion Bit */
    RwClump *cpSpark;                       /* The clump for the Spark bit */
#ifdef WITH_SHADOWS
    RwClump *cpShadow;                      /* The clump for the Bit's shadow. Both
                                               Bit types share the same shadow clump */
#endif                                      
    RwMatrix4d *mpaPos[MAX_BIT_ROTATIONS];  /* Random Bit orientation matrices */
    RwMatrix4d *mpaRot[MAX_BIT_ROTATIONS];  /* Small rotations incrementally applied    
                                            to mpaPos */
} AllBits;


/* Gun definition */

typedef struct 
{
    RwClump *cpGun;         /* The clump for the Gun */
    RwClump *cpSight;       /* The clump for the Gun Sight */
    RwClump *cpHit;
    RwReal nPos;
    RwTexture *tpTex;
    int nFiring;            /* TRUE if gun is firing, FALSE otherwise */
    int nFireFlag;          /* TRUE if gun has been fired */
    RwInt32 nFrameCount;
    int nEnabled;           /* 1 if enabled, 0 if disabled */
} Gun;

/*--- Global Variable ---*/

static Gun          gGGun;
static RwPickRecord prGCityPick;
static RwPickRecord prGPick;
static AllBits      abGBits;

/*--- Function Definitions ---*/

/* Bit Functions */

/************************************************************************
 *
 *      Function:       AllBitsSetup()
 *                      
 *      Description:    Initialise the global AllBits data structure
 *
 *      Return Value:   TRUE on success, FALSE on failure
 *
 ************************************************************************/
int AllBitsSetup(void)
{
    int nCount;    RwV3d vTmp;

    /* Read the RenderWare script files for the 3 bit clump types */

    if (!(abGBits.cpExplode = DoRwReadShape("bit.rwx")) || 
#ifdef WITH_SHADOWS
        !(abGBits.cpShadow = DoRwReadShape("bitshad.rwx")) || 
#endif
        !(abGBits.cpSpark = DoRwReadShape("spark.rwx"))) 
    {
        return FALSE;       /* Unable to read script files - Error */
    }

    /* Create the Random Rotation matrices */

    for (nCount=0; nCount < MAX_BIT_ROTATIONS; nCount++) 
    {
        if (!(abGBits.mpaRot[nCount] = RwCreateMatrix()) ||
            !(abGBits.mpaPos[nCount] = RwCreateMatrix())) 
        {
            return FALSE;   /* Unable to create matrices - Error */
        }

        /* The mpaRot matrices are rotation matrices about an arbitrary axis
           with an angular component between +10.0 and -10.0 degrees */
        
        RandomVec(&vTmp);
        
        RwRotateMatrix(abGBits.mpaRot[nCount], 
                       vTmp.x, vTmp.y, vTmp.z,
                       RANDOM_REAL(CREAL(-10.0), CREAL(10.0)),			          
                       rwREPLACE);
        
        /* The mpaPos matrices are rotation matrices about an arbitrary axis
           with a random orientation */
        
        RandomVec(&vTmp);

        RwRotateMatrix(abGBits.mpaPos[nCount], 
                       vTmp.x,vTmp.y,vTmp.z,
                       RANDOM_REAL(CREAL(0.0), CREAL(360.0)),
			           rwREPLACE);
    }
    return TRUE;    /* success */
}

/************************************************************************
 *
 *      Function:       AllBitsDestroy()
 *                      
 *      Description:    Destroy the global AllBits data structure
 *
 *
 ************************************************************************/
void AllBitsDestroy(void)
{
    int nCount;

    RwDestroyClump(abGBits.cpExplode);          /* destroy the Explosion clump */
#ifdef WITH_SHADOWS
    RwDestroyClump(abGBits.cpShadow);           /* destroy the shadow clump */
#endif
    RwDestroyClump(abGBits.cpSpark);            /* destroy the spark clump */

    /* destroy the random rotation matrices */

    for (nCount=0; nCount < MAX_BIT_ROTATIONS; nCount++) 
    {
        RwDestroyMatrix(abGBits.mpaRot[nCount]);
        RwDestroyMatrix(abGBits.mpaPos[nCount]);
    }
}

/************************************************************************
 *
 *      Function:       AllBitsUpdate()
 *                      
 *      Description:    Increment all of the orientation matrices in the
 *                      global AllBits data structure. 
 *
 ************************************************************************/
void AllBitsUpdate(void)
{
    int nCount;
    static int nOrtho;  /* orthonormalise a different matrix each time this function 
                           is called */   


    for (nCount=0; nCount < MAX_BIT_ROTATIONS; nCount++) 
    {
        /* Postconcat the mpaRot incremental matrix
           onto the mpaPos position matrix */

        RwTransformMatrix(abGBits.mpaPos[nCount],
                          abGBits.mpaRot[nCount],
                          rwPOSTCONCAT);
    }

    nOrtho++;
    RwOrthoNormalizeMatrix(abGBits.mpaPos[nOrtho & BIT_ROT_MASK],  
                           abGBits.mpaPos[nOrtho & BIT_ROT_MASK]);
}

/************************************************************************
 *
 *      Function:       BitObjectDestroy()                     
 * 
 *      Description:    Destroy a single Bit Object 
 *
 ************************************************************************/
static void BitObjectDestroy(Object *opObj)
{
    Bit *bpBit;

    bpBit = opObj->pExtra;
    
    /* since Bit's are allocated in blocks, decrement the reference
       count and only free the allocated space once all Bit's in the
       block have been freed */

    if (--(*(bpBit->npCount)) <= 0) 
    {
        free(bpBit->npCount);
    }
}

/************************************************************************
 *
 *      Function:       BitRender()
 *                      
 *      Description:    Render a single Bit Object 
 *
 ************************************************************************/
static void BitRender(Object *opObj)
{
    Bit *bpBit;

    bpBit = opObj->pExtra;

#ifdef WITH_SHADOWS
    /* Render the shadow */

    /* Check if the bit is in the scene */

    if ((opObj->vPos.z > CREAL(FAR_STREET)) &&
        (opObj->vPos.z < CREAL(NEAR_STREET))) 
    {
        /* Bit in the scene. Check whether the shadow is on the road
           or on the kerb */

        if ((opObj->vPos.x < CREAL(LEFT_KERB)) ||
            (opObj->vPos.x > CREAL(RIGHT_KERB)) ) 
        {
            /* Its on the kerb */
            RwTranslateMatrix(RwScratchMatrix(),
                              opObj->vPos.x,
                              CREAL(KERB_HEIGHT),
                              opObj->vPos.z,
                              rwREPLACE);
        } 
        else 
        {
            /* Its on the street */
            RwTranslateMatrix(RwScratchMatrix(),
                              opObj->vPos.x,
                              CREAL(STREET_HEIGHT),
                              opObj->vPos.z,
                              rwREPLACE);
        }

        /* Position and Render the shadow */
        RwTransformClump(abGBits.cpShadow, RwScratchMatrix(), rwREPLACE);
        RwRenderClump(abGBits.cpShadow);
    }
#endif /* WITH_SHADOWS */

    /* Render the Bit */

    /* Get the rotation matrix for this Bit */    

     RwCopyMatrix(abGBits.mpaPos[bpBit->nPos], RwScratchMatrix());

    /* Position the bit */

     RwTranslateMatrix(RwScratchMatrix(),
                       opObj->vPos.x,
                       opObj->vPos.y,
                       opObj->vPos.z,      
                       rwPOSTCONCAT);

     RwTransformClump(opObj->cpClump, RwScratchMatrix(), rwREPLACE);

    /* And render it */

    RwRenderClump(opObj->cpClump);
}

/************************************************************************
 *
 *      Function:       BitUpdate()
 *                      
 *      Description:    Update the position of a single Bit
 *
 ************************************************************************/
static void BitUpdate(Object *opObj)
{
    Bit *bpBit;

    bpBit = (Bit *)opObj->pExtra;

    /* Move bit to new position */
    RwAddVector(&bpBit->oObj.vPos, &bpBit->vVel, &bpBit->oObj.vPos);

    /* Apply gravity */
    bpBit->vVel.y -= CREAL(GRAVITY);

    /* Test for collisions with the side walls */

    if(bpBit->vVel.x < CREAL(0.0)) 
    {
        /* moving towards left wall */
        if (bpBit->oObj.vPos.x < CREAL(LEFT_STREET + BIT_SIZE)) 
        {
            /* collided with left wall - bounce */
            bpBit->vVel.x = RMul(bpBit->vVel.x, CREAL(RICOCHET));
        }
    }
    else
    {
        /* moving towards the right wall */

        if (bpBit->oObj.vPos.x > CREAL(RIGHT_STREET - BIT_SIZE)) 
        {
            bpBit->vVel.x = RMul(bpBit->vVel.x, CREAL(RICOCHET));
        }
    }

    /* Test for collisions with the end walls */

    if (bpBit->vVel.z < CREAL(0.0))
    {
        /* travelling towards the far wall */

        if (bpBit->oObj.vPos.z < CREAL(FAR_STREET + BIT_SIZE)) 
        {
            /* collided with far wall - bounce */

            bpBit->vVel.z = RMul(bpBit->vVel.z, CREAL(RICOCHET));
        }
    }
    else
    {
        /* travelling towards the near wall */
        if (bpBit->oObj.vPos.z > CREAL(NEAR_STREET - BIT_SIZE)) 
        {
            /* collided with near wall - bounce */

            bpBit->vVel.z = -RMul(bpBit->vVel.z, CREAL(RICOCHET));
        }
    }

    /* Test for collision with side of kerb */

    if (bpBit->oObj.vPos.y < CREAL(KERB_HEIGHT + BIT_SIZE)) 
    {
        /* below kerb height - possible collision */

        if (bpBit->vVel.x < CREAL(0.0)) 
        {
            /* moving to the left - test against left kerb */
            if (bpBit->oObj.vPos.x < CREAL(LEFT_KERB + BIT_SIZE)) 
            {
                /* collided with left kerb - bounce */
                bpBit->vVel.x = RMul(bpBit->vVel.x, CREAL(RICOCHET));
            }
        } 
        else 
        {
            /* moving to the right - test against right kerb */
            if (bpBit->oObj.vPos.x > CREAL(RIGHT_KERB - BIT_SIZE)) 
            {
                /* collided with right kerb */
                bpBit->vVel.x = RMul(bpBit->vVel.x, CREAL(RICOCHET));
            }
        }
    }

    /* Check for collisions with ground */

    if (bpBit->vVel.y < CREAL(0.0)) 
    {
        /* falling bit */

        if ((bpBit->oObj.vPos.x > CREAL(LEFT_KERB + BIT_SIZE)) &&
            (bpBit->oObj.vPos.x < CREAL(RIGHT_KERB - BIT_SIZE))) 
        {
            /* Over the road - test against road height */

            if (bpBit->oObj.vPos.y < CREAL(STREET_HEIGHT + BIT_SIZE)) 
            {
                /* collided with road - bounce */
                bpBit->vVel.y = RMul(bpBit->vVel.y, CREAL(BIT_BOUNCE));
            }
        } 
        else 
        {
            /* Over the kerb - test against kerb height */

            if (bpBit->oObj.vPos.y < CREAL(KERB_HEIGHT + BIT_SIZE))
            {
                /* collided with kerb - bounce */
                bpBit->vVel.y = RMul(bpBit->vVel.y, CREAL(BIT_BOUNCE));
            }
        }
    }

    /* If the bit isn't moving in then destroy it */

    if ((bpBit->vVel.y < CREAL(0.0)) &&
        (bpBit->vVel.y > CREAL(-0.001)) ) 
    {
        ObjectDelete(&bpBit->oObj);
    }
}

/************************************************************************
 *
 *      Function:       BitSetup()
 *                      
 *      Description:    Initialise a single Bit. 
 *
 ************************************************************************/
static void BitSetup(
              Bit *bpBit,         /* the bit to setup */
              RwReal nX,          /* initial bit position */
              RwReal nY,
              RwReal nZ,
              int *npCount,       /* the global reference counter location */
              RwClump *cpClump    /* the clump type to use for the bit */
              )
{
    static RwInt32 nPos;

    ObjectSetup(&bpBit->oObj,nX,nY,nZ,cpClump); /* initialise the object data */

    bpBit->oObj.fpUpdate = BitUpdate;         /* function to update object */ 
    bpBit->oObj.fpRender = BitRender;         /* function to render object */
    bpBit->oObj.fpDestroy = BitObjectDestroy; /* function to destroy object */    
    bpBit->oObj.pExtra = (void *)bpBit;       /* how to get Bit from object */
    bpBit->nPos = nPos & BIT_ROT_MASK;        /* pick a Bit rotation matrix */

    nPos++;                     /* next call picks next Bit rotation matrix */

    /* Set the initial Bit velocity */
    
    bpBit->vVel.x = RANDOM_REAL(CREAL(-EXP_VEL), CREAL(EXP_VEL));
    bpBit->vVel.y = RANDOM_REAL(CREAL(-EXP_VEL), CREAL(EXP_VEL));
    bpBit->vVel.z = RANDOM_REAL(CREAL(-EXP_VEL), CREAL(EXP_VEL));

    bpBit->npCount = npCount;  /* Set the Bit reference count */ 

    AllObjectsAddObject(&bpBit->oObj); /* Add this new object to the scene */
}

/************************************************************************
 *
 *      Function:       BitExplosionSetup()
 *                      
 *      Description:    Create and Initialise all the Bits for an
 *                      explosion.
 *
 ************************************************************************/
static void BitExplosionSetup( 
                       RwReal nX,   /* Start position for the explosion */
                       RwReal nY,  
                       RwReal nZ,
                       int nAmo,    /* The number of Bits in the explosion */
                       RwClump *cpClump  /* The Bit clump to use for this
                                            explosion */
                       )
{
    Bit *bpBit;
    int *npCount;

    /* Allocate enough space for all the Bits and a reference counter */

    npCount = (int *)malloc(sizeof(int) + (sizeof(Bit) * nAmo));

    if (npCount) 
    {
        /* Initialise reference count with the number of bits */  

        (*npCount) = nAmo;  

        bpBit = (Bit *) (&npCount[1]);

        /* Set up each of the Bits in turn */       

        for (; nAmo > 0; nAmo--) 
        {
            BitSetup(bpBit, nX, nY, nZ, npCount, cpClump);
            bpBit++;
        }
    }
}

/*--- Gun Functions ---*/

/************************************************************************
 *
 *      Function:       GunVisible()
 *                      
 *      Description:    return TRUE if gun visible, FALSE otherwise
 *
 ************************************************************************/
int GunVisible(void)
{
    return(!(gGGun.nPos <= CREAL(GUN_REMOVE)));
}

/************************************************************************
 *
 *      Function:       GunEnable()
 *                      
 *      Description:    Enable display of the gun
 *
 ************************************************************************/
void GunEnable(void)
{
    gGGun.nEnabled = 1;
}

/************************************************************************
 *
 *      Function:       GunDisable()
 *                      
 *      Description:    Disable display of the gun
 *
 ************************************************************************/
void GunDisable(void)
{
    gGGun.nEnabled = 0;
}

/************************************************************************
 *
 *      Function:       GunToggle()
 *                      
 *      Description:    Toggle display of the gun
 *
 ************************************************************************/
void GunToggle(void)
{
     gGGun.nEnabled^=1;
}

/************************************************************************
 *
 *      Function:       GunFiring()
 *                      
 *      Description:    Set firing state for the gun
 *
 ************************************************************************/
void GunFiring(int state)
{
    if (!gGGun.nFiring && state)
    {
        if (gGGun.nFrameCount==RwGetTextureNumFrames(gGGun.tpTex)-1)
        {
            gGGun.nFrameCount=0;
            gGGun.nFireFlag = 1;
        }
    }
    else
    {
        gGGun.nFireFlag = 0;
    }
    gGGun.nFiring=state;
}

/************************************************************************
 *
 *      Function:       GunSetup()
 *                      
 *      Description:    Setup the gun
 *
 ************************************************************************/
int GunSetup(void)
{
    /* Read the scripts for the gun */

    if (!(gGGun.cpGun =   DoRwReadShape("gun.rwx")) ||
        !(gGGun.cpSight = DoRwReadShape("sight.rwx")) ||
	!(gGGun.cpHit =   DoRwReadShape("hit.rwx")))
    {
        /* couldn't read the scripts - fail */
        return FALSE;
    }

    /* Find the gun texture and set the current frame to be the last one */

    gGGun.tpTex = RwFindNamedTexture("gun");
    gGGun.nFrameCount = RwGetTextureNumFrames(gGGun.tpTex) - 1;

    gGGun.nPos     = CREAL(GUN_UP); /* gun starts in the visible position */
    gGGun.nFiring  = FALSE;         /* gun isn't firing */
    gGGun.nEnabled = 1;             /* gun is enabled */

    return TRUE;
}

/************************************************************************
 *
 *      Function:       GunDestroy()
 *                      
 *      Description:    Destroy the gun
 *
 ************************************************************************/
void GunDestroy(void)
{
    RwDestroyClump(gGGun.cpGun);
    RwDestroyClump(gGGun.cpSight);
    RwDestroyClump(gGGun.cpHit);
}

/************************************************************************
 *
 *      Function:       GunUpdate()
 *                      
 *      Description:    Update the gun
 *
 ************************************************************************/
void GunUpdate(void)
{
#ifdef WITH_SOUND
    RwInt32 nRand;
#endif
    Object *opObj;
    RwInt32 nW, nH; 

    if (!GunVisible())
        return;

    RwGetCameraViewport(cpGCamera, NULL, NULL, &nW, &nH);

    /* First see if we are targetting a rat. The sight is in the
       centre of the viewport */

    RwPickScene(spGTargetScene, nW/2, nH/2, cpGCamera, &prGPick);

    if (gGGun.nFireFlag) 
    {
#ifdef WITH_SOUND
        /* Make the gun firing sound */
        AllSoundsPlaySound("gun");
#endif

        gGGun.nFireFlag = 0;    /* only fire once each round */

        if (prGPick.type == rwPICKCLUMP) 
        {
            /* Hit a target so destroy it. We identify whether the target is
               a rat or the trophy by the number of polygons on the picked clump
               Each rat only has one polygon in the target scene */

			if (RwGetClumpNumPolygons(prGPick.object.clump.clump) == 1)
			{
				/* We've hit a rat. Identify the rat that has been hit */

            	opObj = (Object *) RwGetClumpData(prGPick.object.clump.clump);

                /* Rat hit returns true if the rat has been destroyed, 
                   false otherwise */

            	if (RatHit(opObj)) 
            	{
                	/* Explode the rat */
#ifdef WITH_SOUND
                	/* Make the squish sound */
                	AllSoundsPlaySound("squish");
#endif
                	BitExplosionSetup(
                 	        opObj->vPos.x,
                                opObj->vPos.y + CREAL(EXPLOSION_HEIGHT),
                   	        opObj->vPos.z,
                    		EXPLOSION_BITS,
                    	    abGBits.cpExplode);

                    /* Destroy the Rat */
                    ObjectDelete(opObj);
                    if (NumRats() == 1)
                    {
                        TrophyEnable();
                    }
                } 
                else 
                {
                    /* Have a little bit of an explosion */
        
                    BitExplosionSetup(
                                prGPick.object.clump.wcpoint.x,
                                prGPick.object.clump.wcpoint.y,
                                prGPick.object.clump.wcpoint.z,
                                HIT_BITS,
                                abGBits.cpExplode);

#ifdef WITH_SOUND
                    /* Make the ricochet sound */
                    nRand = RwRandom() & 255;

                    if (nRand < 80) 
                    {
                        AllSoundsPlaySound("squeak");
                    } 
                    else 
                    {
                        if (nRand < 160) 
                        {
                            AllSoundsPlaySound("ratty");
                        } 
                        else 
                        {
                            AllSoundsPlaySound("ratty2");
                        }          
                    }
#endif
                }
            } 
            else 
            {
                TrophyHit();
                /* must be a trophy */
#ifdef WITH_SOUND
                /* Make the clang sound */
                AllSoundsPlaySound("clang");
#endif
                BitExplosionSetup(
                        prGPick.object.clump.wcpoint.x,
                        prGPick.object.clump.wcpoint.y,
                        prGPick.object.clump.wcpoint.z,
                        TROPHY_BITS,
                        abGBits.cpSpark);
            }    
        } 
        else 
        {
            /* Nothing. See if we will hit a wall */

            RwPickScene(spGScene, nW/2, nH/2, cpGCamera, &prGCityPick);

            if (prGCityPick.type == rwPICKCLUMP) 
            {
                /* A wall has been hit !!! */
#ifdef WITH_SOUND
                /* Make the ricochet sound */
                AllSoundsPlaySound("ricochet");
#endif
                /* Wall has been hit */
                BitExplosionSetup(
                      prGCityPick.object.clump.wcpoint.x,
                      prGCityPick.object.clump.wcpoint.y,
                      prGCityPick.object.clump.wcpoint.z,
                      SPARK_BITS,
                      abGBits.cpSpark);
            }
        }
    }
}

/************************************************************************
 *
 *      Function:       GunRender()
 *                      
 *      Description:    Render the gun
 *
 ************************************************************************/
void GunRender(void)
{
    RwV3d vPos;
    RwV3d vAt;
    RwV3d vUp;
					  
    if (gGGun.nPos <= CREAL(GUN_REMOVE)) 
    {
        /* The gun has been removed */

        if (gGGun.nEnabled) 
        {
            /* Bring it back */
            gGGun.nPos += CREAL(GUN_SPEED);
        }
    } 
    else 
    {
        /* The gun is on screen */

        RwSetTextureFrame(gGGun.tpTex, gGGun.nFrameCount);

        if (gGGun.nFrameCount < RwGetTextureNumFrames(gGGun.tpTex) - 1) 
        {
            gGGun.nFrameCount++;
        }

        if (!gGGun.nEnabled) 
        {
            gGGun.nPos -= CREAL(GUN_SPEED);
        } 
        else 
        {
            if (gGGun.nPos >= CREAL(GUN_UP)) 
            {
                gGGun.nPos = CREAL(GUN_UP);

                if ((gGGun.nFrameCount==(RwGetTextureNumFrames(gGGun.tpTex)-1)) &&
                    (gGGun.nFiring))
                {
                        /* The gun has been fired !!!!!!!!!!!!!!! */

                    gGGun.nFrameCount=0;
                    gGGun.nFireFlag = 1;
                }
            } 
            else 
            {
                gGGun.nPos+= CREAL(GUN_SPEED);
            }
        }
        /* Render the Sight */

        RwGetCameraPosition(cpGCamera, &vPos);
        RwGetCameraLookAt(cpGCamera, &vAt);

        RwScaleVector(&vAt, CREAL(GUNSIGHT_POS), &vAt);
        RwAddVector(&vPos, &vAt, &vPos);

        RwTranslateMatrix(RwScratchMatrix(), 
                          vPos.x, vPos.y, vPos.z, rwREPLACE);


        if (prGPick.type == rwPICKCLUMP) 
        {
            /* something has been targetted */

            RwTransformClump(gGGun.cpHit, RwScratchMatrix(), rwREPLACE);
            RwRenderClump(gGGun.cpHit);
        } 
        else 
        {
            RwTransformClump(gGGun.cpSight, RwScratchMatrix(), rwREPLACE);
            RwRenderClump(gGGun.cpSight);
        }

        /* Render the gun */

        RwGetCameraPosition(cpGCamera, &vPos);
        RwGetCameraLookAt(cpGCamera, &vAt);
        RwScaleVector(&vAt, CREAL(GUN_FORWARD), &vAt);
        RwAddVector(&vPos, &vAt, &vPos);
        RwGetCameraLookUp(cpGCamera,&vUp);

        RwScaleVector(&vUp, gGGun.nPos, &vUp);
        RwAddVector(&vPos, &vUp, &vPos);

        RwTranslateMatrix(RwScratchMatrix(), vPos.x, vPos.y, vPos.z, rwREPLACE);
        RwTransformClump(gGGun.cpGun, RwScratchMatrix(), rwREPLACE);
        RwRenderClump(gGGun.cpGun);
    }
}


