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

#include "g_local.h"

//BB moved to g_team.h
//bb_game.c needs to use this too for scoring
//BB moved from g_team.c
//INCIN moved it back -- duplicastes removed and less overhead calls
//Combined battlespawning with team games and move the memset teamgame stuff
// to Initgame area.. it will be initialized regardless of team game or not, 
// but it will help collect spawns easier this way
/*
typedef struct teamgame_s {
    float			last_flag_capture;
    int				last_capture_team;
    flagStatus_t	redStatus;	// CTF
    flagStatus_t	blueStatus;	// CTF

#ifdef MISSIONPACK

    flagStatus_t	flagStatus;	// One Flag CTF
    int				redObeliskAttackedTime;
    int				blueObeliskAttackedTime;
	gentity_t		*neutralObelisk;
#endif
	
    int				redTakenTime;
    int				blueTakenTime;

    //BB
    float lastCarrierDeath[TEAM_BLUE + 1];
    gentity_t *baseFlag[TEAM_BLUE + 2];
    gentity_t *touchableFlag[TEAM_BLUE + 1];
    gentity_t *carrier[TEAM_BLUE + 1];
	
	
	//Battle Spawning Rules
	int numSpawns;
	gentity_t *teamSpawns[MAX_SPAWNS];
//	gentity_t *spawnLocs[MAX_SPAWNS];
	team_t spawnTeams[MAX_SPAWNS];
	
}
teamgame_t;
*/
//BB end move

teamgame_t teamgame;

// BB: new spawning rules
// The point of killing someone in CTF is to take them out of the action for
// some amount of time.  This means when we respawn a player we want to place
// them somewhere that avoids CTF hot spots: flags, carriers and bases.
/*
#define MAX_SPAWNS           48
int numSpawns;
static gentity_t *teamSpawns[MAX_SPAWNS];
static gentity_t *spawnLocs[MAX_SPAWNS];
team_t spawnTeams[MAX_SPAWNS];
*/

//#define BONUS_STRING         S_COLOR_CYAN "BONUS" S_COLOR_WHITE
//#define PENALTY_STRING       S_COLOR_YELLOW "PENALTY" S_COLOR_WHITE

//gentity_t	*neutralObelisk;



void Team_InitGame( void ) {

    //memset(&teamgame, 0, sizeof(teamgame));

    //BB SPAWNS
    // new spawning rules
    //if (g_battleSpawning.integer)
    //    BattleSpawnInit();

    //BB FIXME SCORING
    teamgame.baseFlag[TEAM_RED] =
        teamgame.touchableFlag[TEAM_RED] =
            G_Find (NULL, FOFS(classname), "team_CTF_redflag");

    teamgame.baseFlag[TEAM_BLUE] =
        teamgame.touchableFlag[TEAM_BLUE] =
            G_Find (NULL, FOFS(classname), "team_CTF_blueflag");
    //BB END SCORING


    switch( g_gametype.integer ) {
    case GT_CTF:
        teamgame.redStatus = teamgame.blueStatus = -1; // Invalid to force update
        Team_SetFlagStatus( TEAM_RED, FLAG_ATBASE );
        Team_SetFlagStatus( TEAM_BLUE, FLAG_ATBASE );
        break;
    case GT_RTF:
        teamgame.redStatus = teamgame.blueStatus = -1; // Invalid to force update
        Team_SetFlagStatus( TEAM_RED, FLAG_ATBASE );
        Team_SetFlagStatus( TEAM_BLUE, FLAG_ATBASE );
        break;

#ifdef MISSIONPACK

    case GT_1FCTF:
        teamgame.flagStatus = -1; // Invalid to force update
        Team_SetFlagStatus( TEAM_FREE, FLAG_ATBASE );
        break;
#endif

    default:
        break;
    }
}

int OtherTeam(int team) {
    if (team==TEAM_RED)
        return TEAM_BLUE;
    else if (team==TEAM_BLUE)
        return TEAM_RED;
    return team;
}

const char *TeamName(int team)  {
    if (team==TEAM_RED)
        return "RED";
    else if (team==TEAM_BLUE)
        return "BLUE";
    else if (team==TEAM_SPECTATOR)
        return "SPECTATOR";
    // BB

    else if (team==TEAM_SPECTATOR_RED)
        return "RED SPECTATOR";
    else if (team==TEAM_SPECTATOR_BLUE)
        return "BLUE SPECTATOR";

    // BB
    return "FREE";
}

const char *OtherTeamName(int team) {

    if (team==TEAM_RED)
        return "BLUE";
    else if (team==TEAM_BLUE)
        return "RED";
    else if (team==TEAM_SPECTATOR)
        return "SPECTATOR";
    // BB
    else if (team==TEAM_SPECTATOR_RED)
        return "BLUE SPECTATOR";
    else if (team==TEAM_SPECTATOR_BLUE)
        return "RED SPECTATOR";
    // BB
    return "FREE";
}

const char *TeamColorString(int team) {
    if (team==TEAM_RED)
        return S_COLOR_RED;
    else if (team==TEAM_BLUE)
        return S_COLOR_BLUE;
    else if (team==TEAM_SPECTATOR)
        return S_COLOR_YELLOW;
    // BB
    else if (team==TEAM_SPECTATOR_RED)
        return S_COLOR_RED;
    else if (team==TEAM_SPECTATOR_BLUE)
        return S_COLOR_BLUE;

    // BB
    return S_COLOR_WHITE;
}

// NULL for everyone
void QDECL PrintMsg( gentity_t *ent, const char *fmt, ... ) {
    char		msg[1024];
    va_list		argptr;
    char		*p;

    va_start (argptr,fmt);
    if (vsprintf (msg, fmt, argptr) > sizeof(msg)) {
        G_Error ( "PrintMsg overrun" );
    }
    va_end (argptr);

    // double quotes are bad
    while ((p = strchr(msg, '"')) != NULL)
        *p = '\'';

    trap_SendServerCommand ( ( (ent == NULL) ? -1 : ent-g_entities ), va("print \"%s\"", msg ));
}

/*
==============
AddTeamScore
 
 used for gametype > GT_TEAM
 for gametype GT_TEAM the level.teamScores is updated in AddScore in g_combat.c
==============
*/
void AddTeamScore(vec3_t origin, int team, int score) {
    gentity_t	*te;

    te = G_TempEntity(origin, EV_GLOBAL_TEAM_SOUND );
    te->r.svFlags |= SVF_BROADCAST;

    if ( team == TEAM_RED ) {
        if ( level.teamScores[ TEAM_RED ] + score == level.teamScores[ TEAM_BLUE ] ) {
            //teams are tied sound
            te->s.eventParm = GTS_TEAMS_ARE_TIED;
        } else if ( level.teamScores[ TEAM_RED ] <= level.teamScores[ TEAM_BLUE ] &&
                    level.teamScores[ TEAM_RED ] + score > level.teamScores[ TEAM_BLUE ]) {
            // red took the lead sound
            te->s.eventParm = GTS_REDTEAM_TOOK_LEAD;
        } else {
            // red scored sound
            te->s.eventParm = GTS_REDTEAM_SCORED;
        }
    } else {
        if ( level.teamScores[ TEAM_BLUE ] + score == level.teamScores[ TEAM_RED ] ) {
            //teams are tied sound
            te->s.eventParm = GTS_TEAMS_ARE_TIED;
        } else if ( level.teamScores[ TEAM_BLUE ] <= level.teamScores[ TEAM_RED ] &&
                    level.teamScores[ TEAM_BLUE ] + score > level.teamScores[ TEAM_RED ]) {
            // blue took the lead sound
            te->s.eventParm = GTS_BLUETEAM_TOOK_LEAD;
        } else {
            // blue scored sound
            te->s.eventParm = GTS_BLUETEAM_SCORED;
        }
    }
    level.teamScores[ team ] += score;
}

/*
==============
OnSameTeam
==============
*/
qboolean OnSameTeam( gentity_t *ent1, gentity_t *ent2 ) {
    if ( !ent1->client || !ent2->client ) {
        return qfalse;
    }

    if ( g_gametype.integer < GT_TEAM ) {
        return qfalse;
    }

    //BB CTF-RTF - if either is a spectator, they can't be on the same team
    if ( ent1->client->sess.sessionTeam == TEAM_SPECTATOR
            || ent1->client->sess.sessionTeam == TEAM_SPECTATOR_RED
            || ent1->client->sess.sessionTeam == TEAM_SPECTATOR_BLUE
       ) {
        return qfalse;
    }
    if ( ent2->client->sess.sessionTeam == TEAM_SPECTATOR
            //BB FIXME
            || ent2->client->sess.sessionTeam == TEAM_SPECTATOR_RED
            || ent2->client->sess.sessionTeam == TEAM_SPECTATOR_BLUE

       ) {
        return qfalse;
    }

    if ( ent1->client->sess.sessionTeam == ent2->client->sess.sessionTeam ) {
        return qtrue;
    }

    return qfalse;
}




void Team_SetFlagStatus( int team, flagStatus_t status ) {
    qboolean modified = qfalse;
	//BB CTF-RTF - added status "3" - flag being returned
	static char ctfFlagStatusRemap[] = { '0', '1', '*', '*', '2', '3' };
	static char oneFlagStatusRemap[] = { '0', '1', '2', '3', '4' };

    switch( team ) {
    case TEAM_RED:	// CTF
        if( teamgame.redStatus != status ) {

            teamgame.redStatus = status;


            modified = qtrue;

        }
        break;

    case TEAM_BLUE:	// CTF
        if( teamgame.blueStatus != status ) {

            teamgame.blueStatus = status;



            modified = qtrue;

        }
        break;
#ifdef MISSIONPACK

    case TEAM_FREE:	// One Flag CTF
        if( teamgame.flagStatus != status ) {

            teamgame.flagStatus = status;
            modified = qtrue;

        }
        break;
#endif

    }

    if( modified ) {
        char st[4];

        if( g_gametype.integer >= GT_CTF) {
            st[0] = ctfFlagStatusRemap[teamgame.redStatus];
            st[1] = ctfFlagStatusRemap[teamgame.blueStatus];
            st[2] = 0;
#ifdef MISSIONPACK

        } else {		// GT_1FCTF
            st[0] = oneFlagStatusRemap[teamgame.flagStatus];
            st[1] = 0;
#endif


        }

        trap_SetConfigstring( CS_FLAGSTATUS, st );

        /*
              //G_Printf("^4GOT HERE!\n");
        // BB DEBUG SCORING
        // PULLED FROM ALLIANCE 1.3 SCORING
              // check that state tracked in teamgame is consistent with flag status
              for (team = TEAM_RED; team <= TEAM_BLUE; team++) {

                  G_Printf("%s flag: ", TeamName(team));
                  if (teamgame.touchableFlag[team] == teamgame.baseFlag[team]) {

                      // base flag visible, touchable flag is base flag
                      G_Printf("at base");

        	} else if (teamgame.touchableFlag[team] != NULL) {
        		
        		if (Q_stricmp(teamgame.touchableFlag[team]->classname, "team_CTF_redflag") == 0
        			|| Q_stricmp(teamgame.touchableFlag[team]->classname, "team_CTF_blueflag") == 0) {
        			// no touchable flag, base flag visible 
        			G_Printf("dropped");

        		} else if (
        			(team == TEAM_RED && teamgame.redStatus == FLAG_TAKEN)
        			|| 
        			(team == TEAM_BLUE && teamgame.blueStatus == FLAG_TAKEN)
        			)  {

        			// base flag invisible, touchable flag non-null and not base
        			// flag, touchable flag visible
        			G_Printf("taken");

        			if (teamgame.carrier[team] != NULL) {

        				G_Printf(" by %s, ", teamgame.carrier[team]->client->pers.netname);

        			} else {

        				G_Printf(" none, ");

        			}
        		} 
        		// RTF
        		
        		else if (
        			(team == TEAM_RED && teamgame.redStatus == FLAG_RETURNING)
        			|| 
        			(team == TEAM_BLUE && teamgame.blueStatus == FLAG_RETURNING)
        			)  {

        			// base flag invisible, touchable flag non-null and not base
        			// flag, touchable flag visible
        			G_Printf("returning");
        			
        			if (teamgame.carrier[team] != NULL) {

        				G_Printf(" by %s, ", teamgame.carrier[team]->client->pers.netname);

        			} else {

        				G_Printf(" none, ");

        			}
        		}
        		

                  } else {

                      G_Printf("CORRUPTED");

                  }
                  G_Printf("\n");
              }
        */
        // BB END DEBUG SCORING
    }
}

void Team_CheckDroppedItem( gentity_t *dropped ) {

    if( dropped->item->giTag == PW_REDFLAG ) {

        //BB SCORING
        // keep track of the carrier
        teamgame.carrier[TEAM_RED] = NULL;
        // flag's team no longer has an active flag - player carrying it
        teamgame.touchableFlag[TEAM_RED] = dropped;
        //BB END SCORING

        Team_SetFlagStatus( TEAM_RED, FLAG_DROPPED );

    } else if( dropped->item->giTag == PW_BLUEFLAG ) {

        //BB SCORING
        // keep track of the carrier
        teamgame.carrier[TEAM_BLUE] = NULL;
        // flag's team no longer has an active flag - player carrying it
        teamgame.touchableFlag[TEAM_BLUE] = dropped;
        //BB END SCORING

        Team_SetFlagStatus( TEAM_BLUE, FLAG_DROPPED );

#ifdef MISSIONPACK

    } else if( dropped->item->giTag == PW_NEUTRALFLAG ) {
        Team_SetFlagStatus( TEAM_FREE, FLAG_DROPPED );
#endif

    }
}

/*
================
Team_ForceGesture
================
*/
void Team_ForceGesture(int team) {
    int i;
    gentity_t *ent;

    for (i = 0; i < MAX_CLIENTS; i++) {
        ent = &g_entities[i];
        if (!ent->inuse)
            continue;
        if (!ent->client)
            continue;
        if (ent->client->sess.sessionTeam != team)
            continue;
        //
        ent->flags |= FL_FORCE_GESTURE;
    }
}

/*
================
Team_FragBonuses
 
Calculate the bonuses for flag defense, flag carrier defense, etc.
Note that bonuses are not cumulative.  You get one, they are in importance
order.
================
*/
void Team_FragBonuses(gentity_t *targ, gentity_t *inflictor, gentity_t *attacker) {
    int i;
    gentity_t *ent;
    int flag_pw, enemy_flag_pw;
    int otherteam;
    int tokens;
    gentity_t *flag, *carrier = NULL;
    char *c;
    vec3_t v1, v2;
    int team;

    // no bonus for fragging yourself or team mates
    if (!targ->client || !attacker->client || targ == attacker || OnSameTeam(targ, attacker))
        return;

    // BB begin BUGFIX TEAMKILL
    // no bonus for killing a teammate
    if (OnSameTeam (targ, attacker))
        return;
    // BB end BUGFIX TEAMKILL

    team = targ->client->sess.sessionTeam;
    otherteam = OtherTeam(targ->client->sess.sessionTeam);
    if (otherteam < 0)
        return; // whoever died isn't on a team

    // same team, if the flag at base, check to he has the enemy flag
    //BB CTF-RTF
    if (team == TEAM_RED) {
        flag_pw = PW_REDFLAG;
        enemy_flag_pw = PW_BLUEFLAG;
    } else {
        flag_pw = PW_BLUEFLAG;
        enemy_flag_pw = PW_REDFLAG;
    }
#ifdef MISSIONPACK
    if (g_gametype.integer == GT_1FCTF) {
        enemy_flag_pw = PW_NEUTRALFLAG;
    }
#endif
    // did the attacker frag the flag carrier?
    tokens = 0;
#ifdef MISSIONPACK

    if( g_gametype.integer == GT_HARVESTER ) {
        tokens = targ->client->ps.generic1;
    }
#endif
    if (targ->client->ps.powerups[enemy_flag_pw]) {
        attacker->client->pers.teamState.lastfraggedcarrier = level.time;
        AddScore(attacker, targ->r.currentOrigin, CTF_FRAG_CARRIER_BONUS);

        attacker->client->pers.teamState.fragcarrier++;
        PrintMsg(NULL, "%s" S_COLOR_WHITE " fragged %s's flag carrier!\n",
                 attacker->client->pers.netname, TeamName(team));

        // the target had the flag, clear the hurt carrier
        // field on the other team
        for (i = 0; i < g_maxclients.integer; i++) {
            ent = g_entities + i;
            if (ent->inuse && ent->client->sess.sessionTeam == otherteam)
                ent->client->pers.teamState.lasthurtcarrier = 0;
        }
        return;
    }

    // did the attacker frag a head carrier? other->client->ps.generic1
    if (tokens) {
        attacker->client->pers.teamState.lastfraggedcarrier = level.time;
        AddScore(attacker, targ->r.currentOrigin, CTF_FRAG_CARRIER_BONUS * tokens * tokens);
        attacker->client->pers.teamState.fragcarrier++;
        PrintMsg(NULL, "%s" S_COLOR_WHITE " fragged %s's skull carrier!\n",
                 attacker->client->pers.netname, TeamName(team));

        // the target had the flag, clear the hurt carrier
        // field on the other team
        for (i = 0; i < g_maxclients.integer; i++) {
            ent = g_entities + i;
            if (ent->inuse && ent->client->sess.sessionTeam == otherteam)
                ent->client->pers.teamState.lasthurtcarrier = 0;
        }
        return;
    }

    if (targ->client->pers.teamState.lasthurtcarrier &&
            level.time - targ->client->pers.teamState.lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT &&
            !attacker->client->ps.powerups[flag_pw]) {
        // attacker is on the same team as the flag carrier and
        // fragged a guy who hurt our flag carrier
        AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_DANGER_PROTECT_BONUS);

        attacker->client->pers.teamState.carrierdefense++;
        targ->client->pers.teamState.lasthurtcarrier = 0;

        attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
        team = attacker->client->sess.sessionTeam;
        // add the sprite over the player's head
        attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
        attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
        attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;

        return;
    }

    if (targ->client->pers.teamState.lasthurtcarrier &&
            level.time - targ->client->pers.teamState.lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT) {
        // attacker is on the same team as the skull carrier and
        AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_DANGER_PROTECT_BONUS);

        attacker->client->pers.teamState.carrierdefense++;
        targ->client->pers.teamState.lasthurtcarrier = 0;

        attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
        team = attacker->client->sess.sessionTeam;
        // add the sprite over the player's head
        attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
        attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
        attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;

        return;
    }

    // flag and flag carrier area defense bonuses
    // we have to find the flag and carrier entities

#ifdef MISSIONPACK
    if( g_gametype.integer == GT_OBELISK ) {
        // find the team obelisk
        switch (attacker->client->sess.sessionTeam) {
        case TEAM_RED:
            c = "team_redobelisk";
            break;
        case TEAM_BLUE:
            c = "team_blueobelisk";
            break;
        default:
            return;
        }

    } else if (g_gametype.integer == GT_HARVESTER ) {
        // find the center obelisk
        c = "team_neutralobelisk";
    } else {
#endif
        // find the flag
        switch (attacker->client->sess.sessionTeam) {
        case TEAM_RED:
            c = "team_CTF_redflag";
            break;
        case TEAM_BLUE:
            c = "team_CTF_blueflag";
            break;
        default:
            return;
        }
        // find attacker's team's flag carrier
        for (i = 0; i < g_maxclients.integer; i++) {
            carrier = g_entities + i;
            if (carrier->inuse && carrier->client->ps.powerups[flag_pw])
                break;
            carrier = NULL;
        }
#ifdef MISSIONPACK

    }
#endif
    flag = NULL;
    while ((flag = G_Find (flag, FOFS(classname), c)) != NULL) {
        if (!(flag->flags & FL_DROPPED_ITEM))
            break;
    }

    if (!flag)
        return; // can't find attacker's flag

    // ok we have the attackers flag and a pointer to the carrier
    // check to see if we are defending the base's flag
    VectorSubtract(targ->r.currentOrigin, flag->r.currentOrigin, v1);
    VectorSubtract(attacker->r.currentOrigin, flag->r.currentOrigin, v2);

    if ( ( ( VectorLength(v1) < CTF_TARGET_PROTECT_RADIUS &&
             trap_InPVS(flag->r.currentOrigin, targ->r.currentOrigin ) ) ||
            ( VectorLength(v2) < CTF_TARGET_PROTECT_RADIUS &&
              trap_InPVS(flag->r.currentOrigin, attacker->r.currentOrigin ) ) ) &&
            attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam) {

        // we defended the base flag
        AddScore(attacker, targ->r.currentOrigin, CTF_FLAG_DEFENSE_BONUS);

        attacker->client->pers.teamState.basedefense++;

        attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
        // add the sprite over the player's head
        attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
        attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
        attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;

        return;
    }

    if (carrier && carrier != attacker) {
        VectorSubtract(targ->r.currentOrigin, carrier->r.currentOrigin, v1);
        VectorSubtract(attacker->r.currentOrigin, carrier->r.currentOrigin, v1);

        if ( ( ( VectorLength(v1) < CTF_ATTACKER_PROTECT_RADIUS &&
                 trap_InPVS(carrier->r.currentOrigin, targ->r.currentOrigin ) ) ||
                ( VectorLength(v2) < CTF_ATTACKER_PROTECT_RADIUS &&
                  trap_InPVS(carrier->r.currentOrigin, attacker->r.currentOrigin ) ) ) &&
                attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam) {
            AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_PROTECT_BONUS);

            attacker->client->pers.teamState.carrierdefense++;

            attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
            // add the sprite over the player's head
            attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
            attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
            attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;

            return;
        }
    }
}

/*
================
Team_CheckHurtCarrier
 
Check to see if attacker hurt the flag carrier.  Needed when handing out bonuses for assistance to flag
carrier defense.
================
*/
void Team_CheckHurtCarrier(gentity_t *targ, gentity_t *attacker) {
    int flag_pw;

    if (!targ->client || !attacker->client)
        return;

    //BB CTF-RTF
    if (targ->client->sess.sessionTeam == TEAM_RED)
        flag_pw = PW_BLUEFLAG;
    else
        flag_pw = PW_REDFLAG;

    // flags
    if (targ->client->ps.powerups[flag_pw] &&
            targ->client->sess.sessionTeam != attacker->client->sess.sessionTeam)
        attacker->client->pers.teamState.lasthurtcarrier = level.time;

    // skulls
    if (targ->client->ps.generic1 &&
            targ->client->sess.sessionTeam != attacker->client->sess.sessionTeam)
        attacker->client->pers.teamState.lasthurtcarrier = level.time;
}

//BB CTF-RTF - added this func to drop a player's flags
void Team_DropFlags(gentity_t *ent) {
    gitem_t		*item;
    gentity_t	*drop;
    float		angle;

    angle = 45;

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

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

    item = NULL;
#ifdef MISSIONPACK

    if ( ent->client->ps.powerups[ PW_NEUTRALFLAG ] ) {
        item = BG_FindItemForPowerup( PW_NEUTRALFLAG );
        if ( item ) {
            drop = Drop_Item( ent, item, angle );
            // decide how many seconds it has left
            drop->count = ( ent->client->ps.powerups[ PW_NEUTRALFLAG ] - level.time ) / 1000;
            if ( drop->count < 1 ) {
                drop->count = 1;
            }
            ent->client->ps.powerups[ PW_NEUTRALFLAG ] = 0;
        }
    }
#endif
		//INCIN -- properties for the lightning gun to pull a dropped flag to a player
		/*
		drop->r.contents |= CONTENTS_TRIGGER | CONTENTS_SOLID;
		drop->clipmask = MASK_SHOT;
							//MASK_SHOT;(works but not well)
							//MASK_PLAYERSOLID(works); 
							//MASK_ALL ; 
							//MASK_ALL(works -- but potential for pushing pulling a flag off the map);
		drop->takedamage = qtrue;
		drop->health = INT_MAX;
		//dropped->physicsBounce = 0.50;
		drop->nextthink = level.time + 1;
		*/
}

//BB CTF-RTF - added *flag parameter
gentity_t *Team_ResetFlag( gentity_t *flag, int team ) {
    char *c;
    gentity_t *ent, *rent = NULL;
    // BB
    // PULLED FROM ALLIANCE 1.3 SCORING
    //gentity_t *touchableFlag, *baseFlag;

    //G_Printf("Team_ResetFlag /n");

    // BB
    // PULLED FROM ALLIANCE 1.3 SCORING
    // grab this team's flags
    //baseFlag = teamgame.baseFlag[team];
    //touchableFlag = teamgame.touchableFlag[team];
    //BB END

    switch (team) {
    case TEAM_RED:
        c = "team_CTF_redflag";
        break;
    case TEAM_BLUE:
        c = "team_CTF_blueflag";
        break;
    case TEAM_FREE:
        c = "team_CTF_neutralflag";
        break;
    default:
        return NULL;
    }

    ent = NULL;
    while ((ent = G_Find (ent, FOFS(classname), c)) != NULL) {
        if (ent->flags & FL_DROPPED_ITEM)
            G_FreeEntity(ent);
        else {
            rent = ent;
            RespawnItem(ent);
        }
    }

    //BB SCORING
    // active flag becomes the flag at base
    teamgame.touchableFlag[team] = teamgame.baseFlag[team];
    teamgame.carrier[team] = NULL;
    //BB END SCORING

    Team_SetFlagStatus( team, FLAG_ATBASE );

    return rent;
}

void Team_ResetFlags( void ) {

    if( g_gametype.integer == GT_CTF || g_gametype.integer == GT_RTF ) {
        //BB CTF-RTF - added *flag param
        Team_ResetFlag( NULL, TEAM_RED );
        Team_ResetFlag( NULL, TEAM_BLUE );
    }
#ifdef MISSIONPACK
    else if( g_gametype.integer == GT_1FCTF ) {
        //BB CTF-RTF - added *flag param
        Team_ResetFlag( NULL, TEAM_FREE );
    }
#endif
}

void Team_ReturnFlagSound( gentity_t *ent, int team ) {
    gentity_t	*te;

    if (ent == NULL) {
        G_Printf ("Warning:  NULL passed to Team_ReturnFlagSound\n");
        return;
    }

    te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_TEAM_SOUND );
    //BB CTF-RTF
    if( team == TEAM_BLUE) {
        te->s.eventParm = GTS_RED_RETURN;
    } else {
        te->s.eventParm = GTS_BLUE_RETURN;
    }
    te->r.svFlags |= SVF_BROADCAST;
}

//BB CTF-RTF - added enemy flag to parameter list
void Team_TakeFlagSound( gentity_t *ent, int team, qboolean enemy ) {
    gentity_t	*te;

    if (ent == NULL) {
        G_Printf ("Warning:  NULL passed to Team_TakeFlagSound\n");
        return;
    }

    // only play sound when the flag was at the base
    // or not picked up the last 10 seconds
    switch(team) {
    case TEAM_RED:
        if( teamgame.blueStatus != FLAG_ATBASE ) {
            if (teamgame.blueTakenTime > level.time - 10000)
                return;
        }
        teamgame.blueTakenTime = level.time;
        break;

    case TEAM_BLUE:	// CTF
        if( teamgame.redStatus != FLAG_ATBASE ) {
            if (teamgame.redTakenTime > level.time - 10000)
                return;
        }
        teamgame.redTakenTime = level.time;
        break;
    }


    te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_TEAM_SOUND );
    //BB CTF-RTF - is it the enemy that has the flag?
    if ( enemy ) {
        if( team == TEAM_BLUE) {
            te->s.eventParm = GTS_RED_TAKEN;
        } else {
            te->s.eventParm = GTS_BLUE_TAKEN;
        }
    } else {
        if( team == TEAM_BLUE) {
            te->s.eventParm = GTS_BLUE_TAKEN_OWN;
        } else {
            te->s.eventParm = GTS_RED_TAKEN_OWN;
        }
    }
    te->r.svFlags |= SVF_BROADCAST;
}

void Team_CaptureFlagSound( gentity_t *ent, int team ) {
    gentity_t	*te;

    if (ent == NULL) {
        G_Printf ("Warning:  NULL passed to Team_CaptureFlagSound\n");
        return;
    }

    te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_TEAM_SOUND );
    //BB CTF-RTF
    if( team == TEAM_BLUE ) {
        te->s.eventParm = GTS_BLUE_CAPTURE;
    } else {
        te->s.eventParm = GTS_RED_CAPTURE;
    }
    te->r.svFlags |= SVF_BROADCAST;
}

//BB CTF-RTF - added *flag param
void Team_ReturnFlag( gentity_t *flag, int team ) {

    //BB CTF-RTF - added *flag param
    Team_ReturnFlagSound(Team_ResetFlag(flag, team), team);

    if( team == TEAM_FREE ) {

        PrintMsg(NULL, "The flag has returned!\n" );

    } else {

        PrintMsg(NULL, "The %s flag has returned!\n", TeamName(team));

        //BB SCORING
        // active flag becomes the flag at base
        teamgame.touchableFlag[team] = teamgame.baseFlag[team];
        //BB END SCORING

    }
}

void Team_FreeEntity( gentity_t *ent ) {
    if( ent->item->giTag == PW_REDFLAG ) {
        //BB CTF-RTF - added *flag param
        Team_ReturnFlag( ent, TEAM_RED );
    } else if( ent->item->giTag == PW_BLUEFLAG ) {
        //BB CTF-RTF - added *flag param
        Team_ReturnFlag( ent, TEAM_BLUE );
#ifdef MISSIONPACK

    } else if( ent->item->giTag == PW_NEUTRALFLAG ) {
        //BB CTF-RTF - added *flag param
        Team_ReturnFlag( ent, TEAM_FREE );
#endif

    }
}

/*
==============
Team_DroppedFlagThink
 
Automatically set in Launch_Item if the item is one of the flags
 
Flags are unique in that if they are dropped, the base flag must be respawned when they time out
==============
*/
void Team_DroppedFlagThink(gentity_t *ent) {
    int		team = TEAM_FREE;

    if( ent->item->giTag == PW_REDFLAG ) {
        team = TEAM_RED;
    } else if( ent->item->giTag == PW_BLUEFLAG ) {
        team = TEAM_BLUE;
#ifdef MISSIONPACK

    } else if( ent->item->giTag == PW_NEUTRALFLAG ) {
        team = TEAM_FREE;
#endif

    }

    //BB CTF-RTF - added *flag param
    Team_ReturnFlagSound( Team_ResetFlag( ent, team ), team );
    // Reset Flag will delete this entity
}


/*
==============
Team_TouchOurFlag
==============
*/
int Team_TouchOurFlag( gentity_t *ent, gentity_t *other, int team ) {
    int			i;
    gentity_t	*player;
    gclient_t	*cl = other->client;
    int			enemy_flag, own_flag; //BB CTF-RTF - added own_flag
    //BB SCORING
    int dummy;
    int carryTime;
    vec3_t scoreOrigin;


    // BB: make this work for non-clients
    if (ent->client) {
        VectorCopy( ent->r.currentOrigin, scoreOrigin );
    } else {
        VectorCopy( ent->s.origin, scoreOrigin );
    }

    //G_Printf("Team_TouchOurFlag /n");

#ifdef MISSIONPACK

    if( g_gametype.integer == GT_1FCTF ) {
        enemy_flag = PW_NEUTRALFLAG;
    } else {
#endif

        if (cl->sess.sessionTeam == TEAM_RED) {
            enemy_flag = PW_BLUEFLAG;
            //BB CTF-RTF
            own_flag = PW_REDFLAG;
        } else {
            enemy_flag = PW_REDFLAG;
            //BB CTF-RTF
            own_flag = PW_BLUEFLAG;
        }

        //BB CTF-RTF - is the flag a dropped flag?
        if ( ent->flags & FL_DROPPED_ITEM ) {
            //BB CTF-RTF - yep - are we playing return-the-flag?

            if ( g_gametype.integer == GT_CTF) {

                // hey, its not home.  return it by teleporting it back
                PrintMsg( NULL, "%s" S_COLOR_WHITE " returned the %s flag!\n",
                          cl->pers.netname, TeamName(team));

                //BB SCORING
                // anounce return
                PrintMsg( NULL,
                          "(%s %s" S_COLOR_WHITE "): %s FLAG RETURN: %d point(s)\n",
                          BONUS_STRING,
                          other->client->pers.netname, TeamName(team),
                          (int)(CTF_FLAG_RETURN_BONUS * damageScoringScale()));

                // score for return
                //AddScore(other, ent->r.currentOrigin, CTF_RECOVERY_BONUS);
                AddScore(other, scoreOrigin, CTF_FLAG_RETURN_BONUS * damageScoringScale());
                //BB END SCORING

                // mark return time for assists
                other->client->pers.teamState.flagrecovery++;
				other->client->ps.persistant[PERS_RETURNS]++;

                other->client->pers.teamState.lastreturnedflag = level.time;
				
                //BB SCORING
                other->client->pers.mvpDefense += MVP_DEFENSE_FR_BONUS;
                //player->client->pers.statFlagReturn++;
                //BB END SCORING

                //ResetFlag will remove this entity!  We must return zero
                Team_ReturnFlagSound(Team_ResetFlag(ent, team), team);

                /*
                // BB - medal
                player->client->ps.persistant[PERS_REWARD_COUNT]++;
                player->client->ps.persistant[PERS_REWARD] = REWARD_RETURN;
                //player->client->ps.stats[STAT_FLAGRETURN]++;
                // add the sprite over the player's head
                player->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_HEADSHOT
                							   | EF_AWARD_CAPTURE | EF_AWARD_DEFENSE | EF_AWARD_ASSIST
                							   | EF_AWARD_GAUNTLET | EF_AWARD_OMVP | EF_AWARD_DMVP | EF_AWARD_RETURN);
                player->client->ps.eFlags |= EF_AWARD_DMVP;
                */
                other->client->rewardTime = level.time + REWARD_SPRITE_TIME;


                return 0;

            } else {
                //BB CTF-RTF - we are, so give it to the player
                //if he hasn't already got one
                if ( cl->ps.powerups[own_flag] == 0 ) {
                    // Add it to the player's inventory
                    cl->ps.powerups[own_flag] = INT_MAX; // flags never expire

                    //BB SCORING
                    PrintMsg (NULL, "%s" S_COLOR_WHITE " got the %s flag!\n",
                              other->client->pers.netname, TeamName(team));

                    // anounce pickup
                    PrintMsg( NULL,
                              "(%s %s" S_COLOR_WHITE "): %s FLAG PICKUP: %d point(s)\n",
                              BONUS_STRING,
                              other->client->pers.netname, TeamName(team),
                              (int)(CTF_FLAG_BONUS * damageScoringScale()));

                    // player who just dropped no longer a carrier
                    teamgame.carrier[team] = other;
                    // active flag for the enemy team becomes the dropped flag
                    teamgame.touchableFlag[team] = other;

                    //- avoid players dropping it and picking it up to get mvp points
                    if (!(ent->flags & FL_DROPPED_ITEM)) {
                        // MVP
                        other->client->pers.mvpOffense += MVP_OFFENSE_FPICKUP_BONUS;
                        // medal
                        /*
                        other->client->ps.persistant[PERS_REWARD_COUNT]++;
                        other->client->ps.persistant[PERS_REWARD] = REWARD_OMVP;
                        // add the sprite over the player's head
                        other->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_HEADSHOT
                        							   | EF_AWARD_CAPTURE | EF_AWARD_DEFENSE | EF_AWARD_ASSIST
                        							   | EF_AWARD_GAUNTLET | EF_AWARD_OMVP | EF_AWARD_DMVP | EF_AWARD_RETURN);
                        other->client->ps.eFlags |= EF_AWARD_OMVP;
                        */
                        other->client->rewardTime = level.time + REWARD_SPRITE_TIME;

                    }
                    other->client->pers.teamState.flagsince = level.time;

                    // score for pickup
                    //AddScore(other, ent->r.currentOrigin, CTF_RECOVERY_BONUS);
                    AddScore(other, scoreOrigin, CTF_FLAG_BONUS * damageScoringScale());
                    //BB END SCORING

                    Team_TakeFlagSound( ent, team, qfalse );
                    Team_SetFlagStatus( team, FLAG_RETURNING );
                    return -1;

                } else {

                    return 0;

                }
            }

        } else {

            //BB CTF-RTF - it's not a dropped flag -
            // this means the player touched his own
            // base, so put his flag back
            if ( cl->ps.powerups[own_flag] ) {

                cl->ps.powerups[own_flag] = 0;

                PrintMsg( NULL, "%s" S_COLOR_WHITE " returned the %s flag!\n",
                          cl->pers.netname, TeamName(team));

                //BB SCORING
                // anounce return
                carryTime = (level.time - other->client->pers.teamState.flagsince)/1000;
                // announce to all players
                PrintMsg( NULL,
                          "(%s %s" S_COLOR_WHITE "): %s FLAG RETURN held %d seconds: %d point(s)\n",
                          BONUS_STRING,
                          other->client->pers.netname, TeamName(team),
                          carryTime,
                          (int)(RTF_FLAG_RETURN_BONUS * damageScoringScale()));

                // score for return
                //AddScore(other, ent->r.currentOrigin, CTF_RECOVERY_BONUS);
                AddScore(other, scoreOrigin, RTF_FLAG_RETURN_BONUS * damageScoringScale());

                other->client->pers.mvpOffense += MVP_DEFENSE_FR_BONUS;
                // player who just dropped no longer a carrier
                teamgame.carrier[team] = NULL;
                other->client->rewardTime = level.time + REWARD_SPRITE_TIME;
                //BB END SCORING

                other->client->pers.teamState.flagrecovery++;
				other->client->ps.persistant[PERS_RETURNS]++;
                other->client->pers.teamState.lastreturnedflag = level.time;
                //ResetFlag will remove this entity!  We must return zero
                Team_ReturnFlagSound(Team_ResetFlag(NULL, team), team);
                return 0;
            }
        }
#ifdef MISSIONPACK

    }
#endif

    // the flag is at home base.  if the player has the enemy
    // flag, he's just won!
    if (!cl->ps.powerups[enemy_flag])
        return 0; // We don't have the flag

#ifdef MISSIONPACK

    if( g_gametype.integer == GT_1FCTF ) {
        PrintMsg( NULL, "%s" S_COLOR_WHITE " captured the flag!\n", cl->pers.netname );
    } else {
#endif

        PrintMsg( NULL, "%s" S_COLOR_WHITE " captured the %s flag!\n", cl->pers.netname, TeamName(OtherTeam(team)));

#ifdef MISSIONPACK

    }
#endif

    //BB SCORING
    carryTime = (level.time - other->client->pers.teamState.flagsince)/1000;
    // announce to all players
    PrintMsg( NULL,
              "(%s %s" S_COLOR_WHITE "): %s TEAM CAPTURE held %d seconds: %d point(s)\n",
              BONUS_STRING,
              other->client->pers.netname, TeamName(team),
              carryTime,
              (int)(CTF_CAPTURE_BONUS * damageScoringScale()));

    // keep track of the carrier
    teamgame.carrier[OtherTeam(team)] = NULL;
    // awards
    other->client->pers.mvpOffense += MVP_OFFENSE_FC_BONUS;
    other->client->pers.statCap++;
    //other->client->ps.persistant[PERS_CAP_COUNT]++;

    // begin Hugo - Send players flag captures to the client so we can show it on scoreboard for all players
    //ClientUserinfoChanged( other->client - g_clients );
    //BB END SCORING

    cl->ps.powerups[enemy_flag] = 0;

    teamgame.last_flag_capture = level.time;
    teamgame.last_capture_team = team;

    // Increase the team's score
    AddTeamScore(ent->s.pos.trBase, other->client->sess.sessionTeam, 1);
    Team_ForceGesture(other->client->sess.sessionTeam);
	
    other->client->pers.teamState.captures++;
    // add the sprite over the player's head
    other->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
    other->client->ps.eFlags |= EF_AWARD_CAP;
    other->client->rewardTime = level.time + REWARD_SPRITE_TIME;
    other->client->ps.persistant[PERS_CAPTURES]++;

    // BB END SCORING
    //AddScore(other, ent->r.currentOrigin, CTF_CAPTURE_BONUS);
    // score for capture
    AddScore(other, scoreOrigin, CTF_CAPTURE_BONUS * damageScoringScale());
    //BB END SCORING


    Team_CaptureFlagSound( ent, team );

    // Ok, let's do the player loop, hand out the bonuses
    for (i = 0; i < g_maxclients.integer; i++) {

        player = &g_entities[i];

        // BB SCORING
        dummy = 0;
        //BB END SCORING

        if (!player->inuse)
            continue;

        // BB: make this work for non-clients
        if (player->client) {
            VectorCopy( player->r.currentOrigin, scoreOrigin );
        } else {
            VectorCopy( player->s.origin, scoreOrigin );
        }

        if (player->client->sess.sessionTeam !=
                cl->sess.sessionTeam) {

            player->client->pers.teamState.lasthurtcarrier = -5;

        } else if (player->client->sess.sessionTeam ==
                   cl->sess.sessionTeam) {

            if (player != other) {
                AddScore(player, scoreOrigin, CTF_TEAM_BONUS);

            }
            //BB CTF-RTF - assists for flag games only
            if ( g_gametype.integer >= GT_CTF) {

                // award extra points for capture assists
                if (player->client->pers.teamState.lastreturnedflag +
                        CTF_RETURN_FLAG_ASSIST_TIMEOUT > level.time) {

                    //BB SCORING
                    // returned team's flag recently - assists
                    PrintMsg( NULL,
                              "(%s %s" S_COLOR_WHITE "): %s TEAM ASSIST: %d point(s)\n",
                              BONUS_STRING,
                              player->client->pers.netname,
                              TeamName(player->client->sess.sessionTeam),
                              (int)(CTF_RETURN_FLAG_ASSIST_BONUS * damageScoringScale()));


                    AddScore (player, scoreOrigin, CTF_RETURN_FLAG_ASSIST_BONUS * damageScoringScale());
                    //AddScore (player, ent->r.currentOrigin, CTF_RETURN_FLAG_ASSIST_BONUS);
                    //BB END SCORING

                    other->client->pers.teamState.assists++;
                    player->client->ps.persistant[PERS_ASSIST_COUNT]++;

                    //BB SCORING
                    player->client->pers.mvpDefense += MVP_DEFENSE_FR_BONUS;
                    player->client->pers.statFlagReturnAssist++;
                    //BB END SCORING

                    // add the sprite over the player's head
                    player->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
                    player->client->ps.eFlags |= EF_AWARD_ASSIST;
                    player->client->rewardTime = level.time + REWARD_SPRITE_TIME;

                    // BB - MEDAL
                    /*
                    player->client->ps.persistant[PERS_REWARD_COUNT]++;
                    player->client->ps.persistant[PERS_REWARD] = REWARD_ASSIST;
                    player->client->ps.stats[STAT_ASSISTS]++;
                    // add the sprite over the player's head
                    player->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_HEADSHOT
                    							   | EF_AWARD_CAPTURE | EF_AWARD_DEFENSE | EF_AWARD_ASSIST
                    							   | EF_AWARD_GAUNTLET | EF_AWARD_OMVP | EF_AWARD_DMVP | EF_AWARD_RETURN);
                    player->client->ps.eFlags |= EF_AWARD_ASSIST;
                    */
                    player->client->rewardTime = level.time + REWARD_SPRITE_TIME;
                    dummy = 1;

                    //BB end MEDAL

                }
                else if (player->client->pers.teamState.lastfraggedcarrier +
                         CTF_FRAG_CARRIER_ASSIST_TIMEOUT > level.time) {

                    //BB SCORING
                    // killed enemy carrier recently
                    PrintMsg( NULL,
                              "(BONUS %s" S_COLOR_WHITE "): %s CARRIER KILL: %d point(s)\n",
                              player->client->pers.netname,
                              TeamName(team),
                              (int)(CTF_FRAG_CARRIER_ASSIST_BONUS * damageScoringScale()));

                    AddScore(player, scoreOrigin, CTF_FRAG_CARRIER_ASSIST_BONUS * damageScoringScale());
                    //AddScore(player, ent->r.currentOrigin, CTF_FRAG_CARRIER_ASSIST_BONUS);
                    //BB END SCORING

                    other->client->pers.teamState.assists++;
                    player->client->ps.persistant[PERS_ASSIST_COUNT]++;

                    //BB SCORING
                    player->client->pers.mvpOffense += MVP_OFFENSE_AECK_BONUS;
                    player->client->pers.statAssistEnemyCarrierKill++;
                    //BB END SCORING

                    // add the sprite over the player's head
                    player->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
                    player->client->ps.eFlags |= EF_AWARD_ASSIST;
                    player->client->rewardTime = level.time + REWARD_SPRITE_TIME;

                    //BB MEDAL

                    if (dummy == 0) {
                        // Hugo - medal
                        /*
                        player->client->ps.persistant[PERS_REWARD_COUNT]++;
                        player->client->ps.persistant[PERS_REWARD] = REWARD_ASSIST;
                        player->client->ps.stats[STAT_ASSISTS]++;
                        // add the sprite over the player's head
                        player->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_HEADSHOT
                        							   | EF_AWARD_CAPTURE | EF_AWARD_DEFENSE | EF_AWARD_ASSIST
                        							   | EF_AWARD_GAUNTLET | EF_AWARD_OMVP | EF_AWARD_DMVP | EF_AWARD_RETURN);
                        */
                        player->client->ps.eFlags |= EF_AWARD_ASSIST;
                        player->client->rewardTime = level.time + REWARD_SPRITE_TIME;
                    }

                    //BB END MEDAL
                }
            }
        }
    }

    // reset both flags


    Team_ResetFlags();

    CalculateRanks();

    /*
    // BB FIXME
    //PULLED FROM ALLIANCE 1.3 SCORING
       // Hugo - medal
       capper->client->ps.persistant[PERS_REWARD_COUNT]++;
       capper->client->ps.persistant[PERS_REWARD] = REWARD_CAPTURE;
       // add the sprite over the player's head
       capper->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_HEADSHOT
                                      | EF_AWARD_CAPTURE | EF_AWARD_DEFENSE | EF_AWARD_ASSIST
                                      | EF_AWARD_GAUNTLET | EF_AWARD_OMVP | EF_AWARD_DMVP | EF_AWARD_RETURN);
       capper->client->ps.eFlags |= EF_AWARD_CAPTURE;
       capper->client->rewardTime = level.time + REWARD_SPRITE_TIME;
    */

    return 0; // Do not respawn this automatically
}

int Team_TouchEnemyFlag( gentity_t *ent, gentity_t *other, int team ) {
    gclient_t *cl = other->client;
    int			enemy_flag; //BB CTF-RTF
    vec3_t scoreOrigin;


    // BB: make this work for non-clients
    if (ent->client) {
        VectorCopy( ent->r.currentOrigin, scoreOrigin );
    } else {
        VectorCopy( ent->s.origin, scoreOrigin );
    }

    //BB CTF-RTF - figure out the enemy flag
    if (cl->sess.sessionTeam == TEAM_RED) {
        enemy_flag = PW_BLUEFLAG;
    } else {
        enemy_flag = PW_REDFLAG;
    }

    // If we've already got a flag, leave it alone
    if ( cl->ps.powerups[enemy_flag] == 0 ) {

#ifdef MISSIONPACK
        if( g_gametype.integer == GT_1FCTF ) {
            PrintMsg (NULL, "%s" S_COLOR_WHITE " got the flag!\n", other->client->pers.netname );

            cl->ps.powerups[PW_NEUTRALFLAG] = INT_MAX; // flags never expire

            if( team == TEAM_RED ) {
                Team_SetFlagStatus( TEAM_FREE, FLAG_TAKEN_RED );
            } else {
                Team_SetFlagStatus( TEAM_FREE, FLAG_TAKEN_BLUE );
            }
        } else {
#endif
            PrintMsg (NULL, "%s" S_COLOR_WHITE " got the %s flag!\n",
                      other->client->pers.netname, TeamName(team));


            // BB SCORING
            // anounce return
            PrintMsg( NULL,
                      "(%s %s" S_COLOR_WHITE "): %s FLAG PICKUP: %d point(s)\n",
                      BONUS_STRING,
                      other->client->pers.netname, TeamName(team),
                      (int)(CTF_FLAG_BONUS * damageScoringScale()));


            //- avoid players dropping it and picking it up to get mvp points
            if (!(ent->flags & FL_DROPPED_ITEM)) {
                // MVP
                other->client->pers.mvpOffense += MVP_OFFENSE_FPICKUP_BONUS;
                // medal
                /*
                other->client->ps.persistant[PERS_REWARD_COUNT]++;
                other->client->ps.persistant[PERS_REWARD] = REWARD_OMVP;
                // add the sprite over the player's head
                other->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_HEADSHOT
                							   | EF_AWARD_CAPTURE | EF_AWARD_DEFENSE | EF_AWARD_ASSIST
                							   | EF_AWARD_GAUNTLET | EF_AWARD_OMVP | EF_AWARD_DMVP | EF_AWARD_RETURN);
                other->client->ps.eFlags |= EF_AWARD_OMVP;
                */
                other->client->rewardTime = level.time + REWARD_SPRITE_TIME;

            }
            // keep track of the carrier
            teamgame.carrier[team] = other;
            // remember pickup time
            other->client->pers.teamState.flagsince = level.time;
            // flag's team no longer has an active flag - player carrying it
            teamgame.touchableFlag[team] = other;
            //BB END SCORING

            //BB CTF-RTF - use enemy_flag var
            cl->ps.powerups[enemy_flag] = INT_MAX; // flags never expire

            Team_SetFlagStatus( team, FLAG_TAKEN );

#ifdef MISSIONPACK

        }
#endif

        //BB SCORING
        //AddScore(other, ent->r.currentOrigin, CTF_FLAG_BONUS);
        AddScore(other, scoreOrigin, CTF_FLAG_BONUS * damageScoringScale());
        //BB END SCORING

        cl->pers.teamState.flagsince = level.time;
        //BB CTF-RTF - added enemy boolean to param
        Team_TakeFlagSound( ent, team, qtrue );

        return -1; // Do not respawn this automatically, but do delete it if it was FL_DROPPED

    }
    else {
        return 0;
    }

}

int Pickup_Team( gentity_t *ent, gentity_t *other ) {
    int team;
    gclient_t *cl = other->client;


#ifdef MISSIONPACK

    if( g_gametype.integer == GT_OBELISK ) {
        // there are no team items that can be picked up in obelisk
        G_FreeEntity( ent );
        return 0;
    }

    if( g_gametype.integer == GT_HARVESTER ) {
        // the only team items that can be picked up in harvester are the cubes
        if( ent->spawnflags != cl->sess.sessionTeam ) {
            cl->ps.generic1 += 1;
        }
        G_FreeEntity( ent );
        return 0;
    }
#endif
    // figure out what team this flag is
    if( Q_stricmp(ent->classname, "team_CTF_redflag") == 0 ) {
        team = TEAM_RED;
    } else if( Q_stricmp(ent->classname, "team_CTF_blueflag") == 0 ) {
        team = TEAM_BLUE;
    }
#ifdef MISSIONPACK
    else if( Q_stricmp(ent->classname, "team_CTF_neutralflag") == 0  ) {
        team = TEAM_FREE;
    }
#endif
    else {
        PrintMsg ( other, "Don't know what team the flag is on.\n");
        return 0;
    }
#ifdef MISSIONPACK
    if( g_gametype.integer == GT_1FCTF ) {
        if( team == TEAM_FREE ) {
            return Team_TouchEnemyFlag( ent, other, cl->sess.sessionTeam );
        }
        if( team != cl->sess.sessionTeam) {
            return Team_TouchOurFlag( ent, other, cl->sess.sessionTeam );
        }
        return 0;
    }
#endif
    // GT_CTF
    if( team == cl->sess.sessionTeam) {
        return Team_TouchOurFlag( ent, other, team );
    }
    return Team_TouchEnemyFlag( ent, other, team );

}

/*
===========
Team_GetLocation
 
Report a location for the player. Uses placed nearby target_location entities
============
*/
gentity_t *Team_GetLocation(gentity_t *ent) {
    gentity_t		*eloc, *best;
    float			bestlen, len;
    vec3_t			origin;

    best = NULL;
    bestlen = 3*8192.0*8192.0;


    // BB: make this work for non-clients
    if (ent->client) {
        VectorCopy( ent->r.currentOrigin, origin );
    } else {
        VectorCopy( ent->s.origin, origin );
    }


    for (eloc = level.locationHead; eloc; eloc = eloc->nextTrain) {
        len = ( origin[0] - eloc->r.currentOrigin[0] )
              * ( origin[0] - eloc->r.currentOrigin[0] )
              + ( origin[1] - eloc->r.currentOrigin[1] )
              * ( origin[1] - eloc->r.currentOrigin[1] )
              + ( origin[2] - eloc->r.currentOrigin[2] )
              * ( origin[2] - eloc->r.currentOrigin[2] );

        if ( len > bestlen ) {
            continue;
        }

        // BB: make this work for non-clients
        if (
            ent->client &&
            !trap_InPVS( origin, eloc->r.currentOrigin )
        ) {
            continue;
        }

        bestlen = len;
        best = eloc;
    }

    return best;
}


/*
===========
Team_GetLocationMsg
 
Report a location for the player. Uses placed nearby target_location entities
============
*/
qboolean Team_GetLocationMsg(gentity_t *ent, char *loc, int loclen) {
    gentity_t *best;

    best = Team_GetLocation( ent );

    if (!best)
        return qfalse;

    if (best->count) {
        if (best->count < 0)
            best->count = 0;
        if (best->count > 7)
            best->count = 7;
        Com_sprintf(loc, sizeof(loc) + loclen, "%c%c%s" S_COLOR_WHITE, Q_COLOR_ESCAPE, best->count + '0', best->message );

    } else {
        //BB
        switch (ent->client->pers.aLocation) {
        case LOC_DEEP_RED:
            Com_sprintf(loc, sizeof(loc) +  loclen, "%c%c%s" S_COLOR_WHITE, Q_COLOR_ESCAPE, 1 + '0', best->message );
            break;
        case LOC_HALF_RED:
            Com_sprintf(loc, sizeof(loc) +  loclen, "%c%c%s" S_COLOR_WHITE, Q_COLOR_ESCAPE, 1 + '0', best->message );
            break;
        case LOC_MIDDLE:
            Com_sprintf(loc, sizeof(loc) +  loclen, "%c%c%s" S_COLOR_WHITE, Q_COLOR_ESCAPE, 7 + '0', best->message );
            break;
        case LOC_HALF_BLUE:
            Com_sprintf(loc, sizeof(loc) +  loclen, "%c%c%s" S_COLOR_WHITE, Q_COLOR_ESCAPE, 4 + '0', best->message );
            break;
        case LOC_DEEP_BLUE:
            Com_sprintf(loc, sizeof(loc) +  loclen, "%c%c%s" S_COLOR_WHITE, Q_COLOR_ESCAPE, 4 + '0', best->message );
            break;
        default:
            Com_sprintf(loc, sizeof(loc) +  loclen, "%s", best->message);
            break;
        }
    }
    return qtrue;
}


/*---------------------------------------------------------------------------*/

/*
================
SelectRandomDeathmatchSpawnPoint
 
go to a random point that doesn't telefrag
================
*/
#define	MAX_TEAM_SPAWN_POINTS	32
gentity_t *SelectRandomTeamSpawnPoint( int teamstate, team_t team ) {
    gentity_t	*spot;
    int			count;
    int			selection;
    gentity_t	*spots[MAX_TEAM_SPAWN_POINTS];
    char		*classname;

    if (teamstate == TEAM_BEGIN) {
        //BB CTF-RTF
        if (team == TEAM_RED)
            classname = "team_CTF_redplayer";
        //BB CTF-RTF
        else if (team == TEAM_BLUE)
            classname = "team_CTF_blueplayer";
        else
            return NULL;
    } else {
        //BB CTF-RTF
        if (team == TEAM_RED)
            classname = "team_CTF_redspawn";
        //BB CTF-RTF
        else if (team == TEAM_BLUE)
            classname = "team_CTF_bluespawn";
        else
            return NULL;
    }
    count = 0;

    spot = NULL;

    while ((spot = G_Find (spot, FOFS(classname), classname)) != NULL) {
        if ( SpotWouldTelefrag( spot ) ) {
            continue;
        }
        spots[ count ] = spot;
        if (++count == MAX_TEAM_SPAWN_POINTS)
            break;
    }

    if ( !count ) {	// no spots that won't telefrag
        return NULL;//G_Find( NULL, FOFS(classname), classname);
    }

    selection = rand() % count;
    return spots[ selection ];
}

//BB: new spawning rules
//gentity_t *BattleSpawnPoint(int teamstate, team_t team);

/*
===========
SelectCTFSpawnPoint
 
============
*/
gentity_t *SelectCTFSpawnPoint ( team_t team, int teamstate, vec3_t origin, vec3_t angles ) {
    gentity_t	*spot;

    // BB: new spawning rules
    if (g_battleSpawning.integer && teamgame.numSpawns > 0) {
        spot = BattleSpawnPoint( teamstate, team );
    } else {
	//if(g_gametype.integer >= GT_TEAM){
        spot = SelectRandomTeamSpawnPoint ( teamstate, team );
    }

    if (!spot) {
        return SelectSpawnPoint( vec3_origin, origin, angles );
    }

    VectorCopy (spot->s.origin, origin);
    origin[2] += 9;
    VectorCopy (spot->s.angles, angles);

    return spot;
}



// At map load, collect all spawns spots, their locations and teams
void BattleSpawnInit(gentity_t *spot) {
    //gentity_t *spot = NULL;
/*
    int entnum;

    teamgame.numSpawns = 0;
*/
   /*
       // collect all spawns and the location of each
       while (
    	(spot = G_Find (spot, FOFS(classname), "team_CTF_redspawn")) != NULL
    	) {

           if (g_spawnDebug.integer && 
    		Team_GetLocation(spot) != NULL)
               G_Printf("Red spawn: %s\n", Team_GetLocation(spot)->message);

           teamSpawns[numSpawns] = spot;
           spawnLocs[numSpawns] = Team_GetLocation(spot);
           spawnTeams[numSpawns] = TEAM_RED;
           numSpawns++;
    	
    	if (numSpawns >= MAX_SPAWNS/2)
    		break;

       }

       spot = NULL;
       while (
    	(spot = G_Find (spot, FOFS(classname), "team_CTF_bluespawn")) != NULL
    	) {

           if (g_spawnDebug.integer && 
    		Team_GetLocation(spot) != NULL)
               G_Printf("Blue spawn: %s\n", Team_GetLocation(spot)->message);

           teamSpawns[numSpawns] = spot;
           spawnLocs[numSpawns] = Team_GetLocation(spot);
           spawnTeams[numSpawns] = TEAM_BLUE;
           numSpawns++;
    	
    	if (numSpawns >= MAX_SPAWNS)
    		break;
       }
    */
/*
    for (entnum = 0; entnum < MAX_GENTITIES; entnum++) {

        spot = &g_entities[entnum];
*/
        if(!spot->classname)//INCIN --- classname not found
            return;
			//continue;

        if (teamgame.numSpawns == MAX_SPAWNS - 1 )
            return;
			//break;

        //G_Printf("^5Item - %s^7 : %d\n", spot->classname, numSpawns);

        if (Q_stricmp(spot->classname, "team_CTF_bluespawn") == 0) {

            //G_Printf("Blue spawn: %s\n", Team_GetLocation(spot)->message);

            teamgame.teamSpawns[teamgame.numSpawns] = spot;
//            teamgame.spawnLocs[teamgame.numSpawns] = Team_GetLocation(spot);
            teamgame.spawnTeams[teamgame.numSpawns] = TEAM_BLUE;
            teamgame.numSpawns++;

            //if (teamgame.numSpawns >= MAX_SPAWNS/2 - 1)
            //    break;
        } else if (Q_stricmp(spot->classname, "team_CTF_blueplayer") == 0) {
            //G_Printf("Blue spawn: %s\n", Team_GetLocation(spot)->message);

            teamgame.teamSpawns[teamgame.numSpawns] = spot;
 //           teamgame.spawnLocs[teamgame.numSpawns] = Team_GetLocation(spot);
            teamgame.spawnTeams[teamgame.numSpawns] = TEAM_BLUE;
            teamgame.numSpawns++;

            //if (teamgame.numSpawns >= MAX_SPAWNS/2 - 1)
            //    break;

        } else if (Q_stricmp(spot->classname, "team_CTF_redspawn") == 0) {
            //	G_Printf("Red spawn: %s\n", Team_GetLocation(spot)->message);

            teamgame.teamSpawns[teamgame.numSpawns] = spot;
//            teamgame.spawnLocs[teamgame.numSpawns] = Team_GetLocation(spot);
            teamgame.spawnTeams[teamgame.numSpawns] = TEAM_RED;
            teamgame.numSpawns++;

            //if (teamgame.numSpawns >= MAX_SPAWNS - 1)
            //    break;
        } else if (Q_stricmp(spot->classname, "team_CTF_redplayer") == 0) {
            //G_Printf("Red spawn: %s\n", Team_GetLocation(spot)->message);

            teamgame.teamSpawns[teamgame.numSpawns] = spot;
//            teamgame.spawnLocs[teamgame.numSpawns] = Team_GetLocation(spot);
            teamgame.spawnTeams[teamgame.numSpawns] = TEAM_RED;
            teamgame.numSpawns++;

            //if (teamgame.numSpawns >= MAX_SPAWNS -1 )
            //   break;

        }
/*
    }
*/
	//	Com_Printf("Number of spawns:%i, Max Spawns: %i\n", teamgame.numSpawns, (MAX_SPAWNS - 1));
    //G_Printf("^4GOT HERE\n");

}

// Spawn spot weighting:

// There's some distance at which an object isn't considered in evaluating the
// spawn spot.
#define SPAWN_IRRELEVANT_RADIUS        4096

// Within that radius we care about how long it would take you to get back into
// range of that important object.  For some objects we care more than others.
#define SPAWN_CARRIER_DISTANCE_WEIGHT    10
#define SPAWN_FLAG_DISTANCE_WEIGHT       10
#define SPAWN_BASE_DISTANCE_WEIGHT        2

// There's some range near an object where if you spawned there it would be bad;
// you might surprise a carrier, or get behind the flag defenders.  If the spawn
// is within that range we impose a stiff penalty so that it will basically
// never happen unless we are eg choosing between spawning too near a carrier vs
// spawning too near a flag.  The penalty doesn't change with range since it's
// about as bad to spawn right next to the target as spawn some small distance
// from it.
#define SPAWN_NEAR_OBJECT_RADIUS        800
// It's this many times as important to avoid spawning within the near object
// radius of an object than to be relatively close to it.
#define SPAWN_NEAR_OBJECT_FACTOR          5

// The spot value you'd get for spawning too near a flag.  This is precomputed
// just to calibrate other constants.
#define SPAWN_NEAR_FLAG_PENALTY (SPAWN_CARRIER_DISTANCE_WEIGHT * SPAWN_IRRELEVANT_RADIUS * SPAWN_NEAR_OBJECT_FACTOR)

// We don't actually want to _maximize_ the distance from important objects; if
// we did that we'd stick you in the boonies every time on large maps.  Instead
// we want to make sure that you are neither too close nor too far from the
// action.

// Try to be from ~4 to about ~10 seconds travel of a flag or carrier, or up to
// the perimeter of a empty base.
#define MAXIMUM_ACTION_QUOTIENT ((SPAWN_IRRELEVANT_RADIUS - 1200) * SPAWN_CARRIER_DISTANCE_WEIGHT)

#define MINIMUM_ACTION_QUOTIENT ((SPAWN_IRRELEVANT_RADIUS - 3072) * SPAWN_CARRIER_DISTANCE_WEIGHT)

float rangedWeight(float distance, float distanceWeight) {
    float value = 0;

    if (distance > SPAWN_IRRELEVANT_RADIUS)
        return 0;

    // discrete penalty for being very close
    if (distance < SPAWN_NEAR_OBJECT_RADIUS) {
        value += SPAWN_IRRELEVANT_RADIUS * distanceWeight *
                 SPAWN_NEAR_OBJECT_FACTOR;
    }

    // penalty according to range
    return value + distanceWeight * (SPAWN_IRRELEVANT_RADIUS - distance);
}

float EDistance(gentity_t *ent1, gentity_t *ent2) {
    return Distance(ent1->r.currentOrigin, ent2->r.currentOrigin);
}

// calculate a measure of how near the CTF action this spot is
int CTFActionQuotient(gentity_t *spot, int playerTeam) {
    int team;
    float maxAction = 0, thisAction;
    float distance;

    // nearness to carriers
    for (team = TEAM_RED; team <= TEAM_BLUE; team++) {
        if (teamgame.carrier[team]) {
            distance = EDistance(spot, teamgame.carrier[team]);
            maxAction = rangedWeight(distance,
                                     SPAWN_CARRIER_DISTANCE_WEIGHT);
            //G_Printf("carrier:%d, %d|", (int)distance, (int)maxAction);

        }
    }

    // nearness to flags

    for (team = TEAM_RED; team <= TEAM_BLUE; team++) {
        if (teamgame.touchableFlag[team]) {
            distance = EDistance(spot, teamgame.touchableFlag[team]);
            thisAction = rangedWeight(distance,
                                      SPAWN_FLAG_DISTANCE_WEIGHT);
            if (maxAction < thisAction)
                maxAction = thisAction;

            //G_Printf("flag:%d, %d|", (int)distance, (int)thisAction);

        }
    }

    // nearness to bases

    for (team = TEAM_RED; team <= TEAM_BLUE; team++) {
        if (teamgame.baseFlag[team]) {
            distance = EDistance(spot, teamgame.baseFlag[team]);
            thisAction = rangedWeight(distance,
                                      SPAWN_BASE_DISTANCE_WEIGHT);
            if (maxAction < thisAction)
                maxAction = thisAction;
            //G_Printf("base:%d, %d|", (int)distance, (int)thisAction);

        }
    }

    //G_Printf("\n");


    return maxAction;
}

gentity_t *BattleSpawnPoint(int teamstate, team_t team) {
    int       i;
    int       start;
	int       actionQuotient;
    int       bestMin = 0, bestMax = INT_MAX;
	int aLocation;
//    int relLoc;
	
	gentity_t	*spot;
    gentity_t	*bestMinSpot = NULL, *bestMaxSpot = NULL;
//	gentity_t	*loc;
#ifdef MISSIONPACK
   	gentity_t	*ent;
	char		*classname;
#endif

	aLocation = LOC_INVALID;
    // at game start, use the old rules.  Note only honoring TEAM_BEGIN at the
    // beginning of a game (or match), so that players joining or rejoining
    // mid-game don't get a free trip to base
    if (teamstate == TEAM_BEGIN && level.startTime < 20000) {
        return SelectRandomTeamSpawnPoint(teamstate, team);
    }

    // no team spawns in map, use DM rules
    if (teamgame.numSpawns == 0) {
        return NULL;
    }

    //G_Printf("Spawning for %s\n", TeamName(team));

    // loop through spots evaluating them

    // start somewhere random
    start = rand() % teamgame.numSpawns ;
    i = start;

    // note this isn't quite random, since some spots are less likely to be
    // dynamically eligible than others (like spots where a carrier is likely to
    // be) and thus the spots after unlikely choices will get chosen more
    // often.  Not a big deal.

    // return the first eligible spot
    while (i)
	{
        i++;

        // wrap around once
        if (i > teamgame.numSpawns - 1)
            i = 0;

        if (i == start)
			return NULL;
            //break;

        //G_Printf("Trying %s\n", Team_GetLocation(teamSpawns[i])->message);

        // use spawns marked for the other team?
        if (!g_spawnUseEnemy.integer && teamgame.spawnTeams[i] != team) {
            //G_Printf("enemyspawn ");
            continue;
        }

        spot = teamgame.teamSpawns[i];
		
//		if(spot->message)
//			Com_Printf("Spawn spot message: %s\n", spot->message);
/*		
        // do not use spawns in enemy base
        if (g_spawnNoEnemyBase.integer == 1) 
		{

            loc = teamgame.spawnLocs[i];

            if (loc)
                aLocation = spawnflagsToLocation(loc->spawnflags);

            if (loc && aLocation != LOC_INVALID) {

                relLoc = relativeLocation(team, aLocation);
                if (relLoc == POS_ENEMY_BASE)
                    continue;
*/
                /*
                            // means don't even allow spawning in enemy territory
                            if (g_spawnUseEnemy.integer == 0 &&
                                    relLoc == POS_ENEMY_TERRITORY) {
                                if (g_spawnDebug.integer)
                                    G_Printf("enemybase ");
                                continue;
                            }
                */
/*
        }
		else 
		{

*/
				// we don't have location info in the map, do a crude radial check
				if(g_gametype.integer == GT_CTF || g_gametype.integer == GT_RTF ){
					
					if ((g_spawnNoEnemyBase.integer == 1) && 
						relativeLocation(team, spawnflagsToLocation(spot->spawnflags)) == POS_ENEMY_BASE)
						continue;

					if (EDistance(spot, teamgame.baseFlag[OtherTeam(team)]) <  1200) {
	                    //G_Printf("enemybasenoloc ");
		                continue;
	                }
				}
#ifdef MISSIONPACK
				else if(g_gametype.integer == GT_1FCTF){
					classname = "Neutral Flag";
					ent = G_Find( NULL , FOFS(classname), classname);
					if(ent){
						
						if ((g_spawnNoEnemyBase.integer == 1) && 
							relativeLocation(team, spawnflagsToLocation(spot->spawnflags)) == POS_ENEMY_BASE)
							continue;

						if (EDistance(spot, ent) <  1200) {
							continue;
						}
					}

				}
				else if(g_gametype.integer == GT_OBELISK){
					if (team == TEAM_RED){
						classname = "team_blueobelisk";
						ent = G_Find( NULL, FOFS(classname), classname  );
					}
					else{

						classname = "team_redobelisk";
						ent = G_Find( NULL, FOFS(classname), classname );
					}
					if(ent){
						if ((g_spawnNoEnemyBase.integer == 1) && 
							relativeLocation(team, spawnflagsToLocation(spot->spawnflags)) == POS_ENEMY_BASE)
							continue;

						if (EDistance(spot, ent) <  1200) {
							continue;
						}
					}
				}
				else if(g_gametype.integer == GT_HARVESTER){
					classname = "team_neutralobelisk";
					ent = G_Find( NULL, FOFS(classname), classname );
					if(ent){
						
						if ((g_spawnNoEnemyBase.integer == 1) && 
							relativeLocation(team, spawnflagsToLocation(spot->spawnflags)) == POS_ENEMY_BASE)
							continue;

						if (EDistance(spot, ent) <  1200) {
							
							continue;
						}
					}
				}
#endif			
 //           }
//        }

        // always avoid telefragging
        if (SpotWouldTelefrag(spot)) {
            //G_Printf("telefrag ");
            continue;
        }
		
        // try to find a spot an appropriate distance from the action
        actionQuotient = CTFActionQuotient(spot, team);

        if (actionQuotient < MAXIMUM_ACTION_QUOTIENT &&
                (!g_spawnNearAction.integer ||
                 actionQuotient > MINIMUM_ACTION_QUOTIENT)) {

            // found a spawn just about the right distance from the action
            //G_Printf("Picked %s\n",
            //Team_GetLocation(teamSpawns[i])->message);

            return spot;
            // Remember the least bad fallbacks
        }
        else if (actionQuotient < MINIMUM_ACTION_QUOTIENT &&
                 actionQuotient > bestMin) {

            // remember the least far of the too far
            bestMin = actionQuotient;
            bestMinSpot = spot;

        } else if (actionQuotient > MAXIMUM_ACTION_QUOTIENT &&
                   actionQuotient < bestMax) {

            // remember the least near of the too near
            bestMax = actionQuotient;
            bestMaxSpot = spot;

        }
    }

    // We couldn't get the player as close to the action as we'd have liked.
    // Not so bad.
    if (bestMinSpot) {

        //G_Printf("Couldn't spawn close enough\n");


        return bestMinSpot;
    }

    // We were forced into sticking the player too close to the action, which
    // is considered major gameplay degradation in Combat mode.
    if (bestMaxSpot) {

        //G_Printf("Forced to respawn player too close to the action!\n");

        return bestMaxSpot;
    }

    // Every spot is a telefrag (this should never happen).  The calling
    // function will try DM spawning rules and other fallbacks.
	// G_Printf("All respawn positions would cause telefrags!\n");
    return NULL;
}


/*---------------------------------------------------------------------------*/

static int QDECL SortClients( const void *a, const void *b ) {
    return *(int *)a - *(int *)b;
}


/*
==================
TeamplayLocationsMessage
 
Format:
	clientNum location health armor weapon powerups
 
==================
*/
void TeamplayInfoMessage( gentity_t *ent ) {
    char		entry[1024];
    char		string[8192];
    int			stringlength;
    int			i, j;
    gentity_t	*player;
    int			cnt;
    int			h, a;
    int			clients[TEAM_MAXOVERLAY];

    if ( ! ent->client->pers.teamInfo )
        return;

    // figure out what client should be on the display
    // we are limited to 8, but we want to use the top eight players
    // but in client order (so they don't keep changing position on the overlay)
    for (i = 0, cnt = 0; i < g_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++) {

        player = g_entities + level.sortedClients[i];

        if (player->inuse
                && player->client->sess.sessionTeam ==
                ent->client->sess.sessionTeam ) {

            clients[cnt++] = level.sortedClients[i];
        }
    }

    // We have the top eight players, sort them by clientNum
    qsort( clients, cnt, sizeof( clients[0] ), SortClients );

    // send the latest information on all clients
    string[0] = 0;
    stringlength = 0;

    for (i = 0, cnt = 0; i < g_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++) {

        player = g_entities + i;

        if (player->inuse
                && player->client->sess.sessionTeam ==
                ent->client->sess.sessionTeam ) {

            h = player->client->ps.stats[STAT_HEALTH];
            a = player->client->ps.stats[STAT_ARMOR];
            if (h < 0)
                h = 0;
            if (a < 0)
                a = 0;

            Com_sprintf (entry, sizeof(entry),
                         " %i %i %i %i %i %i",
                         //				level.sortedClients[i], player->client->pers.teamState.location, h, a,
                         i, player->client->pers.teamState.location, h, a,
                         player->client->ps.weapon, player->s.powerups);

            j = strlen(entry);
            if (stringlength + j > sizeof(string))
                break;
            Q_strncpyz (string + stringlength, entry, sizeof(string) + sizeof(stringlength));
            stringlength += j;
            cnt++;
        }
    }

    //This info is sent to Client (CG_ParseTeamInfo)
    // Need to Modify for Alliance 1.3 merge
    // BB FIXME
    /*
       int			aLocation;
       int			seenTeam;

       // BB
       seenTeam = ent->client->sess.sessionTeam;
       //	if ( seenTeam == TEAM_SPECTATOR_RED )
       //		seenTeam = TEAM_RED;
       //	else if ( seenTeam == TEAM_SPECTATOR_BLUE )
       //		seenTeam = TEAM_BLUE;
       //

       for (i = 0, cnt = 0; i < g_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++) {
           player = g_entities + i;
           if (!player->inuse)
               continue;

           if (player->inuse && player->client->sess.sessionTeam ==
                   ent->client->sess.sessionTeam ) {

           // teammate
           //        h = player->client->ps.stats[STAT_HEALTH];
           //        a = player->client->ps.stats[STAT_ARMOR];
           //        if (h < 0) h = 0;
           //        if (a < 0) a = 0;
    	
    	//BB
           aLocation = player->client->pers.aLocation;
           // if no location info is available, show all players as on neutral
           // ground
           if (aLocation == LOC_INVALID)
               aLocation = LOC_MIDDLE;

           // BB: cheesy hack: if 5 has been added to location (which
           // normally ranges 0-4), don't expect armor.  This lets us omit armor in
           // altRestore without having to communicate the altRestore setting to
           // the client.
           //        if (altRestore()) aLocation += 5;

           // BB: Added x and y players coordinates
           Com_sprintf (entry, sizeof(entry),
                        " %i %i %i %i %i %i %i",
                        //level.sortedClients[i], player->client->pers.teamState.location, h, a,
                        i,  
    				 player->s.powerups,
                        player->client->pers.teamState.location,
                        // Alliance: Nacho: Also send back declared position and
                        // Alliance location
                        player->client->pers.position,
                        aLocation, 
    				 (int)player->client->ps.origin[0],
                        (int)player->client->ps.origin[1]);



           j = strlen(entry);
           if (stringlength + j > sizeof(string))
               break;
           Q_strncpyz (string + stringlength, entry, sizeof(string) + sizeof(stringlength));
           stringlength += j;
           cnt++;
           }
       }
    */
    //BB

    trap_SendServerCommand( ent-g_entities, va("tinfo %i %s", cnt, string) );
}

void CheckTeamStatus(void) {
    int i;
    gentity_t *loc, *ent;

    if (level.time - level.lastTeamLocationTime > TEAM_LOCATION_UPDATE_TIME) {

        level.lastTeamLocationTime = level.time;

        for (i = 0; i < g_maxclients.integer; i++) {
            ent = g_entities + i;

            if ( ent->client->pers.connected != CON_CONNECTED ) {
                continue;
            }

            //BB CTF-RTF - TEAM_WATCHING
            if (ent->inuse
                    && (ent->client->sess.sessionTeam == TEAM_RED
                        || ent->client->sess.sessionTeam == TEAM_BLUE
                        || ent->client->sess.sessionTeam == TEAM_SPECTATOR_RED
                        || ent->client->sess.sessionTeam == TEAM_SPECTATOR_BLUE
                       )) {

                loc = Team_GetLocation( ent );

                if (loc) {

                    ent->client->pers.teamState.location = loc->health;
                    // convert spawnflags to location_t enum
                    ent->client->pers.aLocation = spawnflagsToLocation(loc->spawnflags);

                } else {

                    ent->client->pers.teamState.location = 0;
                    ent->client->pers.aLocation = LOC_INVALID;

                }
            }
        }

        for (i = 0; i < g_maxclients.integer; i++) {
            ent = g_entities + i;

            if ( ent->client->pers.connected != CON_CONNECTED ) {
                continue;
            }

            //BB CTF-RTF - TEAM_WATCHING
            if (ent->inuse && (ent->client->sess.sessionTeam == TEAM_RED
                               || ent->client->sess.sessionTeam == TEAM_BLUE
                               //|| ent->client->sess.sessionTeam == TEAM_SPECTATOR_RED
                               //|| ent->client->sess.sessionTeam == TEAM_SPECTATOR_BLUE
                              )) {
                TeamplayInfoMessage( ent );
            }
        }
    }
}

/*-----------------------------------------------------------------*/

/*QUAKED team_CTF_redplayer (1 0 0) (-16 -16 -16) (16 16 32)
Only in CTF games.  Red players spawn here at game start.
*/
void SP_team_CTF_redplayer( gentity_t *ent ) {}


/*QUAKED team_CTF_blueplayer (0 0 1) (-16 -16 -16) (16 16 32)
Only in CTF games.  Blue players spawn here at game start.
*/
void SP_team_CTF_blueplayer( gentity_t *ent ) {}


/*QUAKED team_CTF_redspawn (1 0 0) (-16 -16 -24) (16 16 32)
potential spawning position for red team in CTF games.
Targets will be fired when someone spawns in on them.
*/
void SP_team_CTF_redspawn(gentity_t *ent) {}

/*QUAKED team_CTF_bluespawn (0 0 1) (-16 -16 -24) (16 16 32)
potential spawning position for blue team in CTF games.
Targets will be fired when someone spawns in on them.
*/
void SP_team_CTF_bluespawn(gentity_t *ent) {}

/*QUAKED team_redbase (1 0 0) (-16 -16 0) (16 16 8)
*/
void SP_team_redbase( gentity_t *ent ) {
    gentity_t *base;

    if ( g_gametype.integer <= GT_TEAM ) {
        G_FreeEntity(ent);
        return;
    }

    ent->s.eType = ET_TEAM;
    base = BB_SpawnFlagbase( teamgame.baseFlag[TEAM_RED]->s.origin, TEAM_RED);
    ent->s.modelindex = TEAM_RED;
    trap_LinkEntity(ent);
}

/*QUAKED team_bluebase (0 0 1) (-16 -16 0) (16 16 88)
*/
void SP_team_bluebase( gentity_t *ent ) {
    gentity_t *base;

    if ( g_gametype.integer <= GT_TEAM ) {
        G_FreeEntity(ent);
        return;
    }
    ent->s.eType = ET_TEAM;
    base = BB_SpawnFlagbase( teamgame.baseFlag[TEAM_BLUE]->s.origin, TEAM_BLUE);
    ent->s.modelindex = TEAM_BLUE;
    trap_LinkEntity(ent);
}



#ifdef MISSIONPACK

void SP_team_neutralbase( gentity_t *ent ) {
    gentity_t *base;

    if ( g_gametype.integer != GT_1FCTF ) {
        G_FreeEntity(ent);
        return;
    }
    ent->s.eType = ET_TEAM;
    base = BB_SpawnFlagbase( teamgame.baseFlag[TEAM_FREE]->s.origin, TEAM_FREE);
    ent->s.modelindex = TEAM_FREE;
    trap_LinkEntity(ent);
}

/*
================
Obelisks
================
*/

static void ObeliskRegen( gentity_t *self ) {
    self->nextthink = level.time + g_obeliskRegenPeriod.integer * 1000;
    if( self->health >= g_obeliskHealth.integer ) {
        return;
    }

    G_AddEvent( self, EV_POWERUP_REGEN, 0 );
    self->health += g_obeliskRegenAmount.integer;
    if ( self->health > g_obeliskHealth.integer ) {
        self->health = g_obeliskHealth.integer;
    }

    self->activator->s.modelindex2 = self->health * 0xff / g_obeliskHealth.integer;
    self->activator->s.frame = 0;
}


static void ObeliskRespawn( gentity_t *self ) {
    self->takedamage = qtrue;
    self->health = g_obeliskHealth.integer;

    self->think = ObeliskRegen;
    self->nextthink = level.time + g_obeliskRegenPeriod.integer * 1000;

    self->activator->s.frame = 0;
}


static void ObeliskDie( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
    int			otherTeam;

    otherTeam = OtherTeam( self->spawnflags );
    AddTeamScore(self->s.pos.trBase, otherTeam, 1);
    Team_ForceGesture(otherTeam);

    CalculateRanks();

    self->takedamage = qfalse;
    self->think = ObeliskRespawn;
    self->nextthink = level.time + g_obeliskRespawnDelay.integer * 1000;

    self->activator->s.modelindex2 = 0xff;
    self->activator->s.frame = 2;

    G_AddEvent( self->activator, EV_OBELISKEXPLODE, 0 );

    AddScore(attacker, self->r.currentOrigin, CTF_CAPTURE_BONUS);

    // add the sprite over the player's head
    attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
    attacker->client->ps.eFlags |= EF_AWARD_CAP;
    attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
    attacker->client->ps.persistant[PERS_CAPTURES]++;

    teamgame.redObeliskAttackedTime = 0;
    teamgame.blueObeliskAttackedTime = 0;
}


static void ObeliskTouch( gentity_t *self, gentity_t *other, trace_t *trace ) {
    int			tokens;

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

    if ( OtherTeam(other->client->sess.sessionTeam) != self->spawnflags ) {
        return;
    }

    tokens = other->client->ps.generic1;
    if( tokens <= 0 ) {
        return;
    }

    PrintMsg(NULL, "%s" S_COLOR_WHITE " brought in %i skull%s.\n",
             other->client->pers.netname, tokens, tokens ? "s" : "" );

    AddTeamScore(self->s.pos.trBase, other->client->sess.sessionTeam, tokens);
    Team_ForceGesture(other->client->sess.sessionTeam);

    AddScore(other, self->r.currentOrigin, CTF_CAPTURE_BONUS*tokens);

    // add the sprite over the player's head
    other->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
    other->client->ps.eFlags |= EF_AWARD_CAP;
    other->client->rewardTime = level.time + REWARD_SPRITE_TIME;
    other->client->ps.persistant[PERS_CAPTURES] += tokens;

    other->client->ps.generic1 = 0;
    CalculateRanks();

    Team_CaptureFlagSound( self, self->spawnflags );
}

static void ObeliskPain( gentity_t *self, gentity_t *attacker, int damage ) {
    int actualDamage = damage / 10;
    if (actualDamage <= 0) {
        actualDamage = 1;
    }
    self->activator->s.modelindex2 = self->health * 0xff / g_obeliskHealth.integer;
    if (!self->activator->s.frame) {
        G_AddEvent(self, EV_OBELISKPAIN, 0);
    }
    self->activator->s.frame = 1;
    AddScore(attacker, self->r.currentOrigin, actualDamage);
}

gentity_t *SpawnObelisk( vec3_t origin, int team, int spawnflags) {
    trace_t		tr;
    vec3_t		dest;
    gentity_t	*ent;

    ent = G_Spawn();

    VectorCopy( origin, ent->s.origin );
    VectorCopy( origin, ent->s.pos.trBase );
    VectorCopy( origin, ent->r.currentOrigin );

    VectorSet( ent->r.mins, -15, -15, 0 );
    VectorSet( ent->r.maxs, 15, 15, 87 );

    ent->s.eType = ET_GENERAL;
    ent->flags = FL_NO_KNOCKBACK;

    if( g_gametype.integer == GT_OBELISK ) {
        ent->r.contents = CONTENTS_SOLID;
        ent->takedamage = qtrue;
        ent->health = g_obeliskHealth.integer;
        ent->die = ObeliskDie;
        ent->pain = ObeliskPain;
        ent->think = ObeliskRegen;
        ent->nextthink = level.time + g_obeliskRegenPeriod.integer * 1000;
    }
    if( g_gametype.integer == GT_HARVESTER ) {
        ent->r.contents = CONTENTS_TRIGGER;
        ent->touch = ObeliskTouch;
    }

    if ( spawnflags & 1 ) {
        // suspended
        G_SetOrigin( ent, ent->s.origin );
    } else {
        // mappers like to put them exactly on the floor, but being coplanar
        // will sometimes show up as starting in solid, so lif it up one pixel
        ent->s.origin[2] += 1;

        // drop to floor
        VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
        trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
        if ( tr.startsolid ) {
            ent->s.origin[2] -= 1;
            G_Printf( "SpawnObelisk: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin) );

            ent->s.groundEntityNum = ENTITYNUM_NONE;
            G_SetOrigin( ent, ent->s.origin );
        } else {
            // allow to ride movers
            ent->s.groundEntityNum = tr.entityNum;
            G_SetOrigin( ent, tr.endpos );
        }
    }

    ent->spawnflags = team;

    trap_LinkEntity( ent );

    return ent;
}

/*QUAKED team_redobelisk (1 0 0) (-16 -16 0) (16 16 8)
*/
void SP_team_redobelisk( gentity_t *ent ) {
    gentity_t *obelisk;

    if ( g_gametype.integer <= GT_TEAM ) {
        G_FreeEntity(ent);
        return;
    }
    ent->s.eType = ET_TEAM;
    if ( g_gametype.integer == GT_OBELISK ) {
        obelisk = SpawnObelisk( ent->s.origin, TEAM_RED, ent->spawnflags );
        obelisk->activator = ent;
        // initial obelisk health value
        ent->s.modelindex2 = 0xff;
        ent->s.frame = 0;
    }
    if ( g_gametype.integer == GT_HARVESTER ) {
        obelisk = SpawnObelisk( ent->s.origin, TEAM_RED, ent->spawnflags );
        obelisk->activator = ent;
    }
    ent->s.modelindex = TEAM_RED;
    trap_LinkEntity(ent);
}

/*QUAKED team_blueobelisk (0 0 1) (-16 -16 0) (16 16 88)
*/
void SP_team_blueobelisk( gentity_t *ent ) {
    gentity_t *obelisk;

    if ( g_gametype.integer <= GT_TEAM ) {
        G_FreeEntity(ent);
        return;
    }
    ent->s.eType = ET_TEAM;
    if ( g_gametype.integer == GT_OBELISK ) {
        obelisk = SpawnObelisk( ent->s.origin, TEAM_BLUE, ent->spawnflags );
        obelisk->activator = ent;
        // initial obelisk health value
        ent->s.modelindex2 = 0xff;
        ent->s.frame = 0;
    }
    if ( g_gametype.integer == GT_HARVESTER ) {
        obelisk = SpawnObelisk( ent->s.origin, TEAM_BLUE, ent->spawnflags );
        obelisk->activator = ent;
    }
    ent->s.modelindex = TEAM_BLUE;
    trap_LinkEntity(ent);
}

/*QUAKED team_neutralobelisk (0 0 1) (-16 -16 0) (16 16 88)
*/
void SP_team_neutralobelisk( gentity_t *ent ) {
	//gentity_t	*neutralObelisk;
    if ( g_gametype.integer != GT_1FCTF && g_gametype.integer != GT_HARVESTER ) {
        G_FreeEntity(ent);
        return;
    }
    ent->s.eType = ET_TEAM;
    if ( g_gametype.integer == GT_HARVESTER) {
        teamgame.neutralObelisk = SpawnObelisk( ent->s.origin, TEAM_FREE, ent->spawnflags);
        teamgame.neutralObelisk->spawnflags = TEAM_FREE;
    }
    ent->s.modelindex = TEAM_FREE;
    trap_LinkEntity(ent);
}


/*
================
CheckObeliskAttack
================
*/
qboolean CheckObeliskAttack( gentity_t *obelisk, gentity_t *attacker ) {
    gentity_t	*te;

    // if this really is an obelisk
    if( obelisk->die != ObeliskDie ) {
        return qfalse;
    }

    // if the attacker is a client
    if( !attacker->client ) {
        return qfalse;
    }

    // if the obelisk is on the same team as the attacker then don't hurt it
    if( obelisk->spawnflags == attacker->client->sess.sessionTeam ) {
        return qtrue;
    }

    // obelisk may be hurt

    // if not played any sounds recently
    if ((obelisk->spawnflags == TEAM_RED &&
            teamgame.redObeliskAttackedTime < level.time - OVERLOAD_ATTACK_BASE_SOUND_TIME) ||
            (obelisk->spawnflags == TEAM_BLUE &&
             teamgame.blueObeliskAttackedTime < level.time - OVERLOAD_ATTACK_BASE_SOUND_TIME) ) {

        // tell which obelisk is under attack
        te = G_TempEntity( obelisk->s.pos.trBase, EV_GLOBAL_TEAM_SOUND );
        if( obelisk->spawnflags == TEAM_RED ) {
            te->s.eventParm = GTS_REDOBELISK_ATTACKED;
            teamgame.redObeliskAttackedTime = level.time;
        } else {
            te->s.eventParm = GTS_BLUEOBELISK_ATTACKED;
            teamgame.blueObeliskAttackedTime = level.time;
        }
        te->r.svFlags |= SVF_BROADCAST;
    }

    return qfalse;
}
#endif
