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 Quake III Arena source code; 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
29 #include "../../ui/menudef.h"
30 #endif
31 //==========================================================================
32
33 /*
34 ===================
35 CG_PlaceString
36
37 Also called by scoreboard drawing
38 ===================
39 */
CG_PlaceString(int rank)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 */
CG_Obituary(entityState_t * ent)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 */
CG_UseItem(centity_t * cent)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 */
CG_ItemPickup(int itemNum)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 */
CG_PainEvent(centity_t * cent,int health)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");}
CG_EntityEvent(centity_t * cent,vec3_t position)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 */
CG_CheckEvents(centity_t * cent)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
1206