cg_event.c (32216B)
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 // cg_event.c -- handle entity events at snapshot or playerstate transitions 24 25 #include "cg_local.h" 26 27 // for the voice chats 28 #ifdef MISSIONPACK // bk001205 29 #include "../../ui/menudef.h" 30 #endif 31 //========================================================================== 32 33 /* 34 =================== 35 CG_PlaceString 36 37 Also called by scoreboard drawing 38 =================== 39 */ 40 const char *CG_PlaceString( int rank ) { 41 static char str[64]; 42 char *s, *t; 43 44 if ( rank & RANK_TIED_FLAG ) { 45 rank &= ~RANK_TIED_FLAG; 46 t = "Tied for "; 47 } else { 48 t = ""; 49 } 50 51 if ( rank == 1 ) { 52 s = S_COLOR_BLUE "1st" S_COLOR_WHITE; // draw in blue 53 } else if ( rank == 2 ) { 54 s = S_COLOR_RED "2nd" S_COLOR_WHITE; // draw in red 55 } else if ( rank == 3 ) { 56 s = S_COLOR_YELLOW "3rd" S_COLOR_WHITE; // draw in yellow 57 } else if ( rank == 11 ) { 58 s = "11th"; 59 } else if ( rank == 12 ) { 60 s = "12th"; 61 } else if ( rank == 13 ) { 62 s = "13th"; 63 } else if ( rank % 10 == 1 ) { 64 s = va("%ist", rank); 65 } else if ( rank % 10 == 2 ) { 66 s = va("%ind", rank); 67 } else if ( rank % 10 == 3 ) { 68 s = va("%ird", rank); 69 } else { 70 s = va("%ith", rank); 71 } 72 73 Com_sprintf( str, sizeof( str ), "%s%s", t, s ); 74 return str; 75 } 76 77 /* 78 ============= 79 CG_Obituary 80 ============= 81 */ 82 static void CG_Obituary( entityState_t *ent ) { 83 int mod; 84 int target, attacker; 85 char *message; 86 char *message2; 87 const char *targetInfo; 88 const char *attackerInfo; 89 char targetName[32]; 90 char attackerName[32]; 91 gender_t gender; 92 clientInfo_t *ci; 93 94 target = ent->otherEntityNum; 95 attacker = ent->otherEntityNum2; 96 mod = ent->eventParm; 97 98 if ( target < 0 || target >= MAX_CLIENTS ) { 99 CG_Error( "CG_Obituary: target out of range" ); 100 } 101 ci = &cgs.clientinfo[target]; 102 103 if ( attacker < 0 || attacker >= MAX_CLIENTS ) { 104 attacker = ENTITYNUM_WORLD; 105 attackerInfo = NULL; 106 } else { 107 attackerInfo = CG_ConfigString( CS_PLAYERS + attacker ); 108 } 109 110 targetInfo = CG_ConfigString( CS_PLAYERS + target ); 111 if ( !targetInfo ) { 112 return; 113 } 114 Q_strncpyz( targetName, Info_ValueForKey( targetInfo, "n" ), sizeof(targetName) - 2); 115 strcat( targetName, S_COLOR_WHITE ); 116 117 message2 = ""; 118 119 // check for single client messages 120 121 switch( mod ) { 122 case MOD_SUICIDE: 123 message = "suicides"; 124 break; 125 case MOD_FALLING: 126 message = "cratered"; 127 break; 128 case MOD_CRUSH: 129 message = "was squished"; 130 break; 131 case MOD_WATER: 132 message = "sank like a rock"; 133 break; 134 case MOD_SLIME: 135 message = "melted"; 136 break; 137 case MOD_LAVA: 138 message = "does a back flip into the lava"; 139 break; 140 case MOD_TARGET_LASER: 141 message = "saw the light"; 142 break; 143 case MOD_TRIGGER_HURT: 144 message = "was in the wrong place"; 145 break; 146 default: 147 message = NULL; 148 break; 149 } 150 151 if (attacker == target) { 152 gender = ci->gender; 153 switch (mod) { 154 #ifdef MISSIONPACK 155 case MOD_KAMIKAZE: 156 message = "goes out with a bang"; 157 break; 158 #endif 159 case MOD_GRENADE_SPLASH: 160 if ( gender == GENDER_FEMALE ) 161 message = "tripped on her own grenade"; 162 else if ( gender == GENDER_NEUTER ) 163 message = "tripped on its own grenade"; 164 else 165 message = "tripped on his own grenade"; 166 break; 167 case MOD_ROCKET_SPLASH: 168 if ( gender == GENDER_FEMALE ) 169 message = "blew herself up"; 170 else if ( gender == GENDER_NEUTER ) 171 message = "blew itself up"; 172 else 173 message = "blew himself up"; 174 break; 175 case MOD_PLASMA_SPLASH: 176 if ( gender == GENDER_FEMALE ) 177 message = "melted herself"; 178 else if ( gender == GENDER_NEUTER ) 179 message = "melted itself"; 180 else 181 message = "melted himself"; 182 break; 183 case MOD_BFG_SPLASH: 184 message = "should have used a smaller gun"; 185 break; 186 #ifdef MISSIONPACK 187 case MOD_PROXIMITY_MINE: 188 if( gender == GENDER_FEMALE ) { 189 message = "found her prox mine"; 190 } else if ( gender == GENDER_NEUTER ) { 191 message = "found it's prox mine"; 192 } else { 193 message = "found his prox mine"; 194 } 195 break; 196 #endif 197 default: 198 if ( gender == GENDER_FEMALE ) 199 message = "killed herself"; 200 else if ( gender == GENDER_NEUTER ) 201 message = "killed itself"; 202 else 203 message = "killed himself"; 204 break; 205 } 206 } 207 208 if (message) { 209 CG_Printf( "%s %s.\n", targetName, message); 210 return; 211 } 212 213 // check for kill messages from the current clientNum 214 if ( attacker == cg.snap->ps.clientNum ) { 215 char *s; 216 217 if ( cgs.gametype < GT_TEAM ) { 218 s = va("You fragged %s\n%s place with %i", targetName, 219 CG_PlaceString( cg.snap->ps.persistant[PERS_RANK] + 1 ), 220 cg.snap->ps.persistant[PERS_SCORE] ); 221 } else { 222 s = va("You fragged %s", targetName ); 223 } 224 #ifdef MISSIONPACK 225 if (!(cg_singlePlayerActive.integer && cg_cameraOrbit.integer)) { 226 CG_CenterPrint( s, SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); 227 } 228 #else 229 CG_CenterPrint( s, SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); 230 #endif 231 232 // print the text message as well 233 } 234 235 // check for double client messages 236 if ( !attackerInfo ) { 237 attacker = ENTITYNUM_WORLD; 238 strcpy( attackerName, "noname" ); 239 } else { 240 Q_strncpyz( attackerName, Info_ValueForKey( attackerInfo, "n" ), sizeof(attackerName) - 2); 241 strcat( attackerName, S_COLOR_WHITE ); 242 // check for kill messages about the current clientNum 243 if ( target == cg.snap->ps.clientNum ) { 244 Q_strncpyz( cg.killerName, attackerName, sizeof( cg.killerName ) ); 245 } 246 } 247 248 if ( attacker != ENTITYNUM_WORLD ) { 249 switch (mod) { 250 case MOD_GRAPPLE: 251 message = "was caught by"; 252 break; 253 case MOD_GAUNTLET: 254 message = "was pummeled by"; 255 break; 256 case MOD_MACHINEGUN: 257 message = "was machinegunned by"; 258 break; 259 case MOD_SHOTGUN: 260 message = "was gunned down by"; 261 break; 262 case MOD_GRENADE: 263 message = "ate"; 264 message2 = "'s grenade"; 265 break; 266 case MOD_GRENADE_SPLASH: 267 message = "was shredded by"; 268 message2 = "'s shrapnel"; 269 break; 270 case MOD_ROCKET: 271 message = "ate"; 272 message2 = "'s rocket"; 273 break; 274 case MOD_ROCKET_SPLASH: 275 message = "almost dodged"; 276 message2 = "'s rocket"; 277 break; 278 case MOD_PLASMA: 279 message = "was melted by"; 280 message2 = "'s plasmagun"; 281 break; 282 case MOD_PLASMA_SPLASH: 283 message = "was melted by"; 284 message2 = "'s plasmagun"; 285 break; 286 case MOD_RAILGUN: 287 message = "was railed by"; 288 break; 289 case MOD_LIGHTNING: 290 message = "was electrocuted by"; 291 break; 292 case MOD_BFG: 293 case MOD_BFG_SPLASH: 294 message = "was blasted by"; 295 message2 = "'s BFG"; 296 break; 297 #ifdef MISSIONPACK 298 case MOD_NAIL: 299 message = "was nailed by"; 300 break; 301 case MOD_CHAINGUN: 302 message = "got lead poisoning from"; 303 message2 = "'s Chaingun"; 304 break; 305 case MOD_PROXIMITY_MINE: 306 message = "was too close to"; 307 message2 = "'s Prox Mine"; 308 break; 309 case MOD_KAMIKAZE: 310 message = "falls to"; 311 message2 = "'s Kamikaze blast"; 312 break; 313 case MOD_JUICED: 314 message = "was juiced by"; 315 break; 316 #endif 317 case MOD_TELEFRAG: 318 message = "tried to invade"; 319 message2 = "'s personal space"; 320 break; 321 default: 322 message = "was killed by"; 323 break; 324 } 325 326 if (message) { 327 CG_Printf( "%s %s %s%s\n", 328 targetName, message, attackerName, message2); 329 return; 330 } 331 } 332 333 // we don't know what it was 334 CG_Printf( "%s died.\n", targetName ); 335 } 336 337 //========================================================================== 338 339 /* 340 =============== 341 CG_UseItem 342 =============== 343 */ 344 static void CG_UseItem( centity_t *cent ) { 345 clientInfo_t *ci; 346 int itemNum, clientNum; 347 gitem_t *item; 348 entityState_t *es; 349 350 es = ¢->currentState; 351 352 itemNum = (es->event & ~EV_EVENT_BITS) - EV_USE_ITEM0; 353 if ( itemNum < 0 || itemNum > HI_NUM_HOLDABLE ) { 354 itemNum = 0; 355 } 356 357 // print a message if the local player 358 if ( es->number == cg.snap->ps.clientNum ) { 359 if ( !itemNum ) { 360 CG_CenterPrint( "No item to use", SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); 361 } else { 362 item = BG_FindItemForHoldable( itemNum ); 363 CG_CenterPrint( va("Use %s", item->pickup_name), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); 364 } 365 } 366 367 switch ( itemNum ) { 368 default: 369 case HI_NONE: 370 trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.useNothingSound ); 371 break; 372 373 case HI_TELEPORTER: 374 break; 375 376 case HI_MEDKIT: 377 clientNum = cent->currentState.clientNum; 378 if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) { 379 ci = &cgs.clientinfo[ clientNum ]; 380 ci->medkitUsageTime = cg.time; 381 } 382 trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.medkitSound ); 383 break; 384 385 #ifdef MISSIONPACK 386 case HI_KAMIKAZE: 387 break; 388 389 case HI_PORTAL: 390 break; 391 case HI_INVULNERABILITY: 392 trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.useInvulnerabilitySound ); 393 break; 394 #endif 395 } 396 397 } 398 399 /* 400 ================ 401 CG_ItemPickup 402 403 A new item was picked up this frame 404 ================ 405 */ 406 static void CG_ItemPickup( int itemNum ) { 407 cg.itemPickup = itemNum; 408 cg.itemPickupTime = cg.time; 409 cg.itemPickupBlendTime = cg.time; 410 // see if it should be the grabbed weapon 411 if ( bg_itemlist[itemNum].giType == IT_WEAPON ) { 412 // select it immediately 413 if ( cg_autoswitch.integer && bg_itemlist[itemNum].giTag != WP_MACHINEGUN ) { 414 cg.weaponSelectTime = cg.time; 415 cg.weaponSelect = bg_itemlist[itemNum].giTag; 416 } 417 } 418 419 } 420 421 422 /* 423 ================ 424 CG_PainEvent 425 426 Also called by playerstate transition 427 ================ 428 */ 429 void CG_PainEvent( centity_t *cent, int health ) { 430 char *snd; 431 432 // don't do more than two pain sounds a second 433 if ( cg.time - cent->pe.painTime < 500 ) { 434 return; 435 } 436 437 if ( health < 25 ) { 438 snd = "*pain25_1.wav"; 439 } else if ( health < 50 ) { 440 snd = "*pain50_1.wav"; 441 } else if ( health < 75 ) { 442 snd = "*pain75_1.wav"; 443 } else { 444 snd = "*pain100_1.wav"; 445 } 446 trap_S_StartSound( NULL, cent->currentState.number, CHAN_VOICE, 447 CG_CustomSound( cent->currentState.number, snd ) ); 448 449 // save pain time for programitic twitch animation 450 cent->pe.painTime = cg.time; 451 cent->pe.painDirection ^= 1; 452 } 453 454 455 456 /* 457 ============== 458 CG_EntityEvent 459 460 An entity has an event value 461 also called by CG_CheckPlayerstateEvents 462 ============== 463 */ 464 #define DEBUGNAME(x) if(cg_debugEvents.integer){CG_Printf(x"\n");} 465 void CG_EntityEvent( centity_t *cent, vec3_t position ) { 466 entityState_t *es; 467 int event; 468 vec3_t dir; 469 const char *s; 470 int clientNum; 471 clientInfo_t *ci; 472 473 es = ¢->currentState; 474 event = es->event & ~EV_EVENT_BITS; 475 476 if ( cg_debugEvents.integer ) { 477 CG_Printf( "ent:%3i event:%3i ", es->number, event ); 478 } 479 480 if ( !event ) { 481 DEBUGNAME("ZEROEVENT"); 482 return; 483 } 484 485 clientNum = es->clientNum; 486 if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { 487 clientNum = 0; 488 } 489 ci = &cgs.clientinfo[ clientNum ]; 490 491 switch ( event ) { 492 // 493 // movement generated events 494 // 495 case EV_FOOTSTEP: 496 DEBUGNAME("EV_FOOTSTEP"); 497 if (cg_footsteps.integer) { 498 trap_S_StartSound (NULL, es->number, CHAN_BODY, 499 cgs.media.footsteps[ ci->footsteps ][rand()&3] ); 500 } 501 break; 502 case EV_FOOTSTEP_METAL: 503 DEBUGNAME("EV_FOOTSTEP_METAL"); 504 if (cg_footsteps.integer) { 505 trap_S_StartSound (NULL, es->number, CHAN_BODY, 506 cgs.media.footsteps[ FOOTSTEP_METAL ][rand()&3] ); 507 } 508 break; 509 case EV_FOOTSPLASH: 510 DEBUGNAME("EV_FOOTSPLASH"); 511 if (cg_footsteps.integer) { 512 trap_S_StartSound (NULL, es->number, CHAN_BODY, 513 cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] ); 514 } 515 break; 516 case EV_FOOTWADE: 517 DEBUGNAME("EV_FOOTWADE"); 518 if (cg_footsteps.integer) { 519 trap_S_StartSound (NULL, es->number, CHAN_BODY, 520 cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] ); 521 } 522 break; 523 case EV_SWIM: 524 DEBUGNAME("EV_SWIM"); 525 if (cg_footsteps.integer) { 526 trap_S_StartSound (NULL, es->number, CHAN_BODY, 527 cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] ); 528 } 529 break; 530 531 532 case EV_FALL_SHORT: 533 DEBUGNAME("EV_FALL_SHORT"); 534 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.landSound ); 535 if ( clientNum == cg.predictedPlayerState.clientNum ) { 536 // smooth landing z changes 537 cg.landChange = -8; 538 cg.landTime = cg.time; 539 } 540 break; 541 case EV_FALL_MEDIUM: 542 DEBUGNAME("EV_FALL_MEDIUM"); 543 // use normal pain sound 544 trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*pain100_1.wav" ) ); 545 if ( clientNum == cg.predictedPlayerState.clientNum ) { 546 // smooth landing z changes 547 cg.landChange = -16; 548 cg.landTime = cg.time; 549 } 550 break; 551 case EV_FALL_FAR: 552 DEBUGNAME("EV_FALL_FAR"); 553 trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*fall1.wav" ) ); 554 cent->pe.painTime = cg.time; // don't play a pain sound right after this 555 if ( clientNum == cg.predictedPlayerState.clientNum ) { 556 // smooth landing z changes 557 cg.landChange = -24; 558 cg.landTime = cg.time; 559 } 560 break; 561 562 case EV_STEP_4: 563 case EV_STEP_8: 564 case EV_STEP_12: 565 case EV_STEP_16: // smooth out step up transitions 566 DEBUGNAME("EV_STEP"); 567 { 568 float oldStep; 569 int delta; 570 int step; 571 572 if ( clientNum != cg.predictedPlayerState.clientNum ) { 573 break; 574 } 575 // if we are interpolating, we don't need to smooth steps 576 if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) || 577 cg_nopredict.integer || cg_synchronousClients.integer ) { 578 break; 579 } 580 // check for stepping up before a previous step is completed 581 delta = cg.time - cg.stepTime; 582 if (delta < STEP_TIME) { 583 oldStep = cg.stepChange * (STEP_TIME - delta) / STEP_TIME; 584 } else { 585 oldStep = 0; 586 } 587 588 // add this amount 589 step = 4 * (event - EV_STEP_4 + 1 ); 590 cg.stepChange = oldStep + step; 591 if ( cg.stepChange > MAX_STEP_CHANGE ) { 592 cg.stepChange = MAX_STEP_CHANGE; 593 } 594 cg.stepTime = cg.time; 595 break; 596 } 597 598 case EV_JUMP_PAD: 599 DEBUGNAME("EV_JUMP_PAD"); 600 // CG_Printf( "EV_JUMP_PAD w/effect #%i\n", es->eventParm ); 601 { 602 localEntity_t *smoke; 603 vec3_t up = {0, 0, 1}; 604 605 606 smoke = CG_SmokePuff( cent->lerpOrigin, up, 607 32, 608 1, 1, 1, 0.33f, 609 1000, 610 cg.time, 0, 611 LEF_PUFF_DONT_SCALE, 612 cgs.media.smokePuffShader ); 613 } 614 615 // boing sound at origin, jump sound on player 616 trap_S_StartSound ( cent->lerpOrigin, -1, CHAN_VOICE, cgs.media.jumpPadSound ); 617 trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) ); 618 break; 619 620 case EV_JUMP: 621 DEBUGNAME("EV_JUMP"); 622 trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) ); 623 break; 624 case EV_TAUNT: 625 DEBUGNAME("EV_TAUNT"); 626 trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*taunt.wav" ) ); 627 break; 628 #ifdef MISSIONPACK 629 case EV_TAUNT_YES: 630 DEBUGNAME("EV_TAUNT_YES"); 631 CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_YES); 632 break; 633 case EV_TAUNT_NO: 634 DEBUGNAME("EV_TAUNT_NO"); 635 CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_NO); 636 break; 637 case EV_TAUNT_FOLLOWME: 638 DEBUGNAME("EV_TAUNT_FOLLOWME"); 639 CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_FOLLOWME); 640 break; 641 case EV_TAUNT_GETFLAG: 642 DEBUGNAME("EV_TAUNT_GETFLAG"); 643 CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_ONGETFLAG); 644 break; 645 case EV_TAUNT_GUARDBASE: 646 DEBUGNAME("EV_TAUNT_GUARDBASE"); 647 CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_ONDEFENSE); 648 break; 649 case EV_TAUNT_PATROL: 650 DEBUGNAME("EV_TAUNT_PATROL"); 651 CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_ONPATROL); 652 break; 653 #endif 654 case EV_WATER_TOUCH: 655 DEBUGNAME("EV_WATER_TOUCH"); 656 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrInSound ); 657 break; 658 case EV_WATER_LEAVE: 659 DEBUGNAME("EV_WATER_LEAVE"); 660 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrOutSound ); 661 break; 662 case EV_WATER_UNDER: 663 DEBUGNAME("EV_WATER_UNDER"); 664 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrUnSound ); 665 break; 666 case EV_WATER_CLEAR: 667 DEBUGNAME("EV_WATER_CLEAR"); 668 trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*gasp.wav" ) ); 669 break; 670 671 case EV_ITEM_PICKUP: 672 DEBUGNAME("EV_ITEM_PICKUP"); 673 { 674 gitem_t *item; 675 int index; 676 677 index = es->eventParm; // player predicted 678 679 if ( index < 1 || index >= bg_numItems ) { 680 break; 681 } 682 item = &bg_itemlist[ index ]; 683 684 // powerups and team items will have a separate global sound, this one 685 // will be played at prediction time 686 if ( item->giType == IT_POWERUP || item->giType == IT_TEAM) { 687 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.n_healthSound ); 688 } else if (item->giType == IT_PERSISTANT_POWERUP) { 689 #ifdef MISSIONPACK 690 switch (item->giTag ) { 691 case PW_SCOUT: 692 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.scoutSound ); 693 break; 694 case PW_GUARD: 695 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.guardSound ); 696 break; 697 case PW_DOUBLER: 698 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.doublerSound ); 699 break; 700 case PW_AMMOREGEN: 701 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.ammoregenSound ); 702 break; 703 } 704 #endif 705 } else { 706 trap_S_StartSound (NULL, es->number, CHAN_AUTO, trap_S_RegisterSound( item->pickup_sound, qfalse ) ); 707 } 708 709 // show icon and name on status bar 710 if ( es->number == cg.snap->ps.clientNum ) { 711 CG_ItemPickup( index ); 712 } 713 } 714 break; 715 716 case EV_GLOBAL_ITEM_PICKUP: 717 DEBUGNAME("EV_GLOBAL_ITEM_PICKUP"); 718 { 719 gitem_t *item; 720 int index; 721 722 index = es->eventParm; // player predicted 723 724 if ( index < 1 || index >= bg_numItems ) { 725 break; 726 } 727 item = &bg_itemlist[ index ]; 728 // powerup pickups are global 729 if( item->pickup_sound ) { 730 trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, trap_S_RegisterSound( item->pickup_sound, qfalse ) ); 731 } 732 733 // show icon and name on status bar 734 if ( es->number == cg.snap->ps.clientNum ) { 735 CG_ItemPickup( index ); 736 } 737 } 738 break; 739 740 // 741 // weapon events 742 // 743 case EV_NOAMMO: 744 DEBUGNAME("EV_NOAMMO"); 745 // trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.noAmmoSound ); 746 if ( es->number == cg.snap->ps.clientNum ) { 747 CG_OutOfAmmoChange(); 748 } 749 break; 750 case EV_CHANGE_WEAPON: 751 DEBUGNAME("EV_CHANGE_WEAPON"); 752 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.selectSound ); 753 break; 754 case EV_FIRE_WEAPON: 755 DEBUGNAME("EV_FIRE_WEAPON"); 756 CG_FireWeapon( cent ); 757 break; 758 759 case EV_USE_ITEM0: 760 DEBUGNAME("EV_USE_ITEM0"); 761 CG_UseItem( cent ); 762 break; 763 case EV_USE_ITEM1: 764 DEBUGNAME("EV_USE_ITEM1"); 765 CG_UseItem( cent ); 766 break; 767 case EV_USE_ITEM2: 768 DEBUGNAME("EV_USE_ITEM2"); 769 CG_UseItem( cent ); 770 break; 771 case EV_USE_ITEM3: 772 DEBUGNAME("EV_USE_ITEM3"); 773 CG_UseItem( cent ); 774 break; 775 case EV_USE_ITEM4: 776 DEBUGNAME("EV_USE_ITEM4"); 777 CG_UseItem( cent ); 778 break; 779 case EV_USE_ITEM5: 780 DEBUGNAME("EV_USE_ITEM5"); 781 CG_UseItem( cent ); 782 break; 783 case EV_USE_ITEM6: 784 DEBUGNAME("EV_USE_ITEM6"); 785 CG_UseItem( cent ); 786 break; 787 case EV_USE_ITEM7: 788 DEBUGNAME("EV_USE_ITEM7"); 789 CG_UseItem( cent ); 790 break; 791 case EV_USE_ITEM8: 792 DEBUGNAME("EV_USE_ITEM8"); 793 CG_UseItem( cent ); 794 break; 795 case EV_USE_ITEM9: 796 DEBUGNAME("EV_USE_ITEM9"); 797 CG_UseItem( cent ); 798 break; 799 case EV_USE_ITEM10: 800 DEBUGNAME("EV_USE_ITEM10"); 801 CG_UseItem( cent ); 802 break; 803 case EV_USE_ITEM11: 804 DEBUGNAME("EV_USE_ITEM11"); 805 CG_UseItem( cent ); 806 break; 807 case EV_USE_ITEM12: 808 DEBUGNAME("EV_USE_ITEM12"); 809 CG_UseItem( cent ); 810 break; 811 case EV_USE_ITEM13: 812 DEBUGNAME("EV_USE_ITEM13"); 813 CG_UseItem( cent ); 814 break; 815 case EV_USE_ITEM14: 816 DEBUGNAME("EV_USE_ITEM14"); 817 CG_UseItem( cent ); 818 break; 819 820 //================================================================= 821 822 // 823 // other events 824 // 825 case EV_PLAYER_TELEPORT_IN: 826 DEBUGNAME("EV_PLAYER_TELEPORT_IN"); 827 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.teleInSound ); 828 CG_SpawnEffect( position); 829 break; 830 831 case EV_PLAYER_TELEPORT_OUT: 832 DEBUGNAME("EV_PLAYER_TELEPORT_OUT"); 833 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.teleOutSound ); 834 CG_SpawnEffect( position); 835 break; 836 837 case EV_ITEM_POP: 838 DEBUGNAME("EV_ITEM_POP"); 839 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.respawnSound ); 840 break; 841 case EV_ITEM_RESPAWN: 842 DEBUGNAME("EV_ITEM_RESPAWN"); 843 cent->miscTime = cg.time; // scale up from this 844 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.respawnSound ); 845 break; 846 847 case EV_GRENADE_BOUNCE: 848 DEBUGNAME("EV_GRENADE_BOUNCE"); 849 if ( rand() & 1 ) { 850 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.hgrenb1aSound ); 851 } else { 852 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.hgrenb2aSound ); 853 } 854 break; 855 856 #ifdef MISSIONPACK 857 case EV_PROXIMITY_MINE_STICK: 858 DEBUGNAME("EV_PROXIMITY_MINE_STICK"); 859 if( es->eventParm & SURF_FLESH ) { 860 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.wstbimplSound ); 861 } else if( es->eventParm & SURF_METALSTEPS ) { 862 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.wstbimpmSound ); 863 } else { 864 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.wstbimpdSound ); 865 } 866 break; 867 868 case EV_PROXIMITY_MINE_TRIGGER: 869 DEBUGNAME("EV_PROXIMITY_MINE_TRIGGER"); 870 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.wstbactvSound ); 871 break; 872 case EV_KAMIKAZE: 873 DEBUGNAME("EV_KAMIKAZE"); 874 CG_KamikazeEffect( cent->lerpOrigin ); 875 break; 876 case EV_OBELISKEXPLODE: 877 DEBUGNAME("EV_OBELISKEXPLODE"); 878 CG_ObeliskExplode( cent->lerpOrigin, es->eventParm ); 879 break; 880 case EV_OBELISKPAIN: 881 DEBUGNAME("EV_OBELISKPAIN"); 882 CG_ObeliskPain( cent->lerpOrigin ); 883 break; 884 case EV_INVUL_IMPACT: 885 DEBUGNAME("EV_INVUL_IMPACT"); 886 CG_InvulnerabilityImpact( cent->lerpOrigin, cent->currentState.angles ); 887 break; 888 case EV_JUICED: 889 DEBUGNAME("EV_JUICED"); 890 CG_InvulnerabilityJuiced( cent->lerpOrigin ); 891 break; 892 case EV_LIGHTNINGBOLT: 893 DEBUGNAME("EV_LIGHTNINGBOLT"); 894 CG_LightningBoltBeam(es->origin2, es->pos.trBase); 895 break; 896 #endif 897 case EV_SCOREPLUM: 898 DEBUGNAME("EV_SCOREPLUM"); 899 CG_ScorePlum( cent->currentState.otherEntityNum, cent->lerpOrigin, cent->currentState.time ); 900 break; 901 902 // 903 // missile impacts 904 // 905 case EV_MISSILE_HIT: 906 DEBUGNAME("EV_MISSILE_HIT"); 907 ByteToDir( es->eventParm, dir ); 908 CG_MissileHitPlayer( es->weapon, position, dir, es->otherEntityNum ); 909 break; 910 911 case EV_MISSILE_MISS: 912 DEBUGNAME("EV_MISSILE_MISS"); 913 ByteToDir( es->eventParm, dir ); 914 CG_MissileHitWall( es->weapon, 0, position, dir, IMPACTSOUND_DEFAULT ); 915 break; 916 917 case EV_MISSILE_MISS_METAL: 918 DEBUGNAME("EV_MISSILE_MISS_METAL"); 919 ByteToDir( es->eventParm, dir ); 920 CG_MissileHitWall( es->weapon, 0, position, dir, IMPACTSOUND_METAL ); 921 break; 922 923 case EV_RAILTRAIL: 924 DEBUGNAME("EV_RAILTRAIL"); 925 cent->currentState.weapon = WP_RAILGUN; 926 // if the end was on a nomark surface, don't make an explosion 927 CG_RailTrail( ci, es->origin2, es->pos.trBase ); 928 if ( es->eventParm != 255 ) { 929 ByteToDir( es->eventParm, dir ); 930 CG_MissileHitWall( es->weapon, es->clientNum, position, dir, IMPACTSOUND_DEFAULT ); 931 } 932 break; 933 934 case EV_BULLET_HIT_WALL: 935 DEBUGNAME("EV_BULLET_HIT_WALL"); 936 ByteToDir( es->eventParm, dir ); 937 CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qfalse, ENTITYNUM_WORLD ); 938 break; 939 940 case EV_BULLET_HIT_FLESH: 941 DEBUGNAME("EV_BULLET_HIT_FLESH"); 942 CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qtrue, es->eventParm ); 943 break; 944 945 case EV_SHOTGUN: 946 DEBUGNAME("EV_SHOTGUN"); 947 CG_ShotgunFire( es ); 948 break; 949 950 case EV_GENERAL_SOUND: 951 DEBUGNAME("EV_GENERAL_SOUND"); 952 if ( cgs.gameSounds[ es->eventParm ] ) { 953 trap_S_StartSound (NULL, es->number, CHAN_VOICE, cgs.gameSounds[ es->eventParm ] ); 954 } else { 955 s = CG_ConfigString( CS_SOUNDS + es->eventParm ); 956 trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, s ) ); 957 } 958 break; 959 960 case EV_GLOBAL_SOUND: // play from the player's head so it never diminishes 961 DEBUGNAME("EV_GLOBAL_SOUND"); 962 if ( cgs.gameSounds[ es->eventParm ] ) { 963 trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.gameSounds[ es->eventParm ] ); 964 } else { 965 s = CG_ConfigString( CS_SOUNDS + es->eventParm ); 966 trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, CG_CustomSound( es->number, s ) ); 967 } 968 break; 969 970 case EV_GLOBAL_TEAM_SOUND: // play from the player's head so it never diminishes 971 { 972 DEBUGNAME("EV_GLOBAL_TEAM_SOUND"); 973 switch( es->eventParm ) { 974 case GTS_RED_CAPTURE: // CTF: red team captured the blue flag, 1FCTF: red team captured the neutral flag 975 if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED ) 976 CG_AddBufferedSound( cgs.media.captureYourTeamSound ); 977 else 978 CG_AddBufferedSound( cgs.media.captureOpponentSound ); 979 break; 980 case GTS_BLUE_CAPTURE: // CTF: blue team captured the red flag, 1FCTF: blue team captured the neutral flag 981 if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE ) 982 CG_AddBufferedSound( cgs.media.captureYourTeamSound ); 983 else 984 CG_AddBufferedSound( cgs.media.captureOpponentSound ); 985 break; 986 case GTS_RED_RETURN: // CTF: blue flag returned, 1FCTF: never used 987 if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED ) 988 CG_AddBufferedSound( cgs.media.returnYourTeamSound ); 989 else 990 CG_AddBufferedSound( cgs.media.returnOpponentSound ); 991 // 992 CG_AddBufferedSound( cgs.media.blueFlagReturnedSound ); 993 break; 994 case GTS_BLUE_RETURN: // CTF red flag returned, 1FCTF: neutral flag returned 995 if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE ) 996 CG_AddBufferedSound( cgs.media.returnYourTeamSound ); 997 else 998 CG_AddBufferedSound( cgs.media.returnOpponentSound ); 999 // 1000 CG_AddBufferedSound( cgs.media.redFlagReturnedSound ); 1001 break; 1002 1003 case GTS_RED_TAKEN: // CTF: red team took blue flag, 1FCTF: blue team took the neutral flag 1004 // if this player picked up the flag then a sound is played in CG_CheckLocalSounds 1005 if (cg.snap->ps.powerups[PW_BLUEFLAG] || cg.snap->ps.powerups[PW_NEUTRALFLAG]) { 1006 } 1007 else { 1008 if (cgs.clientinfo[cg.clientNum].team == TEAM_BLUE) { 1009 #ifdef MISSIONPACK 1010 if (cgs.gametype == GT_1FCTF) 1011 CG_AddBufferedSound( cgs.media.yourTeamTookTheFlagSound ); 1012 else 1013 #endif 1014 CG_AddBufferedSound( cgs.media.enemyTookYourFlagSound ); 1015 } 1016 else if (cgs.clientinfo[cg.clientNum].team == TEAM_RED) { 1017 #ifdef MISSIONPACK 1018 if (cgs.gametype == GT_1FCTF) 1019 CG_AddBufferedSound( cgs.media.enemyTookTheFlagSound ); 1020 else 1021 #endif 1022 CG_AddBufferedSound( cgs.media.yourTeamTookEnemyFlagSound ); 1023 } 1024 } 1025 break; 1026 case GTS_BLUE_TAKEN: // CTF: blue team took the red flag, 1FCTF red team took the neutral flag 1027 // if this player picked up the flag then a sound is played in CG_CheckLocalSounds 1028 if (cg.snap->ps.powerups[PW_REDFLAG] || cg.snap->ps.powerups[PW_NEUTRALFLAG]) { 1029 } 1030 else { 1031 if (cgs.clientinfo[cg.clientNum].team == TEAM_RED) { 1032 #ifdef MISSIONPACK 1033 if (cgs.gametype == GT_1FCTF) 1034 CG_AddBufferedSound( cgs.media.yourTeamTookTheFlagSound ); 1035 else 1036 #endif 1037 CG_AddBufferedSound( cgs.media.enemyTookYourFlagSound ); 1038 } 1039 else if (cgs.clientinfo[cg.clientNum].team == TEAM_BLUE) { 1040 #ifdef MISSIONPACK 1041 if (cgs.gametype == GT_1FCTF) 1042 CG_AddBufferedSound( cgs.media.enemyTookTheFlagSound ); 1043 else 1044 #endif 1045 CG_AddBufferedSound( cgs.media.yourTeamTookEnemyFlagSound ); 1046 } 1047 } 1048 break; 1049 case GTS_REDOBELISK_ATTACKED: // Overload: red obelisk is being attacked 1050 if (cgs.clientinfo[cg.clientNum].team == TEAM_RED) { 1051 CG_AddBufferedSound( cgs.media.yourBaseIsUnderAttackSound ); 1052 } 1053 break; 1054 case GTS_BLUEOBELISK_ATTACKED: // Overload: blue obelisk is being attacked 1055 if (cgs.clientinfo[cg.clientNum].team == TEAM_BLUE) { 1056 CG_AddBufferedSound( cgs.media.yourBaseIsUnderAttackSound ); 1057 } 1058 break; 1059 1060 case GTS_REDTEAM_SCORED: 1061 CG_AddBufferedSound(cgs.media.redScoredSound); 1062 break; 1063 case GTS_BLUETEAM_SCORED: 1064 CG_AddBufferedSound(cgs.media.blueScoredSound); 1065 break; 1066 case GTS_REDTEAM_TOOK_LEAD: 1067 CG_AddBufferedSound(cgs.media.redLeadsSound); 1068 break; 1069 case GTS_BLUETEAM_TOOK_LEAD: 1070 CG_AddBufferedSound(cgs.media.blueLeadsSound); 1071 break; 1072 case GTS_TEAMS_ARE_TIED: 1073 CG_AddBufferedSound( cgs.media.teamsTiedSound ); 1074 break; 1075 #ifdef MISSIONPACK 1076 case GTS_KAMIKAZE: 1077 trap_S_StartLocalSound(cgs.media.kamikazeFarSound, CHAN_ANNOUNCER); 1078 break; 1079 #endif 1080 default: 1081 break; 1082 } 1083 break; 1084 } 1085 1086 case EV_PAIN: 1087 // local player sounds are triggered in CG_CheckLocalSounds, 1088 // so ignore events on the player 1089 DEBUGNAME("EV_PAIN"); 1090 if ( cent->currentState.number != cg.snap->ps.clientNum ) { 1091 CG_PainEvent( cent, es->eventParm ); 1092 } 1093 break; 1094 1095 case EV_DEATH1: 1096 case EV_DEATH2: 1097 case EV_DEATH3: 1098 DEBUGNAME("EV_DEATHx"); 1099 trap_S_StartSound( NULL, es->number, CHAN_VOICE, 1100 CG_CustomSound( es->number, va("*death%i.wav", event - EV_DEATH1 + 1) ) ); 1101 break; 1102 1103 1104 case EV_OBITUARY: 1105 DEBUGNAME("EV_OBITUARY"); 1106 CG_Obituary( es ); 1107 break; 1108 1109 // 1110 // powerup events 1111 // 1112 case EV_POWERUP_QUAD: 1113 DEBUGNAME("EV_POWERUP_QUAD"); 1114 if ( es->number == cg.snap->ps.clientNum ) { 1115 cg.powerupActive = PW_QUAD; 1116 cg.powerupTime = cg.time; 1117 } 1118 trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.quadSound ); 1119 break; 1120 case EV_POWERUP_BATTLESUIT: 1121 DEBUGNAME("EV_POWERUP_BATTLESUIT"); 1122 if ( es->number == cg.snap->ps.clientNum ) { 1123 cg.powerupActive = PW_BATTLESUIT; 1124 cg.powerupTime = cg.time; 1125 } 1126 trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.protectSound ); 1127 break; 1128 case EV_POWERUP_REGEN: 1129 DEBUGNAME("EV_POWERUP_REGEN"); 1130 if ( es->number == cg.snap->ps.clientNum ) { 1131 cg.powerupActive = PW_REGEN; 1132 cg.powerupTime = cg.time; 1133 } 1134 trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.regenSound ); 1135 break; 1136 1137 case EV_GIB_PLAYER: 1138 DEBUGNAME("EV_GIB_PLAYER"); 1139 // don't play gib sound when using the kamikaze because it interferes 1140 // with the kamikaze sound, downside is that the gib sound will also 1141 // not be played when someone is gibbed while just carrying the kamikaze 1142 if ( !(es->eFlags & EF_KAMIKAZE) ) { 1143 trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.gibSound ); 1144 } 1145 CG_GibPlayer( cent->lerpOrigin ); 1146 break; 1147 1148 case EV_STOPLOOPINGSOUND: 1149 DEBUGNAME("EV_STOPLOOPINGSOUND"); 1150 trap_S_StopLoopingSound( es->number ); 1151 es->loopSound = 0; 1152 break; 1153 1154 case EV_DEBUG_LINE: 1155 DEBUGNAME("EV_DEBUG_LINE"); 1156 CG_Beam( cent ); 1157 break; 1158 1159 default: 1160 DEBUGNAME("UNKNOWN"); 1161 CG_Error( "Unknown event: %i", event ); 1162 break; 1163 } 1164 1165 } 1166 1167 1168 /* 1169 ============== 1170 CG_CheckEvents 1171 1172 ============== 1173 */ 1174 void CG_CheckEvents( centity_t *cent ) { 1175 // check for event-only entities 1176 if ( cent->currentState.eType > ET_EVENTS ) { 1177 if ( cent->previousEvent ) { 1178 return; // already fired 1179 } 1180 // if this is a player event set the entity number of the client entity number 1181 if ( cent->currentState.eFlags & EF_PLAYER_EVENT ) { 1182 cent->currentState.number = cent->currentState.otherEntityNum; 1183 } 1184 1185 cent->previousEvent = 1; 1186 1187 cent->currentState.event = cent->currentState.eType - ET_EVENTS; 1188 } else { 1189 // check for events riding with another entity 1190 if ( cent->currentState.event == cent->previousEvent ) { 1191 return; 1192 } 1193 cent->previousEvent = cent->currentState.event; 1194 if ( ( cent->currentState.event & ~EV_EVENT_BITS ) == 0 ) { 1195 return; 1196 } 1197 } 1198 1199 // calculate the position at exactly the frame time 1200 BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, cent->lerpOrigin ); 1201 CG_SetEntitySoundPosition( cent ); 1202 1203 CG_EntityEvent( cent, cent->lerpOrigin ); 1204 } 1205