Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

g_team.c (39506B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 //
     23 
     24 #include "g_local.h"
     25 
     26 
     27 typedef struct teamgame_s {
     28 	float			last_flag_capture;
     29 	int				last_capture_team;
     30 	flagStatus_t	redStatus;	// CTF
     31 	flagStatus_t	blueStatus;	// CTF
     32 	flagStatus_t	flagStatus;	// One Flag CTF
     33 	int				redTakenTime;
     34 	int				blueTakenTime;
     35 	int				redObeliskAttackedTime;
     36 	int				blueObeliskAttackedTime;
     37 } teamgame_t;
     38 
     39 teamgame_t teamgame;
     40 
     41 gentity_t	*neutralObelisk;
     42 
     43 void Team_SetFlagStatus( int team, flagStatus_t status );
     44 
     45 void Team_InitGame( void ) {
     46 	memset(&teamgame, 0, sizeof teamgame);
     47 
     48 	switch( g_gametype.integer ) {
     49 	case GT_CTF:
     50 		teamgame.redStatus = teamgame.blueStatus = -1; // Invalid to force update
     51 		Team_SetFlagStatus( TEAM_RED, FLAG_ATBASE );
     52 		Team_SetFlagStatus( TEAM_BLUE, FLAG_ATBASE );
     53 		break;
     54 #ifdef MISSIONPACK
     55 	case GT_1FCTF:
     56 		teamgame.flagStatus = -1; // Invalid to force update
     57 		Team_SetFlagStatus( TEAM_FREE, FLAG_ATBASE );
     58 		break;
     59 #endif
     60 	default:
     61 		break;
     62 	}
     63 }
     64 
     65 int OtherTeam(int team) {
     66 	if (team==TEAM_RED)
     67 		return TEAM_BLUE;
     68 	else if (team==TEAM_BLUE)
     69 		return TEAM_RED;
     70 	return team;
     71 }
     72 
     73 const char *TeamName(int team)  {
     74 	if (team==TEAM_RED)
     75 		return "RED";
     76 	else if (team==TEAM_BLUE)
     77 		return "BLUE";
     78 	else if (team==TEAM_SPECTATOR)
     79 		return "SPECTATOR";
     80 	return "FREE";
     81 }
     82 
     83 const char *OtherTeamName(int team) {
     84 	if (team==TEAM_RED)
     85 		return "BLUE";
     86 	else if (team==TEAM_BLUE)
     87 		return "RED";
     88 	else if (team==TEAM_SPECTATOR)
     89 		return "SPECTATOR";
     90 	return "FREE";
     91 }
     92 
     93 const char *TeamColorString(int team) {
     94 	if (team==TEAM_RED)
     95 		return S_COLOR_RED;
     96 	else if (team==TEAM_BLUE)
     97 		return S_COLOR_BLUE;
     98 	else if (team==TEAM_SPECTATOR)
     99 		return S_COLOR_YELLOW;
    100 	return S_COLOR_WHITE;
    101 }
    102 
    103 // NULL for everyone
    104 void QDECL PrintMsg( gentity_t *ent, const char *fmt, ... ) {
    105 	char		msg[1024];
    106 	va_list		argptr;
    107 	char		*p;
    108 	
    109 	va_start (argptr,fmt);
    110 	if (vsprintf (msg, fmt, argptr) > sizeof(msg)) {
    111 		G_Error ( "PrintMsg overrun" );
    112 	}
    113 	va_end (argptr);
    114 
    115 	// double quotes are bad
    116 	while ((p = strchr(msg, '"')) != NULL)
    117 		*p = '\'';
    118 
    119 	trap_SendServerCommand ( ( (ent == NULL) ? -1 : ent-g_entities ), va("print \"%s\"", msg ));
    120 }
    121 
    122 /*
    123 ==============
    124 AddTeamScore
    125 
    126  used for gametype > GT_TEAM
    127  for gametype GT_TEAM the level.teamScores is updated in AddScore in g_combat.c
    128 ==============
    129 */
    130 void AddTeamScore(vec3_t origin, int team, int score) {
    131 	gentity_t	*te;
    132 
    133 	te = G_TempEntity(origin, EV_GLOBAL_TEAM_SOUND );
    134 	te->r.svFlags |= SVF_BROADCAST;
    135 
    136 	if ( team == TEAM_RED ) {
    137 		if ( level.teamScores[ TEAM_RED ] + score == level.teamScores[ TEAM_BLUE ] ) {
    138 			//teams are tied sound
    139 			te->s.eventParm = GTS_TEAMS_ARE_TIED;
    140 		}
    141 		else if ( level.teamScores[ TEAM_RED ] <= level.teamScores[ TEAM_BLUE ] &&
    142 					level.teamScores[ TEAM_RED ] + score > level.teamScores[ TEAM_BLUE ]) {
    143 			// red took the lead sound
    144 			te->s.eventParm = GTS_REDTEAM_TOOK_LEAD;
    145 		}
    146 		else {
    147 			// red scored sound
    148 			te->s.eventParm = GTS_REDTEAM_SCORED;
    149 		}
    150 	}
    151 	else {
    152 		if ( level.teamScores[ TEAM_BLUE ] + score == level.teamScores[ TEAM_RED ] ) {
    153 			//teams are tied sound
    154 			te->s.eventParm = GTS_TEAMS_ARE_TIED;
    155 		}
    156 		else if ( level.teamScores[ TEAM_BLUE ] <= level.teamScores[ TEAM_RED ] &&
    157 					level.teamScores[ TEAM_BLUE ] + score > level.teamScores[ TEAM_RED ]) {
    158 			// blue took the lead sound
    159 			te->s.eventParm = GTS_BLUETEAM_TOOK_LEAD;
    160 		}
    161 		else {
    162 			// blue scored sound
    163 			te->s.eventParm = GTS_BLUETEAM_SCORED;
    164 		}
    165 	}
    166 	level.teamScores[ team ] += score;
    167 }
    168 
    169 /*
    170 ==============
    171 OnSameTeam
    172 ==============
    173 */
    174 qboolean OnSameTeam( gentity_t *ent1, gentity_t *ent2 ) {
    175 	if ( !ent1->client || !ent2->client ) {
    176 		return qfalse;
    177 	}
    178 
    179 	if ( g_gametype.integer < GT_TEAM ) {
    180 		return qfalse;
    181 	}
    182 
    183 	if ( ent1->client->sess.sessionTeam == ent2->client->sess.sessionTeam ) {
    184 		return qtrue;
    185 	}
    186 
    187 	return qfalse;
    188 }
    189 
    190 
    191 static char ctfFlagStatusRemap[] = { '0', '1', '*', '*', '2' };
    192 static char oneFlagStatusRemap[] = { '0', '1', '2', '3', '4' };
    193 
    194 void Team_SetFlagStatus( int team, flagStatus_t status ) {
    195 	qboolean modified = qfalse;
    196 
    197 	switch( team ) {
    198 	case TEAM_RED:	// CTF
    199 		if( teamgame.redStatus != status ) {
    200 			teamgame.redStatus = status;
    201 			modified = qtrue;
    202 		}
    203 		break;
    204 
    205 	case TEAM_BLUE:	// CTF
    206 		if( teamgame.blueStatus != status ) {
    207 			teamgame.blueStatus = status;
    208 			modified = qtrue;
    209 		}
    210 		break;
    211 
    212 	case TEAM_FREE:	// One Flag CTF
    213 		if( teamgame.flagStatus != status ) {
    214 			teamgame.flagStatus = status;
    215 			modified = qtrue;
    216 		}
    217 		break;
    218 	}
    219 
    220 	if( modified ) {
    221 		char st[4];
    222 
    223 		if( g_gametype.integer == GT_CTF ) {
    224 			st[0] = ctfFlagStatusRemap[teamgame.redStatus];
    225 			st[1] = ctfFlagStatusRemap[teamgame.blueStatus];
    226 			st[2] = 0;
    227 		}
    228 		else {		// GT_1FCTF
    229 			st[0] = oneFlagStatusRemap[teamgame.flagStatus];
    230 			st[1] = 0;
    231 		}
    232 
    233 		trap_SetConfigstring( CS_FLAGSTATUS, st );
    234 	}
    235 }
    236 
    237 void Team_CheckDroppedItem( gentity_t *dropped ) {
    238 	if( dropped->item->giTag == PW_REDFLAG ) {
    239 		Team_SetFlagStatus( TEAM_RED, FLAG_DROPPED );
    240 	}
    241 	else if( dropped->item->giTag == PW_BLUEFLAG ) {
    242 		Team_SetFlagStatus( TEAM_BLUE, FLAG_DROPPED );
    243 	}
    244 	else if( dropped->item->giTag == PW_NEUTRALFLAG ) {
    245 		Team_SetFlagStatus( TEAM_FREE, FLAG_DROPPED );
    246 	}
    247 }
    248 
    249 /*
    250 ================
    251 Team_ForceGesture
    252 ================
    253 */
    254 void Team_ForceGesture(int team) {
    255 	int i;
    256 	gentity_t *ent;
    257 
    258 	for (i = 0; i < MAX_CLIENTS; i++) {
    259 		ent = &g_entities[i];
    260 		if (!ent->inuse)
    261 			continue;
    262 		if (!ent->client)
    263 			continue;
    264 		if (ent->client->sess.sessionTeam != team)
    265 			continue;
    266 		//
    267 		ent->flags |= FL_FORCE_GESTURE;
    268 	}
    269 }
    270 
    271 /*
    272 ================
    273 Team_FragBonuses
    274 
    275 Calculate the bonuses for flag defense, flag carrier defense, etc.
    276 Note that bonuses are not cumulative.  You get one, they are in importance
    277 order.
    278 ================
    279 */
    280 void Team_FragBonuses(gentity_t *targ, gentity_t *inflictor, gentity_t *attacker)
    281 {
    282 	int i;
    283 	gentity_t *ent;
    284 	int flag_pw, enemy_flag_pw;
    285 	int otherteam;
    286 	int tokens;
    287 	gentity_t *flag, *carrier = NULL;
    288 	char *c;
    289 	vec3_t v1, v2;
    290 	int team;
    291 
    292 	// no bonus for fragging yourself or team mates
    293 	if (!targ->client || !attacker->client || targ == attacker || OnSameTeam(targ, attacker))
    294 		return;
    295 
    296 	team = targ->client->sess.sessionTeam;
    297 	otherteam = OtherTeam(targ->client->sess.sessionTeam);
    298 	if (otherteam < 0)
    299 		return; // whoever died isn't on a team
    300 
    301 	// same team, if the flag at base, check to he has the enemy flag
    302 	if (team == TEAM_RED) {
    303 		flag_pw = PW_REDFLAG;
    304 		enemy_flag_pw = PW_BLUEFLAG;
    305 	} else {
    306 		flag_pw = PW_BLUEFLAG;
    307 		enemy_flag_pw = PW_REDFLAG;
    308 	}
    309 
    310 	if (g_gametype.integer == GT_1FCTF) {
    311 		enemy_flag_pw = PW_NEUTRALFLAG;
    312 	} 
    313 
    314 	// did the attacker frag the flag carrier?
    315 	tokens = 0;
    316 #ifdef MISSIONPACK
    317 	if( g_gametype.integer == GT_HARVESTER ) {
    318 		tokens = targ->client->ps.generic1;
    319 	}
    320 #endif
    321 	if (targ->client->ps.powerups[enemy_flag_pw]) {
    322 		attacker->client->pers.teamState.lastfraggedcarrier = level.time;
    323 		AddScore(attacker, targ->r.currentOrigin, CTF_FRAG_CARRIER_BONUS);
    324 		attacker->client->pers.teamState.fragcarrier++;
    325 		PrintMsg(NULL, "%s" S_COLOR_WHITE " fragged %s's flag carrier!\n",
    326 			attacker->client->pers.netname, TeamName(team));
    327 
    328 		// the target had the flag, clear the hurt carrier
    329 		// field on the other team
    330 		for (i = 0; i < g_maxclients.integer; i++) {
    331 			ent = g_entities + i;
    332 			if (ent->inuse && ent->client->sess.sessionTeam == otherteam)
    333 				ent->client->pers.teamState.lasthurtcarrier = 0;
    334 		}
    335 		return;
    336 	}
    337 
    338 	// did the attacker frag a head carrier? other->client->ps.generic1
    339 	if (tokens) {
    340 		attacker->client->pers.teamState.lastfraggedcarrier = level.time;
    341 		AddScore(attacker, targ->r.currentOrigin, CTF_FRAG_CARRIER_BONUS * tokens * tokens);
    342 		attacker->client->pers.teamState.fragcarrier++;
    343 		PrintMsg(NULL, "%s" S_COLOR_WHITE " fragged %s's skull carrier!\n",
    344 			attacker->client->pers.netname, TeamName(team));
    345 
    346 		// the target had the flag, clear the hurt carrier
    347 		// field on the other team
    348 		for (i = 0; i < g_maxclients.integer; i++) {
    349 			ent = g_entities + i;
    350 			if (ent->inuse && ent->client->sess.sessionTeam == otherteam)
    351 				ent->client->pers.teamState.lasthurtcarrier = 0;
    352 		}
    353 		return;
    354 	}
    355 
    356 	if (targ->client->pers.teamState.lasthurtcarrier &&
    357 		level.time - targ->client->pers.teamState.lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT &&
    358 		!attacker->client->ps.powerups[flag_pw]) {
    359 		// attacker is on the same team as the flag carrier and
    360 		// fragged a guy who hurt our flag carrier
    361 		AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_DANGER_PROTECT_BONUS);
    362 
    363 		attacker->client->pers.teamState.carrierdefense++;
    364 		targ->client->pers.teamState.lasthurtcarrier = 0;
    365 
    366 		attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
    367 		team = attacker->client->sess.sessionTeam;
    368 		// add the sprite over the player's head
    369 		attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
    370 		attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
    371 		attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
    372 
    373 		return;
    374 	}
    375 
    376 	if (targ->client->pers.teamState.lasthurtcarrier &&
    377 		level.time - targ->client->pers.teamState.lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT) {
    378 		// attacker is on the same team as the skull carrier and
    379 		AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_DANGER_PROTECT_BONUS);
    380 
    381 		attacker->client->pers.teamState.carrierdefense++;
    382 		targ->client->pers.teamState.lasthurtcarrier = 0;
    383 
    384 		attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
    385 		team = attacker->client->sess.sessionTeam;
    386 		// add the sprite over the player's head
    387 		attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
    388 		attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
    389 		attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
    390 
    391 		return;
    392 	}
    393 
    394 	// flag and flag carrier area defense bonuses
    395 
    396 	// we have to find the flag and carrier entities
    397 
    398 #ifdef MISSIONPACK	
    399 	if( g_gametype.integer == GT_OBELISK ) {
    400 		// find the team obelisk
    401 		switch (attacker->client->sess.sessionTeam) {
    402 		case TEAM_RED:
    403 			c = "team_redobelisk";
    404 			break;
    405 		case TEAM_BLUE:
    406 			c = "team_blueobelisk";
    407 			break;		
    408 		default:
    409 			return;
    410 		}
    411 		
    412 	} else if (g_gametype.integer == GT_HARVESTER ) {
    413 		// find the center obelisk
    414 		c = "team_neutralobelisk";
    415 	} else {
    416 #endif
    417 	// find the flag
    418 	switch (attacker->client->sess.sessionTeam) {
    419 	case TEAM_RED:
    420 		c = "team_CTF_redflag";
    421 		break;
    422 	case TEAM_BLUE:
    423 		c = "team_CTF_blueflag";
    424 		break;		
    425 	default:
    426 		return;
    427 	}
    428 	// find attacker's team's flag carrier
    429 	for (i = 0; i < g_maxclients.integer; i++) {
    430 		carrier = g_entities + i;
    431 		if (carrier->inuse && carrier->client->ps.powerups[flag_pw])
    432 			break;
    433 		carrier = NULL;
    434 	}
    435 #ifdef MISSIONPACK
    436 	}
    437 #endif
    438 	flag = NULL;
    439 	while ((flag = G_Find (flag, FOFS(classname), c)) != NULL) {
    440 		if (!(flag->flags & FL_DROPPED_ITEM))
    441 			break;
    442 	}
    443 
    444 	if (!flag)
    445 		return; // can't find attacker's flag
    446 
    447 	// ok we have the attackers flag and a pointer to the carrier
    448 
    449 	// check to see if we are defending the base's flag
    450 	VectorSubtract(targ->r.currentOrigin, flag->r.currentOrigin, v1);
    451 	VectorSubtract(attacker->r.currentOrigin, flag->r.currentOrigin, v2);
    452 
    453 	if ( ( ( VectorLength(v1) < CTF_TARGET_PROTECT_RADIUS &&
    454 		trap_InPVS(flag->r.currentOrigin, targ->r.currentOrigin ) ) ||
    455 		( VectorLength(v2) < CTF_TARGET_PROTECT_RADIUS &&
    456 		trap_InPVS(flag->r.currentOrigin, attacker->r.currentOrigin ) ) ) &&
    457 		attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam) {
    458 
    459 		// we defended the base flag
    460 		AddScore(attacker, targ->r.currentOrigin, CTF_FLAG_DEFENSE_BONUS);
    461 		attacker->client->pers.teamState.basedefense++;
    462 
    463 		attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
    464 		// add the sprite over the player's head
    465 		attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
    466 		attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
    467 		attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
    468 
    469 		return;
    470 	}
    471 
    472 	if (carrier && carrier != attacker) {
    473 		VectorSubtract(targ->r.currentOrigin, carrier->r.currentOrigin, v1);
    474 		VectorSubtract(attacker->r.currentOrigin, carrier->r.currentOrigin, v1);
    475 
    476 		if ( ( ( VectorLength(v1) < CTF_ATTACKER_PROTECT_RADIUS &&
    477 			trap_InPVS(carrier->r.currentOrigin, targ->r.currentOrigin ) ) ||
    478 			( VectorLength(v2) < CTF_ATTACKER_PROTECT_RADIUS &&
    479 				trap_InPVS(carrier->r.currentOrigin, attacker->r.currentOrigin ) ) ) &&
    480 			attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam) {
    481 			AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_PROTECT_BONUS);
    482 			attacker->client->pers.teamState.carrierdefense++;
    483 
    484 			attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
    485 			// add the sprite over the player's head
    486 			attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
    487 			attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
    488 			attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
    489 
    490 			return;
    491 		}
    492 	}
    493 }
    494 
    495 /*
    496 ================
    497 Team_CheckHurtCarrier
    498 
    499 Check to see if attacker hurt the flag carrier.  Needed when handing out bonuses for assistance to flag
    500 carrier defense.
    501 ================
    502 */
    503 void Team_CheckHurtCarrier(gentity_t *targ, gentity_t *attacker)
    504 {
    505 	int flag_pw;
    506 
    507 	if (!targ->client || !attacker->client)
    508 		return;
    509 
    510 	if (targ->client->sess.sessionTeam == TEAM_RED)
    511 		flag_pw = PW_BLUEFLAG;
    512 	else
    513 		flag_pw = PW_REDFLAG;
    514 
    515 	// flags
    516 	if (targ->client->ps.powerups[flag_pw] &&
    517 		targ->client->sess.sessionTeam != attacker->client->sess.sessionTeam)
    518 		attacker->client->pers.teamState.lasthurtcarrier = level.time;
    519 
    520 	// skulls
    521 	if (targ->client->ps.generic1 &&
    522 		targ->client->sess.sessionTeam != attacker->client->sess.sessionTeam)
    523 		attacker->client->pers.teamState.lasthurtcarrier = level.time;
    524 }
    525 
    526 
    527 gentity_t *Team_ResetFlag( int team ) {
    528 	char *c;
    529 	gentity_t *ent, *rent = NULL;
    530 
    531 	switch (team) {
    532 	case TEAM_RED:
    533 		c = "team_CTF_redflag";
    534 		break;
    535 	case TEAM_BLUE:
    536 		c = "team_CTF_blueflag";
    537 		break;
    538 	case TEAM_FREE:
    539 		c = "team_CTF_neutralflag";
    540 		break;
    541 	default:
    542 		return NULL;
    543 	}
    544 
    545 	ent = NULL;
    546 	while ((ent = G_Find (ent, FOFS(classname), c)) != NULL) {
    547 		if (ent->flags & FL_DROPPED_ITEM)
    548 			G_FreeEntity(ent);
    549 		else {
    550 			rent = ent;
    551 			RespawnItem(ent);
    552 		}
    553 	}
    554 
    555 	Team_SetFlagStatus( team, FLAG_ATBASE );
    556 
    557 	return rent;
    558 }
    559 
    560 void Team_ResetFlags( void ) {
    561 	if( g_gametype.integer == GT_CTF ) {
    562 		Team_ResetFlag( TEAM_RED );
    563 		Team_ResetFlag( TEAM_BLUE );
    564 	}
    565 #ifdef MISSIONPACK
    566 	else if( g_gametype.integer == GT_1FCTF ) {
    567 		Team_ResetFlag( TEAM_FREE );
    568 	}
    569 #endif
    570 }
    571 
    572 void Team_ReturnFlagSound( gentity_t *ent, int team ) {
    573 	gentity_t	*te;
    574 
    575 	if (ent == NULL) {
    576 		G_Printf ("Warning:  NULL passed to Team_ReturnFlagSound\n");
    577 		return;
    578 	}
    579 
    580 	te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_TEAM_SOUND );
    581 	if( team == TEAM_BLUE ) {
    582 		te->s.eventParm = GTS_RED_RETURN;
    583 	}
    584 	else {
    585 		te->s.eventParm = GTS_BLUE_RETURN;
    586 	}
    587 	te->r.svFlags |= SVF_BROADCAST;
    588 }
    589 
    590 void Team_TakeFlagSound( gentity_t *ent, int team ) {
    591 	gentity_t	*te;
    592 
    593 	if (ent == NULL) {
    594 		G_Printf ("Warning:  NULL passed to Team_TakeFlagSound\n");
    595 		return;
    596 	}
    597 
    598 	// only play sound when the flag was at the base
    599 	// or not picked up the last 10 seconds
    600 	switch(team) {
    601 		case TEAM_RED:
    602 			if( teamgame.blueStatus != FLAG_ATBASE ) {
    603 				if (teamgame.blueTakenTime > level.time - 10000)
    604 					return;
    605 			}
    606 			teamgame.blueTakenTime = level.time;
    607 			break;
    608 
    609 		case TEAM_BLUE:	// CTF
    610 			if( teamgame.redStatus != FLAG_ATBASE ) {
    611 				if (teamgame.redTakenTime > level.time - 10000)
    612 					return;
    613 			}
    614 			teamgame.redTakenTime = level.time;
    615 			break;
    616 	}
    617 
    618 	te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_TEAM_SOUND );
    619 	if( team == TEAM_BLUE ) {
    620 		te->s.eventParm = GTS_RED_TAKEN;
    621 	}
    622 	else {
    623 		te->s.eventParm = GTS_BLUE_TAKEN;
    624 	}
    625 	te->r.svFlags |= SVF_BROADCAST;
    626 }
    627 
    628 void Team_CaptureFlagSound( gentity_t *ent, int team ) {
    629 	gentity_t	*te;
    630 
    631 	if (ent == NULL) {
    632 		G_Printf ("Warning:  NULL passed to Team_CaptureFlagSound\n");
    633 		return;
    634 	}
    635 
    636 	te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_TEAM_SOUND );
    637 	if( team == TEAM_BLUE ) {
    638 		te->s.eventParm = GTS_BLUE_CAPTURE;
    639 	}
    640 	else {
    641 		te->s.eventParm = GTS_RED_CAPTURE;
    642 	}
    643 	te->r.svFlags |= SVF_BROADCAST;
    644 }
    645 
    646 void Team_ReturnFlag( int team ) {
    647 	Team_ReturnFlagSound(Team_ResetFlag(team), team);
    648 	if( team == TEAM_FREE ) {
    649 		PrintMsg(NULL, "The flag has returned!\n" );
    650 	}
    651 	else {
    652 		PrintMsg(NULL, "The %s flag has returned!\n", TeamName(team));
    653 	}
    654 }
    655 
    656 void Team_FreeEntity( gentity_t *ent ) {
    657 	if( ent->item->giTag == PW_REDFLAG ) {
    658 		Team_ReturnFlag( TEAM_RED );
    659 	}
    660 	else if( ent->item->giTag == PW_BLUEFLAG ) {
    661 		Team_ReturnFlag( TEAM_BLUE );
    662 	}
    663 	else if( ent->item->giTag == PW_NEUTRALFLAG ) {
    664 		Team_ReturnFlag( TEAM_FREE );
    665 	}
    666 }
    667 
    668 /*
    669 ==============
    670 Team_DroppedFlagThink
    671 
    672 Automatically set in Launch_Item if the item is one of the flags
    673 
    674 Flags are unique in that if they are dropped, the base flag must be respawned when they time out
    675 ==============
    676 */
    677 void Team_DroppedFlagThink(gentity_t *ent) {
    678 	int		team = TEAM_FREE;
    679 
    680 	if( ent->item->giTag == PW_REDFLAG ) {
    681 		team = TEAM_RED;
    682 	}
    683 	else if( ent->item->giTag == PW_BLUEFLAG ) {
    684 		team = TEAM_BLUE;
    685 	}
    686 	else if( ent->item->giTag == PW_NEUTRALFLAG ) {
    687 		team = TEAM_FREE;
    688 	}
    689 
    690 	Team_ReturnFlagSound( Team_ResetFlag( team ), team );
    691 	// Reset Flag will delete this entity
    692 }
    693 
    694 
    695 /*
    696 ==============
    697 Team_DroppedFlagThink
    698 ==============
    699 */
    700 int Team_TouchOurFlag( gentity_t *ent, gentity_t *other, int team ) {
    701 	int			i;
    702 	gentity_t	*player;
    703 	gclient_t	*cl = other->client;
    704 	int			enemy_flag;
    705 
    706 #ifdef MISSIONPACK
    707 	if( g_gametype.integer == GT_1FCTF ) {
    708 		enemy_flag = PW_NEUTRALFLAG;
    709 	}
    710 	else {
    711 #endif
    712 	if (cl->sess.sessionTeam == TEAM_RED) {
    713 		enemy_flag = PW_BLUEFLAG;
    714 	} else {
    715 		enemy_flag = PW_REDFLAG;
    716 	}
    717 
    718 	if ( ent->flags & FL_DROPPED_ITEM ) {
    719 		// hey, its not home.  return it by teleporting it back
    720 		PrintMsg( NULL, "%s" S_COLOR_WHITE " returned the %s flag!\n", 
    721 			cl->pers.netname, TeamName(team));
    722 		AddScore(other, ent->r.currentOrigin, CTF_RECOVERY_BONUS);
    723 		other->client->pers.teamState.flagrecovery++;
    724 		other->client->pers.teamState.lastreturnedflag = level.time;
    725 		//ResetFlag will remove this entity!  We must return zero
    726 		Team_ReturnFlagSound(Team_ResetFlag(team), team);
    727 		return 0;
    728 	}
    729 #ifdef MISSIONPACK
    730 	}
    731 #endif
    732 
    733 	// the flag is at home base.  if the player has the enemy
    734 	// flag, he's just won!
    735 	if (!cl->ps.powerups[enemy_flag])
    736 		return 0; // We don't have the flag
    737 #ifdef MISSIONPACK
    738 	if( g_gametype.integer == GT_1FCTF ) {
    739 		PrintMsg( NULL, "%s" S_COLOR_WHITE " captured the flag!\n", cl->pers.netname );
    740 	}
    741 	else {
    742 #endif
    743 	PrintMsg( NULL, "%s" S_COLOR_WHITE " captured the %s flag!\n", cl->pers.netname, TeamName(OtherTeam(team)));
    744 #ifdef MISSIONPACK
    745 	}
    746 #endif
    747 
    748 	cl->ps.powerups[enemy_flag] = 0;
    749 
    750 	teamgame.last_flag_capture = level.time;
    751 	teamgame.last_capture_team = team;
    752 
    753 	// Increase the team's score
    754 	AddTeamScore(ent->s.pos.trBase, other->client->sess.sessionTeam, 1);
    755 	Team_ForceGesture(other->client->sess.sessionTeam);
    756 
    757 	other->client->pers.teamState.captures++;
    758 	// add the sprite over the player's head
    759 	other->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
    760 	other->client->ps.eFlags |= EF_AWARD_CAP;
    761 	other->client->rewardTime = level.time + REWARD_SPRITE_TIME;
    762 	other->client->ps.persistant[PERS_CAPTURES]++;
    763 
    764 	// other gets another 10 frag bonus
    765 	AddScore(other, ent->r.currentOrigin, CTF_CAPTURE_BONUS);
    766 
    767 	Team_CaptureFlagSound( ent, team );
    768 
    769 	// Ok, let's do the player loop, hand out the bonuses
    770 	for (i = 0; i < g_maxclients.integer; i++) {
    771 		player = &g_entities[i];
    772 		if (!player->inuse)
    773 			continue;
    774 
    775 		if (player->client->sess.sessionTeam !=
    776 			cl->sess.sessionTeam) {
    777 			player->client->pers.teamState.lasthurtcarrier = -5;
    778 		} else if (player->client->sess.sessionTeam ==
    779 			cl->sess.sessionTeam) {
    780 			if (player != other)
    781 				AddScore(player, ent->r.currentOrigin, CTF_TEAM_BONUS);
    782 			// award extra points for capture assists
    783 			if (player->client->pers.teamState.lastreturnedflag + 
    784 				CTF_RETURN_FLAG_ASSIST_TIMEOUT > level.time) {
    785 				AddScore (player, ent->r.currentOrigin, CTF_RETURN_FLAG_ASSIST_BONUS);
    786 				other->client->pers.teamState.assists++;
    787 
    788 				player->client->ps.persistant[PERS_ASSIST_COUNT]++;
    789 				// add the sprite over the player's head
    790 				player->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
    791 				player->client->ps.eFlags |= EF_AWARD_ASSIST;
    792 				player->client->rewardTime = level.time + REWARD_SPRITE_TIME;
    793 
    794 			} else if (player->client->pers.teamState.lastfraggedcarrier + 
    795 				CTF_FRAG_CARRIER_ASSIST_TIMEOUT > level.time) {
    796 				AddScore(player, ent->r.currentOrigin, CTF_FRAG_CARRIER_ASSIST_BONUS);
    797 				other->client->pers.teamState.assists++;
    798 				player->client->ps.persistant[PERS_ASSIST_COUNT]++;
    799 				// add the sprite over the player's head
    800 				player->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
    801 				player->client->ps.eFlags |= EF_AWARD_ASSIST;
    802 				player->client->rewardTime = level.time + REWARD_SPRITE_TIME;
    803 			}
    804 		}
    805 	}
    806 	Team_ResetFlags();
    807 
    808 	CalculateRanks();
    809 
    810 	return 0; // Do not respawn this automatically
    811 }
    812 
    813 int Team_TouchEnemyFlag( gentity_t *ent, gentity_t *other, int team ) {
    814 	gclient_t *cl = other->client;
    815 
    816 #ifdef MISSIONPACK
    817 	if( g_gametype.integer == GT_1FCTF ) {
    818 		PrintMsg (NULL, "%s" S_COLOR_WHITE " got the flag!\n", other->client->pers.netname );
    819 
    820 		cl->ps.powerups[PW_NEUTRALFLAG] = INT_MAX; // flags never expire
    821 
    822 		if( team == TEAM_RED ) {
    823 			Team_SetFlagStatus( TEAM_FREE, FLAG_TAKEN_RED );
    824 		}
    825 		else {
    826 			Team_SetFlagStatus( TEAM_FREE, FLAG_TAKEN_BLUE );
    827 		}
    828 	}
    829 	else{
    830 #endif
    831 		PrintMsg (NULL, "%s" S_COLOR_WHITE " got the %s flag!\n",
    832 			other->client->pers.netname, TeamName(team));
    833 
    834 		if (team == TEAM_RED)
    835 			cl->ps.powerups[PW_REDFLAG] = INT_MAX; // flags never expire
    836 		else
    837 			cl->ps.powerups[PW_BLUEFLAG] = INT_MAX; // flags never expire
    838 
    839 		Team_SetFlagStatus( team, FLAG_TAKEN );
    840 #ifdef MISSIONPACK
    841 	}
    842 #endif
    843 
    844 	AddScore(other, ent->r.currentOrigin, CTF_FLAG_BONUS);
    845 	cl->pers.teamState.flagsince = level.time;
    846 	Team_TakeFlagSound( ent, team );
    847 
    848 	return -1; // Do not respawn this automatically, but do delete it if it was FL_DROPPED
    849 }
    850 
    851 int Pickup_Team( gentity_t *ent, gentity_t *other ) {
    852 	int team;
    853 	gclient_t *cl = other->client;
    854 
    855 #ifdef MISSIONPACK
    856 	if( g_gametype.integer == GT_OBELISK ) {
    857 		// there are no team items that can be picked up in obelisk
    858 		G_FreeEntity( ent );
    859 		return 0;
    860 	}
    861 
    862 	if( g_gametype.integer == GT_HARVESTER ) {
    863 		// the only team items that can be picked up in harvester are the cubes
    864 		if( ent->spawnflags != cl->sess.sessionTeam ) {
    865 			cl->ps.generic1 += 1;
    866 		}
    867 		G_FreeEntity( ent );
    868 		return 0;
    869 	}
    870 #endif
    871 	// figure out what team this flag is
    872 	if( strcmp(ent->classname, "team_CTF_redflag") == 0 ) {
    873 		team = TEAM_RED;
    874 	}
    875 	else if( strcmp(ent->classname, "team_CTF_blueflag") == 0 ) {
    876 		team = TEAM_BLUE;
    877 	}
    878 #ifdef MISSIONPACK
    879 	else if( strcmp(ent->classname, "team_CTF_neutralflag") == 0  ) {
    880 		team = TEAM_FREE;
    881 	}
    882 #endif
    883 	else {
    884 		PrintMsg ( other, "Don't know what team the flag is on.\n");
    885 		return 0;
    886 	}
    887 #ifdef MISSIONPACK
    888 	if( g_gametype.integer == GT_1FCTF ) {
    889 		if( team == TEAM_FREE ) {
    890 			return Team_TouchEnemyFlag( ent, other, cl->sess.sessionTeam );
    891 		}
    892 		if( team != cl->sess.sessionTeam) {
    893 			return Team_TouchOurFlag( ent, other, cl->sess.sessionTeam );
    894 		}
    895 		return 0;
    896 	}
    897 #endif
    898 	// GT_CTF
    899 	if( team == cl->sess.sessionTeam) {
    900 		return Team_TouchOurFlag( ent, other, team );
    901 	}
    902 	return Team_TouchEnemyFlag( ent, other, team );
    903 }
    904 
    905 /*
    906 ===========
    907 Team_GetLocation
    908 
    909 Report a location for the player. Uses placed nearby target_location entities
    910 ============
    911 */
    912 gentity_t *Team_GetLocation(gentity_t *ent)
    913 {
    914 	gentity_t		*eloc, *best;
    915 	float			bestlen, len;
    916 	vec3_t			origin;
    917 
    918 	best = NULL;
    919 	bestlen = 3*8192.0*8192.0;
    920 
    921 	VectorCopy( ent->r.currentOrigin, origin );
    922 
    923 	for (eloc = level.locationHead; eloc; eloc = eloc->nextTrain) {
    924 		len = ( origin[0] - eloc->r.currentOrigin[0] ) * ( origin[0] - eloc->r.currentOrigin[0] )
    925 			+ ( origin[1] - eloc->r.currentOrigin[1] ) * ( origin[1] - eloc->r.currentOrigin[1] )
    926 			+ ( origin[2] - eloc->r.currentOrigin[2] ) * ( origin[2] - eloc->r.currentOrigin[2] );
    927 
    928 		if ( len > bestlen ) {
    929 			continue;
    930 		}
    931 
    932 		if ( !trap_InPVS( origin, eloc->r.currentOrigin ) ) {
    933 			continue;
    934 		}
    935 
    936 		bestlen = len;
    937 		best = eloc;
    938 	}
    939 
    940 	return best;
    941 }
    942 
    943 
    944 /*
    945 ===========
    946 Team_GetLocation
    947 
    948 Report a location for the player. Uses placed nearby target_location entities
    949 ============
    950 */
    951 qboolean Team_GetLocationMsg(gentity_t *ent, char *loc, int loclen)
    952 {
    953 	gentity_t *best;
    954 
    955 	best = Team_GetLocation( ent );
    956 	
    957 	if (!best)
    958 		return qfalse;
    959 
    960 	if (best->count) {
    961 		if (best->count < 0)
    962 			best->count = 0;
    963 		if (best->count > 7)
    964 			best->count = 7;
    965 		Com_sprintf(loc, loclen, "%c%c%s" S_COLOR_WHITE, Q_COLOR_ESCAPE, best->count + '0', best->message );
    966 	} else
    967 		Com_sprintf(loc, loclen, "%s", best->message);
    968 
    969 	return qtrue;
    970 }
    971 
    972 
    973 /*---------------------------------------------------------------------------*/
    974 
    975 /*
    976 ================
    977 SelectRandomDeathmatchSpawnPoint
    978 
    979 go to a random point that doesn't telefrag
    980 ================
    981 */
    982 #define	MAX_TEAM_SPAWN_POINTS	32
    983 gentity_t *SelectRandomTeamSpawnPoint( int teamstate, team_t team ) {
    984 	gentity_t	*spot;
    985 	int			count;
    986 	int			selection;
    987 	gentity_t	*spots[MAX_TEAM_SPAWN_POINTS];
    988 	char		*classname;
    989 
    990 	if (teamstate == TEAM_BEGIN) {
    991 		if (team == TEAM_RED)
    992 			classname = "team_CTF_redplayer";
    993 		else if (team == TEAM_BLUE)
    994 			classname = "team_CTF_blueplayer";
    995 		else
    996 			return NULL;
    997 	} else {
    998 		if (team == TEAM_RED)
    999 			classname = "team_CTF_redspawn";
   1000 		else if (team == TEAM_BLUE)
   1001 			classname = "team_CTF_bluespawn";
   1002 		else
   1003 			return NULL;
   1004 	}
   1005 	count = 0;
   1006 
   1007 	spot = NULL;
   1008 
   1009 	while ((spot = G_Find (spot, FOFS(classname), classname)) != NULL) {
   1010 		if ( SpotWouldTelefrag( spot ) ) {
   1011 			continue;
   1012 		}
   1013 		spots[ count ] = spot;
   1014 		if (++count == MAX_TEAM_SPAWN_POINTS)
   1015 			break;
   1016 	}
   1017 
   1018 	if ( !count ) {	// no spots that won't telefrag
   1019 		return G_Find( NULL, FOFS(classname), classname);
   1020 	}
   1021 
   1022 	selection = rand() % count;
   1023 	return spots[ selection ];
   1024 }
   1025 
   1026 
   1027 /*
   1028 ===========
   1029 SelectCTFSpawnPoint
   1030 
   1031 ============
   1032 */
   1033 gentity_t *SelectCTFSpawnPoint ( team_t team, int teamstate, vec3_t origin, vec3_t angles ) {
   1034 	gentity_t	*spot;
   1035 
   1036 	spot = SelectRandomTeamSpawnPoint ( teamstate, team );
   1037 
   1038 	if (!spot) {
   1039 		return SelectSpawnPoint( vec3_origin, origin, angles );
   1040 	}
   1041 
   1042 	VectorCopy (spot->s.origin, origin);
   1043 	origin[2] += 9;
   1044 	VectorCopy (spot->s.angles, angles);
   1045 
   1046 	return spot;
   1047 }
   1048 
   1049 /*---------------------------------------------------------------------------*/
   1050 
   1051 static int QDECL SortClients( const void *a, const void *b ) {
   1052 	return *(int *)a - *(int *)b;
   1053 }
   1054 
   1055 
   1056 /*
   1057 ==================
   1058 TeamplayLocationsMessage
   1059 
   1060 Format:
   1061 	clientNum location health armor weapon powerups
   1062 
   1063 ==================
   1064 */
   1065 void TeamplayInfoMessage( gentity_t *ent ) {
   1066 	char		entry[1024];
   1067 	char		string[8192];
   1068 	int			stringlength;
   1069 	int			i, j;
   1070 	gentity_t	*player;
   1071 	int			cnt;
   1072 	int			h, a;
   1073 	int			clients[TEAM_MAXOVERLAY];
   1074 
   1075 	if ( ! ent->client->pers.teamInfo )
   1076 		return;
   1077 
   1078 	// figure out what client should be on the display
   1079 	// we are limited to 8, but we want to use the top eight players
   1080 	// but in client order (so they don't keep changing position on the overlay)
   1081 	for (i = 0, cnt = 0; i < g_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++) {
   1082 		player = g_entities + level.sortedClients[i];
   1083 		if (player->inuse && player->client->sess.sessionTeam == 
   1084 			ent->client->sess.sessionTeam ) {
   1085 			clients[cnt++] = level.sortedClients[i];
   1086 		}
   1087 	}
   1088 
   1089 	// We have the top eight players, sort them by clientNum
   1090 	qsort( clients, cnt, sizeof( clients[0] ), SortClients );
   1091 
   1092 	// send the latest information on all clients
   1093 	string[0] = 0;
   1094 	stringlength = 0;
   1095 
   1096 	for (i = 0, cnt = 0; i < g_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++) {
   1097 		player = g_entities + i;
   1098 		if (player->inuse && player->client->sess.sessionTeam == 
   1099 			ent->client->sess.sessionTeam ) {
   1100 
   1101 			h = player->client->ps.stats[STAT_HEALTH];
   1102 			a = player->client->ps.stats[STAT_ARMOR];
   1103 			if (h < 0) h = 0;
   1104 			if (a < 0) a = 0;
   1105 
   1106 			Com_sprintf (entry, sizeof(entry),
   1107 				" %i %i %i %i %i %i", 
   1108 //				level.sortedClients[i], player->client->pers.teamState.location, h, a, 
   1109 				i, player->client->pers.teamState.location, h, a, 
   1110 				player->client->ps.weapon, player->s.powerups);
   1111 			j = strlen(entry);
   1112 			if (stringlength + j > sizeof(string))
   1113 				break;
   1114 			strcpy (string + stringlength, entry);
   1115 			stringlength += j;
   1116 			cnt++;
   1117 		}
   1118 	}
   1119 
   1120 	trap_SendServerCommand( ent-g_entities, va("tinfo %i %s", cnt, string) );
   1121 }
   1122 
   1123 void CheckTeamStatus(void) {
   1124 	int i;
   1125 	gentity_t *loc, *ent;
   1126 
   1127 	if (level.time - level.lastTeamLocationTime > TEAM_LOCATION_UPDATE_TIME) {
   1128 
   1129 		level.lastTeamLocationTime = level.time;
   1130 
   1131 		for (i = 0; i < g_maxclients.integer; i++) {
   1132 			ent = g_entities + i;
   1133 
   1134 			if ( ent->client->pers.connected != CON_CONNECTED ) {
   1135 				continue;
   1136 			}
   1137 
   1138 			if (ent->inuse && (ent->client->sess.sessionTeam == TEAM_RED ||	ent->client->sess.sessionTeam == TEAM_BLUE)) {
   1139 				loc = Team_GetLocation( ent );
   1140 				if (loc)
   1141 					ent->client->pers.teamState.location = loc->health;
   1142 				else
   1143 					ent->client->pers.teamState.location = 0;
   1144 			}
   1145 		}
   1146 
   1147 		for (i = 0; i < g_maxclients.integer; i++) {
   1148 			ent = g_entities + i;
   1149 
   1150 			if ( ent->client->pers.connected != CON_CONNECTED ) {
   1151 				continue;
   1152 			}
   1153 
   1154 			if (ent->inuse && (ent->client->sess.sessionTeam == TEAM_RED ||	ent->client->sess.sessionTeam == TEAM_BLUE)) {
   1155 				TeamplayInfoMessage( ent );
   1156 			}
   1157 		}
   1158 	}
   1159 }
   1160 
   1161 /*-----------------------------------------------------------------*/
   1162 
   1163 /*QUAKED team_CTF_redplayer (1 0 0) (-16 -16 -16) (16 16 32)
   1164 Only in CTF games.  Red players spawn here at game start.
   1165 */
   1166 void SP_team_CTF_redplayer( gentity_t *ent ) {
   1167 }
   1168 
   1169 
   1170 /*QUAKED team_CTF_blueplayer (0 0 1) (-16 -16 -16) (16 16 32)
   1171 Only in CTF games.  Blue players spawn here at game start.
   1172 */
   1173 void SP_team_CTF_blueplayer( gentity_t *ent ) {
   1174 }
   1175 
   1176 
   1177 /*QUAKED team_CTF_redspawn (1 0 0) (-16 -16 -24) (16 16 32)
   1178 potential spawning position for red team in CTF games.
   1179 Targets will be fired when someone spawns in on them.
   1180 */
   1181 void SP_team_CTF_redspawn(gentity_t *ent) {
   1182 }
   1183 
   1184 /*QUAKED team_CTF_bluespawn (0 0 1) (-16 -16 -24) (16 16 32)
   1185 potential spawning position for blue team in CTF games.
   1186 Targets will be fired when someone spawns in on them.
   1187 */
   1188 void SP_team_CTF_bluespawn(gentity_t *ent) {
   1189 }
   1190 
   1191 
   1192 #ifdef MISSIONPACK
   1193 /*
   1194 ================
   1195 Obelisks
   1196 ================
   1197 */
   1198 
   1199 static void ObeliskRegen( gentity_t *self ) {
   1200 	self->nextthink = level.time + g_obeliskRegenPeriod.integer * 1000;
   1201 	if( self->health >= g_obeliskHealth.integer ) {
   1202 		return;
   1203 	}
   1204 
   1205 	G_AddEvent( self, EV_POWERUP_REGEN, 0 );
   1206 	self->health += g_obeliskRegenAmount.integer;
   1207 	if ( self->health > g_obeliskHealth.integer ) {
   1208 		self->health = g_obeliskHealth.integer;
   1209 	}
   1210 
   1211 	self->activator->s.modelindex2 = self->health * 0xff / g_obeliskHealth.integer;
   1212 	self->activator->s.frame = 0;
   1213 }
   1214 
   1215 
   1216 static void ObeliskRespawn( gentity_t *self ) {
   1217 	self->takedamage = qtrue;
   1218 	self->health = g_obeliskHealth.integer;
   1219 
   1220 	self->think = ObeliskRegen;
   1221 	self->nextthink = level.time + g_obeliskRegenPeriod.integer * 1000;
   1222 
   1223 	self->activator->s.frame = 0;
   1224 }
   1225 
   1226 
   1227 static void ObeliskDie( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
   1228 	int			otherTeam;
   1229 
   1230 	otherTeam = OtherTeam( self->spawnflags );
   1231 	AddTeamScore(self->s.pos.trBase, otherTeam, 1);
   1232 	Team_ForceGesture(otherTeam);
   1233 
   1234 	CalculateRanks();
   1235 
   1236 	self->takedamage = qfalse;
   1237 	self->think = ObeliskRespawn;
   1238 	self->nextthink = level.time + g_obeliskRespawnDelay.integer * 1000;
   1239 
   1240 	self->activator->s.modelindex2 = 0xff;
   1241 	self->activator->s.frame = 2;
   1242 
   1243 	G_AddEvent( self->activator, EV_OBELISKEXPLODE, 0 );
   1244 
   1245 	AddScore(attacker, self->r.currentOrigin, CTF_CAPTURE_BONUS);
   1246 
   1247 	// add the sprite over the player's head
   1248 	attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
   1249 	attacker->client->ps.eFlags |= EF_AWARD_CAP;
   1250 	attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
   1251 	attacker->client->ps.persistant[PERS_CAPTURES]++;
   1252 
   1253 	teamgame.redObeliskAttackedTime = 0;
   1254 	teamgame.blueObeliskAttackedTime = 0;
   1255 }
   1256 
   1257 
   1258 static void ObeliskTouch( gentity_t *self, gentity_t *other, trace_t *trace ) {
   1259 	int			tokens;
   1260 
   1261 	if ( !other->client ) {
   1262 		return;
   1263 	}
   1264 
   1265 	if ( OtherTeam(other->client->sess.sessionTeam) != self->spawnflags ) {
   1266 		return;
   1267 	}
   1268 
   1269 	tokens = other->client->ps.generic1;
   1270 	if( tokens <= 0 ) {
   1271 		return;
   1272 	}
   1273 
   1274 	PrintMsg(NULL, "%s" S_COLOR_WHITE " brought in %i skull%s.\n",
   1275 					other->client->pers.netname, tokens, tokens ? "s" : "" );
   1276 
   1277 	AddTeamScore(self->s.pos.trBase, other->client->sess.sessionTeam, tokens);
   1278 	Team_ForceGesture(other->client->sess.sessionTeam);
   1279 
   1280 	AddScore(other, self->r.currentOrigin, CTF_CAPTURE_BONUS*tokens);
   1281 
   1282 	// add the sprite over the player's head
   1283 	other->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
   1284 	other->client->ps.eFlags |= EF_AWARD_CAP;
   1285 	other->client->rewardTime = level.time + REWARD_SPRITE_TIME;
   1286 	other->client->ps.persistant[PERS_CAPTURES] += tokens;
   1287 	
   1288 	other->client->ps.generic1 = 0;
   1289 	CalculateRanks();
   1290 
   1291 	Team_CaptureFlagSound( self, self->spawnflags );
   1292 }
   1293 
   1294 static void ObeliskPain( gentity_t *self, gentity_t *attacker, int damage ) {
   1295 	int actualDamage = damage / 10;
   1296 	if (actualDamage <= 0) {
   1297 		actualDamage = 1;
   1298 	}
   1299 	self->activator->s.modelindex2 = self->health * 0xff / g_obeliskHealth.integer;
   1300 	if (!self->activator->s.frame) {
   1301 		G_AddEvent(self, EV_OBELISKPAIN, 0);
   1302 	}
   1303 	self->activator->s.frame = 1;
   1304 	AddScore(attacker, self->r.currentOrigin, actualDamage);
   1305 }
   1306 
   1307 gentity_t *SpawnObelisk( vec3_t origin, int team, int spawnflags) {
   1308 	trace_t		tr;
   1309 	vec3_t		dest;
   1310 	gentity_t	*ent;
   1311 
   1312 	ent = G_Spawn();
   1313 
   1314 	VectorCopy( origin, ent->s.origin );
   1315 	VectorCopy( origin, ent->s.pos.trBase );
   1316 	VectorCopy( origin, ent->r.currentOrigin );
   1317 
   1318 	VectorSet( ent->r.mins, -15, -15, 0 );
   1319 	VectorSet( ent->r.maxs, 15, 15, 87 );
   1320 
   1321 	ent->s.eType = ET_GENERAL;
   1322 	ent->flags = FL_NO_KNOCKBACK;
   1323 
   1324 	if( g_gametype.integer == GT_OBELISK ) {
   1325 		ent->r.contents = CONTENTS_SOLID;
   1326 		ent->takedamage = qtrue;
   1327 		ent->health = g_obeliskHealth.integer;
   1328 		ent->die = ObeliskDie;
   1329 		ent->pain = ObeliskPain;
   1330 		ent->think = ObeliskRegen;
   1331 		ent->nextthink = level.time + g_obeliskRegenPeriod.integer * 1000;
   1332 	}
   1333 	if( g_gametype.integer == GT_HARVESTER ) {
   1334 		ent->r.contents = CONTENTS_TRIGGER;
   1335 		ent->touch = ObeliskTouch;
   1336 	}
   1337 
   1338 	if ( spawnflags & 1 ) {
   1339 		// suspended
   1340 		G_SetOrigin( ent, ent->s.origin );
   1341 	} else {
   1342 		// mappers like to put them exactly on the floor, but being coplanar
   1343 		// will sometimes show up as starting in solid, so lif it up one pixel
   1344 		ent->s.origin[2] += 1;
   1345 
   1346 		// drop to floor
   1347 		VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
   1348 		trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
   1349 		if ( tr.startsolid ) {
   1350 			ent->s.origin[2] -= 1;
   1351 			G_Printf( "SpawnObelisk: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin) );
   1352 
   1353 			ent->s.groundEntityNum = ENTITYNUM_NONE;
   1354 			G_SetOrigin( ent, ent->s.origin );
   1355 		}
   1356 		else {
   1357 			// allow to ride movers
   1358 			ent->s.groundEntityNum = tr.entityNum;
   1359 			G_SetOrigin( ent, tr.endpos );
   1360 		}
   1361 	}
   1362 
   1363 	ent->spawnflags = team;
   1364 
   1365 	trap_LinkEntity( ent );
   1366 
   1367 	return ent;
   1368 }
   1369 
   1370 /*QUAKED team_redobelisk (1 0 0) (-16 -16 0) (16 16 8)
   1371 */
   1372 void SP_team_redobelisk( gentity_t *ent ) {
   1373 	gentity_t *obelisk;
   1374 
   1375 	if ( g_gametype.integer <= GT_TEAM ) {
   1376 		G_FreeEntity(ent);
   1377 		return;
   1378 	}
   1379 	ent->s.eType = ET_TEAM;
   1380 	if ( g_gametype.integer == GT_OBELISK ) {
   1381 		obelisk = SpawnObelisk( ent->s.origin, TEAM_RED, ent->spawnflags );
   1382 		obelisk->activator = ent;
   1383 		// initial obelisk health value
   1384 		ent->s.modelindex2 = 0xff;
   1385 		ent->s.frame = 0;
   1386 	}
   1387 	if ( g_gametype.integer == GT_HARVESTER ) {
   1388 		obelisk = SpawnObelisk( ent->s.origin, TEAM_RED, ent->spawnflags );
   1389 		obelisk->activator = ent;
   1390 	}
   1391 	ent->s.modelindex = TEAM_RED;
   1392 	trap_LinkEntity(ent);
   1393 }
   1394 
   1395 /*QUAKED team_blueobelisk (0 0 1) (-16 -16 0) (16 16 88)
   1396 */
   1397 void SP_team_blueobelisk( gentity_t *ent ) {
   1398 	gentity_t *obelisk;
   1399 
   1400 	if ( g_gametype.integer <= GT_TEAM ) {
   1401 		G_FreeEntity(ent);
   1402 		return;
   1403 	}
   1404 	ent->s.eType = ET_TEAM;
   1405 	if ( g_gametype.integer == GT_OBELISK ) {
   1406 		obelisk = SpawnObelisk( ent->s.origin, TEAM_BLUE, ent->spawnflags );
   1407 		obelisk->activator = ent;
   1408 		// initial obelisk health value
   1409 		ent->s.modelindex2 = 0xff;
   1410 		ent->s.frame = 0;
   1411 	}
   1412 	if ( g_gametype.integer == GT_HARVESTER ) {
   1413 		obelisk = SpawnObelisk( ent->s.origin, TEAM_BLUE, ent->spawnflags );
   1414 		obelisk->activator = ent;
   1415 	}
   1416 	ent->s.modelindex = TEAM_BLUE;
   1417 	trap_LinkEntity(ent);
   1418 }
   1419 
   1420 /*QUAKED team_neutralobelisk (0 0 1) (-16 -16 0) (16 16 88)
   1421 */
   1422 void SP_team_neutralobelisk( gentity_t *ent ) {
   1423 	if ( g_gametype.integer != GT_1FCTF && g_gametype.integer != GT_HARVESTER ) {
   1424 		G_FreeEntity(ent);
   1425 		return;
   1426 	}
   1427 	ent->s.eType = ET_TEAM;
   1428 	if ( g_gametype.integer == GT_HARVESTER) {
   1429 		neutralObelisk = SpawnObelisk( ent->s.origin, TEAM_FREE, ent->spawnflags);
   1430 		neutralObelisk->spawnflags = TEAM_FREE;
   1431 	}
   1432 	ent->s.modelindex = TEAM_FREE;
   1433 	trap_LinkEntity(ent);
   1434 }
   1435 
   1436 
   1437 /*
   1438 ================
   1439 CheckObeliskAttack
   1440 ================
   1441 */
   1442 qboolean CheckObeliskAttack( gentity_t *obelisk, gentity_t *attacker ) {
   1443 	gentity_t	*te;
   1444 
   1445 	// if this really is an obelisk
   1446 	if( obelisk->die != ObeliskDie ) {
   1447 		return qfalse;
   1448 	}
   1449 
   1450 	// if the attacker is a client
   1451 	if( !attacker->client ) {
   1452 		return qfalse;
   1453 	}
   1454 
   1455 	// if the obelisk is on the same team as the attacker then don't hurt it
   1456 	if( obelisk->spawnflags == attacker->client->sess.sessionTeam ) {
   1457 		return qtrue;
   1458 	}
   1459 
   1460 	// obelisk may be hurt
   1461 
   1462 	// if not played any sounds recently
   1463 	if ((obelisk->spawnflags == TEAM_RED &&
   1464 		teamgame.redObeliskAttackedTime < level.time - OVERLOAD_ATTACK_BASE_SOUND_TIME) ||
   1465 		(obelisk->spawnflags == TEAM_BLUE &&
   1466 		teamgame.blueObeliskAttackedTime < level.time - OVERLOAD_ATTACK_BASE_SOUND_TIME) ) {
   1467 
   1468 		// tell which obelisk is under attack
   1469 		te = G_TempEntity( obelisk->s.pos.trBase, EV_GLOBAL_TEAM_SOUND );
   1470 		if( obelisk->spawnflags == TEAM_RED ) {
   1471 			te->s.eventParm = GTS_REDOBELISK_ATTACKED;
   1472 			teamgame.redObeliskAttackedTime = level.time;
   1473 		}
   1474 		else {
   1475 			te->s.eventParm = GTS_BLUEOBELISK_ATTACKED;
   1476 			teamgame.blueObeliskAttackedTime = level.time;
   1477 		}
   1478 		te->r.svFlags |= SVF_BROADCAST;
   1479 	}
   1480 
   1481 	return qfalse;
   1482 }
   1483 #endif