codemp/game/g_team.c

Go to the documentation of this file.
00001 // Copyright (C) 1999-2000 Id Software, Inc.
00002 //
00003 
00004 #include "g_local.h"
00005 #include "bg_saga.h"
00006 
00007 
00008 typedef struct teamgame_s {
00009         float                   last_flag_capture;
00010         int                             last_capture_team;
00011         flagStatus_t    redStatus;      // CTF
00012         flagStatus_t    blueStatus;     // CTF
00013         flagStatus_t    flagStatus;     // One Flag CTF
00014         int                             redTakenTime;
00015         int                             blueTakenTime;
00016 } teamgame_t;
00017 
00018 teamgame_t teamgame;
00019 
00020 void Team_SetFlagStatus( int team, flagStatus_t status );
00021 
00022 void Team_InitGame( void ) {
00023         memset(&teamgame, 0, sizeof teamgame);
00024 
00025         switch( g_gametype.integer ) {
00026         case GT_CTF:
00027         case GT_CTY:
00028                 teamgame.redStatus = teamgame.blueStatus = -1; // Invalid to force update
00029                 Team_SetFlagStatus( TEAM_RED, FLAG_ATBASE );
00030                 Team_SetFlagStatus( TEAM_BLUE, FLAG_ATBASE );
00031                 break;
00032         default:
00033                 break;
00034         }
00035 }
00036 
00037 int OtherTeam(int team) {
00038         if (team==TEAM_RED)
00039                 return TEAM_BLUE;
00040         else if (team==TEAM_BLUE)
00041                 return TEAM_RED;
00042         return team;
00043 }
00044 
00045 const char *TeamName(int team)  {
00046         if (team==TEAM_RED)
00047                 return "RED";
00048         else if (team==TEAM_BLUE)
00049                 return "BLUE";
00050         else if (team==TEAM_SPECTATOR)
00051                 return "SPECTATOR";
00052         return "FREE";
00053 }
00054 
00055 const char *OtherTeamName(int team) {
00056         if (team==TEAM_RED)
00057                 return "BLUE";
00058         else if (team==TEAM_BLUE)
00059                 return "RED";
00060         else if (team==TEAM_SPECTATOR)
00061                 return "SPECTATOR";
00062         return "FREE";
00063 }
00064 
00065 const char *TeamColorString(int team) {
00066         if (team==TEAM_RED)
00067                 return S_COLOR_RED;
00068         else if (team==TEAM_BLUE)
00069                 return S_COLOR_BLUE;
00070         else if (team==TEAM_SPECTATOR)
00071                 return S_COLOR_YELLOW;
00072         return S_COLOR_WHITE;
00073 }
00074 
00075 // NULL for everyone
00076 /*
00077 void QDECL PrintMsg( gentity_t *ent, const char *fmt, ... ) {
00078         char            msg[1024];
00079         va_list         argptr;
00080         char            *p;
00081         
00082         va_start (argptr,fmt);
00083         if (vsprintf (msg, fmt, argptr) > sizeof(msg)) {
00084                 G_Error ( "PrintMsg overrun" );
00085         }
00086         va_end (argptr);
00087 
00088         // double quotes are bad
00089         while ((p = strchr(msg, '"')) != NULL)
00090                 *p = '\'';
00091 
00092         trap_SendServerCommand ( ( (ent == NULL) ? -1 : ent-g_entities ), va("print \"%s\"", msg ));
00093 }
00094 */
00095 //Printing messages to players via this method is no longer done, StringEd stuff is client only.
00096 
00097 
00098 //plIndex used to print pl->client->pers.netname
00099 //teamIndex used to print team name
00100 void PrintCTFMessage(int plIndex, int teamIndex, int ctfMessage)
00101 {
00102         gentity_t *te;
00103 
00104         if (plIndex == -1)
00105         {
00106                 plIndex = MAX_CLIENTS+1;
00107         }
00108         if (teamIndex == -1)
00109         {
00110                 teamIndex = 50;
00111         }
00112 
00113         te = G_TempEntity(vec3_origin, EV_CTFMESSAGE);
00114         te->r.svFlags |= SVF_BROADCAST;
00115         te->s.eventParm = ctfMessage;
00116         te->s.trickedentindex = plIndex;
00117         if (ctfMessage == CTFMESSAGE_PLAYER_CAPTURED_FLAG)
00118         {
00119                 if (teamIndex == TEAM_RED)
00120                 {
00121                         te->s.trickedentindex2 = TEAM_BLUE;
00122                 }
00123                 else
00124                 {
00125                         te->s.trickedentindex2 = TEAM_RED;
00126                 }
00127         }
00128         else
00129         {
00130                 te->s.trickedentindex2 = teamIndex;
00131         }
00132 }
00133 
00134 /*
00135 ==============
00136 AddTeamScore
00137 
00138  used for gametype > GT_TEAM
00139  for gametype GT_TEAM the level.teamScores is updated in AddScore in g_combat.c
00140 ==============
00141 */
00142 void AddTeamScore(vec3_t origin, int team, int score) {
00143         gentity_t       *te;
00144 
00145         te = G_TempEntity(origin, EV_GLOBAL_TEAM_SOUND );
00146         te->r.svFlags |= SVF_BROADCAST;
00147 
00148         if ( team == TEAM_RED ) {
00149                 if ( level.teamScores[ TEAM_RED ] + score == level.teamScores[ TEAM_BLUE ] ) {
00150                         //teams are tied sound
00151                         te->s.eventParm = GTS_TEAMS_ARE_TIED;
00152                 }
00153                 else if ( level.teamScores[ TEAM_RED ] <= level.teamScores[ TEAM_BLUE ] &&
00154                                         level.teamScores[ TEAM_RED ] + score > level.teamScores[ TEAM_BLUE ]) {
00155                         // red took the lead sound
00156                         te->s.eventParm = GTS_REDTEAM_TOOK_LEAD;
00157                 }
00158                 else {
00159                         // red scored sound
00160                         te->s.eventParm = GTS_REDTEAM_SCORED;
00161                 }
00162         }
00163         else {
00164                 if ( level.teamScores[ TEAM_BLUE ] + score == level.teamScores[ TEAM_RED ] ) {
00165                         //teams are tied sound
00166                         te->s.eventParm = GTS_TEAMS_ARE_TIED;
00167                 }
00168                 else if ( level.teamScores[ TEAM_BLUE ] <= level.teamScores[ TEAM_RED ] &&
00169                                         level.teamScores[ TEAM_BLUE ] + score > level.teamScores[ TEAM_RED ]) {
00170                         // blue took the lead sound
00171                         te->s.eventParm = GTS_BLUETEAM_TOOK_LEAD;
00172                 }
00173                 else {
00174                         // blue scored sound
00175                         te->s.eventParm = GTS_BLUETEAM_SCORED;
00176                 }
00177         }
00178         level.teamScores[ team ] += score;
00179 }
00180 
00181 /*
00182 ==============
00183 OnSameTeam
00184 ==============
00185 */
00186 qboolean OnSameTeam( gentity_t *ent1, gentity_t *ent2 ) {
00187         if ( !ent1->client || !ent2->client ) {
00188                 return qfalse;
00189         }
00190 
00191         if (g_gametype.integer == GT_POWERDUEL)
00192         {
00193                 if (ent1->client->sess.duelTeam == ent2->client->sess.duelTeam)
00194                 {
00195                         return qtrue;
00196                 }
00197 
00198                 return qfalse;
00199         }
00200 
00201         if (g_gametype.integer == GT_SINGLE_PLAYER)
00202         {
00203                 qboolean ent1IsBot = qfalse;
00204                 qboolean ent2IsBot = qfalse;
00205 
00206                 if (ent1->r.svFlags & SVF_BOT)
00207                 {
00208                         ent1IsBot = qtrue;
00209                 }
00210                 if (ent2->r.svFlags & SVF_BOT)
00211                 {
00212                         ent2IsBot = qtrue;
00213                 }
00214 
00215                 if ((ent1IsBot && ent2IsBot) || (!ent1IsBot && !ent2IsBot))
00216                 {
00217                         return qtrue;
00218                 }
00219                 return qfalse;
00220         }
00221 
00222         if ( g_gametype.integer < GT_TEAM ) {
00223                 return qfalse;
00224         }
00225 
00226         if (ent1->s.eType == ET_NPC &&
00227                 ent1->s.NPC_class == CLASS_VEHICLE &&
00228                 ent1->client &&
00229                 ent1->client->sess.sessionTeam != TEAM_FREE &&
00230                 ent2->client &&
00231                 ent1->client->sess.sessionTeam == ent2->client->sess.sessionTeam)
00232         {
00233                 return qtrue;
00234         }
00235         if (ent2->s.eType == ET_NPC &&
00236                 ent2->s.NPC_class == CLASS_VEHICLE &&
00237                 ent2->client &&
00238                 ent2->client->sess.sessionTeam != TEAM_FREE &&
00239                 ent1->client &&
00240                 ent2->client->sess.sessionTeam == ent1->client->sess.sessionTeam)
00241         {
00242                 return qtrue;
00243         }
00244 
00245         if (ent1->client->sess.sessionTeam == TEAM_FREE &&
00246                 ent2->client->sess.sessionTeam == TEAM_FREE &&
00247                 ent1->s.eType == ET_NPC &&
00248                 ent2->s.eType == ET_NPC)
00249         { //NPCs don't do normal team rules
00250                 return qfalse;
00251         }
00252 
00253         if (ent1->s.eType == ET_NPC && ent2->s.eType == ET_PLAYER)
00254         {
00255                 return qfalse;
00256         }
00257         else if (ent1->s.eType == ET_PLAYER && ent2->s.eType == ET_NPC)
00258         {
00259                 return qfalse;
00260         }
00261 
00262         if ( ent1->client->sess.sessionTeam == ent2->client->sess.sessionTeam ) {
00263                 return qtrue;
00264         }
00265 
00266         return qfalse;
00267 }
00268 
00269 
00270 static char ctfFlagStatusRemap[] = { '0', '1', '*', '*', '2' };
00271 
00272 void Team_SetFlagStatus( int team, flagStatus_t status ) {
00273         qboolean modified = qfalse;
00274 
00275         switch( team ) {
00276         case TEAM_RED:  // CTF
00277                 if( teamgame.redStatus != status ) {
00278                         teamgame.redStatus = status;
00279                         modified = qtrue;
00280                 }
00281                 break;
00282 
00283         case TEAM_BLUE: // CTF
00284                 if( teamgame.blueStatus != status ) {
00285                         teamgame.blueStatus = status;
00286                         modified = qtrue;
00287                 }
00288                 break;
00289 
00290         case TEAM_FREE: // One Flag CTF
00291                 if( teamgame.flagStatus != status ) {
00292                         teamgame.flagStatus = status;
00293                         modified = qtrue;
00294                 }
00295                 break;
00296         }
00297 
00298         if( modified ) {
00299                 char st[4];
00300 
00301                 if( g_gametype.integer == GT_CTF || g_gametype.integer == GT_CTY ) {
00302                         st[0] = ctfFlagStatusRemap[teamgame.redStatus];
00303                         st[1] = ctfFlagStatusRemap[teamgame.blueStatus];
00304                         st[2] = 0;
00305                 }
00306 
00307                 trap_SetConfigstring( CS_FLAGSTATUS, st );
00308         }
00309 }
00310 
00311 void Team_CheckDroppedItem( gentity_t *dropped ) {
00312         if( dropped->item->giTag == PW_REDFLAG ) {
00313                 Team_SetFlagStatus( TEAM_RED, FLAG_DROPPED );
00314         }
00315         else if( dropped->item->giTag == PW_BLUEFLAG ) {
00316                 Team_SetFlagStatus( TEAM_BLUE, FLAG_DROPPED );
00317         }
00318         else if( dropped->item->giTag == PW_NEUTRALFLAG ) {
00319                 Team_SetFlagStatus( TEAM_FREE, FLAG_DROPPED );
00320         }
00321 }
00322 
00323 /*
00324 ================
00325 Team_ForceGesture
00326 ================
00327 */
00328 void Team_ForceGesture(int team) {
00329         int i;
00330         gentity_t *ent;
00331 
00332         for (i = 0; i < MAX_CLIENTS; i++) {
00333                 ent = &g_entities[i];
00334                 if (!ent->inuse)
00335                         continue;
00336                 if (!ent->client)
00337                         continue;
00338                 if (ent->client->sess.sessionTeam != team)
00339                         continue;
00340                 //
00341                 ent->flags |= FL_FORCE_GESTURE;
00342         }
00343 }
00344 
00345 /*
00346 ================
00347 Team_FragBonuses
00348 
00349 Calculate the bonuses for flag defense, flag carrier defense, etc.
00350 Note that bonuses are not cumulative.  You get one, they are in importance
00351 order.
00352 ================
00353 */
00354 void Team_FragBonuses(gentity_t *targ, gentity_t *inflictor, gentity_t *attacker)
00355 {
00356         int i;
00357         gentity_t *ent;
00358         int flag_pw, enemy_flag_pw;
00359         int otherteam;
00360         int tokens;
00361         gentity_t *flag, *carrier = NULL;
00362         char *c;
00363         vec3_t v1, v2;
00364         int team;
00365 
00366         // no bonus for fragging yourself or team mates
00367         if (!targ->client || !attacker->client || targ == attacker || OnSameTeam(targ, attacker))
00368                 return;
00369 
00370         team = targ->client->sess.sessionTeam;
00371         otherteam = OtherTeam(targ->client->sess.sessionTeam);
00372         if (otherteam < 0)
00373                 return; // whoever died isn't on a team
00374 
00375         // same team, if the flag at base, check to he has the enemy flag
00376         if (team == TEAM_RED) {
00377                 flag_pw = PW_REDFLAG;
00378                 enemy_flag_pw = PW_BLUEFLAG;
00379         } else {
00380                 flag_pw = PW_BLUEFLAG;
00381                 enemy_flag_pw = PW_REDFLAG;
00382         }
00383 
00384         // did the attacker frag the flag carrier?
00385         tokens = 0;
00386         if (targ->client->ps.powerups[enemy_flag_pw]) {
00387                 attacker->client->pers.teamState.lastfraggedcarrier = level.time;
00388                 AddScore(attacker, targ->r.currentOrigin, CTF_FRAG_CARRIER_BONUS);
00389                 attacker->client->pers.teamState.fragcarrier++;
00390                 //PrintMsg(NULL, "%s" S_COLOR_WHITE " fragged %s's flag carrier!\n",
00391                 //      attacker->client->pers.netname, TeamName(team));
00392                 PrintCTFMessage(attacker->s.number, team, CTFMESSAGE_FRAGGED_FLAG_CARRIER);
00393 
00394                 // the target had the flag, clear the hurt carrier
00395                 // field on the other team
00396                 for (i = 0; i < g_maxclients.integer; i++) {
00397                         ent = g_entities + i;
00398                         if (ent->inuse && ent->client->sess.sessionTeam == otherteam)
00399                                 ent->client->pers.teamState.lasthurtcarrier = 0;
00400                 }
00401                 return;
00402         }
00403 
00404         // did the attacker frag a head carrier? other->client->ps.generic1
00405         if (tokens) {
00406                 attacker->client->pers.teamState.lastfraggedcarrier = level.time;
00407                 AddScore(attacker, targ->r.currentOrigin, CTF_FRAG_CARRIER_BONUS * tokens * tokens);
00408                 attacker->client->pers.teamState.fragcarrier++;
00409                 //PrintMsg(NULL, "%s" S_COLOR_WHITE " fragged %s's skull carrier!\n",
00410                 //      attacker->client->pers.netname, TeamName(team));
00411 
00412                 // the target had the flag, clear the hurt carrier
00413                 // field on the other team
00414                 for (i = 0; i < g_maxclients.integer; i++) {
00415                         ent = g_entities + i;
00416                         if (ent->inuse && ent->client->sess.sessionTeam == otherteam)
00417                                 ent->client->pers.teamState.lasthurtcarrier = 0;
00418                 }
00419                 return;
00420         }
00421 
00422         if (targ->client->pers.teamState.lasthurtcarrier &&
00423                 level.time - targ->client->pers.teamState.lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT &&
00424                 !attacker->client->ps.powerups[flag_pw]) {
00425                 // attacker is on the same team as the flag carrier and
00426                 // fragged a guy who hurt our flag carrier
00427                 AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_DANGER_PROTECT_BONUS);
00428 
00429                 attacker->client->pers.teamState.carrierdefense++;
00430                 targ->client->pers.teamState.lasthurtcarrier = 0;
00431 
00432                 attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
00433                 team = attacker->client->sess.sessionTeam;
00434                 attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
00435 
00436                 return;
00437         }
00438 
00439         if (targ->client->pers.teamState.lasthurtcarrier &&
00440                 level.time - targ->client->pers.teamState.lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT) {
00441                 // attacker is on the same team as the skull carrier and
00442                 AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_DANGER_PROTECT_BONUS);
00443 
00444                 attacker->client->pers.teamState.carrierdefense++;
00445                 targ->client->pers.teamState.lasthurtcarrier = 0;
00446 
00447                 attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
00448                 team = attacker->client->sess.sessionTeam;
00449                 attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
00450 
00451                 return;
00452         }
00453 
00454         // flag and flag carrier area defense bonuses
00455 
00456         // we have to find the flag and carrier entities
00457 
00458         // find the flag
00459         switch (attacker->client->sess.sessionTeam) {
00460         case TEAM_RED:
00461                 c = "team_CTF_redflag";
00462                 break;
00463         case TEAM_BLUE:
00464                 c = "team_CTF_blueflag";
00465                 break;          
00466         default:
00467                 return;
00468         }
00469         // find attacker's team's flag carrier
00470         for (i = 0; i < g_maxclients.integer; i++) {
00471                 carrier = g_entities + i;
00472                 if (carrier->inuse && carrier->client->ps.powerups[flag_pw])
00473                         break;
00474                 carrier = NULL;
00475         }
00476         flag = NULL;
00477         while ((flag = G_Find (flag, FOFS(classname), c)) != NULL) {
00478                 if (!(flag->flags & FL_DROPPED_ITEM))
00479                         break;
00480         }
00481 
00482         if (!flag)
00483                 return; // can't find attacker's flag
00484 
00485         // ok we have the attackers flag and a pointer to the carrier
00486 
00487         // check to see if we are defending the base's flag
00488         VectorSubtract(targ->r.currentOrigin, flag->r.currentOrigin, v1);
00489         VectorSubtract(attacker->r.currentOrigin, flag->r.currentOrigin, v2);
00490 
00491         if ( ( ( VectorLength(v1) < CTF_TARGET_PROTECT_RADIUS &&
00492                 trap_InPVS(flag->r.currentOrigin, targ->r.currentOrigin ) ) ||
00493                 ( VectorLength(v2) < CTF_TARGET_PROTECT_RADIUS &&
00494                 trap_InPVS(flag->r.currentOrigin, attacker->r.currentOrigin ) ) ) &&
00495                 attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam) {
00496 
00497                 // we defended the base flag
00498                 AddScore(attacker, targ->r.currentOrigin, CTF_FLAG_DEFENSE_BONUS);
00499                 attacker->client->pers.teamState.basedefense++;
00500 
00501                 attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
00502                 attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
00503 
00504                 return;
00505         }
00506 
00507         if (carrier && carrier != attacker) {
00508                 VectorSubtract(targ->r.currentOrigin, carrier->r.currentOrigin, v1);
00509                 VectorSubtract(attacker->r.currentOrigin, carrier->r.currentOrigin, v1);
00510 
00511                 if ( ( ( VectorLength(v1) < CTF_ATTACKER_PROTECT_RADIUS &&
00512                         trap_InPVS(carrier->r.currentOrigin, targ->r.currentOrigin ) ) ||
00513                         ( VectorLength(v2) < CTF_ATTACKER_PROTECT_RADIUS &&
00514                                 trap_InPVS(carrier->r.currentOrigin, attacker->r.currentOrigin ) ) ) &&
00515                         attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam) {
00516                         AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_PROTECT_BONUS);
00517                         attacker->client->pers.teamState.carrierdefense++;
00518 
00519                         attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
00520                         attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
00521 
00522                         return;
00523                 }
00524         }
00525 }
00526 
00527 /*
00528 ================
00529 Team_CheckHurtCarrier
00530 
00531 Check to see if attacker hurt the flag carrier.  Needed when handing out bonuses for assistance to flag
00532 carrier defense.
00533 ================
00534 */
00535 void Team_CheckHurtCarrier(gentity_t *targ, gentity_t *attacker)
00536 {
00537         int flag_pw;
00538 
00539         if (!targ->client || !attacker->client)
00540                 return;
00541 
00542         if (targ->client->sess.sessionTeam == TEAM_RED)
00543                 flag_pw = PW_BLUEFLAG;
00544         else
00545                 flag_pw = PW_REDFLAG;
00546 
00547         // flags
00548         if (targ->client->ps.powerups[flag_pw] &&
00549                 targ->client->sess.sessionTeam != attacker->client->sess.sessionTeam)
00550                 attacker->client->pers.teamState.lasthurtcarrier = level.time;
00551 
00552         // skulls
00553         if (targ->client->ps.generic1 &&
00554                 targ->client->sess.sessionTeam != attacker->client->sess.sessionTeam)
00555                 attacker->client->pers.teamState.lasthurtcarrier = level.time;
00556 }
00557 
00558 
00559 gentity_t *Team_ResetFlag( int team ) {
00560         char *c;
00561         gentity_t *ent, *rent = NULL;
00562 
00563         switch (team) {
00564         case TEAM_RED:
00565                 c = "team_CTF_redflag";
00566                 break;
00567         case TEAM_BLUE:
00568                 c = "team_CTF_blueflag";
00569                 break;
00570         case TEAM_FREE:
00571                 c = "team_CTF_neutralflag";
00572                 break;
00573         default:
00574                 return NULL;
00575         }
00576 
00577         ent = NULL;
00578         while ((ent = G_Find (ent, FOFS(classname), c)) != NULL) {
00579                 if (ent->flags & FL_DROPPED_ITEM)
00580                         G_FreeEntity(ent);
00581                 else {
00582                         rent = ent;
00583                         RespawnItem(ent);
00584                 }
00585         }
00586 
00587         Team_SetFlagStatus( team, FLAG_ATBASE );
00588 
00589         return rent;
00590 }
00591 
00592 void Team_ResetFlags( void ) {
00593         if( g_gametype.integer == GT_CTF || g_gametype.integer == GT_CTY ) {
00594                 Team_ResetFlag( TEAM_RED );
00595                 Team_ResetFlag( TEAM_BLUE );
00596         }
00597 }
00598 
00599 void Team_ReturnFlagSound( gentity_t *ent, int team ) {
00600         gentity_t       *te;
00601 
00602         if (ent == NULL) {
00603                 G_Printf ("Warning:  NULL passed to Team_ReturnFlagSound\n");
00604                 return;
00605         }
00606 
00607         te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_TEAM_SOUND );
00608         if( team == TEAM_BLUE ) {
00609                 te->s.eventParm = GTS_RED_RETURN;
00610         }
00611         else {
00612                 te->s.eventParm = GTS_BLUE_RETURN;
00613         }
00614         te->r.svFlags |= SVF_BROADCAST;
00615 }
00616 
00617 void Team_TakeFlagSound( gentity_t *ent, int team ) {
00618         gentity_t       *te;
00619 
00620         if (ent == NULL) {
00621                 G_Printf ("Warning:  NULL passed to Team_TakeFlagSound\n");
00622                 return;
00623         }
00624 
00625         // only play sound when the flag was at the base
00626         // or not picked up the last 10 seconds
00627         switch(team) {
00628                 case TEAM_RED:
00629                         if( teamgame.blueStatus != FLAG_ATBASE ) {
00630                                 if (teamgame.blueTakenTime > level.time - 10000)
00631                                         return;
00632                         }
00633                         teamgame.blueTakenTime = level.time;
00634                         break;
00635 
00636                 case TEAM_BLUE: // CTF
00637                         if( teamgame.redStatus != FLAG_ATBASE ) {
00638                                 if (teamgame.redTakenTime > level.time - 10000)
00639                                         return;
00640                         }
00641                         teamgame.redTakenTime = level.time;
00642                         break;
00643         }
00644 
00645         te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_TEAM_SOUND );
00646         if( team == TEAM_BLUE ) {
00647                 te->s.eventParm = GTS_RED_TAKEN;
00648         }
00649         else {
00650                 te->s.eventParm = GTS_BLUE_TAKEN;
00651         }
00652         te->r.svFlags |= SVF_BROADCAST;
00653 }
00654 
00655 void Team_CaptureFlagSound( gentity_t *ent, int team ) {
00656         gentity_t       *te;
00657 
00658         if (ent == NULL) {
00659                 G_Printf ("Warning:  NULL passed to Team_CaptureFlagSound\n");
00660                 return;
00661         }
00662 
00663         te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_TEAM_SOUND );
00664         if( team == TEAM_BLUE ) {
00665                 te->s.eventParm = GTS_BLUE_CAPTURE;
00666         }
00667         else {
00668                 te->s.eventParm = GTS_RED_CAPTURE;
00669         }
00670         te->r.svFlags |= SVF_BROADCAST;
00671 }
00672 
00673 void Team_ReturnFlag( int team ) {
00674         Team_ReturnFlagSound(Team_ResetFlag(team), team);
00675         if( team == TEAM_FREE ) {
00676                 //PrintMsg(NULL, "The flag has returned!\n" );
00677         }
00678         else { //flag should always have team in normal CTF
00679                 //PrintMsg(NULL, "The %s flag has returned!\n", TeamName(team));
00680                 PrintCTFMessage(-1, team, CTFMESSAGE_FLAG_RETURNED);
00681         }
00682 }
00683 
00684 void Team_FreeEntity( gentity_t *ent ) {
00685         if( ent->item->giTag == PW_REDFLAG ) {
00686                 Team_ReturnFlag( TEAM_RED );
00687         }
00688         else if( ent->item->giTag == PW_BLUEFLAG ) {
00689                 Team_ReturnFlag( TEAM_BLUE );
00690         }
00691         else if( ent->item->giTag == PW_NEUTRALFLAG ) {
00692                 Team_ReturnFlag( TEAM_FREE );
00693         }
00694 }
00695 
00696 /*
00697 ==============
00698 Team_DroppedFlagThink
00699 
00700 Automatically set in Launch_Item if the item is one of the flags
00701 
00702 Flags are unique in that if they are dropped, the base flag must be respawned when they time out
00703 ==============
00704 */
00705 void Team_DroppedFlagThink(gentity_t *ent) {
00706         int             team = TEAM_FREE;
00707 
00708         if( ent->item->giTag == PW_REDFLAG ) {
00709                 team = TEAM_RED;
00710         }
00711         else if( ent->item->giTag == PW_BLUEFLAG ) {
00712                 team = TEAM_BLUE;
00713         }
00714         else if( ent->item->giTag == PW_NEUTRALFLAG ) {
00715                 team = TEAM_FREE;
00716         }
00717 
00718         Team_ReturnFlagSound( Team_ResetFlag( team ), team );
00719         // Reset Flag will delete this entity
00720 }
00721 
00722 
00723 /*
00724 ==============
00725 Team_DroppedFlagThink
00726 ==============
00727 */
00728 int Team_TouchOurFlag( gentity_t *ent, gentity_t *other, int team ) {
00729         int                     i;
00730         gentity_t       *player;
00731         gclient_t       *cl = other->client;
00732         int                     enemy_flag;
00733 
00734         if (cl->sess.sessionTeam == TEAM_RED) {
00735                 enemy_flag = PW_BLUEFLAG;
00736         } else {
00737                 enemy_flag = PW_REDFLAG;
00738         }
00739 
00740         if ( ent->flags & FL_DROPPED_ITEM ) {
00741                 // hey, its not home.  return it by teleporting it back
00742                 //PrintMsg( NULL, "%s" S_COLOR_WHITE " returned the %s flag!\n", 
00743                 //      cl->pers.netname, TeamName(team));
00744                 PrintCTFMessage(other->s.number, team, CTFMESSAGE_PLAYER_RETURNED_FLAG);
00745 
00746                 AddScore(other, ent->r.currentOrigin, CTF_RECOVERY_BONUS);
00747                 other->client->pers.teamState.flagrecovery++;
00748                 other->client->pers.teamState.lastreturnedflag = level.time;
00749                 //ResetFlag will remove this entity!  We must return zero
00750                 Team_ReturnFlagSound(Team_ResetFlag(team), team);
00751                 return 0;
00752         }
00753 
00754         // the flag is at home base.  if the player has the enemy
00755         // flag, he's just won!
00756         if (!cl->ps.powerups[enemy_flag])
00757                 return 0; // We don't have the flag
00758         //PrintMsg( NULL, "%s" S_COLOR_WHITE " captured the %s flag!\n", cl->pers.netname, TeamName(OtherTeam(team)));
00759         PrintCTFMessage(other->s.number, team, CTFMESSAGE_PLAYER_CAPTURED_FLAG);
00760 
00761         cl->ps.powerups[enemy_flag] = 0;
00762 
00763         teamgame.last_flag_capture = level.time;
00764         teamgame.last_capture_team = team;
00765 
00766         // Increase the team's score
00767         AddTeamScore(ent->s.pos.trBase, other->client->sess.sessionTeam, 1);
00768 //      Team_ForceGesture(other->client->sess.sessionTeam);
00769         //rww - don't really want to do this now. Mainly because performing a gesture disables your upper torso animations until it's done and you can't fire
00770 
00771         other->client->pers.teamState.captures++;
00772         other->client->rewardTime = level.time + REWARD_SPRITE_TIME;
00773         other->client->ps.persistant[PERS_CAPTURES]++;
00774 
00775         // other gets another 10 frag bonus
00776         AddScore(other, ent->r.currentOrigin, CTF_CAPTURE_BONUS);
00777 
00778         Team_CaptureFlagSound( ent, team );
00779 
00780         // Ok, let's do the player loop, hand out the bonuses
00781         for (i = 0; i < g_maxclients.integer; i++) {
00782                 player = &g_entities[i];
00783                 if (!player->inuse)
00784                         continue;
00785 
00786                 if (player->client->sess.sessionTeam !=
00787                         cl->sess.sessionTeam) {
00788                         player->client->pers.teamState.lasthurtcarrier = -5;
00789                 } else if (player->client->sess.sessionTeam ==
00790                         cl->sess.sessionTeam) {
00791                         if (player != other)
00792                                 AddScore(player, ent->r.currentOrigin, CTF_TEAM_BONUS);
00793                         // award extra points for capture assists
00794                         if (player->client->pers.teamState.lastreturnedflag + 
00795                                 CTF_RETURN_FLAG_ASSIST_TIMEOUT > level.time) {
00796                                 AddScore (player, ent->r.currentOrigin, CTF_RETURN_FLAG_ASSIST_BONUS);
00797                                 other->client->pers.teamState.assists++;
00798 
00799                                 player->client->ps.persistant[PERS_ASSIST_COUNT]++;
00800                                 player->client->rewardTime = level.time + REWARD_SPRITE_TIME;
00801 
00802                         } else if (player->client->pers.teamState.lastfraggedcarrier + 
00803                                 CTF_FRAG_CARRIER_ASSIST_TIMEOUT > level.time) {
00804                                 AddScore(player, ent->r.currentOrigin, CTF_FRAG_CARRIER_ASSIST_BONUS);
00805                                 other->client->pers.teamState.assists++;
00806                                 player->client->ps.persistant[PERS_ASSIST_COUNT]++;
00807                                 player->client->rewardTime = level.time + REWARD_SPRITE_TIME;
00808                         }
00809                 }
00810         }
00811         Team_ResetFlags();
00812 
00813         CalculateRanks();
00814 
00815         return 0; // Do not respawn this automatically
00816 }
00817 
00818 int Team_TouchEnemyFlag( gentity_t *ent, gentity_t *other, int team ) {
00819         gclient_t *cl = other->client;
00820 
00821         //PrintMsg (NULL, "%s" S_COLOR_WHITE " got the %s flag!\n",
00822         //      other->client->pers.netname, TeamName(team));
00823         PrintCTFMessage(other->s.number, team, CTFMESSAGE_PLAYER_GOT_FLAG);
00824 
00825         if (team == TEAM_RED)
00826                 cl->ps.powerups[PW_REDFLAG] = INT_MAX; // flags never expire
00827         else
00828                 cl->ps.powerups[PW_BLUEFLAG] = INT_MAX; // flags never expire
00829 
00830         Team_SetFlagStatus( team, FLAG_TAKEN );
00831 
00832         AddScore(other, ent->r.currentOrigin, CTF_FLAG_BONUS);
00833         cl->pers.teamState.flagsince = level.time;
00834         Team_TakeFlagSound( ent, team );
00835 
00836         return -1; // Do not respawn this automatically, but do delete it if it was FL_DROPPED
00837 }
00838 
00839 int Pickup_Team( gentity_t *ent, gentity_t *other ) {
00840         int team;
00841         gclient_t *cl = other->client;
00842 
00843         // figure out what team this flag is
00844         if( strcmp(ent->classname, "team_CTF_redflag") == 0 ) {
00845                 team = TEAM_RED;
00846         }
00847         else if( strcmp(ent->classname, "team_CTF_blueflag") == 0 ) {
00848                 team = TEAM_BLUE;
00849         }
00850         else if( strcmp(ent->classname, "team_CTF_neutralflag") == 0  ) {
00851                 team = TEAM_FREE;
00852         }
00853         else {
00854 //              PrintMsg ( other, "Don't know what team the flag is on.\n");
00855                 return 0;
00856         }
00857         // GT_CTF
00858         if( team == cl->sess.sessionTeam) {
00859                 return Team_TouchOurFlag( ent, other, team );
00860         }
00861         return Team_TouchEnemyFlag( ent, other, team );
00862 }
00863 
00864 /*
00865 ===========
00866 Team_GetLocation
00867 
00868 Report a location for the player. Uses placed nearby target_location entities
00869 ============
00870 */
00871 gentity_t *Team_GetLocation(gentity_t *ent)
00872 {
00873         gentity_t               *eloc, *best;
00874         float                   bestlen, len;
00875         vec3_t                  origin;
00876 
00877         best = NULL;
00878         bestlen = 3*8192.0*8192.0;
00879 
00880         VectorCopy( ent->r.currentOrigin, origin );
00881 
00882         for (eloc = level.locationHead; eloc; eloc = eloc->nextTrain) {
00883                 len = ( origin[0] - eloc->r.currentOrigin[0] ) * ( origin[0] - eloc->r.currentOrigin[0] )
00884                         + ( origin[1] - eloc->r.currentOrigin[1] ) * ( origin[1] - eloc->r.currentOrigin[1] )
00885                         + ( origin[2] - eloc->r.currentOrigin[2] ) * ( origin[2] - eloc->r.currentOrigin[2] );
00886 
00887                 if ( len > bestlen ) {
00888                         continue;
00889                 }
00890 
00891                 if ( !trap_InPVS( origin, eloc->r.currentOrigin ) ) {
00892                         continue;
00893                 }
00894 
00895                 bestlen = len;
00896                 best = eloc;
00897         }
00898 
00899         return best;
00900 }
00901 
00902 
00903 /*
00904 ===========
00905 Team_GetLocation
00906 
00907 Report a location for the player. Uses placed nearby target_location entities
00908 ============
00909 */
00910 qboolean Team_GetLocationMsg(gentity_t *ent, char *loc, int loclen)
00911 {
00912         gentity_t *best;
00913 
00914         best = Team_GetLocation( ent );
00915         
00916         if (!best)
00917                 return qfalse;
00918 
00919         if (best->count) {
00920                 if (best->count < 0)
00921                         best->count = 0;
00922                 if (best->count > 7)
00923                         best->count = 7;
00924                 Com_sprintf(loc, loclen, "%c%c%s" S_COLOR_WHITE, Q_COLOR_ESCAPE, best->count + '0', best->message );
00925         } else
00926                 Com_sprintf(loc, loclen, "%s", best->message);
00927 
00928         return qtrue;
00929 }
00930 
00931 
00932 /*---------------------------------------------------------------------------*/
00933 
00934 /*
00935 ================
00936 SelectRandomDeathmatchSpawnPoint
00937 
00938 go to a random point that doesn't telefrag
00939 ================
00940 */
00941 #define MAX_TEAM_SPAWN_POINTS   32
00942 gentity_t *SelectRandomTeamSpawnPoint( int teamstate, team_t team, int siegeClass ) {
00943         gentity_t       *spot;
00944         int                     count;
00945         int                     selection;
00946         gentity_t       *spots[MAX_TEAM_SPAWN_POINTS];
00947         char            *classname;
00948         qboolean        mustBeEnabled = qfalse;
00949 
00950         if (g_gametype.integer == GT_SIEGE)
00951         {
00952                 if (team == SIEGETEAM_TEAM1)
00953                 {
00954                         classname = "info_player_siegeteam1";
00955                 }
00956                 else
00957                 {
00958                         classname = "info_player_siegeteam2";
00959                 }
00960 
00961                 mustBeEnabled = qtrue; //siege spawn points need to be "enabled" to be used (because multiple spawnpoint sets can be placed at once)
00962         }
00963         else
00964         {
00965                 if (teamstate == TEAM_BEGIN) {
00966                         if (team == TEAM_RED)
00967                                 classname = "team_CTF_redplayer";
00968                         else if (team == TEAM_BLUE)
00969                                 classname = "team_CTF_blueplayer";
00970                         else
00971                                 return NULL;
00972                 } else {
00973                         if (team == TEAM_RED)
00974                                 classname = "team_CTF_redspawn";
00975                         else if (team == TEAM_BLUE)
00976                                 classname = "team_CTF_bluespawn";
00977                         else
00978                                 return NULL;
00979                 }
00980         }
00981         count = 0;
00982 
00983         spot = NULL;
00984 
00985         while ((spot = G_Find (spot, FOFS(classname), classname)) != NULL) {
00986                 if ( SpotWouldTelefrag( spot ) ) {
00987                         continue;
00988                 }
00989 
00990                 if (mustBeEnabled && !spot->genericValue1)
00991                 { //siege point that's not enabled, can't use it
00992                         continue;
00993                 }
00994 
00995                 spots[ count ] = spot;
00996                 if (++count == MAX_TEAM_SPAWN_POINTS)
00997                         break;
00998         }
00999 
01000         if ( !count ) { // no spots that won't telefrag
01001                 return G_Find( NULL, FOFS(classname), classname);
01002         }
01003 
01004         if (g_gametype.integer == GT_SIEGE && siegeClass >= 0 &&
01005                 bgSiegeClasses[siegeClass].name[0])
01006         { //out of the spots found, see if any have an idealclass to match our class name
01007                 gentity_t *classSpots[MAX_TEAM_SPAWN_POINTS];
01008                 int classCount = 0;
01009                 int i = 0;
01010 
01011         while (i < count)
01012                 {
01013                         if (spots[i] && spots[i]->idealclass && spots[i]->idealclass[0] &&
01014                                 !Q_stricmp(spots[i]->idealclass, bgSiegeClasses[siegeClass].name))
01015                         { //this spot's idealclass matches the class name
01016                 classSpots[classCount] = spots[i];
01017                                 classCount++;
01018                         }
01019                         i++;
01020                 }
01021 
01022                 if (classCount > 0)
01023                 { //found at least one
01024                         selection = rand() % classCount;
01025                         return spots[ selection ];
01026                 }
01027         }
01028 
01029         selection = rand() % count;
01030         return spots[ selection ];
01031 }
01032 
01033 
01034 /*
01035 ===========
01036 SelectCTFSpawnPoint
01037 
01038 ============
01039 */
01040 gentity_t *SelectCTFSpawnPoint ( team_t team, int teamstate, vec3_t origin, vec3_t angles ) {
01041         gentity_t       *spot;
01042 
01043         spot = SelectRandomTeamSpawnPoint ( teamstate, team, -1 );
01044 
01045         if (!spot) {
01046                 return SelectSpawnPoint( vec3_origin, origin, angles, team );
01047         }
01048 
01049         VectorCopy (spot->s.origin, origin);
01050         origin[2] += 9;
01051         VectorCopy (spot->s.angles, angles);
01052 
01053         return spot;
01054 }
01055 
01056 /*
01057 ===========
01058 SelectSiegeSpawnPoint
01059 
01060 ============
01061 */
01062 gentity_t *SelectSiegeSpawnPoint ( int siegeClass, team_t team, int teamstate, vec3_t origin, vec3_t angles ) {
01063         gentity_t       *spot;
01064 
01065         spot = SelectRandomTeamSpawnPoint ( teamstate, team, siegeClass );
01066 
01067         if (!spot) {
01068                 return SelectSpawnPoint( vec3_origin, origin, angles, team );
01069         }
01070 
01071         VectorCopy (spot->s.origin, origin);
01072         origin[2] += 9;
01073         VectorCopy (spot->s.angles, angles);
01074 
01075         return spot;
01076 }
01077 
01078 /*---------------------------------------------------------------------------*/
01079 
01080 static int QDECL SortClients( const void *a, const void *b ) {
01081         return *(int *)a - *(int *)b;
01082 }
01083 
01084 
01085 /*
01086 ==================
01087 TeamplayLocationsMessage
01088 
01089 Format:
01090         clientNum location health armor weapon powerups
01091 
01092 ==================
01093 */
01094 void TeamplayInfoMessage( gentity_t *ent ) {
01095         char            entry[1024];
01096         char            string[8192];
01097         int                     stringlength;
01098         int                     i, j;
01099         gentity_t       *player;
01100         int                     cnt;
01101         int                     h, a;
01102         int                     clients[TEAM_MAXOVERLAY];
01103 
01104         if ( ! ent->client->pers.teamInfo )
01105                 return;
01106 
01107         // figure out what client should be on the display
01108         // we are limited to 8, but we want to use the top eight players
01109         // but in client order (so they don't keep changing position on the overlay)
01110         for (i = 0, cnt = 0; i < g_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++) {
01111                 player = g_entities + level.sortedClients[i];
01112                 if (player->inuse && player->client->sess.sessionTeam == 
01113                         ent->client->sess.sessionTeam ) {
01114                         clients[cnt++] = level.sortedClients[i];
01115                 }
01116         }
01117 
01118         // We have the top eight players, sort them by clientNum
01119         qsort( clients, cnt, sizeof( clients[0] ), SortClients );
01120 
01121         // send the latest information on all clients
01122         string[0] = 0;
01123         stringlength = 0;
01124 
01125         for (i = 0, cnt = 0; i < g_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++) {
01126                 player = g_entities + i;
01127                 if (player->inuse && player->client->sess.sessionTeam == 
01128                         ent->client->sess.sessionTeam ) {
01129 
01130                         h = player->client->ps.stats[STAT_HEALTH];
01131                         a = player->client->ps.stats[STAT_ARMOR];
01132                         if (h < 0) h = 0;
01133                         if (a < 0) a = 0;
01134 
01135                         Com_sprintf (entry, sizeof(entry),
01136                                 " %i %i %i %i %i %i", 
01137 //                              level.sortedClients[i], player->client->pers.teamState.location, h, a, 
01138                                 i, player->client->pers.teamState.location, h, a, 
01139                                 player->client->ps.weapon, player->s.powerups);
01140                         j = strlen(entry);
01141                         if (stringlength + j > sizeof(string))
01142                                 break;
01143                         strcpy (string + stringlength, entry);
01144                         stringlength += j;
01145                         cnt++;
01146                 }
01147         }
01148 
01149         trap_SendServerCommand( ent-g_entities, va("tinfo %i %s", cnt, string) );
01150 }
01151 
01152 void CheckTeamStatus(void) {
01153         int i;
01154         gentity_t *loc, *ent;
01155 
01156         if (level.time - level.lastTeamLocationTime > TEAM_LOCATION_UPDATE_TIME) {
01157 
01158                 level.lastTeamLocationTime = level.time;
01159 
01160                 for (i = 0; i < g_maxclients.integer; i++) {
01161                         ent = g_entities + i;
01162 
01163                         if ( !ent->client )
01164                         {
01165                                 continue;
01166                         }
01167 
01168                         if ( ent->client->pers.connected != CON_CONNECTED ) {
01169                                 continue;
01170                         }
01171 
01172                         if (ent->inuse && (ent->client->sess.sessionTeam == TEAM_RED || ent->client->sess.sessionTeam == TEAM_BLUE)) {
01173                                 loc = Team_GetLocation( ent );
01174                                 if (loc)
01175                                         ent->client->pers.teamState.location = loc->health;
01176                                 else
01177                                         ent->client->pers.teamState.location = 0;
01178                         }
01179                 }
01180 
01181                 for (i = 0; i < g_maxclients.integer; i++) {
01182                         ent = g_entities + i;
01183 
01184                         if ( ent->client->pers.connected != CON_CONNECTED ) {
01185                                 continue;
01186                         }
01187 
01188                         if (ent->inuse && (ent->client->sess.sessionTeam == TEAM_RED || ent->client->sess.sessionTeam == TEAM_BLUE)) {
01189                                 TeamplayInfoMessage( ent );
01190                         }
01191                 }
01192         }
01193 }
01194 
01195 /*-----------------------------------------------------------------*/
01196 
01197 /*QUAKED team_CTF_redplayer (1 0 0) (-16 -16 -16) (16 16 32)
01198 Only in CTF games.  Red players spawn here at game start.
01199 */
01200 void SP_team_CTF_redplayer( gentity_t *ent ) {
01201 }
01202 
01203 
01204 /*QUAKED team_CTF_blueplayer (0 0 1) (-16 -16 -16) (16 16 32)
01205 Only in CTF games.  Blue players spawn here at game start.
01206 */
01207 void SP_team_CTF_blueplayer( gentity_t *ent ) {
01208 }
01209 
01210 
01211 /*QUAKED team_CTF_redspawn (1 0 0) (-16 -16 -24) (16 16 32)
01212 potential spawning position for red team in CTF games.
01213 Targets will be fired when someone spawns in on them.
01214 */
01215 void SP_team_CTF_redspawn(gentity_t *ent) {
01216 }
01217 
01218 /*QUAKED team_CTF_bluespawn (0 0 1) (-16 -16 -24) (16 16 32)
01219 potential spawning position for blue team in CTF games.
01220 Targets will be fired when someone spawns in on them.
01221 */
01222 void SP_team_CTF_bluespawn(gentity_t *ent) {
01223 }
01224 
01225