// Copyright (C) 1999-2000 Id Software, Inc.
//

#include "g_local.h"
//#ifdef  __ZCAM__
//#include "zcam.h"
//#endif /* __ZCAM__ */


/*
===============
G_DamageFeedback
 
Called just before a snapshot is sent to the given player.
Totals up all damage and generates both the player_state_t
damage values to that client for pain blends and kicks, and
global pain sound events for all clients.
===============
*/
void P_DamageFeedback( gentity_t *player ) {
    gclient_t	*client;
    float	count;
    vec3_t	angles;

    client = player->client;
    if ( client->ps.pm_type == PM_DEAD ) {
        return;
    }

    // total points of damage shot at the player this frame
    count = client->damage_blood + client->damage_armor;
    if ( count == 0 ) {
        return;		// didn't take any damage
    }

    if ( count > 255 ) {
        count = 255;
    }

    // send the information to the client

    // world damage (falling, slime, etc) uses a special code
    // to make the blend blob centered instead of positional
    if ( client->damage_fromWorld ) {
        client->ps.damagePitch = 255;
        client->ps.damageYaw = 255;

        client->damage_fromWorld = qfalse;
    } else {
        vectoangles( client->damage_from, angles );
        client->ps.damagePitch = angles[PITCH]/360.0 * 256;
        client->ps.damageYaw = angles[YAW]/360.0 * 256;
    }

    // play an apropriate pain sound
    if ( (level.time > player->pain_debounce_time)
            && !(player->flags & FL_GODMODE) ) {
        player->pain_debounce_time = level.time + 700;
        G_AddEvent( player, EV_PAIN, player->health );
        client->ps.damageEvent++;
    }


    client->ps.damageCount = count;

    //
    // clear totals
    //
    client->damage_blood = 0;
    client->damage_armor = 0;
    client->damage_knockback = 0;
}



/*
=============
P_WorldEffects
 
Check for lava / slime contents and drowning
=============
*/
void P_WorldEffects( gentity_t *ent ) {
    qboolean	envirosuit;
    int			waterlevel;

    // hugo
    //if ( ent->client->noclip ) {
    if ( ( ent->client->noclip ) ) {
        ent->client->airOutTime = level.time + 12000;	// don't need air
        return;
    }

    waterlevel = ent->waterlevel;
#ifdef MISSIONPACK
    envirosuit = ent->client->ps.powerups[PW_BATTLESUIT] > level.time;
#else
    envirosuit = ent->client->ps.powerups[PW_RUNERESIST] > level.time;
#endif
    //
    // check for drowning
    //
    if ( waterlevel == 3 ) {
        // envirosuit give air
        if ( envirosuit ) {
            ent->client->airOutTime = level.time + 10000;
        }

        // if out of air, start drowning
        if ( ent->client->airOutTime < level.time) {
            // drown!
            ent->client->airOutTime += 1000;
            if ( ent->health > 0 ) {
                // take more damage the longer underwater
                ent->damage += 2;
                if (ent->damage > 15)
                    ent->damage = 15;

                // play a gurp sound instead of a normal pain sound
                if (ent->health <= ent->damage) {
                    G_Sound(ent, CHAN_VOICE, G_SoundIndex("*drown.wav"));
                } else if (rand()&1) {
                    G_Sound(ent, CHAN_VOICE, G_SoundIndex("sound/player/gurp1.wav"));
                } else {
                    G_Sound(ent, CHAN_VOICE, G_SoundIndex("sound/player/gurp2.wav"));
                }

                // don't play a normal pain sound
                ent->pain_debounce_time = level.time + 200;

                G_Damage (ent, NULL, NULL, NULL, NULL,
                          ent->damage, DAMAGE_NO_ARMOR, MOD_WATER);
            }
        }
    } else {
        ent->client->airOutTime = level.time + 12000;
        ent->damage = 2;
    }

    //
    // check for sizzle damage (move to pmove?)
    //
    if (waterlevel &&
            (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) {
        if (ent->health > 0
                && ent->pain_debounce_time <= level.time	) {

            if ( envirosuit ) {
                G_AddEvent( ent, EV_POWERUP_BATTLESUIT, 0 );
            } else {
                if (ent->watertype & CONTENTS_LAVA) {
                    G_Damage (ent, NULL, NULL, NULL, NULL,
                              30*waterlevel, 0, MOD_LAVA);
                }

                if (ent->watertype & CONTENTS_SLIME) {
                    G_Damage (ent, NULL, NULL, NULL, NULL,
                              10*waterlevel, 0, MOD_SLIME);
                }
            }
        }
    }
}



/*
===============
G_SetClientSound
===============
*/
void G_SetClientSound( gentity_t *ent ) {
#ifdef MISSIONPACK
    if( ent->s.eFlags & EF_TICKING ) {
        ent->client->ps.loopSound = G_SoundIndex( "sound/weapons/proxmine/wstbtick.wav");
    } else
#endif
        if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) {
            ent->client->ps.loopSound = level.snd_fry;
        } else {
            ent->client->ps.loopSound = 0;
        }
}



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

/*
==============
ClientImpacts
==============
*/
void ClientImpacts( gentity_t *ent, pmove_t *pm ) {
    int		i, j;
    trace_t	trace;
    gentity_t	*other;

    memset( &trace, 0, sizeof( trace ) );
    for (i=0 ; i<pm->numtouch ; i++) {
        for (j=0 ; j<i ; j++) {
            if (pm->touchents[j] == pm->touchents[i] ) {
                break;
            }
        }
        if (j != i) {
            continue;	// duplicated
        }
        other = &g_entities[ pm->touchents[i] ];

        if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) {
            ent->touch( ent, other, &trace );
        }

        if ( !other->touch ) {
            continue;
        }

        other->touch( other, ent, &trace );
    }

}

/*
============
G_TouchTriggers
 
Find all trigger entities that ent's current position touches.
Spectators will only interact with teleporters.
============
*/
void	G_TouchTriggers( gentity_t *ent ) {
    int			i, num;
    int			touch[MAX_GENTITIES];
    gentity_t	*hit;
    trace_t		trace;
    vec3_t		mins, maxs;
    static vec3_t	range = { 40, 40, 52 };

    if ( !ent->client ) {
        return;
    }

    // dead clients don't activate triggers!
    if ( ent->client->ps.stats[STAT_HEALTH] <= 0 ) {
        return;
    }

    VectorSubtract( ent->client->ps.origin, range, mins );
    VectorAdd( ent->client->ps.origin, range, maxs );

    num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );

    // can't use ent->absmin, because that has a one unit pad
    VectorAdd( ent->client->ps.origin, ent->r.mins, mins );
    VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs );

    for ( i=0 ; i<num ; i++ ) {
        hit = &g_entities[touch[i]];

        if ( !hit->touch && !ent->touch ) {
            continue;
        }

        //NT - There's a better way to do this (e.g. always fire the ->touch
        // func for all flags even if r.contents == 0) but I don't feel
        // like rewriting more CTF code
        if ( ! (hit->flags & FL_DROPPED_ITEM) &&
                Q_stricmp( hit->classname, "team_CTF_blueflag" ) == 0 &&
                ent->client->sess.sessionTeam == TEAM_BLUE &&
            ent->client->ps.powerups[PW_BLUEFLAG] ) {}
        else if ( ! (hit->flags & FL_DROPPED_ITEM) &&
                  Q_stricmp( hit->classname, "team_CTF_redflag" ) == 0 &&
                  ent->client->sess.sessionTeam == TEAM_RED &&
              ent->client->ps.powerups[PW_REDFLAG] ) {}
        else if ( !( hit->r.contents & CONTENTS_TRIGGER ) ) {
            continue;
        }

        // ignore most entities if a spectator
        if (
            (ent->client->sess.sessionTeam == TEAM_SPECTATOR)		||
            (ent->client->sess.sessionTeam == TEAM_SPECTATOR_RED )	||
            (ent->client->sess.sessionTeam == TEAM_SPECTATOR_BLUE )
        ) {
            if ( hit->s.eType != ET_TELEPORT_TRIGGER &&
                    // this is ugly but adding a new ET_? type will
                    // most likely cause network incompatibilities
                    hit->touch != Touch_DoorTrigger) {
                continue;
            }
        }

        // use seperate code for determining if an item is picked up
        // so you don't have to actually contact its bounding box
        if ( hit->s.eType == ET_ITEM ) {
            if ( !BG_PlayerTouchesItem( &ent->client->ps, &hit->s, level.time ) ) {
                continue;
            }
        } else {
            if ( !trap_EntityContact( mins, maxs, hit ) ) {
                continue;
            }
        }

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

        if ( hit->touch ) {
            hit->touch (hit, ent, &trace);
        }

        if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) {
            ent->touch( ent, hit, &trace );
        }
    }

    // if we didn't touch a jump pad this pmove frame
    if ( ent->client->ps.jumppad_frame != ent->client->ps.pmove_framecount ) {
        ent->client->ps.jumppad_frame = 0;
        ent->client->ps.jumppad_ent = 0;
    }
}

/*
=================
SpectatorThink
=================
*/
/*
void Pmove (pmove_t *pmove);
 
void SpectatorThink( gentity_t *ent, usercmd_t *ucmd ) {
    pmove_t	pm;
    gclient_t	*client;//
 
    client = ent->client;
 
    if (ent->client->camera->mode != CAMERA_MODE_OFF) {
        //#ifdef  __ZCAM__
 
        client->ps.commandTime = ucmd->serverTime;
        client->oldbuttons = client->buttons;
        client->buttons = ucmd->buttons;
 
        if ( client->sess.spectatorState != SPECTATOR_FOLLOW ) {
            camera_think(ent);
            return;
        }
    } else {
        //#else
 
        if ( client->sess.spectatorState != SPECTATOR_FOLLOW ) {
            client->ps.pm_type = PM_SPECTATOR;
			//client->ps.pm_type = PM_NOCLIP;
            client->ps.speed = 400;	// faster than normal
 
            // set up for pmove
            memset (&pm, 0, sizeof(pm));
            pm.ps = &client->ps;
            pm.cmd = *ucmd;
            pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;	// spectators can fly through bodies
            pm.trace = trap_Trace;
            pm.pointcontents = trap_PointContents;
 
            // perform a pmove
            Pmove (&pm); //incin -- test
            // save results of pmove
            VectorCopy( client->ps.origin, ent->s.origin );
 
            G_TouchTriggers( ent );
            trap_UnlinkEntity( ent );
        }
 
        client->oldbuttons = client->buttons;
        client->buttons = ucmd->buttons;
 
    }
 
    //#endif
 
    // attack button cycles through spectators
    if ( ( client->buttons & BUTTON_ATTACK ) && ! ( client->oldbuttons & BUTTON_ATTACK ) ) {
        Cmd_FollowCycle_f( ent, 1 );
    }
}
*/


/*
=================
ClientInactivityTimer
 
Returns qfalse if the client is dropped
=================
*/
qboolean ClientInactivityTimer( gclient_t *client ) {
    if ( ! g_inactivity.integer ) {
        // give everyone some time, so if the operator sets g_inactivity during
        // gameplay, everyone isn't kicked
        client->inactivityTime = level.time + 60 * 1000;
        client->inactivityWarning = qfalse;
    } else if ( client->pers.cmd.forwardmove ||
                client->pers.cmd.rightmove ||
                client->pers.cmd.upmove ||
                (client->pers.cmd.buttons & BUTTON_ATTACK) ) {
        client->inactivityTime = level.time + g_inactivity.integer * 1000;
        client->inactivityWarning = qfalse;
    } else if ( !client->pers.localClient ) {
        if ( level.time > client->inactivityTime ) {
            trap_DropClient( client - level.clients, "Dropped due to inactivity" );
            return qfalse;
        }
        if ( level.time > client->inactivityTime - 10000 && !client->inactivityWarning ) {
            client->inactivityWarning = qtrue;
            trap_SendServerCommand( client - level.clients, "cp \"Ten seconds until inactivity drop!\n\"" );
        }
    }
    return qtrue;
}

/*
==================
ClientTimerActions
 
Actions that happen once a second
==================
*/
void ClientTimerActions( gentity_t *ent, int msec ) {
    int g_hr;
    int g_ar;

    gclient_t	*client;
#ifdef MISSIONPACK

    int			maxHealth;
#endif
    g_hr = g_healthRestore.integer;
    g_ar = g_armorRestore.integer;

    client = ent->client;
    client->timeResidual += msec;

    while ( client->timeResidual >= 1000 ) {
        client->timeResidual -= 1000;

        // regenerate
#ifdef MISSIONPACK

        if( bg_itemlist[client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) {
            maxHealth = client->ps.stats[STAT_MAX_HEALTH] / 2;
        } else if ( client->ps.powerups[PW_REGEN] ) {
            maxHealth = client->ps.stats[STAT_MAX_HEALTH];
        } else {
            maxHealth = 0;
        }
        if( maxHealth ) {
            if ( ent->health < maxHealth ) {
                ent->health += 15;
                if ( ent->health > maxHealth * 1.1 ) {
                    ent->health = maxHealth * 1.1;
                }
                G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
            } else if ( ent->health < maxHealth * 2) {
                ent->health += 5;
                if ( ent->health > maxHealth * 2 ) {
                    ent->health = maxHealth * 2;
                }
                G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
            }
        }
#else

        // BB - Regen Rune
        if ( client->ps.powerups[PW_RUNEREGEN] ) {

            //int g_hr;
            //int g_ar;
            //int healthmax;
            //int armormax;

            //BATTLEBOTS_DEFAULT_HEALTH_RESTORE_MAX == 160
            //BATTLEBOTS_DEFAULT_ARMOR_RESTORE_MAX == 120
            int healthmax = (g_healthRestoreMax.integer <= (int)BATTLEBOTS_DEFAULT_HEALTH_RESTORE_MAX) ? g_healthRestoreMax.integer : (int)BATTLEBOTS_DEFAULT_HEALTH_RESTORE_MAX;
            int armormax = (g_armorRestoreMax.integer <= (int)BATTLEBOTS_DEFAULT_ARMOR_RESTORE_MAX) ? g_armorRestoreMax.integer : (int)BATTLEBOTS_DEFAULT_ARMOR_RESTORE_MAX;


            //g_healthRestore
            //g_armorRestore, "g_armorRestore", "0", CVAR_LATCH, 0, qfalse },

            //g_healthRestoreMax;
            //g_armorRestoreMax

            switch(g_hr) {
            case 0:
                if ( ent->health < client->ps.stats[STAT_MAX_HEALTH]) {
                    //ent->health += 15;
                    client->ps.stats[STAT_HEALTH] += 15;
                    G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
                } else if ( ent->health < client->ps.stats[STAT_MAX_HEALTH] * 1.5) {
                    //ent->health += 5;
                    client->ps.stats[STAT_HEALTH] += 5;
                    G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
                }
                break;

            case 1:
                //G_Printf("Stat Max Health = %i\n", client->ps.stats[STAT_MAX_HEALTH]);
                // INCIN --health is set at max values when healthrestore enabled, no need to give extra
                // otherwise its futile code working here
                if ( ent->health < 100 ) {
                    if(g_healthRestoreStep.integer < 20)
                        //ent->health += g_healthRestoreStep.integer; //limit to under 20
                        client->ps.stats[STAT_HEALTH] += g_healthRestoreStep.integer;
                    else
                        //ent->health += 15;
                        client->ps.stats[STAT_HEALTH] += g_healthRestoreStep.integer;
                    G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
                } else if(ent->health < healthmax) {
                    if(g_healthRestoreStep.integer < 20)
                        client->ps.stats[STAT_HEALTH] += (g_healthRestoreStep.integer /3);
                    //ent->health += (g_healthRestoreStep.integer / 3); //limit to under 20
                    else
                        //ent->health += 5;
                        client->ps.stats[STAT_HEALTH] += 5;
                    G_AddEvent( ent, EV_POWERUP_REGEN, 0 );

                }
                break;

            default:

                break;
            }
            //end health part

            switch(g_ar) {
            case 0:	//g_ar
                switch(g_hr)//health restore cvar enabled ?
                {

                case 0://g_hr
                    //G_Printf("Stat Max Health 0,0= %i\n", client->ps.stats[STAT_MAX_HEALTH]);
                    //	G_Printf("Stat Max Armor 0,0= %i\n",  client->ps.stats[STAT_MAX_ARMOR]);
                    //only add armor if health is maxxed --- INCIN
                    if ( ent->health >= (client->ps.stats[STAT_MAX_HEALTH] * 1.5) &&
                            ent->armor < client->ps.stats[STAT_MAX_ARMOR]) {
                        //ent->armor += 15;
                        client->ps.stats[STAT_ARMOR] += 15;
                        /*	if ( ent->armor > client->ps.stats[STAT_MAX_ARMOR] * 1.1 )
                        {
                        ent->armor = client->ps.stats[STAT_MAX_ARMOR] * 1.1;
                        }*/
                        G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
                    } else if ( ent->health >= (client->ps.stats[STAT_MAX_HEALTH] * 1.5) &&
                                ent->armor < client->ps.stats[STAT_MAX_ARMOR] * 1.5) {
                        //ent->armor += 5;
                        client->ps.stats[STAT_ARMOR] += 5;
                        /*		if ( ent->armor > client->ps.stats[STAT_MAX_ARMOR] * 1.5 )
                        {
                        ent->armor = client->ps.stats[STAT_MAX_ARMOR] * 1.5;
                        }*/
                        G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
                    }
                    break;

                case 1://g_hr
                    //G_Printf("Stat Max Armor 0,1= %i\n",  client->ps.stats[STAT_MAX_ARMOR]);
                    if ( ent->health >= healthmax &&
                            ent->armor < client->ps.stats[STAT_MAX_ARMOR]) {
                        //ent->armor += 15;
                        client->ps.stats[STAT_ARMOR] += 15;
                        /*	if ( ent->armor > client->ps.stats[STAT_MAX_ARMOR] * 1.1 )
                        {
                        ent->armor = client->ps.stats[STAT_MAX_ARMOR] * 1.1;
                        }*/
                        G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
                    } else if ( ent->health >= healthmax &&
                                ent->armor < client->ps.stats[STAT_MAX_ARMOR] * 1.5) {
                        //ent->armor += 5;
                        client->ps.stats[STAT_ARMOR] += 5;
                        /*	if ( ent->armor > client->ps.stats[STAT_MAX_ARMOR] * 1.5 )
                        {
                        ent->armor = client->ps.stats[STAT_MAX_ARMOR] * 1.5;
                        }*/
                        G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
                    }
                    break;
                }

                break;

            case 1://g_armore restore is on
                //dont update armor till health is maxed out --- INCIN
                //armor regen dependent on health restore mode

                switch(g_hr)//health restore cvar enabled ?
                {
                case 0://no
                    //150
                    //G_Printf("Stat Max Armor 1,0= %i\n",  client->ps.stats[STAT_MAX_ARMOR]);
                    if ( ent->health >= (client->ps.stats[STAT_MAX_HEALTH] * 1.5) &&
                            ent->armor < STAT_MAX_ARMOR ) {
                        if(g_armorRestoreStep.integer < 20)
                            //ent->armor += g_armorRestoreStep.integer;
                            client->ps.stats[STAT_ARMOR] +=  g_armorRestoreStep.integer;
                        else
                            //ent->armor += 15;
                            client->ps.stats[STAT_ARMOR] += 15;
                        /*if ( ent->armor > armormax) {
                        ent->armor = armormax;
                        }*/
                        G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
                    } else if ( ent->health >= (client->ps.stats[STAT_MAX_HEALTH] * 1.5) &&
                                ent->armor < armormax) {
                        if(g_armorRestoreStep.integer < 20)
                            //ent->armor += (g_armorRestoreStep.integer / 3);
                            client->ps.stats[STAT_ARMOR] +=  (g_armorRestoreStep.integer / 3);
                        else
                            //ent->armor += 5;
                            client->ps.stats[STAT_ARMOR] += 5;
                        /*if ( ent->armor > armormax) {
                        ent->armor = armormax;
                        }*/
                        G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
                    }
                    break;

                case 1://yes
                    //G_Printf("Stat Max Armor 1,1= %i\n",  client->ps.stats[STAT_MAX_ARMOR]);
                    if ( ent->health >= healthmax && ent->armor < STAT_MAX_ARMOR ) {
                        if(g_armorRestoreStep.integer < 20)
                            //ent->armor += g_armorRestoreStep.integer;
                            client->ps.stats[STAT_ARMOR] += g_armorRestoreStep.integer;
                        else
                            //ent->armor += 15;
                            client->ps.stats[STAT_ARMOR] += 15;
                        /*if ( ent->armor > armormax) {
                        ent->armor = armormax;
                        }*/
                        G_AddEvent( ent, EV_POWERUP_REGEN, 0 );

                    } else if(ent->health >= healthmax && ent->armor < armormax ) {
                        if(g_armorRestoreStep.integer < 20)
                            //ent->armor += (g_armorRestoreStep.integer / 3);
                            client->ps.stats[STAT_ARMOR] += (g_armorRestoreStep.integer / 3);
                        else
                            //ent->armor += 5;
                            client->ps.stats[STAT_ARMOR] += 5;
                        /*if ( ent->armor > armormax) {
                        ent->armor = armormax;
                        }*/
                        G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
                    }
                    break;
                } //end inside switch
                break;

            default:
                break;
            }//end switch here

            ent->armor = client->ps.stats[STAT_ARMOR];
            ent->health = client->ps.stats[STAT_HEALTH];

        }
        // BB
#endif
        else if ( client->ps.powerups[PW_REGEN] ) {
            if ( ent->health < client->ps.stats[STAT_MAX_HEALTH]) {
                //ent->health += 15;
                client->ps.stats[STAT_HEALTH] += 15;
                if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] * 1.1 ) {
                    //ent->health = client->ps.stats[STAT_MAX_HEALTH] * 1.1;
                    client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] * 1.1;
                }
                G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
            } else if ( ent->health < client->ps.stats[STAT_MAX_HEALTH] * 1.5) {
                //ent->health += 5;
                client->ps.stats[STAT_HEALTH] += 5;
                if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] * 1.5 ) {
                    //ent->health = client->ps.stats[STAT_MAX_HEALTH] * 1.5;
                    client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] * 1.5;
                }
                G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
            }
        }
        // count down armor when over max
        else {
            /*            if ( client->ps.stats[STAT_HEALTH] > client->ps.stats[STAT_MAX_HEALTH] ) {
                            //client->ps.stats[STAT_HEALTH]--;
                            ent->health--;
                        }
                        if ( client->ps.stats[STAT_ARMOR] > client->ps.stats[STAT_MAX_ARMOR] ) {
                            //client->ps.stats[STAT_ARMOR]--;
                            ent->armor--;
                        }*/
        }

        //INCIN -- do this any way
        //If player has regen rune dont remove health
        switch(g_hr) {
        case 0:
#ifndef MISSIONPACK
            if ( client->ps.stats[STAT_HEALTH] > client->ps.stats[STAT_MAX_HEALTH] && ! client->ps.powerups[PW_RUNEREGEN] ) {
                //client->ps.stats[STAT_HEALTH]--;
                //ent->health--;
                client->ps.stats[STAT_HEALTH]--;
            }
#endif
            break;
        case 1:
            if ( client->ps.stats[STAT_HEALTH] > client->ps.stats[STAT_MAX_HEALTH]) {
                //client->ps.stats[STAT_HEALTH]--;
                //ent->health -= 3;
				if(g_damageRegen.integer)
					client->ps.stats[STAT_HEALTH] --;
				else
					client->ps.stats[STAT_HEALTH] -= 3;
            }
            break;
        }

        switch(g_ar) {

        case 0:
#ifndef MISSIONPACK
            if ( client->ps.stats[STAT_ARMOR] > client->ps.stats[STAT_MAX_ARMOR] && ! client->ps.powerups[PW_RUNEREGEN]) {
                //client->ps.stats[STAT_ARMOR]--;
                //ent->armor--;
                client->ps.stats[STAT_ARMOR] -- ;
            }
#endif
            break;

        case 1:
            if ( client->ps.stats[STAT_ARMOR] > client->ps.stats[STAT_MAX_ARMOR]) {
                //ent->armor -= 3;
				if(g_damageRegen.integer)
					client->ps.stats[STAT_ARMOR] --;
				else
					client->ps.stats[STAT_ARMOR] -= 3;
            }
            break;
        }

        ent->armor = client->ps.stats[STAT_ARMOR];
        ent->health = client->ps.stats[STAT_HEALTH];

        // Alliance health restore
        if (healthRestore())
            healthRegen(ent);

        // Alliance: armor restore
        if (armorRestore())
            armorRegen(ent);

        // Alliance: ammo restore
        if (ammoRestore())
            ammoRegen(client);

    }

#ifdef MISSIONPACK
    if( bg_itemlist[client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_AMMOREGEN ) {
        int w, max, inc, t, i;
        int weapList[]={WP_MACHINEGUN,WP_SHOTGUN,WP_GRENADE_LAUNCHER,WP_ROCKET_LAUNCHER,WP_LIGHTNING,WP_RAILGUN,WP_PLASMAGUN,WP_BFG,WP_NAILGUN,WP_PROX_LAUNCHER,WP_CHAINGUN,WP_HANDGRENADE};
        int weapCount = sizeof(weapList) / sizeof(int);
        //
        for (i = 0; i < weapCount; i++) {
            w = weapList[i];

            switch(w) {
            case WP_MACHINEGUN:
                max = 50;
                inc = 4;
                t = 1000;
                break;
            case WP_SHOTGUN:
                max = 10;
                inc = 1;
                t = 1500;
                break;
            case WP_GRENADE_LAUNCHER:
                max = 10;
                inc = 1;
                t = 2000;
                break;
            case WP_ROCKET_LAUNCHER:
                max = 10;
                inc = 1;
                t = 1750;
                break;
            case WP_LIGHTNING:
                max = 50;
                inc = 5;
                t = 1500;
                break;
            case WP_RAILGUN:
                max = 10;
                inc = 1;
                t = 1750;
                break;
            case WP_PLASMAGUN:
                max = 50;
                inc = 5;
                t = 1500;
                break;
            case WP_BFG:
                max = 10;
                inc = 1;
                t = 4000;
                break;
            case WP_NAILGUN:
                max = 10;
                inc = 1;
                t = 1250;
                break;
            case WP_PROX_LAUNCHER:
                max = 5;
                inc = 1;
                t = 2000;
                break;
            case WP_CHAINGUN:
                max = 100;
                inc = 5;
                t = 1000;
                break;
	    //dio
	    case WP_HANDGRENADE:	//INCIN---added
                max = 5;		//INCIN -- shouldnt max = 5, was 10?
                inc = 1;
                t = 2000;
                break;
            default:
                max = 0;
                inc = 0;
                t = 1000;
                break;
            }
            client->ammoTimes[w] += msec;
            if ( client->ps.ammo[w] >= max ) {
                client->ammoTimes[w] = 0;
            }
            if ( client->ammoTimes[w] >= t ) {
                while ( client->ammoTimes[w] >= t )
                    client->ammoTimes[w] -= t;
                client->ps.ammo[w] += inc;
                if ( client->ps.ammo[w] > max ) {
                    client->ps.ammo[w] = max;
                }
            }
        }
    }
#else

    if( client->ps.powerups[PW_RUNEAMMOREGEN] ) {
        int w, max, inc, t, i;
        int weapList[]={WP_MACHINEGUN,WP_SHOTGUN,WP_GRENADE_LAUNCHER,WP_ROCKET_LAUNCHER,WP_LIGHTNING,WP_RAILGUN,WP_PLASMAGUN,WP_BFG,WP_HANDGRENADE};//INCIN -- added WP_HANDGRENADE
        int weapCount = sizeof(weapList) / sizeof(int);
        //
        for (i = 0; i < weapCount; i++) {
            w = weapList[i];

            switch(w) {
            case WP_MACHINEGUN:
                max = 50;
                inc = 4;
                t = 1000;
                break;
            case WP_SHOTGUN:
                max = 10;
                inc = 1;
                t = 1500;
                break;
            case WP_GRENADE_LAUNCHER:
            case WP_HANDGRENADE:	//INCIN---added
                max = 5;		//INCIN -- shouldnt max = 4, was 10?
                inc = 1;
                t = 2000;
                break;

            case WP_ROCKET_LAUNCHER:
                max = 10;
                inc = 1;
                t = 1750;
                break;
            case WP_LIGHTNING:
                max = 50;
                inc = 5;
                t = 1500;
                break;
            case WP_RAILGUN:
                max = 10;
                inc = 1;
                t = 1750;
                break;
            case WP_PLASMAGUN:
                max = 50;
                inc = 5;
                t = 1500;
                break;
            case WP_BFG:
                max = 10;
                inc = 1;
                t = 4000;
                break;
            default:
                max = 0;
                inc = 0;
                t = 1000;
                break;
            }
            client->ammoTimes[w] += msec;
            if ( client->ps.ammo[w] >= max ) {
                client->ammoTimes[w] = 0;
            }
            if ( client->ammoTimes[w] >= t ) {
                while ( client->ammoTimes[w] >= t )
                    client->ammoTimes[w] -= t;
                client->ps.ammo[w] += inc;
                if ( client->ps.ammo[w] > max ) {
                    client->ps.ammo[w] = max;
                }
            }
        }
    }

#endif
}

/*
====================
ClientIntermissionThink
====================
*/
void ClientIntermissionThink( gclient_t *client ) {
    client->ps.eFlags &= ~EF_TALK;
    client->ps.eFlags &= ~EF_FIRING;

    // the level will exit when everyone wants to or after timeouts

    // swap and latch button actions
    client->oldbuttons = client->buttons;
    client->buttons = client->pers.cmd.buttons;
    if ( client->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) & ( client->oldbuttons ^ client->buttons ) ) {
        // this used to be an ^1 but once a player says ready, it should stick
        client->readyToExit = 1;
    }
}


/*
================
ClientEvents
 
Events will be passed on to the clients for presentation,
but any server game effects are handled here
================
*/
void ClientEvents( gentity_t *ent, int oldEventSequence ) {
    int		i; //, j;
    int		event;
    gclient_t *client;
    int		damage;
    vec3_t	dir;
    vec3_t	origin, angles;

#ifdef MISSIONPACK   //INCIN --updated to get TA WIN32 to build

    gitem_t *item; //= NULL;
    gentity_t *drop;// = NULL;
    int j;
#endif	//MISSIONPACK

    client = ent->client;

    if ( oldEventSequence < client->ps.eventSequence - MAX_PS_EVENTS ) {
        oldEventSequence = client->ps.eventSequence - MAX_PS_EVENTS;
    }
    for ( i = oldEventSequence ; i < client->ps.eventSequence ; i++ ) {
        event = client->ps.events[ i & (MAX_PS_EVENTS-1) ];

        switch ( event ) {

        case EV_FIRE_OFFHAND_GRAPPLE:
            //G_Printf("EV_FIRE_OFFHAND_GRAPPLE\n");
            // need client side of these checks
            if (ent->client->offhandhook == NULL 
				&& playerMayFireGrapple(ent)
                    && !freeOnhandGrapple()) {
                Weapon_Offhand_GrapplingHook_Fire (ent);
            }
            break;
            /*
            case EV_FIRE_GRAPPLE:
                //G_Printf("EV_FIRE_GRAPPLE\n");
                // need client side of these checks
                if (ent->client->hook == NULL 
            && playerMayFireGrapple(ent)
            && freeOnhandGrapple()) {
                    Weapon_GrapplingHook_Fire (ent);
                }
                break;
            */
        case EV_FALL_MEDIUM:
            break;
        case EV_FALL_FAR:
            if ( ent->s.eType != ET_PLAYER ) {
                break;		// not in the player model
            }
            if ( g_dmflags.integer & DF_NO_FALLING ) {
                break;
            }
            if ( event == EV_FALL_FAR ) {
                damage = 10;
            } else {
                damage = 5;
            }
            VectorSet (dir, 0, 0, 1);
            ent->pain_debounce_time = level.time + 200;	// no normal pain sound
            G_Damage (ent, NULL, NULL, NULL, NULL, damage, 0, MOD_FALLING);
            break;

        case EV_FIRE_WEAPON:
            FireWeapon( ent );
            break;

            //case EV_DROP_FLAG:
            //break;

        case EV_USE_ITEM1:		// teleporter
            // drop flags in CTF
            Team_DropFlags( ent );
            /*   diordna
                     item = NULL;
                     j = 0;

                     if ( ent->client->ps.powerups[ PW_REDFLAG ] ) {
                         item = BG_FindItemForPowerup( PW_REDFLAG );
                         j = PW_REDFLAG;
                     } else if ( ent->client->ps.powerups[ PW_BLUEFLAG ] ) {
                         item = BG_FindItemForPowerup( PW_BLUEFLAG );
                         j = PW_BLUEFLAG;
                     } else if ( ent->client->ps.powerups[ PW_NEUTRALFLAG ] ) {
                         item = BG_FindItemForPowerup( PW_NEUTRALFLAG );
                         j = PW_NEUTRALFLAG;
                     }

                     if ( item ) {
                         drop = Drop_Item( ent, item, 0 );
                         // decide how many seconds it has left
                         drop->count = ( ent->client->ps.powerups[ j ] - level.time ) / 1000;
                         if ( drop->count < 1 ) {
                             drop->count = 1;
                         }

                         ent->client->ps.powerups[ j ] = 0;
                     }
            */

#ifdef MISSIONPACK

            if ( g_gametype.integer == GT_HARVESTER ) {
                if ( ent->client->ps.generic1 > 0 ) {
                    if ( ent->client->sess.sessionTeam == TEAM_RED ) {
                        item = BG_FindItem( "Blue Cube" );
                    } else {
                        item = BG_FindItem( "Red Cube" );
                    }
                    if ( item ) {
                        for ( j = 0; j < ent->client->ps.generic1; j++ ) {
                            drop = Drop_Item( ent, item, 0 );
                            if ( ent->client->sess.sessionTeam == TEAM_RED ) {
                                drop->spawnflags = TEAM_BLUE;
                            } else {
                                drop->spawnflags = TEAM_RED;
                            }
                        }
                    }
                    ent->client->ps.generic1 = 0;
                }
            }
#endif
            SelectSpawnPoint( ent->client->ps.origin, origin, angles );
            TeleportPlayer( ent, origin, angles );
            break;

        case EV_USE_ITEM2:		// medkit
            ent->health = ent->client->ps.stats[STAT_MAX_HEALTH] + 25;

            break;

#ifdef MISSIONPACK

        case EV_USE_ITEM3:		// kamikaze
            // make sure the invulnerability is off
            ent->client->invulnerabilityTime = 0;
            // start the kamikze
            G_StartKamikaze( ent );
            break;

        case EV_USE_ITEM4:		// portal
            if( ent->client->portalID ) {
                DropPortalSource( ent );
            } else {
                DropPortalDestination( ent );
            }
            break;
        case EV_USE_ITEM5:		// invulnerability
            ent->client->invulnerabilityTime = level.time + 10000;
            break;
#endif


        default:
            break;
        }
    }

}



void BotTestSolid(vec3_t origin);

/*
==============
SendPendingPredictableEvents
==============
*/
void SendPendingPredictableEvents( playerState_t *ps ) {
    gentity_t *t;
    int event, seq;
    int extEvent, number;

    // if there are still events pending
    if ( ps->entityEventSequence < ps->eventSequence ) {
        // create a temporary entity for this event which is sent to everyone
        // except the client who generated the event
        seq = ps->entityEventSequence & (MAX_PS_EVENTS-1);
        event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 );
        // set external event to zero before calling BG_PlayerStateToEntityState
        extEvent = ps->externalEvent;
        ps->externalEvent = 0;
        // create temporary entity for event
        t = G_TempEntity( ps->origin, event );
        number = t->s.number;
        BG_PlayerStateToEntityState( ps, &t->s, qtrue );
        t->s.number = number;
        t->s.eType = ET_EVENTS + event;
        t->s.eFlags |= EF_PLAYER_EVENT;
        t->s.otherEntityNum = ps->clientNum;
        // send to everyone except the client who generated the event
        t->r.svFlags |= SVF_NOTSINGLECLIENT;
        t->r.singleClient = ps->clientNum;
        // set back external event
        ps->externalEvent = extEvent;
    }
}


//MOVED TO g_pmove.c to isolate pmove() function calls and makePmove functions static..
/*
==============
ClientThink
 
This will be called once for each client frame, which will
usually be a couple times for each server frame on fast clients.
 
If "g_synchronousClients 1" is set, this will be called exactly
once for each server frame, which makes for smooth demo recording.
==============
*/
//vmCvar_t cg_pmove_msec;
/*
void ClientThink_real( gentity_t *ent ) {
    gclient_t	*client;
    pmove_t		pm;
    int			oldEventSequence;
    int			msec;
    usercmd_t	*ucmd;
    //Crock - for triple rail
    int         clientnum;
 
    client = ent->client;
 
    // don't think if the client is not yet connected (and thus not yet spawned in)
    if (client->pers.connected != CON_CONNECTED) {
        return;
    }
 
    //Crock - Triple Rail - code by dream
    if (battleWeapons()) {
        clientnum = ent - g_entities;  //stores clients connection #
    }
 
    // mark the time, so the connection sprite can be removed
 
    ucmd = &ent->client->pers.cmd;
 
    // sanity check the command time to prevent speedup cheating
    if ( ucmd->serverTime > level.time + 200 ) {
        ucmd->serverTime = level.time + 200;
        //		G_Printf("serverTime <<<<<\n" );
    }
    if ( ucmd->serverTime < level.time - 1000 ) {
        ucmd->serverTime = level.time - 1000;
        //		G_Printf("serverTime >>>>>\n" );
    }
 
 
//unlagged - backward reconciliation #4
	// frameOffset should be about the number of milliseconds into a frame 
	// this command packet was received, depending on how fast the server
	// does a G_RunFrame()
	client->frameOffset = trap_Milliseconds() - level.frameStartTime;
//unlagged - backward reconciliation #4
 
 
//unlagged - lag simulation #3
	// if the client wants to simulate outgoing packet loss
	if ( client->pers.plOut ) {
		// see if a random value is below the threshhold
		float thresh = (float)client->pers.plOut / 100.0f;
		if ( random() < thresh ) {
			// do nothing at all if it is - this is a lost command
			return;
		}
	}
//unlagged - lag simulation #3
 
 
//unlagged - true ping
	// save the estimated ping in a queue for averaging later
 
	// we use level.previousTime to account for 50ms lag correction
	// besides, this will turn out numbers more like what players are used to
	client->pers.pingsamples[client->pers.samplehead] = level.previousTime + client->frameOffset - ucmd->serverTime;
	client->pers.samplehead++;
	if ( client->pers.samplehead >= NUM_PING_SAMPLES ) {
		client->pers.samplehead -= NUM_PING_SAMPLES;
	}
 
	// initialize the real ping
	if ( g_truePing.integer ) {
		int i, sum = 0;
 
		// get an average of the samples we saved up
		for ( i = 0; i < NUM_PING_SAMPLES; i++ ) {
			sum += client->pers.pingsamples[i];
		}
 
		client->pers.realPing = sum / NUM_PING_SAMPLES;
	}
	else {
		// if g_truePing is off, use the normal ping
		client->pers.realPing = client->ps.ping;
	}
//unlagged - true ping
 
 
//unlagged - lag simulation #2
	// keep a queue of past commands
	client->pers.cmdqueue[client->pers.cmdhead] = client->pers.cmd;
	client->pers.cmdhead++;
	if ( client->pers.cmdhead >= MAX_LATENT_CMDS ) {
		client->pers.cmdhead -= MAX_LATENT_CMDS;
	}
 
	// if the client wants latency in commands (client-to-server latency)
	if ( client->pers.latentCmds ) {
		// save the actual command time
		int time = ucmd->serverTime;
 
		// find out which index in the queue we want
		int cmdindex = client->pers.cmdhead - client->pers.latentCmds - 1;
		while ( cmdindex < 0 ) {
			cmdindex += MAX_LATENT_CMDS;
		}
 
		// read in the old command
		client->pers.cmd = client->pers.cmdqueue[cmdindex];
 
		// adjust the real ping to reflect the new latency
		client->pers.realPing += time - ucmd->serverTime;
	}
//unlagged - lag simulation #2
 
 
//unlagged - backward reconciliation #4
	// save the command time *before* pmove_fixed messes with the serverTime,
	// and *after* lag simulation messes with it :)
	// attackTime will be used for backward reconciliation later (time shift)
	client->attackTime = ucmd->serverTime;
//unlagged - backward reconciliation #4
 
 
//unlagged - smooth clients #1
	// keep track of this for later - we'll use this to decide whether or not
	// to send extrapolated positions for this client
	client->lastUpdateFrame = level.framenum;
//unlagged - smooth clients #1
 
 
//unlagged - lag simulation #1
	// if the client is adding latency to received snapshots (server-to-client latency)
	if ( client->pers.latentSnaps ) {
		// adjust the real ping
		client->pers.realPing += client->pers.latentSnaps * (1000 / sv_fps.integer);
		// adjust the attack time so backward reconciliation will work
		client->attackTime -= client->pers.latentSnaps * (1000 / sv_fps.integer);
	}
//unlagged - lag simulation #1
 
 
//unlagged - true ping
	// make sure the true ping is over 0 - with cl_timenudge it can be less
	if ( client->pers.realPing < 0 ) {
		client->pers.realPing = 0;
	}
//unlagged - true ping
 
 
    msec = ucmd->serverTime - client->ps.commandTime;
    // following others may result in bad times, but we still want
    // to check for follow toggles
    if ( msec < 1 && client->sess.spectatorState != SPECTATOR_FOLLOW ) {
        return;
    }
    if ( msec > 200 ) {
        msec = 200;
    }
*///commented out
/*
   if ( pmove_msec.integer < 8 ) {
       trap_Cvar_Set("pmove_msec", "8");
	trap_Cvar_Update(&pmove_msec);
   } else if (pmove_msec.integer > 33) {
       trap_Cvar_Set("pmove_msec", "33");
	trap_Cvar_Update(&pmove_msec);
   }
*/
/*
if ( pmove_msec.integer < 8 ) {
       trap_Cvar_Set("pmove_msec", "8");
	trap_Cvar_Update(&pmove_msec);
	trap_Cvar_Set("cg_pmove_msec", "8");
	trap_Cvar_Update(&cg_pmove_msec);

   } else if (pmove_msec.integer > 33) {
       trap_Cvar_Set("pmove_msec", "33");
	trap_Cvar_Update(&pmove_msec);
	trap_Cvar_Set("cg_pmove_msec", "33");
	trap_Cvar_Update(&cg_pmove_msec);
   }
*/
/* //commented out
    if ( pmove_fixed.integer || client->pers.pmoveFixed ) {
        ucmd->serverTime = ((ucmd->serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer;
        //if (ucmd->serverTime - client->ps.commandTime <= 0)
        //	return;
    }
 
    //
    // check for exiting intermission
    //
 
 
    if ( level.intermissiontime ) {
        ClientIntermissionThink( client );
        return;
    }
 
    // spectators don't do much
	// alliance - Hugo - added spec red and blue in condition
    if ((client->sess.sessionTeam == TEAM_SPECTATOR)		|| 
		(client->sess.sessionTeam == TEAM_SPECTATOR_RED )	||
		 ( client->sess.sessionTeam == TEAM_SPECTATOR_BLUE ) ) {
        if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) {
           	return;
        }
        SpectatorThink( ent, ucmd );
        return;
    }
 
    // check for inactivity timer, but never drop the local client of a non-dedicated server
    if ( !ClientInactivityTimer( client ) ) {
        return;
    }
 
    // clear the rewards if time
    if ( level.time > client->rewardTime ) {
        client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
    }
 
    if ( client->noclip ) {
        client->ps.pm_type = PM_NOCLIP;
    } else if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
        client->ps.pm_type = PM_DEAD;
    } else {
        client->ps.pm_type = PM_NORMAL;
    }
 
 
    client->ps.gravity = g_gravity.value;
 
    // set speed
    client->ps.speed = g_speed.value;
 
#ifdef MISSIONPACK
 
    if( bg_itemlist[client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) {
        client->ps.speed *= 1.5;
    } else {
#else
		if ( client->ps.powerups[PW_RUNEHASTE] ) {
			client->ps.speed *= 1.2;
		}
#endif
        if ( client->ps.powerups[PW_HASTE] ) {
            client->ps.speed *= 1.3;
        }
#ifdef MISSIONPACK
 
		}
#endif
    // Let go of the hook if we aren't firing
    // Alliance changed to offhand.
   if ((client->offhandhook && !(ucmd->buttons & BUTTON_GRAPPLE)) ) {
        //G_Printf("Freeing hook\n");
        Weapon_Offhand_HookFree(client->offhandhook);
    }
 
	// Let go of the hook if we aren't firing -- default grapple
	if ( client->ps.weapon == WP_GRAPPLING_HOOK &&
		client->hook && !( ucmd->buttons & BUTTON_ATTACK ) ) {
		Weapon_HookFree(client->hook);
	}
 
*///commented out
/*
if (ucmd->buttons & BUTTON_DROPFLAG) {
       //G_Printf("Drop Flag\n");
       //G_AddEvent( ent, EV_DROP_FLAG, 0 );
	Team_DropFlags( ent );
   }
*/
/* //commented out
    // set up for pmove
    oldEventSequence = client->ps.eventSequence;
 
    memset (&pm, 0, sizeof(pm));
 
    // check for the hit-scan gauntlet, don't let the action
    // go through as an attack unless it actually hits something
    if ( client->ps.weapon == WP_GAUNTLET && !( ucmd->buttons & BUTTON_TALK ) &&
            ( ucmd->buttons & BUTTON_ATTACK ) && client->ps.weaponTime <= 0 ) {
        pm.gauntletHit = CheckGauntletAttack( ent );
    }
 
    if ( ent->flags & FL_FORCE_GESTURE ) {
        ent->flags &= ~FL_FORCE_GESTURE;
        ent->client->pers.cmd.buttons |= BUTTON_GESTURE;
    }
 
#ifdef MISSIONPACK
    // check for invulnerability expansion before doing the Pmove
    if (client->ps.powerups[PW_INVULNERABILITY] ) {
        if ( !(client->ps.pm_flags & PMF_INVULEXPAND) ) {
            vec3_t mins = { -42, -42, -42 };
            vec3_t maxs = { 42, 42, 42 };
            vec3_t oldmins, oldmaxs;
 
            VectorCopy (ent->r.mins, oldmins);
            VectorCopy (ent->r.maxs, oldmaxs);
            // expand
            VectorCopy (mins, ent->r.mins);
            VectorCopy (maxs, ent->r.maxs);
            trap_LinkEntity(ent);
            // check if this would get anyone stuck in this player
            if ( !StuckInOtherClient(ent) ) {
                // set flag so the expanded size will be set in PM_CheckDuck
                client->ps.pm_flags |= PMF_INVULEXPAND;
            }
            // set back
            VectorCopy (oldmins, ent->r.mins);
            VectorCopy (oldmaxs, ent->r.maxs);
            trap_LinkEntity(ent);
        }
    }
#endif
 
    pm.ps = &client->ps;
    pm.cmd = *ucmd;
 
    if ( pm.ps->pm_type == PM_DEAD ) {
        pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
    } else if ( ent->r.svFlags & SVF_BOT ) {
        pm.tracemask = MASK_PLAYERSOLID | CONTENTS_BOTCLIP;
    } else {
        pm.tracemask = MASK_PLAYERSOLID;
    }
    pm.trace = trap_Trace;
    pm.pointcontents = trap_PointContents;
    pm.debugLevel = g_debugMove.integer;
    pm.noFootsteps = ( g_dmflags.integer & DF_NO_FOOTSTEPS ) > 0;
 
    pm.pmove_fixed = pmove_fixed.integer | client->pers.pmoveFixed;
    pm.pmove_msec = pmove_msec.integer;
 
    VectorCopy( client->ps.origin, client->oldOrigin );
 
#ifdef MISSIONPACK
 
    if (level.intermissionQueued != 0 && g_singlePlayer.integer) {
        if ( level.time - level.intermissionQueued >= 1000  ) {
            pm.cmd.buttons = 0;
            pm.cmd.forwardmove = 0;
            pm.cmd.rightmove = 0;
            pm.cmd.upmove = 0;
            if ( level.time - level.intermissionQueued >= 2000 && level.time - level.intermissionQueued <= 2500 ) {
                trap_SendConsoleCommand( EXEC_APPEND, "centerview\n");
            }
            ent->client->ps.pm_type = PM_SPINTERMISSION;
        }
    }
    Pmove (&pm);
#else
 
    Pmove (&pm);
#endif
 
    // save results of pmove
    if ( ent->client->ps.eventSequence != oldEventSequence ) {
        ent->eventTime = level.time;
    }
*/ //commented out
//unlagged - smooth clients #2
// clients no longer do extrapolation if cg_smoothClients is 1, because
// skip correction is all handled server-side now
// since that's the case, it makes no sense to store the extra info
// in the client's snapshot entity, so let's save a little bandwidth
/*
    if (g_smoothClients.integer) {
        BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue );
	}
	else {
*/
/* //commented out
        BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );
//	}
//unlagged - smooth clients #2
 
    SendPendingPredictableEvents( &ent->client->ps );
 
    if ( !( ent->client->ps.eFlags & EF_FIRING ) ) {
        client->fireHeld = qfalse;		// for grapple
    }
 
    // use the snapped origin for linking so it matches client predicted versions
 
 
    VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin );
 
    VectorCopy (pm.mins, ent->r.mins);
    VectorCopy (pm.maxs, ent->r.maxs);
 
    ent->waterlevel = pm.waterlevel;
    ent->watertype = pm.watertype;
 
    // execute client events
    ClientEvents( ent, oldEventSequence );
 
    // link entity now, after any personal teleporters have been used
    trap_LinkEntity (ent);
    if ( !ent->client->noclip ) {
        G_TouchTriggers( ent );
    }
 
    // NOTE: now copy the exact origin over otherwise clients can be snapped into solid
    VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
 
    //test for solid areas in the AAS file
//    BotTestAAS(ent->r.currentOrigin);
 
    // touch other objects
    ClientImpacts( ent, &pm );
 
    // save results of triggers and client events
    if (ent->client->ps.eventSequence != oldEventSequence) {
        ent->eventTime = level.time;
    }
 
    // swap and latch button actions
    client->oldbuttons = client->buttons;
    client->buttons = ucmd->buttons;
    client->latched_buttons |= client->buttons & ~client->oldbuttons;
 
 
  
    // begin handgrenade Joe
    // be better with no tick and only 1 charged event
    // note:  no more weapon think funcs in q3
    // should make hg events for sounds
    // game frametime is 50 ms or .05 sec
    // sometimes same level.time repeats in ClientThink()
    if(client->ps.weapon == WP_HANDGRENADE) {
        int clientNum = ent - g_entities;
        // just pressed button, record time
        if (client->buttons & BUTTON_ATTACK ) {
            if(! (client->oldbuttons & BUTTON_ATTACK)) {
                gentity_t *te;

                // old q2 field grenade_time put back in
                // 1.7 seconds
                client->grenade_time = level.time +
                                       (int)((BATTLE_HG_SPEED_TIMER + 0.2) * 1000);

                // Joe - start sound moved here
                // new way
                te = G_TempEntity(ent->r.currentOrigin,
                                  EV_HANDGRENADE_START);
                // weapon owner
                te->s.eventParm = clientNum;
            } else {
                int timeleft = client->grenade_time - level.time;
                // Joe - hmm these make the background sounds choppy
                // tick sound, every 300 ms
                if(timeleft >= 0 && ! (timeleft % 300) && timeleft != client->oldtimeleft) {

                    if (! (ent->client->ps.eFlags & EF_BOUNCE_HALF) )
                        ent->client->ps.eFlags |= EF_BOUNCE_HALF;

                }
                // Joe - for some reason the same time happens more than
                // once, check for it
                // charged sound, every 250 ms

                else if(timeleft < 0 && !(timeleft % 250) && timeleft != client->oldtimeleft) {
                    // EF_BOUNCE flags for missiles, use for players to
                    // show tickting state of handgrenade
                    if(! (ent->client->ps.eFlags & EF_BOUNCE) ) {
                        ent->client->ps.eFlags &= ~EF_BOUNCE_HALF;
                        ent->client->ps.eFlags |= EF_BOUNCE;
                    }
                }

                client->oldtimeleft = timeleft;
            } // end else (client->oldbuttons & BUTTON_ATTACK)
        } // end if (client->buttons & BUTTON_ATTACK)

        else {
            client->oldtimeleft = 0;
        }
    } // end if(client->ps.weapon == WP_HANDGRENADE)
    // end handgrenade Joe


  // check for respawning






  if ( client->ps.stats[STAT_HEALTH] <= 0 ) {

      // wait for the attack button to be pressed
      if ( level.time > client->respawnTime ) {
	
          // forcerespawn is to prevent users from waiting out powerups
          if ( g_forcerespawn.integer > 0 &&
                  ( level.time - client->respawnTime ) > g_forcerespawn.integer * 1000 ) {
              
		respawn( ent );
              return;
          }

          // pressing attack or use is the normal respawn method
          if ( ucmd->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) ) {
              
		respawn( ent );
          }
      }
      return;
  }

  // perform once-a-second actions
  ClientTimerActions( ent, msec );
}
*/

/*
==================
ClientThink
 
A new command has arrived from the client
==================
*/
void ClientThink_real( gentity_t *ent );
void ClientThink( int clientNum ) {
    gentity_t *ent;

    ent = g_entities + clientNum;
    trap_GetUsercmd( clientNum, &ent->client->pers.cmd );

    //unlagged - smooth clients #1
    // this is handled differently now
    /*
        // mark the time we got info, so we can display the
        // phone jack if they don't get any for a while
        ent->client->lastCmdTime = level.time;
    */
    //unlagged - smooth clients #1

    if ( !(ent->r.svFlags & SVF_BOT) && !g_synchronousClients.integer
            //#ifdef  __ZCAM__
            /* camera jitter fix (server side) */
            && (ent->client->sess.sessionTeam != TEAM_SPECTATOR
                &&	ent->client->sess.sessionTeam != TEAM_SPECTATOR_RED
                &&  ent->client->sess.sessionTeam != TEAM_SPECTATOR_BLUE
                || (ent->client->sess.sessionTeam == TEAM_SPECTATOR  && ent->client->sess.spectatorState == SPECTATOR_FOLLOW))
            //#endif /* __ZCAM__ */
       ) {
        ClientThink_real( ent );
    }
}


void G_RunClient( gentity_t *ent ) {
    if ( !(ent->r.svFlags & SVF_BOT) && !g_synchronousClients.integer
            //#ifdef  __ZCAM__
            /* camera jitter fix (server side) */
            && (ent->client->sess.sessionTeam != TEAM_SPECTATOR
                &&	ent->client->sess.sessionTeam != TEAM_SPECTATOR_RED
                &&  ent->client->sess.sessionTeam != TEAM_SPECTATOR_BLUE
                || (ent->client->sess.sessionTeam == TEAM_SPECTATOR
                    && ent->client->sess.spectatorState == SPECTATOR_FOLLOW))
            //#endif /* __ZCAM__ */
       ) {
        return;
    }
    ent->client->pers.cmd.serverTime = level.time;
    ClientThink_real( ent );
}


/*
==================
SpectatorClientEndFrame
 
==================
*/
/*
void SpectatorClientEndFrame( gentity_t *ent ) {
    gclient_t	*cl;
 
    // if we are doing a chase cam or a remote view, grab the latest info
    if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) {
        int		clientNum, flags;
 
        clientNum = ent->client->sess.spectatorClient;
 
        // team follow1 and team follow2 go to whatever clients are playing
        if ( clientNum == -1 ) {
            clientNum = level.follow1;
        } else if ( clientNum == -2 ) {
            clientNum = level.follow2;
        }
        if ( clientNum >= 0 ) {
            cl = &level.clients[ clientNum ];
            if ( cl->pers.connected == CON_CONNECTED && cl->sess.sessionTeam != TEAM_SPECTATOR ) {
                flags = (cl->ps.eFlags & ~(EF_VOTED | EF_TEAMVOTED)) | (ent->client->ps.eFlags & (EF_VOTED | EF_TEAMVOTED));
                ent->client->ps = cl->ps;
                ent->client->ps.pm_flags |= PMF_FOLLOW;
                ent->client->ps.eFlags = flags;
                return;
            } else {
                // drop them to free spectators unless they are dedicated camera followers
                if ( ent->client->sess.spectatorClient >= 0 ) {
                    ent->client->sess.spectatorState = SPECTATOR_FREE;
                    ClientBegin( ent->client - level.clients );
                }
            }
        }
    }
 
    if ( ent->client->sess.spectatorState == SPECTATOR_SCOREBOARD ) {
        ent->client->ps.pm_flags |= PMF_SCOREBOARD;
    } else {
        ent->client->ps.pm_flags &= ~PMF_SCOREBOARD;
    }
}
*/

/*
==================
SpectatorClientEndFrame
 
==================
*/
void SpectatorClientEndFrame( gentity_t *ent ) {
    gclient_t	*cl;

    // if we are doing a chase cam or a remote view, grab the latest info
    if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) {
        int		clientNum, flags;

        clientNum = ent->client->sess.spectatorClient;

        // team follow1 and team follow2 go to whatever clients are playing
        if ( clientNum == -1 ) {
            clientNum = level.follow1;
        } else if ( clientNum == -2 ) {
            clientNum = level.follow2;
        }
        if ( clientNum >= 0 ) {
            cl = &level.clients[ clientNum ];
            // alliance - Hugo - Added spec red & blue in condition
            if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR_RED ) {
                if ( ( cl->pers.connected == CON_CONNECTED ) && (cl->sess.sessionTeam == TEAM_RED ) ) {
                    flags = (cl->ps.eFlags & ~(EF_VOTED | EF_TEAMVOTED)) | (ent->client->ps.eFlags & (EF_VOTED | EF_TEAMVOTED));
                    ent->client->ps = cl->ps;
                    ent->client->ps.pm_flags |= PMF_FOLLOW;
                    ent->client->ps.eFlags = flags;
                    return;
                } else {
                    Cmd_FollowCycle_f( ent, 1 );
                }
            } else if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR_BLUE ) {
                if ( ( cl->pers.connected == CON_CONNECTED ) && ( cl->sess.sessionTeam == TEAM_BLUE ) ) {
                    flags = (cl->ps.eFlags & ~(EF_VOTED | EF_TEAMVOTED)) | (ent->client->ps.eFlags & (EF_VOTED | EF_TEAMVOTED));
                    ent->client->ps = cl->ps;
                    ent->client->ps.pm_flags |= PMF_FOLLOW;
                    ent->client->ps.eFlags = flags;
                    return;
                } else {
                    Cmd_FollowCycle_f( ent, 1 );
                }

            } else {
                if ( ( cl->pers.connected == CON_CONNECTED ) && (cl->sess.sessionTeam != TEAM_SPECTATOR ) ) {
                    flags = (cl->ps.eFlags & ~(EF_VOTED | EF_TEAMVOTED)) | (ent->client->ps.eFlags & (EF_VOTED | EF_TEAMVOTED));
                    ent->client->ps = cl->ps;
                    ent->client->ps.pm_flags |= PMF_FOLLOW;
                    ent->client->ps.eFlags = flags;
                    return;
                } else {
                    // drop them to free spectators unless they are dedicated camera followers
                    if ( ent->client->sess.spectatorClient >= 0 ) {
                        ent->client->sess.spectatorState = SPECTATOR_FREE;
                        ClientBegin( ent->client - level.clients );
                    }
                }
            }
        }
    }

    if ( ent->client->sess.spectatorState == SPECTATOR_SCOREBOARD ) {
        ent->client->ps.pm_flags |= PMF_SCOREBOARD;
    } else {
        ent->client->ps.pm_flags &= ~PMF_SCOREBOARD;
    }
}

/*
==============
ClientEndFrame
 
Called at the end of each server frame for each connected client
A fast client will have multiple ClientThink for each ClientEdFrame,
while a slow client may have multiple ClientEndFrame between ClientThink.
==============
*/
void ClientEndFrame( gentity_t *ent ) {
    int			i;
    clientPersistant_t	*pers;

    //unlagged - smooth clients #1
    int frames;
    //unlagged - smooth clients #1

    // Alliance - Hugo - Added spec red & blue in condition
    if ( ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) ||
            ( ent->client->sess.sessionTeam == TEAM_SPECTATOR_RED ) ||
            ( ent->client->sess.sessionTeam == TEAM_SPECTATOR_BLUE ) ) {
        SpectatorClientEndFrame( ent );
        return;
    }

    pers = &ent->client->pers;

    // turn off any expired powerups
    for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
        if ( ent->client->ps.powerups[ i ] < level.time ) {
            ent->client->ps.powerups[ i ] = 0;
        }
    }

#ifdef MISSIONPACK
    // set powerup for player animation
    if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) {
        ent->client->ps.powerups[PW_GUARD] = level.time;
    }
    if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) {
        ent->client->ps.powerups[PW_SCOUT] = level.time;
    }
    if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_DOUBLER ) {
        ent->client->ps.powerups[PW_DOUBLER] = level.time;
    }
    if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_AMMOREGEN ) {
        ent->client->ps.powerups[PW_AMMOREGEN] = level.time;
    }
    if ( ent->client->invulnerabilityTime > level.time ) {
        ent->client->ps.powerups[PW_INVULNERABILITY] = level.time;
    }
#else
    /*
    // set powerup for player animation
    if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_RUNEHASTE ) {
    ent->client->ps.powerups[PW_RUNEHASTE] = level.time;
    }
    if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_RUNERESIST ) {
    ent->client->ps.powerups[PW_RUNERESIST] = level.time;
    }
    if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_RUNEDAMAGE ) {
    ent->client->ps.powerups[PW_RUNEDAMAGE] = level.time;
    }
    if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_RUNEAMMOREGEN ) {
    ent->client->ps.powerups[PW_RUNEAMMOREGEN] = level.time;
    }
    if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_RUNEREGEN ) {
    ent->client->ps.powerups[PW_RUNEREGEN] = level.time;
    }
    if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_RUNEVAMPIRE ) {
    ent->client->ps.powerups[PW_RUNEVAMPIRE ] = level.time;
    }
    */
#endif

    // save network bandwidth
#if 0
    if ( !g_synchronousClients->integer && ent->client->ps.pm_type == PM_NORMAL ) {
        // FIXME: this must change eventually for non-sync demo recording
        VectorClear( ent->client->ps.viewangles );
    }
#endif

    //
    // If the end of unit layout is displayed, don't give
    // the player any normal movement attributes
    //
    if ( level.intermissiontime ) {
        return;
    }

    // burn from lava, etc
    P_WorldEffects (ent);

    // apply all the damage taken this frame
    P_DamageFeedback (ent);

    //unlagged - smooth clients #1
    // this is handled differently now
    /*
        // add the EF_CONNECTION flag if we haven't gotten commands recently
        if ( level.time - ent->client->lastCmdTime > 1000 ) {
            ent->s.eFlags |= EF_CONNECTION;
        } else {
            ent->s.eFlags &= ~EF_CONNECTION;
        }
    */
    //unlagged - smooth clients #1

    ent->client->ps.stats[STAT_HEALTH] = ent->health;	// FIXME: get rid of ent->health...
    ent->client->ps.stats[STAT_ARMOR] = ent->armor;	// FIXME: get rid of ent->health...

    G_SetClientSound (ent);

    // set the latest information
    //unlagged - smooth clients #2
    // clients no longer do extrapolation if cg_smoothClients is 1, because
    // skip correction is all handled server-side now
    // since that's the case, it makes no sense to store the extra info
    // in the client's snapshot entity, so let's save a little bandwidth
    /*
        if (g_smoothClients.integer) {
            BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue );
    	}
    	else {
    */
    BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );
    //	}
    //unlagged - smooth clients #2

    SendPendingPredictableEvents( &ent->client->ps );

    //unlagged - smooth clients #1
    // mark as not missing updates initially
    ent->client->ps.eFlags &= ~EF_CONNECTION;

    // see how many frames the client has missed
    frames = level.framenum - ent->client->lastUpdateFrame - 1;

    // don't extrapolate more than two frames
    if ( frames > 2 ) {
        frames = 2;

        // if they missed more than two in a row, show the phone jack
        ent->client->ps.eFlags |= EF_CONNECTION;
        ent->s.eFlags |= EF_CONNECTION;
    }

    // did the client miss any frames?
    if ( frames > 0 && g_smoothClients.integer ) {
        // yep, missed one or more, so extrapolate the player's movement
        G_PredictPlayerMove( ent, (float)frames / sv_fps.integer );
        // save network bandwidth
        SnapVector( ent->s.pos.trBase );
    }
    //unlagged - smooth clients #1

    //unlagged - backward reconciliation #1
    // store the client's position for backward reconciliation later
    G_StoreHistory( ent );
    //unlagged - backward reconciliation #1

    // set the bit for the reachability area the client is currently in
    //	i = trap_AAS_PointReachabilityAreaIndex( ent->client->ps.origin );
    //	ent->client->areabits[i >> 3] |= 1 << (i & 7);
}


