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 = &cent->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 = &cent->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( &cent->currentState.pos, cg.snap->serverTime, cent->lerpOrigin );
1201 	CG_SetEntitySoundPosition( cent );
1202 
1203 	CG_EntityEvent( cent, cent->lerpOrigin );
1204 }
1205 
1206