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