// Copyright (C) 1999-2000 Id Software, Inc.
//
// cg_ents.c -- present snapshot entities, happens every single frame

#include "cg_local.h"


/*
======================
CG_PositionEntityOnTag
 
Modifies the entities position and axis by the given
tag location
======================
*/
void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
                             qhandle_t parentModel, char *tagName ) {
    int				i;
    orientation_t	lerped;

    // lerp the tag
    trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
                    1.0 - parent->backlerp, tagName );

    // FIXME: allow origin offsets along tag?
    VectorCopy( parent->origin, entity->origin );
    for ( i = 0 ; i < 3 ; i++ ) {
        VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
    }

    // had to cast away the const to avoid compiler problems...
    MatrixMultiply( lerped.axis, ((refEntity_t *)parent)->axis, entity->axis );
    entity->backlerp = parent->backlerp;
}


/*
======================
CG_PositionRotatedEntityOnTag
 
Modifies the entities position and axis by the given
tag location
======================
*/
void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
                                    qhandle_t parentModel, char *tagName ) {
    int				i;
    orientation_t	lerped;
    vec3_t			tempAxis[3];

    //AxisClear( entity->axis );
    // lerp the tag
    trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
                    1.0 - parent->backlerp, tagName );

    // FIXME: allow origin offsets along tag?
    VectorCopy( parent->origin, entity->origin );
    for ( i = 0 ; i < 3 ; i++ ) {
        VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
    }

    // had to cast away the const to avoid compiler problems...
    MatrixMultiply( entity->axis, lerped.axis, tempAxis );
    MatrixMultiply( tempAxis, ((refEntity_t *)parent)->axis, entity->axis );
}



/*
==========================================================================
 
FUNCTIONS CALLED EACH FRAME
 
==========================================================================
*/

/*
======================
CG_SetEntitySoundPosition
 
Also called by event processing code
======================
*/
void CG_SetEntitySoundPosition( centity_t *cent ) {
    if ( cent->currentState.solid == SOLID_BMODEL ) {
        vec3_t	origin;
        float	*v;

        v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
        VectorAdd( cent->lerpOrigin, v, origin );
        trap_S_UpdateEntityPosition( cent->currentState.number, origin );
    } else {
        trap_S_UpdateEntityPosition( cent->currentState.number, cent->lerpOrigin );
    }
}

/*
==================
CG_EntityEffects
 
Add continuous entity effects, like local entity emission and lighting
==================
*/
static void CG_EntityEffects( centity_t *cent ) {

    // update sound origins
    CG_SetEntitySoundPosition( cent );

    // add loop sound
    if ( cent->currentState.loopSound ) {
        if (cent->currentState.eType != ET_SPEAKER) {
            trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin,
                                    cgs.gameSounds[ cent->currentState.loopSound ] );
        } else {
            trap_S_AddRealLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin,
                                        cgs.gameSounds[ cent->currentState.loopSound ] );
        }
    }


    // constant light glow
    if ( cent->currentState.constantLight ) {
        int		cl;
        int		i, r, g, b;

        cl = cent->currentState.constantLight;
        r = cl & 255;
        g = ( cl >> 8 ) & 255;
        b = ( cl >> 16 ) & 255;
        i = ( ( cl >> 24 ) & 255 ) * 4;

        trap_R_AddLightToScene( cent->lerpOrigin, i, r, g, b );
    }

}


/*
==================
CG_General
==================
*/
static void CG_General( centity_t *cent ) {
    refEntity_t			ent;
    entityState_t		*s1;

    s1 = &cent->currentState;

    // if set to invisible, skip
    if (!s1->modelindex) {
        return;
    }

    memset (&ent, 0, sizeof(ent));

    // set frame

    ent.frame = s1->frame;
    ent.oldframe = ent.frame;
    ent.backlerp = 0;

    VectorCopy( cent->lerpOrigin, ent.origin);
    VectorCopy( cent->lerpOrigin, ent.oldorigin);

    ent.hModel = cgs.gameModels[s1->modelindex];

    // player model
    if (s1->number == cg.snap->ps.clientNum) {
        ent.renderfx |= RF_THIRD_PERSON;	// only draw from mirrors
    }

    // convert angles to axis








    AnglesToAxis( cent->lerpAngles, ent.axis );

    // add to refresh list
    trap_R_AddRefEntityToScene (&ent);
}

/*
==================
CG_Speaker
 
Speaker entities can automatically play sounds
==================
*/
static void CG_Speaker( centity_t *cent ) {
    if ( ! cent->currentState.clientNum ) {	// FIXME: use something other than clientNum...
        return;		// not auto triggering
    }

    if ( cg.time < cent->miscTime ) {
        return;
    }

    trap_S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.gameSounds[cent->currentState.eventParm] );

    //	ent->s.frame = ent->wait * 10;
    //	ent->s.clientNum = ent->random * 10;
    cent->miscTime = cg.time + cent->currentState.frame * 100 + cent->currentState.clientNum * 100 * crandom();
}

/*
==================
CG_Item
==================
*/
static void CG_Item( centity_t *cent ) {
    refEntity_t		ent;
    entityState_t	*es;
    gitem_t			*item;
    int				msec;
    float			frac;
    //    float			scale;
    weaponInfo_t	*wi;
    //BB COLOR
    vec3_t		hcolor;
    char *bbcolor;
#ifndef MISSIONPACK
    int     runeColor=0;
#endif
    bbcolor = "";


    es = &cent->currentState;
    if ( es->modelindex >= bg_numItems ) {
        CG_Error( "Bad item index %i on entity", es->modelindex );
    }

    // if set to invisible, skip
    if ( !es->modelindex || ( es->eFlags & EF_NODRAW ) ) {
        return;
    }

    item = &bg_itemlist[ es->modelindex ];
    if ( cg_simpleItems.integer && item->giType != IT_TEAM ) {
        memset( &ent, 0, sizeof( ent ) );
        ent.reType = RT_SPRITE;
        VectorCopy( cent->lerpOrigin, ent.origin );
        ent.radius = 14;
        ent.customShader = cg_items[es->modelindex].icon;
        ent.shaderRGBA[0] = 255;
        ent.shaderRGBA[1] = 255;
        ent.shaderRGBA[2] = 255;
        ent.shaderRGBA[3] = 255;
        trap_R_AddRefEntityToScene(&ent);
        return;
    }

    //BB
    cent->lerpOrigin[2] += 4;
    /*
        // items bob up and down continuously
        scale = 0.005 + cent->currentState.number * 0.00001;
        cent->lerpOrigin[2] += 4 + cos( ( cg.time + 1000 ) *  scale ) * 4;
    */
    //BB end

    memset (&ent, 0, sizeof(ent));

    // autorotate at one of two speeds
    if ( item->giType == IT_HEALTH ) {
        VectorCopy( cg.autoAnglesFast, cent->lerpAngles );
        AxisCopy( cg.autoAxisFast, ent.axis );
    } else {
        VectorCopy( cg.autoAngles, cent->lerpAngles );
        AxisCopy( cg.autoAxis, ent.axis );
    }

    wi = NULL;
    // the weapons have their origin where they attatch to player
    // models, so we need to offset them or they will rotate
    // eccentricly
    if ( item->giType == IT_WEAPON ) {
        weaponInfo_t	*wi;

        wi = &cg_weapons[item->giTag];
        cent->lerpOrigin[0] -=
            wi->weaponMidpoint[0] * ent.axis[0][0] +
            wi->weaponMidpoint[1] * ent.axis[1][0] +
            wi->weaponMidpoint[2] * ent.axis[2][0];
        cent->lerpOrigin[1] -=
            wi->weaponMidpoint[0] * ent.axis[0][1] +
            wi->weaponMidpoint[1] * ent.axis[1][1] +
            wi->weaponMidpoint[2] * ent.axis[2][1];
        cent->lerpOrigin[2] -=
            wi->weaponMidpoint[0] * ent.axis[0][2] +
            wi->weaponMidpoint[1] * ent.axis[1][2] +
            wi->weaponMidpoint[2] * ent.axis[2][2];

        cent->lerpOrigin[2] += 8;	// an extra height boost
    }

    ent.hModel = cg_items[es->modelindex].models[0];

    VectorCopy( cent->lerpOrigin, ent.origin);
    VectorCopy( cent->lerpOrigin, ent.oldorigin);

    ent.nonNormalizedAxes = qfalse;

    // if just respawned, slowly scale up
    msec = cg.time - cent->miscTime;
    // Alliance: never scale up with weapons and weapons stay combined
    if ( msec >= 0 && msec < ITEM_SCALEUP_TIME &&
            (!BG_weaponsStayEnabled() || item->giType != IT_WEAPON)) {
        frac = (float)msec / ITEM_SCALEUP_TIME;
        VectorScale( ent.axis[0], frac, ent.axis[0] );
        VectorScale( ent.axis[1], frac, ent.axis[1] );
        VectorScale( ent.axis[2], frac, ent.axis[2] );
        ent.nonNormalizedAxes = qtrue;
    } else {
        frac = 1.0;
    }

    // items without glow textures need to keep a minimum light value
    // so they are always visible
    if ( ( item->giType == IT_WEAPON ) ||
            ( item->giType == IT_ARMOR ) ) {
        ent.renderfx |= RF_MINLIGHT;
    }

    // increase the size of the weapons when they are presented as items
    if ( item->giType == IT_WEAPON ) {
        VectorScale( ent.axis[0], 1.5, ent.axis[0] );
        VectorScale( ent.axis[1], 1.5, ent.axis[1] );
        VectorScale( ent.axis[2], 1.5, ent.axis[2] );
        ent.nonNormalizedAxes = qtrue;
#ifdef MISSIONPACK

        trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.weaponHoverSound );
#endif

    }

#ifdef MISSIONPACK
    if ( item->giType == IT_HOLDABLE && item->giTag == HI_KAMIKAZE ) {
        VectorScale( ent.axis[0], 2, ent.axis[0] );
        VectorScale( ent.axis[1], 2, ent.axis[1] );
        VectorScale( ent.axis[2], 2, ent.axis[2] );
        ent.nonNormalizedAxes = qtrue;
    }
#endif

    // add to refresh list
    trap_R_AddRefEntityToScene(&ent);

#ifdef MISSIONPACK

    if ( wi && item->giType == IT_WEAPON && wi->barrelModel  ) {//INCIN wi remove after model paks are added
        refEntity_t	barrel;

        memset( &barrel, 0, sizeof( barrel ) );

        barrel.hModel = wi->barrelModel;

        VectorCopy( ent.lightingOrigin, barrel.lightingOrigin );
        barrel.shadowPlane = ent.shadowPlane;
        barrel.renderfx = ent.renderfx;

        CG_PositionRotatedEntityOnTag( &barrel, &ent, wi->weaponModel, "tag_barrel" );

        AxisCopy( ent.axis, barrel.axis );
        barrel.nonNormalizedAxes = ent.nonNormalizedAxes;

        trap_R_AddRefEntityToScene( &barrel );
    }
#endif


    //alliance banshee - flag glow for loose flags :)
    if ( item->giType == IT_TEAM ) {
        //BB begin flag team colors

        // redflag
        if ( item->giTag == PW_REDFLAG ) {
            if (cg_forceColor.integer == 2) {
                if(cg.team == TEAM_BLUE) {
                    bbcolor = cg_enemyColor.string;
                } else {
                    bbcolor = cg_teamColor.string;
                }

                if (Q_stricmp(bbcolor, "blue") == 0) {
                    hcolor[0] = 0.0f;
                    hcolor[1] = 0.0f;
                    hcolor[2] = 1.0f;
                    //Com_Printf( "^2blue\n");
                }
                else if (Q_stricmp(bbcolor, "red") == 0) {
                    hcolor[0] = 1.0f;
                    hcolor[1] = 0.0f;
                    hcolor[2] = 0.0f;
                    //Com_Printf( "^2red\n");
                }
                else if (Q_stricmp(bbcolor, "yellow") == 0) {
                    hcolor[0] = 1.0f;
                    hcolor[1] = 1.0f;
                    hcolor[2] = 0.0f;
                    //Com_Printf( "^2yellow\n");
                }
                else if (Q_stricmp(bbcolor, "orange") == 0) {
                    hcolor[0] = 1.0f;
                    hcolor[1] = 0.45f;
                    hcolor[2] = 0.0f;
                    //Com_Printf( "^2orange\n");
                }
                else if (Q_stricmp(bbcolor, "green") == 0) {
                    hcolor[0] = 0.0f;
                    hcolor[1] = 1.0f;
                    hcolor[2] = 0.0f;
                    //Com_Printf( "^2green\n");
                }
                else if (Q_stricmp(bbcolor, "pink") == 0) {
                    hcolor[0] = 1.0f;
                    hcolor[1] = 0.0f;
                    hcolor[2] = 1.0f;
                    //Com_Printf( "^2pink\n");
                }
                else if (Q_stricmp(bbcolor, "aqua") == 0) {
                    hcolor[0] = 0.0f;
                    hcolor[1] = 1.0f;
                    hcolor[2] = 1.0f;
                    //Com_Printf( "^2aqua\n");
                }
                else if (Q_stricmp(bbcolor, "black") == 0) {
                    hcolor[0] = 0.5f;
                    hcolor[1] = 0.5f;
                    hcolor[2] = 0.5f;
                    //Com_Printf( "^2black\n");
                }
                else if (Q_stricmp(bbcolor, "white") == 0) {
                    hcolor[0] = 1.0f;
                    hcolor[1] = 1.0f;
                    hcolor[2] = 1.0f;
                    //Com_Printf( "^2white\n");
                }
                else {

                    hcolor[0] = 1.0f;
                    hcolor[1] = 0.0f;
                    hcolor[2] = 0.0f;

                }

            } else {


                hcolor[0] = 1.0f;
                hcolor[1] = 0.0f;
                hcolor[2] = 0.0f;

            }
            //BB end Colors
            //trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 1.0, 0.2f, 0.2f );
            trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), hcolor[0], hcolor[1], hcolor[2] );
        }

        // blueflag
        if ( item->giTag == PW_BLUEFLAG  ) {
            //BB begin flag team colors
            if (cg_forceColor.integer == 2) {

                if(cg.team == TEAM_BLUE) {
                    bbcolor = cg_teamColor.string;
                } else {
                    bbcolor = cg_enemyColor.string;
                }

                if (Q_stricmp(bbcolor, "blue") == 0) {
                    hcolor[0] = 0.0f;
                    hcolor[1] = 0.0f;
                    hcolor[2] = 1.0f;
                    //Com_Printf( "^2blue\n");
                }
                else if (Q_stricmp(bbcolor, "red") == 0) {
                    hcolor[0] = 1.0f;
                    hcolor[1] = 0.0f;
                    hcolor[2] = 0.0f;
                    //Com_Printf( "^2red\n");
                }
                else if (Q_stricmp(bbcolor, "yellow") == 0) {
                    hcolor[0] = 1.0f;
                    hcolor[1] = 1.0f;
                    hcolor[2] = 0.0f;
                    //Com_Printf( "^2yellow\n");
                }
                else if (Q_stricmp(bbcolor, "orange") == 0) {
                    hcolor[0] = 1.0f;
                    hcolor[1] = 0.45f;
                    hcolor[2] = 0.0f;
                    //Com_Printf( "^2orange\n");
                }
                else if (Q_stricmp(bbcolor, "green") == 0) {
                    hcolor[0] = 0.0f;
                    hcolor[1] = 1.0f;
                    hcolor[2] = 0.0f;
                    //Com_Printf( "^2green\n");
                }
                else if (Q_stricmp(bbcolor, "pink") == 0) {
                    hcolor[0] = 1.0f;
                    hcolor[1] = 0.0f;
                    hcolor[2] = 1.0f;
                    //Com_Printf( "^2pink\n");
                }
                else if (Q_stricmp(bbcolor, "aqua") == 0) {
                    hcolor[0] = 0.0f;
                    hcolor[1] = 1.0f;
                    hcolor[2] = 1.0f;
                    //Com_Printf( "^2aqua\n");
                }
                else if (Q_stricmp(bbcolor, "black") == 0) {
                    hcolor[0] = 0.5f;
                    hcolor[1] = 0.5f;
                    hcolor[2] = 0.5f;
                    //Com_Printf( "^2black\n");
                }
                else if (Q_stricmp(bbcolor, "white") == 0) {
                    hcolor[0] = 1.0f;
                    hcolor[1] = 1.0f;
                    hcolor[2] = 1.0f;
                    //Com_Printf( "^2white\n");
                }
                else {

                    hcolor[0] = 0.0f;
                    hcolor[1] = 0.0f;
                    hcolor[2] = 1.0f;

                }

            } else {

                hcolor[0] = 0.0f;
                hcolor[1] = 0.0f;
                hcolor[2] = 1.0f;
            }
            //BB end Colors
            //trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.2f, 0.2f, 1 );
            trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), hcolor[0], hcolor[1], hcolor[2] );
        }
    }

#ifndef MISSIONPACK
    //alliance banshee - rune glow :)
    if (item->giType == IT_PERSISTANT_POWERUP ) {

        if (item->giTag == PW_RUNEREGEN) {
            runeColor = 1;//red
        }
        if (item->giTag == PW_RUNERESIST) {
            runeColor = 2;//green
        }
        if (item->giTag == PW_RUNEDAMAGE) {
            runeColor = 3;//blue
        }
        if (item->giTag == PW_RUNEHASTE) {
            runeColor = 4;//yellowish
        }
        if (item->giTag == PW_RUNEAMMOREGEN) {
            runeColor = 5;//orange
        }
        if (item->giTag == PW_RUNEVAMPIRE) {
            runeColor = 6;//pink
        }
        //INCIN --- local variable 'runeColor' may be used without having been initialized
        CG_RuneGlow ( cent, runeColor);
        //CG_RuneFX ( cent, runeColor);
    }
#endif

    // accompanying rings / spheres for powerups

    if ( !cg_simpleItems.integer ) {
        vec3_t spinAngles;

        VectorClear( spinAngles );

        if ( item->giType == IT_HEALTH || item->giType == IT_POWERUP ||  item->giType == IT_PERSISTANT_POWERUP ) {
            if ( ( ent.hModel = cg_items[es->modelindex].models[1] ) != 0 ) {
                if ( item->giType == IT_POWERUP || item->giType == IT_PERSISTANT_POWERUP ) {
                    ent.origin[2] += 12;
                    spinAngles[1] = ( cg.time & 1023 ) * 360 / -1024.0f;
                }
                AnglesToAxis( spinAngles, ent.axis );

                // scale up if respawning
                if ( frac != 1.0 ) {
                    VectorScale( ent.axis[0], frac, ent.axis[0] );
                    VectorScale( ent.axis[1], frac, ent.axis[1] );
                    VectorScale( ent.axis[2], frac, ent.axis[2] );
                    ent.nonNormalizedAxes = qtrue;
                }
                trap_R_AddRefEntityToScene( &ent );
            }
        }
    }
}

//============================================================================

/*
===============
CG_Missile
===============
*/
static void CG_Missile( centity_t *cent ) {
    refEntity_t			ent;
    entityState_t		*s1;
    const weaponInfo_t		*weapon;
    // Alliance: Myrkul
    vec3_t  velocity;
    int team, enemy;


    // Joe
    //    CG_Printf("CG_Missile()\n");

    s1 = &cent->currentState;
    if ( s1->weapon > WP_NUM_WEAPONS ) {
        s1->weapon = 0;
    }
    weapon = &cg_weapons[s1->weapon];

    // calculate the axis
    VectorCopy( s1->angles, cent->lerpAngles);

    // add trails
    if ( weapon->missileTrailFunc ) {
        weapon->missileTrailFunc( cent, weapon );
    }
    /*
    	if ( cent->currentState.modelindex == TEAM_RED ) {
    		col = 1;
    	}
    	else if ( cent->currentState.modelindex == TEAM_BLUE ) {
    		col = 2;
    	}
    	else {
    		col = 0;
    	}
     
    	// add dynamic light
    	if ( weapon->missileDlight ) {
    		trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, 
    			weapon->missileDlightColor[col][0], weapon->missileDlightColor[col][1], weapon->missileDlightColor[col][2] );
    	}
    */

    team = getColorFromString(cg_teamColor.string);
    enemy = getColorFromString(cg_enemyColor.string);

    // add dynamic light
    if ( weapon->missileDlight 
		&& cent->currentState.weapon != WP_GRAPPLING_HOOK 
		//&& cent->currentState.weapon != WP_GRAPPLING_HOOK
		) {

        // BB COLORS: tint rocket's dynamic light color
        // owner num sent in otherEntityNum of missile
        // changed to clientNum
        if ( cgs.gametype >= GT_TEAM ) {

			if (cg_forceColor.integer == 2) {

                //SAME TEAM
				switch(cg.team)//if (cg.team == TEAM_BLUE ){
				{
					case TEAM_BLUE:	
						if (cgs.clientinfo[s1->clientNum].team == TEAM_BLUE) 
						{
							switch(team)
							{
								case BC_BLUE:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, 0, 0, 1);
									break;
								case BC_RED:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,1, 0, 0);
									break;
								case BC_ORANGE:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,1, 0.43f, 0);
									break;
								case BC_PINK:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,1, 0, 1);
									break;
								case BC_GREEN:
									 trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,0, 1, 0);
									break;
								case BC_AQUA:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,0, 1, 1);
									break;
								case BC_BLACK:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,0.25f, 0.25f, 0.25f);
									break;
								case BC_WHITE:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,1, 1, 1);
									break;
								case BC_YELLOW:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, 1, 1, 0);
									break;
							}
						} 
						else 
						{
                       		switch(enemy)
							{
								case BC_BLUE:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, 0, 0, 1);
									break;
								case BC_RED:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,1, 0, 0);
									break;
								case BC_ORANGE:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,1, 0.43f, 0);
									break;
								case BC_PINK:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,1, 0, 1);
									break;
								case BC_GREEN:
									 trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,0, 1, 0);
									break;
								case BC_AQUA:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,0, 1, 1);
									break;
								case BC_BLACK:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,0.25f, 0.25f, 0.25f);
									break;
								case BC_WHITE:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,1, 1, 1);
									break;
								case BC_YELLOW:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, 1, 1, 0);
									break;
							}
						}
						break;

					case TEAM_RED:
				//}
				//else 
				//{

						if (cgs.clientinfo[s1->clientNum].team == TEAM_BLUE ) 
						{
							switch(enemy)
							{
								case BC_BLUE:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, 0, 0, 1);
									break;
								case BC_RED:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,1, 0, 0);
									break;
								case BC_ORANGE:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,1, 0.43f, 0);
									break;
								case BC_PINK:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,1, 0, 1);
									break;
								case BC_GREEN:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,0, 1, 0);
									break;
								case BC_AQUA:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,0, 1, 1);
									break;
								case BC_BLACK:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,0.25f, 0.25f, 0.25f);
									break;
								case BC_WHITE:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,1, 1, 1);
									break;
								case BC_YELLOW:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, 1, 1, 0);
									break;
							}
										
						} 
						else 
						{
							switch(team)
							{
								case BC_BLUE:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, 0, 0, 1);
									break;
								case BC_RED:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,1, 0, 0);
									break;
								case BC_ORANGE:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,1, 0.43f, 0);
									break;
								case BC_PINK:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,1, 0, 1);
									break;
								case BC_GREEN:
									 trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,0, 1, 0);
									break;
								case BC_AQUA:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,0, 1, 1);
									break;
								case BC_BLACK:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,0.25f, 0.25f, 0.25f);
									break;
								case BC_WHITE:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,1, 1, 1);
									break;
								case BC_YELLOW:
									trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, 1, 1, 0);
									break;
							}
						}
					break;	
                }
            }//if (cg_forceColor.integer == 2)  
			else if (cgs.clientinfo[s1->clientNum].team == TEAM_BLUE) 
			{
                trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,
                                       0, 0, 1);
            } 
			else
			{
                trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,
                                       1, 0, 0);
			}

        } //if ( cgs.gametype >= GT_TEAM ) 
		else 
		{
            //BB COLORS
            trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,
                                   1, 1, 0);
        }
    }

    // add missile sound
    if ( weapon->missileSound ) {

        BG_EvaluateTrajectoryDelta( &cent->currentState.pos, cg.time, velocity );

        trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->missileSound );
    }

    // create the render entity
    memset (&ent, 0, sizeof(ent));
    VectorCopy( cent->lerpOrigin, ent.origin);
    VectorCopy( cent->lerpOrigin, ent.oldorigin);

    if ( cent->currentState.weapon == WP_PLASMAGUN ) {
        ent.reType = RT_SPRITE;
        ent.radius = 16;
        ent.rotation = 0;
        ent.customShader = cgs.media.plasmaBallShader;

        // Alliance: Myrkul: team-colored plasma projectiles
        // changed to clientNum

        if (cgs.gametype >= GT_TEAM) {
            if (cgs.clientinfo[cent->currentState.clientNum].team == TEAM_BLUE
                    && cgs.media.plasmaBallShader_blue) {

                ent.customShader = cgs.media.plasmaBallShader_blue;

            } else
                ent.customShader = cgs.media.plasmaBallShader_red;

            ent.shaderRGBA[0] = ent.shaderRGBA[1] = ent.shaderRGBA[2] =
                                                        ent.shaderRGBA[3] = 255;
        }

        trap_R_AddRefEntityToScene( &ent );
        return;
    }

    //BB
    // light flare around rocket
if (cent->currentState.weapon == WP_ROCKET_LAUNCHER) {}
    //BB end

    // flicker between two skins





    ent.skinNum = cg.clientFrame & 1;
    ent.hModel = weapon->missileModel;
    ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;

#ifdef MISSIONPACK

    if ( cent->currentState.weapon == WP_PROX_LAUNCHER ) {
        if (s1->generic1 == TEAM_BLUE) {
            ent.hModel = cgs.media.blueProxMine;
        }
    }
#endif

    // Alliance: Myrkul: BFG mortar: orient BFG model toward velocity
    //if (s1->weapon == WP_BFG && BG_battleWeaponsEnabled()) {
    //VectorCopy(velocity, ent.axis[0]);
    //VectorNormalize(ent.axis[0]);
    //} else {
    // convert direction of travel into axis
    if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
        ent.axis[0][2] = 1;
    }
    //}

    // spin as it moves
    if ( s1->pos.trType != TR_STATIONARY ) {
        RotateAroundDirection( ent.axis, cg.time / 4 );
    } else {
#ifdef MISSIONPACK
        if ( s1->weapon == WP_PROX_LAUNCHER ) {
            AnglesToAxis( cent->lerpAngles, ent.axis );
        } else
#endif
        {
            RotateAroundDirection( ent.axis, s1->time );
        }
    }

    // BB COLORS: team-colored rockets
    // changed to clientNum
    if (cgs.gametype >= GT_TEAM) {

        if (cent->currentState.weapon == WP_ROCKET_LAUNCHER) {

            if (cgs.clientinfo[cent->currentState.clientNum].team == TEAM_BLUE) {

                ent.customSkin = cgs.media.rocketSkin_blue;
            } else
                ent.customSkin = cgs.media.rocketSkin_red;

        }

        if (cent->currentState.weapon == WP_BFG) {

            if (cgs.clientinfo[cent->currentState.clientNum].team == TEAM_BLUE) {

                ent.customSkin = cgs.media.bfgMissleSkin_blue;
            } else
                ent.customSkin = cgs.media.bfgMissleSkin_red;

        }

        // BB COLORS: brighter, team-specific grenade shaders
        // changed to clientNum
        if (cent->currentState.weapon == WP_HANDGRENADE) {
            if (cgs.clientinfo[cent->currentState.clientNum].team == TEAM_BLUE) {
                ent.customSkin = cgs.media.handgrenadeSkin_blue;
            } else {
                ent.customSkin = cgs.media.handgrenadeSkin_red;
            }
        }
		if (cent->currentState.weapon == WP_GRENADE_LAUNCHER) {
            if (cgs.clientinfo[cent->currentState.clientNum].team == TEAM_BLUE) {
                ent.customSkin = cgs.media.grenadeSkin_blue;
            } else {
                ent.customSkin = cgs.media.grenadeSkin_red;
			}
		}
    }
    // add to refresh list, possibly with quad glow
    CG_AddRefEntityWithPowerups( &ent, s1, TEAM_FREE );
}

// Alliance
/*
===============
CG_Offhand_Grapple_Air
 
This is called when a grapple is being thrown through the air
===============
*/

// Joe debugging, hook pull predict
//INCIN -- commented these out, not being used
//extern int movestate;
//extern int hookStartTime;
// end Joe
extern vmCvar_t cg_noTrailOnPull; // alliance

// Joe - took off static
void CG_Offhand_Grapple_Air(centity_t *cent) {
    // blatantly ripped from CG_Missile

    refEntity_t			ent;
    entityState_t		*s1;

    // begin Joe - grapple pull prediction?

    // trace server does with missiles to detect impact:
    //
    //  ** hey I can do this
    //  BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
    //
    //  trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins,
    //    ent->r.maxs, origin, ent->r.ownerNum, ent->clipmask );
    //
    // here's the cgame trace for the lightning bolt
    //
    // VectorMA( muzzlePoint, LIGHTNING_RANGE, forward, endPoint );
    // CG_Trace( &trace, muzzlePoint, vec3_origin, vec3_origin, endPoint,
    //	cent->currentState.number, MASK_SHOT );
    //
    // can't find in bg_pmove.c where player collides with wall

    // ok this is the real thing:
    // doesn't really work yet
    // test it with '/cg_showmiss 1'
    // current position is lerpOrigin, which is between
    // currentState.pos and nextState.pos I think

    // should only do this for local client
    /*
            if (cg_predictHookPull.integer)
            {
     
                trace_t tr; 
                vec3_t lastPos, nextPos;
     
                // box is 0 for hook
                vec3_t mins = {0,0,0};
                vec3_t maxs = {0,0,0};
     
                // get an old position somehow
                // current position is really cent->lerpOrigin
                // see call stack:
     
                // CG_CalcEntityLerpPositions()  // current position calc here
                // CG_AddCentity()  // cent added here
                // CG_AddPacketEntities()
                // CG_DrawActiveFrame()  // main loop
     
                BG_EvaluateTrajectory( &cent->currentState.pos, cg.time - 50,
                                       lastPos ); 
                BG_EvaluateTrajectory( &cent->currentState.pos, cg.time,
                                       nextPos );
                // like CG_RocketTrail() ? 
                // BG_EvaluateTrajectory( &s1->pos, cent->trailTime, lastPos );
     
                // do a trace and see if it hits something (fraction != 1)
                
                // CG_Trace(trace_t *result, const vec3_t start, const
                //   vec3_t mins, const vec3_t maxs, const vec3_t end, int
                //   skipNumber, int mask)
     
                // not sure what to put for args
                CG_Trace( &tr, 
                          lastPos, mins, maxs,
                          cent->lerpOrigin, cent->currentState.otherEntityNum,
                          MASK_SHOT);
     
                // grapple hit wall/pull prediction here
                // make sure fraction isn't 0
                // no test for start in solid or not hit wall yet
                if (tr.fraction && tr.fraction != 1)
                {
                    // prediction check against server in OffHand_grappleImpact()
                    CG_Printf("CG: grapple hit something @ %d snaptime "
                              "%d\n", cg.time, cg.latestSnapshotTime); 
     
                    // these are used right before the Pmove() call in
                    // CG_PredictPlayerState()
                    hookStartTime = cg.latestSnapshotTime;
                    movestate = 3;
     
                    // will this just get overwritten later?
                    // yes
                    // might have to do this before
                    // CG_PredictPlayerState()
                    // which we do
                    // or in bg_pmove.c
                    cg.predictedPlayerState.pm_flags |= PMF_GRAPPLE_PULL;
                }
            }
    */
    // end Joe


    //	CG_Printf("CG_Offhand_Grapple_Air()\n");

    // calculate the axis
    s1 = &cent->currentState;
    VectorCopy( s1->angles, cent->lerpAngles);

    // add trails
    // only trail before hit wall
    if (s1->pos.trType != TR_STATIONARY || ! cg_noTrailOnPull.integer) // alliance

        // Joe new grapples
    {
        // cg_grapple.missileTrailFunc(cent);
        weaponInfo_t wi;
        wi.trailRadius = 64;
        wi.wiTrailTime = 2000;
        // CG_RocketTrail(cent, &wi);
        cg_grapple.missileTrailFunc(cent, &wi);
    }
    // end Joe new grapples

    /*
       // add dynamic light
       if ( cg_grapple.missileDlight ) {
           trap_R_AddLightToScene(cent->lerpOrigin, cg_grapple.missileDlight,
                                  cg_grapple.missileDlightColor[0], cg_grapple.missileDlightColor[1], cg_grapple.missileDlightColor[2] );
       }
    */

    // add missile sound
    // looping grapple sound
#if 0
    if ( cg_grapple.firingSound ) {
        vec3_t	velocity;

        BG_EvaluateTrajectoryDelta( &cent->currentState.pos, cg.time, velocity );

        trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, cg_grapple.firingSound );
    }
#endif

    // nonlooping sound, seemed to loop anyway
    //	  if ( cg_grapple.fireSound ) {
    //		  vec3_t	velocity;
    //
    //		  BG_EvaluateTrajectoryDelta( &cent->currentState.pos, cg.time,
    //					      velocity );
    //
    //		  trap_S_StartSound( NULL, cent->currentState.number, CHAN_ITEM,
    //				     cg_grapple.fireSound );
    //	  }

    // create the render entity
    memset (&ent, 0, sizeof(ent));
    //	CG_Printf("grapple_air origin: %f %f %f\n", cent->lerpOrigin[0], cent->lerpOrigin[2], cent->lerpOrigin[2]);
    VectorCopy( cent->lerpOrigin, ent.origin);
    VectorCopy( cent->lerpOrigin, ent.oldorigin);

    // flicker between two skins
    ent.skinNum = cg.clientFrame & 1;
    ent.hModel = cg_grapple.missileModel;
    ent.renderfx = cg_grapple.missileRenderfx | RF_NOSHADOW;

    // convert direction of travel into axis
    if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
        ent.axis[0][2] = /*1*/0;
    }

    // spin as it moves
    if ( s1->pos.trType != TR_STATIONARY ) {
        RotateAroundDirection( ent.axis, cg.time / 4 );
    } else {
        RotateAroundDirection( ent.axis, s1->time );
    }
    trap_R_AddRefEntityToScene( &ent );

}


/*
===============
CG_Grapple
 
This is called when the grapple is sitting up against the wall
===============
*/

// Joe  this function is unused right now
static void CG_Offhand_Grapple( centity_t *cent ) {

    //	CG_Printf("CG_Offhand_Grapple()\n");

#if 0
    refEntity_t			ent;
    entityState_t		*s1;

    // Alliance: FYI, this is called every snap.

    /*
       const weaponInfo_t		*weapon;

       s1 = &cent->currentState;
       if ( s1->weapon > WP_NUM_WEAPONS ) {
           s1->weapon = 0;
       }
       weapon = &cg_weapons[s1->weapon];
    */

    // Alliance: Move the grapple in sync with whatever it's touching
    //	CG_Printf("GRAPPLE\n");

    // calculate the axis
    s1 = &cent->currentState;
    VectorCopy( s1->angles, cent->lerpAngles);

#if 0 // FIXME add grapple pull sound here..?
    // add missile sound
    if ( weapon->missileSound ) {
        trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->missileSound );
    }
#endif

    // non looping grapple sound, seem a little delayed after the wall
    // sound (still lightning gun sound)
    //	  if ( cg_grapple.pullingSound ) {
    //		  vec3_t	velocity;
    //		  BG_EvaluateTrajectoryDelta( &cent->currentState.pos, cg.time,
    //					      velocity );
    //		  trap_S_StartSound( NULL, cent->currentState.number, CHAN_ITEM,
    //				     cg_grapple.pullingSound );
    //	  }

    // Will draw cable if needed
    cg_grapple.missileTrailFunc(cent);

    // create the render entity
    memset (&ent, 0, sizeof(ent));
    VectorCopy( cent->lerpOrigin, ent.origin);
    VectorCopy( cent->lerpOrigin, ent.oldorigin);

    // flicker between two skins
    ent.skinNum = cg.clientFrame & 1;

    ent.hModel = cg_grapple.missileModel;
    ent.renderfx = cg_grapple.missileRenderfx | RF_NOSHADOW;

    // convert direction of travel into axis
    if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
        ent.axis[0][2] = 1;
    }

    trap_R_AddRefEntityToScene( &ent );
    //CG_AddRefEntityWithPowerups(&ent, s1->powerups, TEAM_FREE);

    //	CG_Printf("finished CG_Offhand_Grapple\n");
#endif

    CG_Offhand_Grapple_Air(cent);
}


/*
===============
CG_Grapple
 
This is called when the grapple is sitting up against the wall
===============
*/
static void CG_Grapple( centity_t *cent ) {
    refEntity_t			ent;
    entityState_t		*s1;
    const weaponInfo_t		*weapon;

    s1 = &cent->currentState;
    if ( s1->weapon > WP_NUM_WEAPONS ) {
        s1->weapon = 0;
    }
    weapon = &cg_weapons[s1->weapon];

    // calculate the axis
    VectorCopy( s1->angles, cent->lerpAngles);

#if 0 // FIXME add grapple pull sound here..?
    // add missile sound
    if ( weapon->missileSound ) {
        trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->missileSound );
    }
#endif

    // Will draw cable if needed
    CG_GrappleTrail ( cent, weapon );

    // create the render entity
    memset (&ent, 0, sizeof(ent));
    VectorCopy( cent->lerpOrigin, ent.origin);
    VectorCopy( cent->lerpOrigin, ent.oldorigin);

    // flicker between two skins
    ent.skinNum = cg.clientFrame & 1;
    ent.hModel = weapon->missileModel;
    ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;

    // convert direction of travel into axis
    if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
        ent.axis[0][2] = 1;
    }

    trap_R_AddRefEntityToScene( &ent );
}

/*
===============
CG_Mover
===============
*/
static void CG_Mover( centity_t *cent ) {
    refEntity_t			ent;
    entityState_t		*s1;

    s1 = &cent->currentState;

    // create the render entity
    memset (&ent, 0, sizeof(ent));
    VectorCopy( cent->lerpOrigin, ent.origin);
    VectorCopy( cent->lerpOrigin, ent.oldorigin);
    AnglesToAxis( cent->lerpAngles, ent.axis );

    ent.renderfx = RF_NOSHADOW;

    // flicker between two skins (FIXME?)
    ent.skinNum = ( cg.time >> 6 ) & 1;

    // get the model, either as a bmodel or a modelindex
    if ( s1->solid == SOLID_BMODEL ) {
        ent.hModel = cgs.inlineDrawModel[s1->modelindex];
    } else {
        ent.hModel = cgs.gameModels[s1->modelindex];
    }

    // add to refresh list
    trap_R_AddRefEntityToScene(&ent);

    // add the secondary model
    if ( s1->modelindex2 ) {
        ent.skinNum = 0;
        ent.hModel = cgs.gameModels[s1->modelindex2];
        trap_R_AddRefEntityToScene(&ent);
    }

}

/*
===============
CG_Beam
 
Also called as an event
===============
*/
void CG_Beam( centity_t *cent ) {
    refEntity_t			ent;
    entityState_t		*s1;

    s1 = &cent->currentState;

    // create the render entity
    memset (&ent, 0, sizeof(ent));
    VectorCopy( s1->pos.trBase, ent.origin );
    VectorCopy( s1->origin2, ent.oldorigin );
    AxisClear( ent.axis );
    ent.reType = RT_BEAM;

    ent.renderfx = RF_NOSHADOW;

    // add to refresh list
    trap_R_AddRefEntityToScene(&ent);
}


/*
===============
CG_Portal
===============
*/
static void CG_Portal( centity_t *cent ) {
    refEntity_t			ent;
    entityState_t		*s1;

    s1 = &cent->currentState;

    // create the render entity
    memset (&ent, 0, sizeof(ent));
    VectorCopy( cent->lerpOrigin, ent.origin );
    VectorCopy( s1->origin2, ent.oldorigin );
    ByteToDir( s1->eventParm, ent.axis[0] );
    PerpendicularVector( ent.axis[1], ent.axis[0] );

    // negating this tends to get the directions like they want
    // we really should have a camera roll value
    VectorSubtract( vec3_origin, ent.axis[1], ent.axis[1] );

    CrossProduct( ent.axis[0], ent.axis[1], ent.axis[2] );
    ent.reType = RT_PORTALSURFACE;
    ent.oldframe = s1->powerups;
    ent.frame = s1->frame;		// rotation speed
    ent.skinNum = s1->clientNum/256.0 * 360;	// roll offset

    // add to refresh list
    trap_R_AddRefEntityToScene(&ent);
}


/*
=========================
CG_AdjustPositionForMover
 
Also called by client movement prediction code
=========================
*/
void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out ) {
    centity_t	*cent;
    vec3_t	oldOrigin, origin, deltaOrigin;
    vec3_t	oldAngles, angles, deltaAngles;

    if ( moverNum <= 0 || moverNum >= ENTITYNUM_MAX_NORMAL ) {
        VectorCopy( in, out );
        return;
    }

    cent = &cg_entities[ moverNum ];
    if ( cent->currentState.eType != ET_MOVER ) {
        VectorCopy( in, out );
        return;
    }

    BG_EvaluateTrajectory( &cent->currentState.pos, fromTime, oldOrigin );
    BG_EvaluateTrajectory( &cent->currentState.apos, fromTime, oldAngles );

    BG_EvaluateTrajectory( &cent->currentState.pos, toTime, origin );
    BG_EvaluateTrajectory( &cent->currentState.apos, toTime, angles );

    VectorSubtract( origin, oldOrigin, deltaOrigin );
    VectorSubtract( angles, oldAngles, deltaAngles );

    VectorAdd( in, deltaOrigin, out );

    // FIXME: origin change when on a rotating object
}


/*
=============================
CG_InterpolateEntityPosition
=============================
*/
static void CG_InterpolateEntityPosition( centity_t *cent ) {
    vec3_t		current, next;
    float		f;

    // it would be an internal error to find an entity that interpolates without
    // a snapshot ahead of the current one
    if ( cg.nextSnap == NULL ) {
        CG_Error( "CG_InterpoateEntityPosition: cg.nextSnap == NULL" );
    }

    f = cg.frameInterpolation;
    //CG_Printf("interpolation fraction: %.2f\n", cg.frameInterpolation);

    // this will linearize a sine or parabolic curve, but it is important
    // to not extrapolate player positions if more recent data is available
    BG_EvaluateTrajectory( &cent->currentState.pos, cg.snap->serverTime, current );
    BG_EvaluateTrajectory( &cent->nextState.pos, cg.nextSnap->serverTime, next );

    cent->lerpOrigin[0] = current[0] + f * ( next[0] - current[0] );
    cent->lerpOrigin[1] = current[1] + f * ( next[1] - current[1] );
    cent->lerpOrigin[2] = current[2] + f * ( next[2] - current[2] );

    BG_EvaluateTrajectory( &cent->currentState.apos, cg.snap->serverTime, current );
    BG_EvaluateTrajectory( &cent->nextState.apos, cg.nextSnap->serverTime, next );

    cent->lerpAngles[0] = LerpAngle( current[0], next[0], f );
    cent->lerpAngles[1] = LerpAngle( current[1], next[1], f );
    cent->lerpAngles[2] = LerpAngle( current[2], next[2], f );

}

/*
===============
CG_CalcEntityLerpPositions
 
===============
*/
static void CG_CalcEntityLerpPositions( centity_t *cent ) {
    //unlagged - projectile nudge
    // this will be set to how far forward projectiles will be extrapolated
    int timeshift = 0;
    //unlagged - projectile nudge

    //unlagged - smooth clients #2
    // this is done server-side now - cg_smoothClients is undefined
    // players will always be TR_INTERPOLATE
    /*
        // if this player does not want to see extrapolated players
        if ( !cg_smoothClients.integer ) {
            // make sure the clients use TR_INTERPOLATE
            if ( cent->currentState.number < MAX_CLIENTS ) {
                cent->currentState.pos.trType = TR_INTERPOLATE;
                cent->nextState.pos.trType = TR_INTERPOLATE;
            }
        }
    */
    //unlagged - smooth clients #2

    if ( cent->interpolate && cent->currentState.pos.trType == TR_INTERPOLATE ) {
        CG_InterpolateEntityPosition( cent );
        return;
    }

    // first see if we can interpolate between two snaps for
    // linear extrapolated clients
    if ( cent->interpolate && cent->currentState.pos.trType == TR_LINEAR_STOP &&
            cent->currentState.number < MAX_CLIENTS) {
        CG_InterpolateEntityPosition( cent );
        return;
    }

    //unlagged - timenudge extrapolation
    // interpolating failed (probably no nextSnap), so extrapolate
    // this can also happen if the teleport bit is flipped, but that
    // won't be noticeable
    if ( cent->currentState.number < MAX_CLIENTS &&
            cent->currentState.clientNum != cg.predictedPlayerState.clientNum ) {
        cent->currentState.pos.trType = TR_LINEAR_STOP;
        cent->currentState.pos.trTime = cg.snap->serverTime;
        cent->currentState.pos.trDuration = 1000 / sv_fps.integer;
    }
    //unlagged - timenudge extrapolation

    //unlagged - projectile nudge
    // if it's a missile but not a grappling hook
    if ( cent->currentState.eType == ET_MISSILE &&
		cent->currentState.weapon != WP_GRAPPLING_HOOK){
		// && cent->currentState.weapon != WP_OFFHAND_GRAPPLING_HOOK ) {
        // if it's one of ours
        if ( cent->currentState.otherEntityNum == cg.clientNum ) {
            // extrapolate one server frame's worth - this will correct for tiny
            // visual inconsistencies introduced by backward-reconciling all players
            // one server frame before running projectiles
            timeshift = 1000 / sv_fps.integer;
        }
        // if it's not, and it's not a grenade launcher
        else if ( cent->currentState.weapon != WP_GRENADE_LAUNCHER ) {
            // extrapolate based on cg_projectileNudge
            timeshift = cg_projectileNudge.integer + 1000 / sv_fps.integer;
        }
    }

    // just use the current frame and evaluate as best we can
    //	BG_EvaluateTrajectory( &cent->currentState.pos, cg.time, cent->lerpOrigin );
    //	BG_EvaluateTrajectory( &cent->currentState.apos, cg.time, cent->lerpAngles );
    BG_EvaluateTrajectory( &cent->currentState.pos, cg.time + timeshift, cent->lerpOrigin );
    BG_EvaluateTrajectory( &cent->currentState.apos, cg.time + timeshift, cent->lerpAngles );

    // if there's a time shift
    if ( timeshift != 0 ) {
        trace_t tr;
        vec3_t lastOrigin;

        BG_EvaluateTrajectory( &cent->currentState.pos, cg.time, lastOrigin );

        CG_Trace( &tr, lastOrigin, vec3_origin, vec3_origin, cent->lerpOrigin, cent->currentState.number, MASK_SHOT );

        // don't let the projectile go through the floor
        if ( tr.fraction < 1.0f ) {
            cent->lerpOrigin[0] = lastOrigin[0] + tr.fraction * ( cent->lerpOrigin[0] - lastOrigin[0] );
            cent->lerpOrigin[1] = lastOrigin[1] + tr.fraction * ( cent->lerpOrigin[1] - lastOrigin[1] );
            cent->lerpOrigin[2] = lastOrigin[2] + tr.fraction * ( cent->lerpOrigin[2] - lastOrigin[2] );
        }
    }
    //unlagged - projectile nudge


    // adjust for riding a mover if it wasn't rolled into the predicted
    // player state
    if ( cent != &cg.predictedPlayerEntity ) {
        CG_AdjustPositionForMover( cent->lerpOrigin, cent->currentState.groundEntityNum,
                                   cg.snap->serverTime, cg.time, cent->lerpOrigin );
    }
}

/*
===============
CG_TeamBase
===============
*/
static void CG_TeamBase( centity_t *cent ) {
    refEntity_t model;

#ifdef MISSIONPACK

    vec3_t angles;
    int t, h;
    float c;

    if ( cgs.gametype == GT_CTF || cgs.gametype == GT_RTF || cgs.gametype == GT_1FCTF ){
	//!(cgs.gametype == GT_OBELISK) && !(cgs.gametype == GT_HARVESTER) ){
#else
    if ( cgs.gametype >= GT_CTF) {
#endif

        // show the flag base
        memset(&model, 0, sizeof(model));
        model.reType = RT_MODEL;
        VectorCopy( cent->lerpOrigin, model.lightingOrigin );
        VectorCopy( cent->lerpOrigin, model.origin );
        AnglesToAxis( cent->currentState.angles, model.axis );

//#ifndef MISSIONPACK

        if ( cent->currentState.generic1 == TEAM_RED ) {
            model.hModel = cgs.media.redFlagBaseModel;
            model.customSkin = cgs.media.flagbaseSkin_red;
        } else if ( cent->currentState.generic1 == TEAM_BLUE ) {
            model.hModel = cgs.media.blueFlagBaseModel;
            model.customSkin = cgs.media.flagbaseSkin_blue;
        } else {
            model.hModel = cgs.media.neutralFlagBaseModel;
        }

        trap_R_AddRefEntityToScene( &model );
        //CG_Printf("CG_TeamBase\n");
	}
/*
#else

		if ( cent->currentState.modelindex == TEAM_RED ) {
			model.hModel = cgs.media.redFlagBaseModel;
		}
		else if ( cent->currentState.modelindex == TEAM_BLUE ) {
			model.hModel = cgs.media.blueFlagBaseModel;
		}
		else {
			model.hModel = cgs.media.neutralFlagBaseModel;
		}
		trap_R_AddRefEntityToScene( &model );
    }
#endif
*/
    
#ifdef MISSIONPACK

    else if ( cgs.gametype == GT_OBELISK ) {
        // show the obelisk
        memset(&model, 0, sizeof(model));
        model.reType = RT_MODEL;
        VectorCopy( cent->lerpOrigin, model.lightingOrigin );
        VectorCopy( cent->lerpOrigin, model.origin );
        AnglesToAxis( cent->currentState.angles, model.axis );

        model.hModel = cgs.media.overloadBaseModel;
        trap_R_AddRefEntityToScene( &model );
        // if hit
        if ( cent->currentState.frame == 1) {
            // show hit model
            // modelindex2 is the health value of the obelisk
            c = cent->currentState.modelindex2;
            model.shaderRGBA[0] = 0xff;
            model.shaderRGBA[1] = c;
            model.shaderRGBA[2] = c;
            model.shaderRGBA[3] = 0xff;
            //
            model.hModel = cgs.media.overloadEnergyModel;
            trap_R_AddRefEntityToScene( &model );
        }
        // if respawning
        if ( cent->currentState.frame == 2) {
            if ( !cent->miscTime ) {
                cent->miscTime = cg.time;
            }
            t = cg.time - cent->miscTime;
            h = (cg_obeliskRespawnDelay.integer - 5) * 1000;
            //
            if (t > h) {
                c = (float) (t - h) / h;
                if (c > 1)
                    c = 1;
            } else {
                c = 0;
            }
            // show the lights
            AnglesToAxis( cent->currentState.angles, model.axis );
            //
            model.shaderRGBA[0] = c * 0xff;
            model.shaderRGBA[1] = c * 0xff;
            model.shaderRGBA[2] = c * 0xff;
            model.shaderRGBA[3] = c * 0xff;

            model.hModel = cgs.media.overloadLightsModel;
            trap_R_AddRefEntityToScene( &model );
            // show the target
            if (t > h) {
                if ( !cent->muzzleFlashTime ) {
                    trap_S_StartSound (cent->lerpOrigin, ENTITYNUM_NONE, CHAN_BODY,  cgs.media.obeliskRespawnSound);
                    cent->muzzleFlashTime = 1;
                }
                VectorCopy(cent->currentState.angles, angles);
                angles[YAW] += (float) 16 * acos(1-c) * 180 / M_PI;
                AnglesToAxis( angles, model.axis );

                VectorScale( model.axis[0], c, model.axis[0]);
                VectorScale( model.axis[1], c, model.axis[1]);
                VectorScale( model.axis[2], c, model.axis[2]);

                model.shaderRGBA[0] = 0xff;
                model.shaderRGBA[1] = 0xff;
                model.shaderRGBA[2] = 0xff;
                model.shaderRGBA[3] = 0xff;
                //
                model.origin[2] += 56;
                model.hModel = cgs.media.overloadTargetModel;
                trap_R_AddRefEntityToScene( &model );
            } else {
                //FIXME: show animated smoke
            }
        }
        else {
            cent->miscTime = 0;
            cent->muzzleFlashTime = 0;
            // modelindex2 is the health value of the obelisk
            c = cent->currentState.modelindex2;
            model.shaderRGBA[0] = 0xff;
            model.shaderRGBA[1] = c;
            model.shaderRGBA[2] = c;
            model.shaderRGBA[3] = 0xff;
            // show the lights
            model.hModel = cgs.media.overloadLightsModel;
            trap_R_AddRefEntityToScene( &model );
            // show the target
            model.origin[2] += 56;
            model.hModel = cgs.media.overloadTargetModel;
            trap_R_AddRefEntityToScene( &model );
        }
    } else if ( cgs.gametype == GT_HARVESTER ) {
        // show harvester model
        memset(&model, 0, sizeof(model));
        model.reType = RT_MODEL;
        VectorCopy( cent->lerpOrigin, model.lightingOrigin );
        VectorCopy( cent->lerpOrigin, model.origin );
        AnglesToAxis( cent->currentState.angles, model.axis );

        if ( cent->currentState.modelindex == TEAM_RED ) {
            model.hModel = cgs.media.harvesterModel;
            model.customSkin = cgs.media.harvesterRedSkin;
        } else if ( cent->currentState.modelindex == TEAM_BLUE ) {
            model.hModel = cgs.media.harvesterModel;
            model.customSkin = cgs.media.harvesterBlueSkin;
        } else {
            model.hModel = cgs.media.harvesterNeutralModel;
            model.customSkin = 0;
        }
        trap_R_AddRefEntityToScene( &model );
    }
#endif
}

/*
===============
CG_AddCEntity
 
===============
*/
static void CG_AddCEntity( centity_t *cent ) {
    // event-only entities will have been dealt with already
    if ( cent->currentState.eType >= ET_EVENTS ) {
        return;
    }

    // calculate the current origin
    CG_CalcEntityLerpPositions( cent );

    // add automatic effects
    CG_EntityEffects( cent );

    switch ( cent->currentState.eType ) {
    default:
        CG_Error( "Bad entity type: %i\n", cent->currentState.eType );
        break;
    case ET_INVISIBLE:
    case ET_PUSH_TRIGGER:
    case ET_TELEPORT_TRIGGER:
        break;
    case ET_GENERAL:
        CG_General( cent );
        break;
    case ET_PLAYER:
        CG_Player( cent );
        break;
    case ET_ITEM:
        CG_Item( cent );
        break;
    case ET_MISSILE:
        // Alliance: if the ent is a grapple....
        // XXX removed because we don't need id cgame compatibility anymore
        //		CG_Printf("%d\n", cent->currentState.eFlags);
        // EF_MOVER_STOP is there to mark hook from missile
        if ((cent->currentState.eFlags & EF_MOVER_STOP))
            // already displaying hook with prediction
            CG_Offhand_Grapple_Air(cent);
        else
            // end alliance
            CG_Missile( cent );
        break;
    case ET_MOVER:
        CG_Mover( cent );
        break;
    case ET_BEAM:
        CG_Beam( cent );
        break;
    case ET_PORTAL:
        CG_Portal( cent );
        break;
    case ET_SPEAKER:
        CG_Speaker( cent );
        break;
        // Alliance: grapple in air
        /*	case ET_GRAPPLE_AIR:
        		CG_Offhand_Grapple_Air(cent);
        		break;  */
        // end alliance
    case ET_OFFHAND_GRAPPLE:
        CG_Offhand_Grapple( cent );
        break;

    case ET_GRAPPLE:
        CG_Grapple( cent );
        break;

    case ET_TEAM:
        CG_TeamBase( cent );
        break;
    }
}

/*
===============
CG_AddPacketEntities
 
===============
*/
void CG_AddPacketEntities( void ) {
    int					num;
    centity_t			*cent;
    playerState_t		*ps;

    // set cg.frameInterpolation
    if ( cg.nextSnap ) {
        int		delta;

        delta = (cg.nextSnap->serverTime - cg.snap->serverTime);
        if ( delta == 0 ) {
            cg.frameInterpolation = 0;
        } else {
            cg.frameInterpolation = (float)( cg.time - cg.snap->serverTime ) / delta;
        }
    } else {
        cg.frameInterpolation = 0;	// actually, it should never be used, because
        // no entities should be marked as interpolating
    }

    // the auto-rotating items will all have the same axis








    cg.autoAngles[0] = 0;
    cg.autoAngles[1] = ( cg.time & 2047 ) * 360 / 2048.0;
    cg.autoAngles[2] = 0;

    cg.autoAnglesFast[0] = 0;
    cg.autoAnglesFast[1] = ( cg.time & 1023 ) * 360 / 1024.0f;
    cg.autoAnglesFast[2] = 0;

    AnglesToAxis( cg.autoAngles, cg.autoAxis );
    AnglesToAxis( cg.autoAnglesFast, cg.autoAxisFast );

    // generate and add the entity from the playerstate
    ps = &cg.predictedPlayerState;
    BG_PlayerStateToEntityState( ps, &cg.predictedPlayerEntity.currentState, qfalse );
    CG_AddCEntity( &cg.predictedPlayerEntity );

    // lerp the non-predicted value for lightning gun origins
    CG_CalcEntityLerpPositions( &cg_entities[ cg.snap->ps.clientNum ] );

    //unlagged - early transitioning
    if ( cg.nextSnap ) {
        // pre-add some of the entities sent over by the server
        // we have data for them and they don't need to interpolate
        for ( num = 0 ; num < cg.nextSnap->numEntities ; num++ ) {
            cent = &cg_entities[ cg.nextSnap->entities[ num ].number ];
            if ( cent->nextState.eType == ET_MISSILE || cent->nextState.eType == ET_GENERAL ) {
                // transition it immediately and add it
                CG_TransitionEntity( cent );
                cent->interpolate = qtrue;
                CG_AddCEntity( cent );
            }
        }
    }
    //unlagged - early transitioning

    // add each entity sent over by the server
    for ( num = 0 ; num < cg.snap->numEntities ; num++ ) {
        cent = &cg_entities[ cg.snap->entities[ num ].number ];
        //unlagged - early transitioning
        if ( !cg.nextSnap || cent->nextState.eType != ET_MISSILE && cent->nextState.eType != ET_GENERAL ) {
            //unlagged - early transitioning
            CG_AddCEntity( cent );
        }
    }
}
