D:\cygwin\wf_fusion_src_r8\CVSROOT\cgame\cg_debris.c

/*
==============================================================================
The Weapons Factory - 
  
Modified code by Keith Pase

Copyright (C) 1997-2003 Weapons Factory Software
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.

  $Id$
  client-game side debris
  
==============================================================================
*/

#include "cg_local.h"

#define FRAMETIME 0.01
#define GRAVITYEPSILON .001
#define SV_GRAVITY 800
#define VELOCITYFACTOR 1000

/*
=================
CG_ClearCGDebrischunks
=================
*/
void CG_ClearCGDebrischunks (void)
{
    memset ( cg_debrischunks, 0, sizeof(cg_debrischunks) );
    cg_numDebrischunks = 0;
}



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

=================
*/
void CG_UpdateDebris ( int owner, int type, vec3_t origin )
{
    int number = cg_numDebrischunks;
    int i,j;
    qboolean free = qfalse;

    //remove all in this case
    if (type == DEBRIS_ALL)
    {
        for(i=0; i<cg_numDebrischunks; i++)
            debrischunks_drawlist[i] = NULL;

        cg_numDebrischunks = 0;

        for(i=0; i<MAX_CG_DEBRISCHUNKS; i++)
            cg_debrischunks[i].inuse = qfalse;

        return;
    }

    if (cg_numDebrischunks)
    {
        for (j=0; j<=cg_numDebrischunks; j++)
        {
            if (cg_debrischunks[j].inuse == qfalse)
            {
                number = j;
                free = qtrue;
                break;
            }
        }
    }

    if (cg_numDebrischunks+2 > MAX_CG_DEBRISCHUNKS)
        CG_ClearCGDebrischunks(); // todo clean up chunks that are first to die

    if (cg_numDebrischunks+1 < MAX_CG_DEBRISCHUNKS)
    {
        cg_debrischunks[number].inuse = qtrue;
        cg_debrischunks[number].number = number;
        cg_debrischunks[number].origin[2] +=4;
        
        // todo types
        // DEBRIS_CHUNK_BOUNCE 
//      if (cg_debrischunks[number].type == DEBRIS_CHUNK_BOUNCE)
        for (i=0; i<3; i++)
        {
            cg_debrischunks[number].velocity[i] = (40 * cg.normalfps) * crandom();
            cg_debrischunks[number].avelocity[i] = 10 * random();
        }

        cg_debrischunks[number].gravity = 1 * cg.normalfps;
        cg_debrischunks[number].time = cg.frame.serverTime + 10000 + (random()*2000); //2500 is 2.5 seconds
        cg_debrischunks[number].lightlevel = 1;
        cg_debrischunks[number].effects = EF_BLASTER;
        cg_debrischunks[number].model = trap_R_RegisterModel("models/weapons2/spike/spike.md3");
        cg_debrischunks[number].customShader = CG_MediaShader( cgs.media.shaderShellEffect );
        // end type

        //add this debris to draw list
        cg_debrischunks[number].type = type;
        VectorCopy( origin, cg_debrischunks[number].origin );
        VectorScale(cg_debrischunks[number].avelocity,cg.normalfps,cg_debrischunks[number].avelocity);
        cg_debrischunks[number].originalgravity = cg_debrischunks[number].gravity;
        debrischunks_drawlist[cg_numDebrischunks] = &cg_debrischunks[number];
        if (!free)
            cg_numDebrischunks++;
    }
    else
    {
        Com_Printf("can't create chunk: %i\n",cg_numDebrischunks);
    }

}


void CG_UpdateDebrischunks (void)
{
    int                 pnum;
    entity_state_t      *state;
    int                 i,k = 0;

    for ( pnum = 0; pnum < cg.frame.numEntities; pnum++ )
    {
        state = &cg_parseEntities[(cg.frame.parseEntities+pnum)&(MAX_PARSE_ENTITIES-1)];

        if ( state->type != ET_EVENT )
            continue;

        for ( i = 0; i < 2; i++ ) {
            if ( state->events[i] == EV_DRAWDEBRIS )
            {
                for (k=0; k<10; k++)
                CG_UpdateDebris ( state->ownerNum, state->eventParms[i], state->origin2 );
            }
        }
    }
}

void CG_CheckVelocity (cg_debris_t *chunk)
{
    float   scale;

//
// bound velocity
//
    scale = VectorLength ( chunk->velocity );
    if ( (scale > 2000) && (scale) ) // sv_maxvelocity
    {
        scale = 2000 / scale;
        VectorScale ( chunk->velocity, scale, chunk->velocity );
    }
}

void CG_AddGravity (cg_debris_t *chunk)
{
    chunk->velocity[2] -= chunk->gravity * SV_GRAVITY * GRAVITYEPSILON;
}

trace_t CG_PushEntity (cg_debris_t *ent, vec3_t push)
{
    trace_t trace;
    vec3_t  start;
    vec3_t  end;
    int     mask;

    VectorCopy (ent->origin, start);
    VectorAdd (start, push, end);

//retry:
    if (ent->clipmask)
        mask = ent->clipmask;
    else
        mask = MASK_SOLID;

    CG_Trace (&trace, start, ent->mins, ent->maxs, end, ent->number, mask);
    
    VectorCopy (trace.endpos, ent->origin);

    if (trace.fraction != 1.0) // todo pushers use traps? contents client side on bmodels == bitch
    {
/*      
        SV_Impact (ent, &trace);
    
        if (!game.edicts[trace.ent].r.inuse && ent->r.inuse)
        {
            VectorCopy (start, ent->origin);
            goto retry;
        }

*/
    }

    if (trace.contents < 0)
        Com_Printf("DEBUG: VOID\n"); // Free it?

    return trace;
}   

/*
==================
CG_ClipChunkVelocity
copy of g_phys.c

Slide off of the impacting object
returns the blocked flags (1 = floor, 2 = step / wall)
==================
*/
#define STOP_EPSILON    0.1

int CG_ClipChunkVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
{
    float   backoff;
    float   change;
    int     i, blocked;
    
    blocked = 0;
    if (normal[2] > 0)
        blocked |= 1;       // floor
    if (!normal[2])
        blocked |= 2;       // step
    
    backoff = DotProduct (in, normal) * overbounce;

    for (i=0 ; i<3 ; i++)
    {
        change = normal[i]*backoff;
        out[i] = in[i] - change;
        if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
            out[i] = 0;
    }
    
    return blocked;
}

void CG_ReduceChunkVelocity (vec3_t in, vec3_t out, float multiplier)
{
    int     i;
    
    for (i=0 ; i<3 ; i++)
    {
        out[i] = in[i] * multiplier;
        if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
            out[i] = 0;
    }
}


void CG_DebrisToss (cg_debris_t *chunk)
{
    vec3_t      old_origin;
    trace_t     trace;
    vec3_t      move;
    float       backoff;
    qboolean    wasinwater,isinwater,hitsolid,isatrest; // states?

    if (chunk->velocity[2] > 0)
        chunk->groundentity = 0;

    if (!chunk->velocity)
    {
        return;
    }

    VectorCopy (chunk->origin, old_origin);

    CG_CheckVelocity (chunk);

    CG_AddGravity (chunk);

    VectorMA (chunk->angles, FRAMETIME, chunk->avelocity, chunk->angles);

    VectorScale (chunk->velocity, .1, move);

    trace = CG_PushEntity (chunk, move);

    if (trace.fraction < 1)
    {
        if (chunk->type == DEBRIS_CHUNK_FLYRICOCHET)
            backoff = 1.8;
        else if (
            (chunk->type == DEBRIS_CHUNK_BOUNCE)
            ||
            (chunk->type == DEBRIS_CHUNK_FLYRICOCHET2)
            ||
            (chunk->type == DEBRIS_CHUNK_FLOAT)
            ||
            (chunk->type == DEBRIS_CHUNK_BOUNCEALIGN)
            )
            backoff = 1.5;
        else
            backoff = 1;

        CG_ClipChunkVelocity (chunk->velocity, trace.plane.normal, chunk->velocity, backoff);
        hitsolid = qtrue;

        if ((chunk->type == DEBRIS_CHUNK_FLYRICOCHET) || (chunk->type == DEBRIS_CHUNK_FLYRICOCHET2) || (chunk->type == DEBRIS_CHUNK_BOUNCE))
            VecToAngles(chunk->velocity, chunk->angles);

    // stop if on ground
        if  ((trace.plane.normal[2] > 0.6 ) && chunk->type != DEBRIS_CHUNK_FLYRICOCHET && chunk->type != DEBRIS_CHUNK_BOUNCEALIGN)
        {       
            if (chunk->velocity[2] < 60 ||
                (
                chunk->type != DEBRIS_CHUNK_BOUNCE 
                && chunk->type != DEBRIS_CHUNK_FLOAT
                ) 
                )
            {
                chunk->groundentity = 1;
                VectorClear (chunk->velocity);
                VectorClear (chunk->avelocity);
                isatrest = qtrue;
            }
        }

        if ( CG_PointContents (chunk->origin) & MASK_SOLID )
        {
            chunk->groundentity = 1;
            VectorClear (chunk->velocity);
            VectorClear (chunk->avelocity);
            isatrest = qtrue;
        }

        if  ((trace.plane.normal[2] > 0.6) && chunk->type == DEBRIS_CHUNK_BOUNCEALIGN   )
        {       
            if (chunk->velocity[2] < 30 )
            {
                chunk->groundentity = 1;
                VectorClear (chunk->velocity);
                VectorClear (chunk->avelocity);
                chunk->angles[0] = 270;
                isatrest = qtrue;
            }
        }
    }

    wasinwater = (chunk->watertype & MASK_WATER);
    chunk->watertype = trap_CM_PointContents (chunk->origin, NULL);
    isinwater = chunk->watertype & MASK_WATER;

    if (chunk->movetype == DEBRIS_CHUNK_FLOAT && !chunk->groundentity && chunk->CanFloat)
        CG_ReduceChunkVelocity (chunk->velocity, chunk->velocity, 0.9);

    if (isinwater)
        chunk->waterlevel = 1;
    else
        chunk->waterlevel = 0;

    if (chunk->waterlevel > 1)
        chunk->gravity = 0.1;
    else if (!chunk->groundentity)
        chunk->gravity = chunk->originalgravity;
    else
        chunk->gravity = 0;

    //todo wasinwater sounds, tink/impact sounds.

}

void CG_DoDebrisEffects (cg_debris_t *chunk)
{
    int effects = chunk->effects;
    vec3_t origin;
    float intensity;

    if (chunk->groundentity && chunk->lightlevel > 0)
        chunk->lightlevel -= .01;

    intensity = (int)(chunk->lightlevel * 255);

    VectorCopy (chunk->origin,origin);

    if ( effects & EF_BLASTER )
    {
        CG_BlasterTrail ( chunk->lerpOrigin, origin );
        CG_AddLight ( origin, intensity, 1, 1, 0 );
    }
    else if ( effects & EF_HYPERBLASTER )
        CG_AddLight ( origin, 300, 1, 1, 0 );
    else if ( effects & EF_GIB )
        CG_BloodTrail ( chunk->lerpOrigin, origin, 0.9, 0, 0 );
    else if ( effects & EF_GREENGIB )
        CG_BloodTrail ( chunk->lerpOrigin, origin, 0, 0.9, 0);
    else if ( effects & EF_FLARE )
        CG_BloodTrail ( chunk->lerpOrigin, origin, 0, 0.9, 0);
    
    if ( effects & EF_ROCKET ) 
    {
        CG_RocketTrail ( chunk->lerpOrigin, origin );
        CG_AddLight ( origin, 300, 1, 1, 0 );
    } 
    else if ( effects & EF_GRENADE )
        CG_GrenadeTrail ( chunk->lerpOrigin, origin );
    else if ( effects & EF_KAMI) 
    {
//      CG_KamiEffect ( cent, origin ); // whoo boy 
        CG_AddLight ( origin, random()*400, 0, 1, 0 );
    } 
    else if ( effects & EF_BFG ) 
    {
        CG_BfgParticles ( origin, 0, 1.0f, 0 );
        CG_AddLight ( origin, 300, 0, 1, 0 );
    }
    else if ( effects & EF_ONFIRE ) 
    {
        CG_FireTrail ( chunk->lerpOrigin, origin );
        CG_AddLight ( origin, 150+(150*random()), 1, 1, 0 );
        CG_SmokeTrail (chunk->lerpOrigin,origin);
    }
    else if ( effects & EF_REDPB ) 
    {
        CG_BfgParticles ( origin, 1.0f, .8, .7 );
        CG_AddLight ( origin, 300, 1, 0, 0 );
    }
    else if ( effects & EF_BLUEPB ) 
    {
        CG_BfgParticles ( origin, 0, 0, 1 );
        CG_AddLight ( origin, 300, 0, 0, 1 );
    }
    else if ( (effects & (EF_FLAG1|EF_FLAG2)) == EF_FLAG1 ) 
    {
        if (!(effects & EF_NOPARTICLES))
        {
            CG_FlagTrail ( chunk->lerpOrigin, origin, EF_FLAG1 );
        }
        CG_AddLight ( origin, 225, 1, 0.1, 0.1 );
    } 
    else if ( (effects & (EF_FLAG1|EF_FLAG2)) == EF_FLAG2 ) 
    {
        if (!(effects & EF_NOPARTICLES))
        {
            CG_FlagTrail ( chunk->lerpOrigin, origin, EF_FLAG2 );
        }
        CG_AddLight ( origin, 225, 0.1, 0.1, 1 );
    } 
    else if ( (effects & (EF_FLAG1|EF_FLAG2)) == (EF_FLAG1|EF_FLAG2) ) 
    {
        CG_FlagTrail ( chunk->lerpOrigin, origin, EF_FLAG1 );
        CG_FlagTrail ( chunk->lerpOrigin, origin, EF_FLAG2 );
        CG_AddLight ( origin, 225, 1, 0.2, 1 );
    }
}
void CG_CheckDebrisTime (void)
{
    int i;
    qboolean none;

    for (i=0; i<=cg_numDebrischunks; i++)
    {
        if (debrischunks_drawlist[i] == NULL)
            continue;

        if (debrischunks_drawlist[i]->inuse)
            return;

        none = qtrue;
    }

    if (none)
        CG_ClearCGDebrischunks();
}


void CG_RemoveOldDebris (cg_debris_t *chunk)
{
    if (chunk->time < cg.frame.serverTime)
    {
        chunk->inuse = qfalse;
        debrischunks_drawlist[chunk->number]->inuse = qfalse;
        chunk = NULL;
    }
}

void CG_RunDebris (cg_debris_t *chunk)
{
    int i;

    VectorCopy (chunk->origin,chunk->lerpOrigin);

    for (i=0; i<3; i++)
    {
        if (chunk->velocity[i] !=0)
        {
            chunk->origin[i] += (chunk->velocity[i]/(VELOCITYFACTOR/cg.normalfps));
        }
        if (chunk->avelocity[i] !=0)
        {
            chunk->angles[i] += ((chunk->avelocity[i])*(cg.normalfps));
            if (chunk->angles[i] > 360)
                chunk->angles[i] -=360;
            if (chunk->angles[i] < 0)
                chunk->angles[i] +=360;
        }
    }

    CG_DoDebrisEffects (chunk);

    CG_RemoveOldDebris (chunk);
}

void CG_AddDebrischunks ( void )
{
    int i, j;
    entity_t    *ent;
    vec4_t shadelight = { 0, 0, 0, 0.3 };

    cg_debris_t *chunk;

    if(!cg_numDebrischunks)
        return;

    CG_CheckDebrisTime ();

    for( i=0; i<cg_numDebrischunks; i++)
    {
        ent = &debrischunks_drawlist[i]->ent;

        chunk = debrischunks_drawlist[i];

        if (chunk == NULL)
            continue;

        CG_RunDebris (chunk);

        if (chunk == NULL)
            continue;

        if (chunk->inuse)
        {
            CG_DebrisToss (chunk);

            VectorCopy(chunk->origin, ent->origin);
            AnglesToAxis(chunk->angles, ent->axis);

            VectorCopy(ent->origin, ent->oldorigin);
            VectorCopy(ent->origin, ent->lightingOrigin );

            //todo model types
            ent->model = chunk->model;

            ent->flags = RF_MINLIGHT;
            ent->rtype = RT_MODEL;
            ent->scale = 1.0;
            ent->frame = 0;
            ent->oldframe = 0;
            ent->backlerp = 0;

            ent->customSkin = NULL;
            ent->customShader = NULL;


            //------------------------
            
            if(debrischunks_drawlist[i]->type == DEBRIS_CHUNK) // testing nullmodel
            {
                shadelight[0] = 0;      //R
                shadelight[1] = 0;      //G
                shadelight[2] = 1.0;    //B
                shadelight[3] = 0.3;
            }
            else
            {
                shadelight[0] = 0;
                shadelight[1] = 1.0;
                shadelight[2] = 0;
                shadelight[3] = 0.3;
            }
                
            for( j = 0; j < 4; j++ )
                ent->color[j] = shadelight[j] * 255;
            
            ent->customShader = chunk->customShader;
            
            //-------------------

            CG_AddEntity( ent );
        }
    }
}