D:\cygwin\wf_fusion_src_r8\cgame\cg_wmodels.c

/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

/*
==========================================================================

                         - SPLITMODELS -

==========================================================================
*/
// - Adding the weapon models in split pieces
// by Jalisk0

#include "cg_local.h"
//#include "../game/gs_pmodels.h"


//======================================================================
//                      weaponinfo Registering
//======================================================================

weaponinfo_t    cg_pWeaponModels[WEAP_TOTAL];

static char *wmPartSufix[] = { "", "_barrel", "_flash", "_hand", NULL };

/*
===============
CG_vWeap_ParseAnimationScript

script:
0 = first frame
1 = lastframe/number of frames
2 = looping frames
3 = frame time

keywords: 
  "islastframe":Will read the second value of each animation as lastframe (usually means numframes)
  "rotationscale": value between 0 and 1 witch will scale the barrel rotation speed 
===============
*/
qboolean CG_vWeap_ParseAnimationScript ( weaponinfo_t *weaponinfo, char *filename )
{
    qbyte       *buf;
    char        *ptr, *token;
    int         rounder, counter, i;
    qboolean    debug = qtrue;
    qboolean    islastframe = qfalse;
    int         anim_data[4][VWEAP_MAXANIMS];
    
    rounder = 0;
    counter = 1; //reseve 0 for 'no animation'

    weaponinfo->rotationscale = 1;//default

    if (!cg_debugPModels->value)
        debug = qfalse;

    if (!trap_FS_FileExists(filename))
        return qfalse;
    
    trap_FS_LoadFile ( filename, (void **)&buf );
    
    if ( !buf ) {
        trap_FS_FreeFile ( buf );
        return qfalse;
    }
    
    //proceed
    ptr = ( char * )buf;
    while ( ptr )
    {
        token = COM_ParseExt ( &ptr, qtrue );
        if ( !token )
            break;
        
        //see if it is keyword or number
        if (*token < '0' || *token > '9') {
            
            //islastframe
            if ( !Q_stricmp (token, "islastframe") ) 
            {
                islastframe = qtrue;
                if (debug)
                    Com_Printf ("Script: Second value is read as lastframe\n");

                //islastframe
            } else if ( !Q_stricmp (token, "rotationscale") ) 
            {
                if (debug)
                    Com_Printf ("Script: rotation scale:");

                token = COM_ParseExt ( &ptr, qfalse );
                weaponinfo->rotationscale = (float)atoi(token);

                if (debug)
                    Com_Printf ("%i\n", weaponinfo->rotationscale);

            } else if (debug)
                Com_Printf ("ignored: %s\n", token);
            
        } else {
            
            //frame & animation values
            i = (int)atoi(token);
            if (debug)
                Com_Printf ("%i - ", i);
            anim_data[rounder][counter] = i;
            rounder++;
            if (rounder > 3){
                rounder = 0;
                if (debug)
                    Com_Printf (" anim: %i\n", counter);
                counter++;
            }
        }
    }
    
    trap_FS_FreeFile ( buf );

    if (counter < VWEAP_MAXANIMS)
    {
        Com_Printf ("ERROR: incomplete WEAPON script: %s - Using default\n", filename);
        return qfalse;
    }

    //reorganize to make my life easier
    for (i=0 ; i<VWEAP_MAXANIMS ; i++)
    {
        weaponinfo->firstframe[i] = anim_data[0][i];

        if (islastframe)
            weaponinfo->lastframe[i] = anim_data[1][i];
        else
            weaponinfo->lastframe[i] = ((anim_data[0][i]) + (anim_data[1][i]));
        
        weaponinfo->loopingframes[i] = anim_data[2][i];
        
        if (anim_data[3][i] < 10)//never allow less than 10 fps
            anim_data[3][i] = 10;
        
        weaponinfo->frametime[i] = 1000/anim_data[3][i];
    }

    return qtrue;
}

/*
===============
CG_LoadHandAnimations
===============
*/
void CG_CreateHandDefaultAnimations ( weaponinfo_t *weaponinfo )
{
    int defaultfps = 15;

    weaponinfo->rotationscale = 1;//default

    //default Q3 hand
    weaponinfo->firstframe[VWEAP_STANDBY] = 0;
    weaponinfo->lastframe[VWEAP_STANDBY] = 0;
    weaponinfo->loopingframes[VWEAP_STANDBY] = 1;
    weaponinfo->frametime[VWEAP_STANDBY] = 1000/defaultfps;

    weaponinfo->firstframe[VWEAP_ATTACK] = 1;//attack animation (1-5)
    weaponinfo->lastframe[VWEAP_ATTACK] = 5;
    weaponinfo->loopingframes[VWEAP_ATTACK] = 0;
    weaponinfo->frametime[VWEAP_ATTACK] = 1000/defaultfps;

    weaponinfo->firstframe[VWEAP_ATTACK2_HOLD] = 0;
    weaponinfo->lastframe[VWEAP_ATTACK2_HOLD] = 0;
    weaponinfo->loopingframes[VWEAP_ATTACK2_HOLD] = 1;
    weaponinfo->frametime[VWEAP_ATTACK2_HOLD] = 1000/defaultfps;

    weaponinfo->firstframe[VWEAP_ATTACK2_RELEASE] = 0;
    weaponinfo->lastframe[VWEAP_ATTACK2_RELEASE] = 0;
    weaponinfo->loopingframes[VWEAP_ATTACK2_RELEASE] = 1;
    weaponinfo->frametime[VWEAP_ATTACK2_RELEASE] = 1000/defaultfps;

    weaponinfo->firstframe[VWEAP_RELOAD] = 0;
    weaponinfo->lastframe[VWEAP_RELOAD] = 0;
    weaponinfo->loopingframes[VWEAP_RELOAD] = 1;
    weaponinfo->frametime[VWEAP_RELOAD] = 1000/defaultfps;

    weaponinfo->firstframe[VWEAP_WEAPDOWN] = 6;//flipout animation (6-10)
    weaponinfo->lastframe[VWEAP_WEAPDOWN] = 10;
    weaponinfo->loopingframes[VWEAP_WEAPDOWN] = 1;
    weaponinfo->frametime[VWEAP_WEAPDOWN] = 1000/defaultfps;

    weaponinfo->firstframe[VWEAP_WEAPONUP] = 11;//flipin animation(11-15)
    weaponinfo->lastframe[VWEAP_WEAPONUP] = 15;
    weaponinfo->loopingframes[VWEAP_WEAPONUP] = 0;
    weaponinfo->frametime[VWEAP_WEAPONUP] = 1000/defaultfps;

    return;
}

/*
===============
CG_WeaponModelUpdateRegistration
===============
*/
qboolean CG_WeaponModelUpdateRegistration ( weaponinfo_t *weaponinfo, char *filename )
{
    int             p;
    char            scratch[MAX_QPATH];

    for (p = 0; p<WEAPMODEL_PARTS; p++)
    {
        //md3
        if (!weaponinfo->model[p]) 
        {       
            Com_sprintf (scratch, sizeof(scratch), "models/weapons2/%s%s.md3", filename, wmPartSufix[p]);
            weaponinfo->model[p] = trap_R_RegisterModel(scratch);
        }
        //skm
        if (!weaponinfo->model[p]) 
        {       
            Com_sprintf (scratch, sizeof(scratch), "models/weapons2/%s%s.skm", filename, wmPartSufix[p]);
            weaponinfo->model[p] = trap_R_RegisterModel(scratch);
        }
        //md2
        if (!weaponinfo->model[p]) 
        {       
            Com_sprintf (scratch, sizeof(scratch), "models/weapons2/%s%s.md2", filename, wmPartSufix[p]);
            weaponinfo->model[p] = trap_R_RegisterModel(scratch);
        }
    }

    //load failed
    if (!weaponinfo->model[HAND])
        return qfalse;

    //Load animation script for the hand model
    Com_sprintf (scratch, sizeof(scratch), "models/weapons2/%s.cfg", filename);

    if ( !CG_vWeap_ParseAnimationScript ( weaponinfo, scratch ) )
    {
        CG_CreateHandDefaultAnimations ( weaponinfo );
    }

    if (cg_debugPModels->value)
        Com_Printf ("WEAPmodel: Loaded successful\n");

    strcpy(weaponinfo->name, filename);

    return qtrue;
}

/*
===============
CG_FindWeaponModelSpot

  Stored names format is without extension, like this: "rocketl/rocketl"
===============
*/
struct weaponinfo_s *CG_FindWeaponModelSpot ( char *filename )
{
    int             i;
    int             freespot = -1;


    for (i=0; i<WEAP_TOTAL; i++)
    {
        if (cg_pWeaponModels[i].inuse == qtrue)
        {
            if (!Q_stricmp (cg_pWeaponModels[i].name, filename))//found it
            {
                if (cg_debugPModels->value)
                    Com_Printf ("WEAPModel: found at spot %i: %s\n", i, filename);

                return &cg_pWeaponModels[i];
            }
        }
        else if (freespot < 0)
            freespot = i;
    }

    if (freespot < 0)
        CG_Error ("CG_FindWeaponModelSpot: Couldn't find a free weaponinfo spot");

    //we have a free spot
    if (cg_debugPModels->value)
        Com_Printf ("WEAPmodel: assigned free spot %i for weaponinfo %s\n", freespot, filename);

    return &cg_pWeaponModels[freespot];
}

/*
===============
CG_RegisterWeaponModel
===============
*/
struct weaponinfo_s *CG_RegisterWeaponModel ( char *cgs_name )
{
    char            filename[MAX_QPATH];
    weaponinfo_t    *weaponinfo;

    COM_StripExtension ( cgs_name, filename );

    weaponinfo = CG_FindWeaponModelSpot (filename);

    if (weaponinfo->inuse == qtrue)
        return weaponinfo;

    weaponinfo->inuse = CG_WeaponModelUpdateRegistration ( weaponinfo, filename );

    if (!weaponinfo->inuse)
    {
        if (cg_debugPModels->value)
            Com_Printf ("WEAPmodel: Failed:%s\n", filename);

        memset (weaponinfo, 0, sizeof(weaponinfo));
        return NULL;
    }

    return weaponinfo;
}


/*
===============
CG_CreateWeaponZeroModel

  we can't allow NULL weaponmodels to be passed to the viewweapon.
  They will produce crashes because the lack of animation script. 
  We need to have at least one weaponinfo with a script to be used
  as a replacement, so, weapon 0 will have the animation script 
  even if the registration failed
===============
*/
struct weaponinfo_s *CG_CreateWeaponZeroModel ( char *filename )
{
    weaponinfo_t    *weaponinfo;

    COM_StripExtension ( filename, filename );

    weaponinfo = CG_FindWeaponModelSpot (filename);

    if (weaponinfo->inuse == qtrue)
        return weaponinfo;

    if (cg_debugPModels->value)
        Com_Printf ("WEAPmodel: Failed to load generic weapon. Creatin fake one\n");

    CG_CreateHandDefaultAnimations ( weaponinfo );

    weaponinfo->inuse = qtrue;

    strcpy( weaponinfo->name, filename );

    return weaponinfo;//no checks
}


//======================================================================
//                          weapons
//======================================================================

/*
===============
CG_GetWeaponFromClientIndex

  Each clientinfo stores and array of weaponmodels for every weapon.
  This function returns the requested one

  I don't use baseClientInfo replacements because all them point
  to the same struct now.

  Each pmodelinfo stores a pointer to the active weapon, so there's no
  need to call this function to access the pmodelinfo weapon, but for
  accessing the other weapons in the client index
===============
*/

struct weaponinfo_s *CG_GetWeaponFromClientIndex(cg_clientInfo_t *ci, int currentweapon)
{
    weaponinfo_t    *weaponinfo;

    if ((!cg_vwep->value ) || (currentweapon > WEAP_TOTAL - 1))
        currentweapon = 0; // 0 is generic weapon

    weaponinfo = ci->weaponIndex[currentweapon];

    if (!weaponinfo)                        //we can't allow NULL newweapons to be passed.
        weaponinfo = ci->weaponIndex[0];    //They will produce crashes because the lack of animation script
                                            //weapon 0 will have the animation script even if the registration failed
    return weaponinfo;
}


/*
/*
===============
CG_AddWeaponOnTagWeapon

  Adds the weaponinfo model(s) inside pweapon_t, positioned at
  the tag_weapon inside the entity_t.
===============
*/
void CG_AddWeaponOnTagWeapon (entity_t *ent, pweapon_t *pweapon, int effects)
{
    orientation_t   tag;
    entity_t        weapon;
    int             i;
    vec3_t      flashangles; // rotflash
    qboolean    gotbarrel; // rotflash

    gotbarrel = qfalse; // rotflash


    //don't try without base model
    if ( !ent->model || !pweapon->weaponInfo)
        return;

    //don't try without a tag_weapon
    if (!CG_GrabTag( &tag, ent, "tag_weapon" ))
        return;


    //weapon

    memset (&weapon, 0, sizeof(weapon));
    weapon.scale = ent->scale;
    weapon.flags = ent->flags;
    weapon.frame = 0;
    weapon.oldframe = 0;
    weapon.model = pweapon->weaponInfo->model[WEAPON];
    
    CG_PlaceModelOnTag (&weapon, ent, &tag);
    
    CG_AddEntity (&weapon);

    CG_AddShellEffects (&weapon, effects);
    //CG_AddColorShell (&weapon, renderfx);

    if (!weapon.model)
        return;

    //barrel

    if ( pweapon->weaponInfo->model[BARREL] )
    {
        if (CG_GrabTag( &tag, &weapon, "tag_barrel" ))
        {
            float scaledTime;

            gotbarrel = qtrue; // rotflash

            entity_t    barrel;
            memset (&barrel, 0, sizeof(barrel));
            barrel.model = pweapon->weaponInfo->model[BARREL];
            barrel.scale = ent->scale;
            barrel.flags = ent->flags;
            barrel.frame = 0;
            barrel.oldframe = 0;

            //rotation
            scaledTime = cg.frameTime*100; //not precise, but enough

            pweapon->rotationSpeed += scaledTime*((pweapon->flashtime > cg.time) * (pweapon->rotationSpeed < 8));
            pweapon->rotationSpeed -= scaledTime/15;
            if (pweapon->rotationSpeed < 0)
                pweapon->rotationSpeed = 0.0f;

            pweapon->angles[2] += scaledTime * pweapon->rotationSpeed * pweapon->weaponInfo->rotationscale;
            if ( pweapon->angles[2] > 360 )
                pweapon->angles[2] -= 360;

            AnglesToAxis ( pweapon->angles, barrel.axis );

            //barrel requires special tagging
            CG_PlaceRotatedModelOnTag (&barrel, &weapon, &tag);
            
            CG_AddEntity (&barrel);

            CG_AddShellEffects (&barrel, effects);
            //CG_AddColorShell (&barrel, renderfx);
        }
    }
    
    //flash
    
    if (!CG_GrabTag(&tag, &weapon, "tag_flash" ))
        return;

    //store flash origin for fire trails projection
    VectorCopy (weapon.origin, pweapon->flashOrigin);
    for ( i = 0 ; i < 3 ; i++ )
        VectorMA( pweapon->flashOrigin, tag.origin[i], weapon.axis[i], pweapon->flashOrigin );

    if (pweapon->flashtime < cg.time)
        return;

    if (pweapon->weaponInfo->model[FLASH])
    {
        entity_t    flash;
        memset (&flash, 0, sizeof(flash));

        if (gotbarrel) // rotflash
        {
            VectorCopy (pweapon->angles, flashangles);

            flashangles[2] = 10;

            flashangles[2] = flashangles[2] + (crandom() * 10); 

            AnglesToAxis ( flashangles, flash.axis);
        }

        flash.model = pweapon->weaponInfo->model[FLASH];
        flash.scale = ent->scale;
        flash.flags = ent->flags | RF_NOSHADOW;
        flash.frame = 0;
        flash.oldframe = 0;
        
        CG_PlaceRotatedModelOnTag (&flash, &weapon, &tag);
        
        CG_AddEntity (&flash);
    }
}