1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19 
20 //
21 // cg_entities.c
22 //
23 
24 #include "cg_local.h"
25 
26 cgEntity_t		cg_entityList[MAX_CS_EDICTS];
27 entityState_t	cg_parseEntities[MAX_PARSE_ENTITIES];
28 
29 static qBool	cg_inFrameSequence = qFalse;
30 
31 /*
32 ==========================================================================
33 
34 	ENTITY STATE
35 
36 ==========================================================================
37 */
38 
39 /*
40 ==================
41 CG_BeginFrameSequence
42 ==================
43 */
CG_BeginFrameSequence(frame_t frame)44 void CG_BeginFrameSequence (frame_t frame)
45 {
46 	if (cg_inFrameSequence) {
47 		Com_Error (ERR_DROP, "CG_BeginFrameSequence: already in sequence");
48 		return;
49 	}
50 
51 	cg.oldFrame = cg.frame;
52 	cg.frame = frame;
53 
54 	cg_inFrameSequence = qTrue;
55 }
56 
57 
58 /*
59 ==================
60 CG_NewPacketEntityState
61 ==================
62 */
CG_NewPacketEntityState(int entNum,entityState_t state)63 void CG_NewPacketEntityState (int entNum, entityState_t state)
64 {
65 	cgEntity_t		*ent;
66 
67 	if (!cg_inFrameSequence)
68 		Com_Error (ERR_DROP, "CG_NewPacketEntityState: no sequence");
69 
70 	ent = &cg_entityList[entNum];
71 	cg_parseEntities[(cg.frame.parseEntities+cg.frame.numEntities) & (MAX_PARSEENTITIES_MASK)] = state;
72 	cg.frame.numEntities++;
73 
74 	// Some data changes will force no lerping
75 	if (state.modelIndex != ent->current.modelIndex
76 	|| state.modelIndex2 != ent->current.modelIndex2
77 	|| state.modelIndex3 != ent->current.modelIndex3
78 	|| state.modelIndex4 != ent->current.modelIndex4
79 	|| abs ((int)state.origin[0] - (int)ent->current.origin[0]) > 512
80 	|| abs ((int)state.origin[1] - (int)ent->current.origin[1]) > 512
81 	|| abs ((int)state.origin[2] - (int)ent->current.origin[2]) > 512
82 	|| state.event == EV_PLAYER_TELEPORT
83 	|| state.event == EV_OTHER_TELEPORT)
84 		ent->serverFrame = -99;
85 
86 	if (ent->serverFrame != cg.frame.serverFrame - 1) {
87 		// Wasn't in last update, so initialize some things duplicate
88 		// the current state so lerping doesn't hurt anything
89 		ent->prev = state;
90 		if (state.event == EV_OTHER_TELEPORT) {
91 			Vec3Copy (state.origin, ent->prev.origin);
92 			Vec3Copy (state.origin, ent->lerpOrigin);
93 		}
94 		else {
95 			Vec3Copy (state.oldOrigin, ent->prev.origin);
96 			Vec3Copy (state.oldOrigin, ent->lerpOrigin);
97 		}
98 	}
99 	else {
100 		// Shuffle the last state to previous
101 		ent->prev = ent->current;
102 	}
103 
104 	ent->serverFrame = cg.frame.serverFrame;
105 	ent->current = state;
106 }
107 
108 
109 /*
110 ==============
111 CG_EntityEvent
112 
113 An entity has just been parsed that has an event value
114 the female events are there for backwards compatability
115 ==============
116 */
117 // this here is ugly and hacky, will be scripted soon
118 enum {
119 	SURF_NORMAL			= 1 << 10,	// 0x400
120 
121 	SURF_CONCRETE		= 1 << 11,	// 0x800
122 	SURF_DIRT			= 1 << 12,	// 0x1000
123 	SURF_DUCT			= 1 << 13,	// 0x2000
124 	SURF_GRASS			= 1 << 14,	// 0x4000
125 	SURF_GRAVEL			= 1 << 15,	// 0x8000
126 	SURF_METAL			= 1 << 16,	// 0x10000
127 	SURF_METALGRATE		= 1 << 17,	// 0x20000
128 	SURF_METALLADDER	= 1 << 18,	// 0x40000
129 	SURF_MUD			= 1 << 19,	// 0x80000
130 	SURF_SAND			= 1 << 20,	// 0x100000
131 	SURF_SLOSH			= 1 << 21,	// 0x200000
132 	SURF_SNOW			= 1 << 22,	// 0x400000
133 	SURF_TILE			= 1 << 23,	// 0x800000
134 	SURF_WADE			= 1 << 24,	// 0x1000000
135 	SURF_WOOD			= 1 << 25,	// 0x2000000
136 	SURF_WOODPANEL		= 1 << 26	// 0x4000000
137 	// 0x8000000
138 	// 0x10000000
139 	// 0x20000000
140 	// 0x40000000
141 	// 0x80000000
142 };
143 #define SURF_MAXFLAGS (SURF_NORMAL-1)
144 
145 enum {
146 	STEP_NORMAL,
147 
148 	STEP_CONCRETE,
149 	STEP_DIRT,
150 	STEP_DUCT,
151 	STEP_GRASS,
152 	STEP_GRAVEL,
153 	STEP_METAL,
154 	STEP_METALGRATE,
155 	STEP_METALLADDER,
156 	STEP_MUD,
157 	STEP_SAND,
158 	STEP_SLOSH,
159 	STEP_SNOW,
160 	STEP_TILE,
161 	STEP_WADE,
162 	STEP_WOOD,
163 	STEP_WOODPANEL
164 };
165 
CG_StepTypeForTexture(cBspSurface_t * surf)166 int CG_StepTypeForTexture (cBspSurface_t *surf)
167 {
168 //	int		surfflags;
169 	char	newName[16];
170 	int		type;
171 
172 	// some maps have UPPERCASE TEXTURE NAMES
173 	strcpy (newName, surf->name);
174 	Q_strlwr (newName);
175 
176 	// this will be done after map load
177 //	surfflags = surf->flags;
178 //	if (surfflags > SURF_MAXFLAGS)
179 //		surfflags = SURF_MAXFLAGS;
180 
181 	if (strstr (newName, "/dirt")) {
182 		type = STEP_DIRT;
183 	}
184 	else if (strstr (newName, "/mud")) {
185 		type = STEP_MUD;
186 	}
187 	else if (strstr (newName, "/cindr5_2")) {
188 		type = STEP_CONCRETE;
189 	}
190 	else if (strstr (newName, "/grass")) {
191 		type = STEP_GRASS;
192 	}
193 	else if (strstr (newName, "/c_met")
194 	|| strstr (newName, "/florr")
195 	|| strstr (newName, "/stairs")
196 	|| strstr (newName, "/rmetal")
197 	|| strstr (newName, "/blum")
198 	|| strstr (newName, "/metal")
199 	|| strstr (newName, "/floor3_1")
200 	|| strstr (newName, "/floor3_2")
201 	|| strstr (newName, "/floor3_3")
202 	|| strstr (newName, "/bflor3_1")
203 	|| strstr (newName, "/bflor3_2")
204 	|| strstr (newName, "/grate")
205 	|| strstr (newName, "/grnx")
206 	|| strstr (newName, "/grill")) {
207 		type = STEP_METAL;
208 	}
209 	else if (strstr (newName, "/rock")
210 	|| strstr (newName, "/rrock")) {
211 		type = STEP_GRAVEL;
212 	}
213 	else if (strstr (newName, "/airduc")) {
214 		type = STEP_DUCT;
215 	}
216 	else {
217 	//	Com_Printf (0, "normal: ");
218 		type = STEP_NORMAL;
219 	}
220 	//Com_Printf (0, "%s\n", newName);
221 
222 //	surfflags |= type;
223 
224 	// strip the type out of the flags
225 //	type = surfflags &~ SURF_MAXFLAGS;
226 
227 	return type;
228 }
229 
CG_FootStep(entityState_t * ent)230 static void CG_FootStep (entityState_t *ent)
231 {
232 	trace_t			tr;
233 	vec3_t			end;
234 	int				stepType;
235 	struct sfx_s	*sound;
236 
237 	Vec3Set (end, ent->origin[0], ent->origin[1], ent->origin[2]-64);
238 	CG_PMTrace (&tr, ent->origin, NULL, NULL, end, qFalse);
239 
240 	if (!tr.surface || !tr.surface->name || !tr.surface->name[0]) {
241 		sound = cgMedia.sfx.steps.standard[rand () & 3];
242 	}
243 	else {
244 		stepType = CG_StepTypeForTexture (tr.surface);
245 
246 		switch (stepType) {
247 		case STEP_CONCRETE:		sound = cgMedia.sfx.steps.concrete[rand () & 3];	break;
248 		case STEP_DIRT:			sound = cgMedia.sfx.steps.dirt[rand () & 3];		break;
249 		case STEP_DUCT:			sound = cgMedia.sfx.steps.duct[rand () & 3];		break;
250 		case STEP_GRASS:		sound = cgMedia.sfx.steps.grass[rand () & 3];		break;
251 		case STEP_GRAVEL:		sound = cgMedia.sfx.steps.gravel[rand () & 3];		break;
252 		case STEP_METAL:		sound = cgMedia.sfx.steps.metal[rand () & 3];		break;
253 		case STEP_METALGRATE:	sound = cgMedia.sfx.steps.metalGrate[rand () & 3];	break;
254 		case STEP_METALLADDER:	sound = cgMedia.sfx.steps.metalLadder[rand () & 3];	break;
255 		case STEP_MUD:			sound = cgMedia.sfx.steps.mud[rand () & 3];			break;
256 		case STEP_SAND:			sound = cgMedia.sfx.steps.sand[rand () & 3];		break;
257 		case STEP_SLOSH:		sound = cgMedia.sfx.steps.slosh[rand () & 3];		break;
258 		case STEP_SNOW:			sound = cgMedia.sfx.steps.snow[rand () % 6];		break;
259 		case STEP_TILE:			sound = cgMedia.sfx.steps.tile[rand () & 3];		break;
260 		case STEP_WADE:			sound = cgMedia.sfx.steps.wade[rand () & 3];		break;
261 		case STEP_WOOD:			sound = cgMedia.sfx.steps.wood[rand () & 3];		break;
262 		case STEP_WOODPANEL:	sound = cgMedia.sfx.steps.woodPanel[rand () & 3];	break;
263 
264 		default:
265 		case STEP_NORMAL:
266 			sound = cgMedia.sfx.steps.standard[rand () & 3];
267 			break;
268 		}
269 	}
270 
271 	cgi.Snd_StartSound (NULL, ent->number, CHAN_BODY, sound, 1.0f, ATTN_NORM, 0);
272 }
CG_EntityEvent(entityState_t * ent)273 static void CG_EntityEvent (entityState_t *ent)
274 {
275 	switch (ent->event) {
276 	case EV_ITEM_RESPAWN:
277 		cgi.Snd_StartSound (NULL, ent->number, CHAN_WEAPON, cgMedia.sfx.itemRespawn, 1, ATTN_IDLE, 0);
278 		CG_ItemRespawnEffect (ent->origin);
279 		break;
280 
281 	case EV_FOOTSTEP:
282 		if (cl_footsteps->intVal)
283 			CG_FootStep (ent);
284 		break;
285 
286 	case EV_FALL:
287 		cgi.Snd_StartSound (NULL, ent->number, CHAN_AUTO, cgMedia.sfx.playerFall, 1, ATTN_NORM, 0);
288 		break;
289 
290 	case EV_FALLSHORT:
291 		cgi.Snd_StartSound (NULL, ent->number, CHAN_AUTO, cgMedia.sfx.playerFallShort, 1, ATTN_NORM, 0);
292 		break;
293 
294 	case EV_FALLFAR:
295 		cgi.Snd_StartSound (NULL, ent->number, CHAN_AUTO, cgMedia.sfx.playerFallFar, 1, ATTN_NORM, 0);
296 		break;
297 
298 	case EV_PLAYER_TELEPORT:
299 		cgi.Snd_StartSound (NULL, ent->number, CHAN_WEAPON, cgMedia.sfx.playerTeleport, 1, ATTN_IDLE, 0);
300 		CG_TeleportParticles (ent->origin);
301 		break;
302 
303 	case EV_NONE:
304 	default:
305 		break;
306 	}
307 }
308 
309 
310 /*
311 ==================
312 CG_FireEntityEvents
313 ==================
314 */
CG_FireEntityEvents(void)315 static void CG_FireEntityEvents (void)
316 {
317 	entityState_t	*state;
318 	int				pNum;
319 
320 	for (pNum=0 ; pNum<cg.frame.numEntities ; pNum++) {
321 		state = &cg_parseEntities[(cg.frame.parseEntities+pNum)&(MAX_PARSEENTITIES_MASK)];
322 		if (state->event)
323 			CG_EntityEvent (state);
324 	}
325 }
326 
327 
328 /*
329 ==================
330 CG_EndFrameSequence
331 ==================
332 */
CG_EndFrameSequence(int numEntities)333 void CG_EndFrameSequence (int numEntities)
334 {
335 	if (!cg_inFrameSequence) {
336 		Com_Error (ERR_DROP, "CG_EndFrameSequence: no sequence started");
337 		return;
338 	}
339 
340 	cg_inFrameSequence = qFalse;
341 
342 	// Clamp time
343 	cg.netTime = clamp (cg.netTime, cg.frame.serverTime - 100, cg.frame.serverTime);
344 	cg.refreshTime = clamp (cg.refreshTime, cg.frame.serverTime - 100, cg.frame.serverTime);
345 
346 	if (!cg.frame.valid)
347 		return;
348 
349 	// Verify our data is valid
350 	if (cg.frame.numEntities != numEntities) {
351 		Com_Error (ERR_DROP, "CG_EndFrameSequence: bad sequence");
352 		return;
353 	}
354 
355 	// Build a list of collision solids
356 	CG_BuildSolidList ();
357 
358 	// Fire entity events
359 	CG_FireEntityEvents ();
360 
361 	// Check for a prediction error
362 	if (!cl_predict->intVal || !(cg.frame.playerState.pMove.pmFlags & PMF_NO_PREDICTION))
363 		CG_CheckPredictionError ();
364 }
365 
366 /*
367 ==========================================================================
368 
369 	INTERPOLATE BETWEEN FRAMES TO GET RENDERING PARMS
370 
371 ==========================================================================
372 */
373 
374 /*
375 ===============
376 CG_AddPacketEntities
377 ===============
378 */
379 static float	cg_gloomFLightMins[3] = { -4, -4, -4 };
380 static float	cg_gloomFLightMaxs[3] = { 4, 4, 4 };
CG_AddEntityShells(refEntity_t * ent)381 static void CG_AddEntityShells (refEntity_t *ent)
382 {
383 	// Double
384 	if (ent->flags & RF_SHELL_DOUBLE) {
385 		ent->skin = cgMedia.modelShellDouble;
386 		cgi.R_AddEntity (ent);
387 	}
388 	// Half-dam
389 	if (ent->flags & RF_SHELL_HALF_DAM) {
390 		ent->skin = cgMedia.modelShellHalfDam;
391 		cgi.R_AddEntity (ent);
392 	}
393 
394 	// God mode
395 	if (ent->flags & RF_SHELL_RED && ent->flags & RF_SHELL_GREEN && ent->flags & RF_SHELL_BLUE) {
396 		ent->skin = cgMedia.modelShellGod;
397 		cgi.R_AddEntity (ent);
398 	}
399 	else {
400 		// Red
401 		if (ent->flags & RF_SHELL_RED) {
402 			ent->skin = cgMedia.modelShellRed;
403 			cgi.R_AddEntity (ent);
404 		}
405 		// Green
406 		if (ent->flags & RF_SHELL_GREEN) {
407 			ent->skin = cgMedia.modelShellGreen;
408 			cgi.R_AddEntity (ent);
409 		}
410 		// Blue
411 		if (ent->flags & RF_SHELL_BLUE) {
412 			ent->skin = cgMedia.modelShellBlue;
413 			cgi.R_AddEntity (ent);
414 		}
415 	}
416 }
CG_AddPacketEntities(void)417 void CG_AddPacketEntities (void)
418 {
419 	refEntity_t		ent;
420 	entityState_t	*state;
421 	clientInfo_t	*clInfo;
422 	cgEntity_t		*cent;
423 	vec3_t			autoRotate, angles;
424 	mat3x3_t		autoRotateAxis;
425 	int				i, pNum, autoAnim;
426 	uint32			effects;
427 	qBool			isSelf, isPred, isDrawn;
428 	uint32			delta;
429 
430 	// bonus items rotate at a fixed rate
431 	Vec3Set (autoRotate, 0, AngleModf (cg.realTime * 0.1f), 0);
432 	Angles_Matrix3 (autoRotate, autoRotateAxis);
433 
434 	autoAnim = cg.realTime / 1000;	// brush models can auto animate their frames
435 
436 	memset (&ent, 0, sizeof (ent));
437 
438 	cg.thirdPerson = qFalse;
439 	for (pNum=0 ; pNum<cg.frame.numEntities ; pNum++) {
440 		state = &cg_parseEntities[(cg.frame.parseEntities+pNum)&(MAX_PARSEENTITIES_MASK)];
441 		cent = &cg_entityList[state->number];
442 
443 		effects = state->effects;
444 		ent.flags = state->renderFx;
445 
446 		isSelf = isPred = qFalse;
447 		isDrawn = qTrue;
448 
449 		// Set frame
450 		if (effects & EF_ANIM01)
451 			ent.frame = autoAnim & 1;
452 		else if (effects & EF_ANIM23)
453 			ent.frame = 2 + (autoAnim & 1);
454 		else if (effects & EF_ANIM_ALL)
455 			ent.frame = autoAnim;
456 		else if (effects & EF_ANIM_ALLFAST)
457 			ent.frame = cg.realTime / 100;
458 		else
459 			ent.frame = state->frame;
460 		ent.oldFrame = cent->prev.frame;
461 
462 		// Check effects
463 		if (effects & EF_PENT) {
464 			effects &= ~EF_PENT;
465 			effects |= EF_COLOR_SHELL;
466 			ent.flags |= RF_SHELL_RED;
467 		}
468 
469 		if (effects & EF_POWERSCREEN)
470 			ent.flags |= RF_SHELL_GREEN;
471 
472 		if (effects & EF_QUAD) {
473 			effects &= ~EF_QUAD;
474 			effects |= EF_COLOR_SHELL;
475 			ent.flags |= RF_SHELL_BLUE;
476 		}
477 
478 		if (effects & EF_DOUBLE) {
479 			effects &= ~EF_DOUBLE;
480 			effects |= EF_COLOR_SHELL;
481 			ent.flags |= RF_SHELL_DOUBLE;
482 		}
483 
484 		if (effects & EF_HALF_DAMAGE) {
485 			effects &= ~EF_HALF_DAMAGE;
486 			effects |= EF_COLOR_SHELL;
487 			ent.flags |= RF_SHELL_HALF_DAM;
488 		}
489 
490 		ent.backLerp = 1.0f - cg.lerpFrac;
491 		ent.scale = 1;
492 		Vec4Set (ent.color, 255, 255, 255, 255);
493 
494 		// Is it me?
495 		if (state->number == cg.playerNum+1) {
496 			isSelf = qTrue;
497 
498 			if (cl_predict->intVal
499 			&& !(cg.frame.playerState.pMove.pmFlags & PMF_NO_PREDICTION)
500 			&& cg.frame.playerState.pMove.pmType == PMT_NORMAL) {
501 				// Use prediction origins, add predicted.error since it seems to solve stutteryness on platforms
502 				ent.origin[0] = cg.predicted.origin[0] - ((1.0f-cg.lerpFrac) * cg.predicted.error[0]);
503 				ent.origin[1] = cg.predicted.origin[1] - ((1.0f-cg.lerpFrac) * cg.predicted.error[1]);
504 				ent.origin[2] = cg.predicted.origin[2] - ((1.0f-cg.lerpFrac) * cg.predicted.error[2]);
505 
506 				// Smooth out stair climbing
507 				delta = cg.realTime - cg.predicted.stepTime;
508 				if (delta < 150)
509 					ent.origin[2] -= cg.predicted.step * (150 - delta) / 150;
510 
511 				Vec3Copy (ent.origin, ent.oldOrigin);
512 				isPred = qTrue;
513 			}
514 		}
515 
516 		if (!isPred) {
517 			if (ent.flags & (RF_FRAMELERP|RF_BEAM)) {
518 				// Step origin discretely, because the frames do the animation properly
519 				Vec3Copy (cent->current.origin, ent.origin);
520 				Vec3Copy (cent->current.oldOrigin, ent.oldOrigin);
521 			}
522 			else {
523 				// Interpolate origin
524 				ent.origin[0] = ent.oldOrigin[0] = cent->prev.origin[0] + cg.lerpFrac * (cent->current.origin[0] - cent->prev.origin[0]);
525 				ent.origin[1] = ent.oldOrigin[1] = cent->prev.origin[1] + cg.lerpFrac * (cent->current.origin[1] - cent->prev.origin[1]);
526 				ent.origin[2] = ent.oldOrigin[2] = cent->prev.origin[2] + cg.lerpFrac * (cent->current.origin[2] - cent->prev.origin[2]);
527 			}
528 		}
529 
530 		// Tweak the color of beams
531 		if (ent.flags & RF_BEAM) {
532 			// The four beam colors are encoded in 32 bits of skinNum (hack)
533 			int		clr;
534 			vec3_t	length;
535 
536 			clr = ((state->skinNum >> ((rand () % 4)*8)) & 0xff);
537 
538 			if (rand () % 2)
539 				CG_BeamTrail (ent.origin, ent.oldOrigin,
540 					clr, (float)ent.frame,
541 					0.33f + (frand () * 0.2f), -1.0f / (5 + (frand () * 0.3f)));
542 
543 			Vec3Subtract (ent.oldOrigin, ent.origin, length);
544 
545 			CG_SpawnParticle (
546 				ent.origin[0],					ent.origin[1],					ent.origin[2],
547 				length[0],						length[1],						length[2],
548 				0,								0,								0,
549 				0,								0,								0,
550 				palRed (clr),					palGreen (clr),					palBlue (clr),
551 				palRed (clr),					palGreen (clr),					palBlue (clr),
552 				0.30f,							PART_INSTANT,
553 				ent.frame + ((ent.frame * 0.1f) * (rand () & 1)),
554 				ent.frame + ((ent.frame * 0.1f) * (rand () & 1)),
555 				PT_BEAM,						0,
556 				0,								qFalse,
557 				PART_STYLE_BEAM,
558 				0);
559 
560 			goto done;
561 		}
562 		else {
563 			// Set skin
564 			if (state->modelIndex == 255) {
565 				// Use custom player skin
566 				ent.skinNum = 0;
567 				clInfo = &cg.clientInfo[state->skinNum & 0xff];
568 				ent.skin = clInfo->skin;
569 				ent.model = clInfo->model;
570 				if (!ent.skin || !ent.model) {
571 					ent.skin = cg.baseClientInfo.skin;
572 					ent.model = cg.baseClientInfo.model;
573 				}
574 
575 				//PGM
576 				if (ent.flags & RF_USE_DISGUISE) {
577 					if (!Q_strnicmp ((char *)ent.skin, "players/male", 12)) {
578 						ent.skin = cgMedia.maleDisguiseSkin;
579 						ent.model = cgMedia.maleDisguiseModel;
580 					}
581 					else if (!Q_strnicmp ((char *)ent.skin, "players/female", 14)) {
582 						ent.skin = cgMedia.femaleDisguiseSkin;
583 						ent.model = cgMedia.femaleDisguiseModel;
584 					}
585 					else if (!Q_strnicmp ((char *)ent.skin, "players/cyborg", 14)) {
586 						ent.skin = cgMedia.cyborgDisguiseSkin;
587 						ent.model = cgMedia.cyborgDisguiseModel;
588 					}
589 				}
590 				//PGM
591 			}
592 			else {
593 				ent.skinNum = state->skinNum;
594 				ent.skin = NULL;
595 				ent.model = cg.modelCfgDraw[state->modelIndex];
596 			}
597 		}
598 
599 		if (ent.model) {
600 			// Gloom-specific effects
601 			if (cg.currGameMod == GAME_MOD_GLOOM) {
602 				// Stinger fire/C4 debris
603 				if (!Q_stricmp ((char *)ent.model, "sprites/s_firea.sp2")
604 				|| !Q_stricmp ((char *)ent.model, "sprites/s_fireb.sp2")
605 				|| !Q_stricmp ((char *)ent.model, "sprites/s_flame.sp2")) {
606 					if (effects & EF_ROCKET) {
607 						// C4 debris
608 						CG_GloomEmberTrail (cent->lerpOrigin, ent.origin);
609 					}
610 					else if (glm_advstingfire->intVal) {
611 						// Stinger fire
612 						CG_GloomStingerFire (cent->lerpOrigin, ent.origin, 25 + (frand () * 15), qTrue);
613 					}
614 
615 					// Skip the original lighting/trail effects
616 					if (effects & EF_ROCKET || glm_advstingfire->intVal)
617 						goto done;
618 				}
619 
620 				// Bio flare
621 				else if ((effects & EF_ROCKET || !(effects & EF_BLASTER)) && !Q_stricmp ((char *)ent.model, "models/objects/laser/tris.md2")) {
622 					CG_GloomFlareTrail (cent->lerpOrigin, ent.origin);
623 
624 					if (effects & EF_ROCKET) {
625 						effects &= ~EF_ROCKET;
626 						cgi.R_AddLight (ent.origin, 200, 0, 1, 0);
627 					}
628 				}
629 
630 				// Blob model
631 				else if (!Q_stricmp ((char *)ent.model, "models/objects/tlaser/tris.md2")) {
632 					CG_GloomBlobTip (cent->lerpOrigin, ent.origin);
633 					isDrawn = qFalse;
634 				}
635 
636 				// ST/Stinger gas
637 				else if (!Q_stricmp ((char *)ent.model, "models/objects/smokexp/tris.md2")) {
638 					if (glm_advgas->intVal) {
639 						CG_GloomGasEffect (ent.origin);
640 						goto done;
641 					}
642 				}
643 
644 				// C4 explosion sprite
645 				else if (!Q_stricmp ((char *)ent.model, "models/objects/r_explode/tris.md2")) {
646 					cgi.R_AddLight (ent.origin, 200.0f + (150*(ent.frame - 29)/36), 1.0f, 0.8f, 0.6f);
647 					goto done;
648 				}
649 				else if (!Q_stricmp ((char *)ent.model, "models/objects/r_explode/tris2.md2") ||
650 						!Q_stricmp ((char *)ent.model, "models/objects/explode/tris.md2")) {
651 					// Just don't draw this crappy looking crap
652 					goto done;
653 				}
654 			}
655 
656 			// Xatrix-specific effects
657 			else if (cg.currGameMod == GAME_MOD_XATRIX) {
658 				// Ugly phalanx tip
659 				if (!Q_stricmp ((char *)ent.model, "sprites/s_photon.sp2")) {
660 					CG_PhalanxTip (cent->lerpOrigin, ent.origin);
661 					isDrawn = qFalse;
662 				}
663 			}
664 
665 			// Ugly model-based blaster tip
666 			if (!Q_stricmp ((char *)ent.model, "models/objects/laser/tris.md2")) {
667 				CG_BlasterTip (cent->lerpOrigin, ent.origin);
668 				isDrawn = qFalse;
669 			}
670 
671 			// Don't draw the BFG sprite
672 			if (effects & EF_BFG && Q_WildcardMatch ("sprites/s_bfg*.sp2", (char *)ent.model, 1))
673 				isDrawn = qFalse;
674 		}
675 
676 		// Generically translucent
677 		if (ent.flags & RF_TRANSLUCENT)
678 			ent.color[3] = 255 * 0.70f;
679 
680 		// Calculate angles
681 		if (effects & EF_ROTATE) {
682 			// Some bonus items auto-rotate
683 			Matrix3_Copy (autoRotateAxis, ent.axis);
684 		}
685 		else if (effects & EF_SPINNINGLIGHTS) {
686 			vec3_t	forward;
687 			vec3_t	start;
688 
689 			angles[0] = 0;
690 			angles[1] = AngleModf (cg.realTime/2.0f) + state->angles[1];
691 			angles[2] = 180;
692 
693 			Angles_Matrix3 (angles, ent.axis);
694 
695 			Angles_Vectors (angles, forward, NULL, NULL);
696 			Vec3MA (ent.origin, 64, forward, start);
697 			cgi.R_AddLight (start, 100, 1, 0, 0);
698 		}
699 		else {
700 			if (isPred) {
701 				if (cg.predicted.angles[PITCH] > 180)
702 					angles[PITCH] = (-360 + cg.predicted.angles[PITCH]) * 0.333f;
703 				else
704 					angles[PITCH] = cg.predicted.angles[PITCH] * 0.333f;
705 
706 				angles[YAW] = cg.predicted.angles[YAW];
707 				angles[ROLL] = cg.predicted.angles[ROLL];
708 			}
709 			else {
710 				angles[0] = LerpAngle (cent->prev.angles[0], cent->current.angles[0], cg.lerpFrac);
711 				angles[1] = LerpAngle (cent->prev.angles[1], cent->current.angles[1], cg.lerpFrac);
712 				angles[2] = LerpAngle (cent->prev.angles[2], cent->current.angles[2], cg.lerpFrac);
713 			}
714 
715 			if (angles[0] || angles[1] || angles[2])
716 				Angles_Matrix3 (angles, ent.axis);
717 			else
718 				Matrix3_Identity (ent.axis);
719 		}
720 
721 		// Flip your shadow around for lefty
722 		if (isSelf && hand->intVal == 1) {
723 			ent.flags |= RF_CULLHACK;
724 			Vec3Negate (ent.axis[1], ent.axis[1]);
725 		}
726 
727 		// If set to invisible, skip
728 		if (!state->modelIndex)
729 			goto done;
730 
731 		if (effects & EF_BFG) {
732 			ent.flags |= RF_TRANSLUCENT;
733 			ent.color[3] = 255 * 0.30f;
734 		}
735 
736 		// RAFAEL
737 		if (effects & EF_PLASMA) {
738 			ent.flags |= RF_TRANSLUCENT;
739 			ent.color[3] = 255 * 0.6f;
740 		}
741 
742 		if (effects & EF_SPHERETRANS) {
743 			ent.flags |= RF_TRANSLUCENT;
744 
745 			// PMM - *sigh*  yet more EF overloading
746 			if (effects & EF_TRACKERTRAIL)
747 				ent.color[3] = 255 * 0.6f;
748 			else
749 				ent.color[3] = 255 * 0.3f;
750 		}
751 
752 		// Some items dont deserve a shadow
753 		if (effects & (EF_GIB|EF_GREENGIB|EF_GRENADE|EF_ROCKET|EF_BLASTER|EF_HYPERBLASTER|EF_BLUEHYPERBLASTER))
754 			ent.flags |= RF_NOSHADOW;
755 
756 		// Hackish mod handling for shells
757 		if (effects & EF_COLOR_SHELL) {
758 			if (ent.flags & RF_SHELL_HALF_DAM) {
759 				if (cg.currGameMod == GAME_MOD_ROGUE) {
760 					if (ent.flags & (RF_SHELL_RED|RF_SHELL_BLUE|RF_SHELL_DOUBLE))
761 						ent.flags &= ~RF_SHELL_HALF_DAM;
762 				}
763 			}
764 
765 			if (ent.flags & RF_SHELL_DOUBLE) {
766 				if (cg.currGameMod == GAME_MOD_ROGUE) {
767 					if (ent.flags & (RF_SHELL_RED|RF_SHELL_BLUE|RF_SHELL_GREEN))
768 						ent.flags &= ~RF_SHELL_DOUBLE;
769 					if (ent.flags & RF_SHELL_RED)
770 						ent.flags |= RF_SHELL_BLUE;
771 					else if (ent.flags & RF_SHELL_BLUE)
772 						if (ent.flags & RF_SHELL_GREEN)
773 							ent.flags &= ~RF_SHELL_BLUE;
774 						else
775 							ent.flags |= RF_SHELL_GREEN;
776 				}
777 			}
778 		}
779 
780 		// Check for third person
781 		if (isSelf) {
782 			if (cg_thirdPerson->intVal && cl_predict->intVal && !(cg.frame.playerState.pMove.pmFlags & PMF_NO_PREDICTION) && !cg.attractLoop) {
783 				cg.thirdPerson = (state->modelIndex != 0 && cg.frame.playerState.pMove.pmType != PMT_SPECTATOR);
784 			}
785 			else {
786 				cg.thirdPerson = qFalse;
787 			}
788 
789 			if (cg.thirdPerson && cg.cameraTrans > 0) {
790 				ent.color[3] = cg.cameraTrans;
791 				if (cg.cameraTrans < 255)
792 					ent.flags |= RF_TRANSLUCENT;
793 			}
794 			else {
795 				ent.flags |= RF_VIEWERMODEL;
796 			}
797 		}
798 
799 		// Force entity colors for these flags
800 		if (cg.refDef.rdFlags & RDF_IRGOGGLES && ent.flags & RF_IR_VISIBLE) {
801 			ent.flags |= RF_FULLBRIGHT;
802 			Vec3Set (ent.color, 255, 0, 0);
803 		}
804 		else if (cg.refDef.rdFlags & RDF_UVGOGGLES) {
805 			ent.flags |= RF_FULLBRIGHT;
806 			Vec3Set (ent.color, 0, 255, 0);
807 		}
808 
809 		// Add lights to shells
810 		if (effects & EF_COLOR_SHELL) {
811 			if (ent.flags & RF_SHELL_RED) {
812 				ent.flags |= RF_MINLIGHT;
813 				cgi.R_AddLight (ent.origin, 50, 1, 0, 0);
814 			}
815 			if (ent.flags & RF_SHELL_GREEN) {
816 				ent.flags |= RF_MINLIGHT;
817 				cgi.R_AddLight (ent.origin, 50, 0, 1, 0);
818 			}
819 			if (ent.flags & RF_SHELL_BLUE) {
820 				ent.flags |= RF_MINLIGHT;
821 				cgi.R_AddLight (ent.origin, 50, 0, 0, 1);
822 			}
823 			if (ent.flags & RF_SHELL_DOUBLE) {
824 				ent.flags |= RF_MINLIGHT;
825 				cgi.R_AddLight (ent.origin, 50, 0.9f, 0.7f, 0);
826 			}
827 			if (ent.flags & RF_SHELL_HALF_DAM) {
828 				ent.flags |= RF_MINLIGHT;
829 				cgi.R_AddLight (ent.origin, 50, 0.56f, 0.59f, 0.45f);
830 			}
831 		}
832 
833 		// Add to refresh list
834 		if (isDrawn) {
835 			cgi.R_AddEntity (&ent);
836 
837 			// Add entity shell(s)
838 			if (effects & EF_COLOR_SHELL) {
839 				ent.skinNum = 0;
840 				CG_AddEntityShells (&ent);
841 			}
842 		}
843 
844 		// Linked models
845 		if (state->modelIndex2) {
846 			ent.skin = NULL;	// Never use a custom skin on others
847 			ent.skinNum = 0;
848 
849 			if (state->modelIndex2 == 255) {
850 				// Custom weapon
851 				clInfo = &cg.clientInfo[state->skinNum & 0xff];
852 				i = (state->skinNum >> 8);	// 0 is default weapon model
853 				if (!cl_vwep->intVal || i > MAX_CLIENTWEAPONMODELS-1)
854 					i = 0;
855 
856 				ent.model = clInfo->weaponModels[i];
857 				if (!ent.model) {
858 					if (i != 0)
859 						ent.model = clInfo->weaponModels[0];
860 					if (!ent.model)
861 						ent.model = cg.baseClientInfo.weaponModels[0];
862 				}
863 			}
864 			else {
865 				ent.model = cg.modelCfgDraw[state->modelIndex2];
866 			}
867 
868 			// PMM - check for the defender sphere shell .. make it translucent
869 			// replaces the previous version which used the high bit on modelIndex2 to determine transparency
870 			if (!Q_stricmp (cg.configStrings[CS_MODELS+(state->modelIndex2)], "models/items/shell/tris.md2")) {
871 				ent.flags |= RF_TRANSLUCENT;
872 				ent.color[3] = 255 * 0.32f;
873 			}
874 			// pmm
875 
876 			if (isDrawn) {
877 				cgi.R_AddEntity (&ent);
878 
879 				// Add entity shell(s)
880 				if (effects & EF_COLOR_SHELL) {
881 					ent.skinNum = 0;
882 					CG_AddEntityShells (&ent);
883 				}
884 			}
885 		}
886 
887 		if (state->modelIndex3) {
888 			ent.skin = NULL;	// Never use a custom skin on others
889 			ent.skinNum = 0;
890 			ent.model = cg.modelCfgDraw[state->modelIndex3];
891 
892 			if (isDrawn) {
893 				cgi.R_AddEntity (&ent);
894 
895 				// Add entity shell(s)
896 				if (effects & EF_COLOR_SHELL) {
897 					ent.skinNum = 0;
898 					CG_AddEntityShells (&ent);
899 				}
900 			}
901 		}
902 
903 		if (state->modelIndex4) {
904 			ent.skin = NULL;	// Never use a custom skin on others
905 			ent.skinNum = 0;
906 			ent.model = cg.modelCfgDraw[state->modelIndex4];
907 
908 			if (isDrawn) {
909 				cgi.R_AddEntity (&ent);
910 
911 				// Add entity shell(s)
912 				if (effects & EF_COLOR_SHELL) {
913 					ent.skinNum = 0;
914 					CG_AddEntityShells (&ent);
915 				}
916 			}
917 		}
918 
919 		// EF_POWERSCREEN shield
920 		if (effects & EF_POWERSCREEN) {
921 			ent.skin = NULL;	// Never use a custom skin on others
922 			ent.skinNum = 0;
923 			ent.model = cgMedia.powerScreenModel;
924 			ent.oldFrame = 0;
925 			ent.frame = 0;
926 			ent.flags |= RF_TRANSLUCENT|RF_FULLBRIGHT;
927 			ent.color[0] = ent.color[2] = 0;
928 			ent.color[1] = 255;
929 			ent.color[3] = 0.3f * 255;
930 			cgi.R_AddEntity (&ent);
931 		}
932 
933 		// EF_TELEPORTER acts like an event, but is not cleared each frame
934 		// moved so it doesn't stutter on packet loss
935 		if (effects & EF_TELEPORTER)
936 			CG_TeleporterParticles (state);
937 
938 		// add automatic particle trails
939 		if (effects &~ EF_ROTATE) {
940 			if (effects & EF_ROCKET) {
941 				CG_RocketTrail (cent->lerpOrigin, ent.origin);
942 				cgi.R_AddLight (ent.origin, 200, 1, 1, 0.6f);
943 			}
944 			// PGM - Do not reorder EF_BLASTER and EF_HYPERBLASTER
945 			else if (effects & EF_BLASTER) {
946 				//PGM
947 				// EF_BLASTER | EF_TRACKER is a special case for EF_BLASTER2... Cheese!
948 				if (effects & EF_TRACKER) {
949 					CG_BlasterGreenTrail (cent->lerpOrigin, ent.origin);
950 					cgi.R_AddLight (ent.origin, 200, 0, 1, 0);
951 				}
952 				else {
953 					CG_BlasterGoldTrail (cent->lerpOrigin, ent.origin);
954 					cgi.R_AddLight (ent.origin, 200, 1, 1, 0);
955 				}
956 				//PGM
957 			}
958 			else if (effects & EF_HYPERBLASTER) {
959 				if (effects & EF_TRACKER)						// PGM	overloaded for blaster2.
960 					cgi.R_AddLight (ent.origin, 200, 0, 1, 0);		// PGM
961 				else {
962 					// See if it is a Gloom flashlight
963 					if (ent.model && cg.currGameMod == GAME_MOD_GLOOM && !Q_stricmp ((char *)ent.model, "sprites/s_shine.sp2")) {
964 						static int	flPredLastTime = -1;
965 						qBool		flPred;
966 						vec3_t		flOrg;
967 
968 						// Flashlight prediction
969 						switch (cg.gloomClassType) {
970 							default:
971 							case GLM_DEFAULT:
972 
973 							case GLM_OBSERVER:
974 							case GLM_BREEDER:
975 							case GLM_HATCHLING:
976 							case GLM_DRONE:
977 							case GLM_WRAITH:
978 							case GLM_KAMIKAZE:
979 							case GLM_STINGER:
980 							case GLM_GUARDIAN:
981 							case GLM_STALKER:
982 								flPred = qFalse;
983 								break;
984 
985 							case GLM_ENGINEER:
986 							case GLM_GRUNT:
987 							case GLM_ST:
988 							case GLM_BIOTECH:
989 							case GLM_HT:
990 							case GLM_COMMANDO:
991 							case GLM_EXTERM:
992 							case GLM_MECH:
993 								flPred = qTrue;
994 								break;
995 						}
996 						if (glm_flashpred->intVal && flPred && flPredLastTime != cg.realTime) {
997 							playerStateNew_t	*ps;
998 							vec3_t				org, forward;
999 							trace_t				tr;
1000 
1001 							ps = &cg.frame.playerState; // Calc server side player origin
1002 							org[0] = ps->pMove.origin[0] * (1.0f/8.0f);
1003 							org[1] = ps->pMove.origin[1] * (1.0f/8.0f);
1004 							org[2] = ps->pMove.origin[2] * (1.0f/8.0f);
1005 
1006 							// Project our own flashlight forward
1007 							Angles_Vectors (ps->viewAngles, forward, NULL, NULL);
1008 							Vec3Scale (forward, 2048, forward);
1009 							Vec3Add (forward, org, forward);
1010 							tr = cgi.CM_BoxTrace (org, forward, cg_gloomFLightMins, cg_gloomFLightMaxs, 0,
1011 												CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
1012 
1013 							// Check if it's close
1014 							Vec3Subtract (tr.endPos, ent.origin, forward);
1015 							if (Vec3LengthFast (forward) > 256) {
1016 								Vec3Copy (ent.origin, flOrg);
1017 							}
1018 							else {
1019 								flPredLastTime = cg.realTime;
1020 
1021 								// Project a flashlight
1022 								Vec3Scale (cg.refDef.viewAxis[0], 2048, forward);
1023 								Vec3Add (forward, cg.refDef.viewOrigin, forward);
1024 								tr = cgi.CM_BoxTrace (cg.refDef.viewOrigin, forward, cg_gloomFLightMins, cg_gloomFLightMaxs, 0,
1025 													CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
1026 
1027 								Vec3Copy (tr.endPos, flOrg);
1028 							}
1029 						}
1030 						else
1031 							Vec3Copy (ent.origin, flOrg);
1032 
1033 						// White flashlight?
1034 						if (glm_flwhite->intVal)
1035 							cgi.R_AddLight (flOrg, 200, 1, 1, 1);
1036 						else
1037 							cgi.R_AddLight (flOrg, 200, 1, 1, 0);
1038 					}
1039 					else
1040 						cgi.R_AddLight (ent.origin, 200, 1, 1, 0);
1041 				}
1042 			}
1043 			else if (effects & EF_GIB)
1044 				CG_GibTrail (cent->lerpOrigin, ent.origin, EF_GIB);
1045 			else if (effects & EF_GRENADE)
1046 				CG_GrenadeTrail (cent->lerpOrigin, ent.origin);
1047 			else if (effects & EF_FLIES)
1048 				CG_FlyEffect (cent, ent.origin);
1049 			else if (effects & EF_BFG) {
1050 				if (effects & EF_ANIM_ALLFAST) {
1051 					// flying
1052 					CG_BfgTrail (&ent);
1053 					i = 200;
1054 				}
1055 				else {
1056 					// explosion
1057 					static const int BFG_BrightRamp[6] = { 300, 400, 600, 300, 150, 75 };
1058 					i = BFG_BrightRamp[state->frame%6];
1059 				}
1060 
1061 				cgi.R_AddLight (ent.origin, (float)i, 0, 1, 0);
1062 			// RAFAEL
1063 			}
1064 			else if (effects & EF_TRAP) {
1065 				ent.origin[2] += 32;
1066 				CG_TrapParticles (&ent);
1067 				i = ((int)frand () * 100) + 100;
1068 				cgi.R_AddLight (ent.origin, (float)i, 1, 0.8f, 0.1f);
1069 			}
1070 			else if (effects & EF_FLAG1) {
1071 				CG_FlagTrail (cent->lerpOrigin, ent.origin, EF_FLAG1);
1072 				cgi.R_AddLight (ent.origin, 225, 1, 0.1f, 0.1f);
1073 			}
1074 			else if (effects & EF_FLAG2) {
1075 				CG_FlagTrail (cent->lerpOrigin, ent.origin, EF_FLAG2);
1076 				cgi.R_AddLight (ent.origin, 225, 0.1f, 0.1f, 1);
1077 
1078 			//ROGUE
1079 			}
1080 			else if (effects & EF_TAGTRAIL) {
1081 				CG_TagTrail (cent->lerpOrigin, ent.origin);
1082 				cgi.R_AddLight (ent.origin, 225, 1.0, 1.0, 0.0);
1083 			}
1084 			else if (effects & EF_TRACKERTRAIL) {
1085 				if (effects & EF_TRACKER) {
1086 					float intensity;
1087 
1088 					intensity = 50 + (500 * ((float)sin (cg.realTime/500.0f) + 1.0f));
1089 					cgi.R_AddLight (ent.origin, intensity, -1.0, -1.0, -1.0);
1090 				}
1091 				else {
1092 					CG_TrackerShell (cent->lerpOrigin);
1093 					cgi.R_AddLight (ent.origin, 155, -1.0, -1.0, -1.0);
1094 				}
1095 			}
1096 			else if (effects & EF_TRACKER) {
1097 				CG_TrackerTrail (cent->lerpOrigin, ent.origin);
1098 				cgi.R_AddLight (ent.origin, 200, -1, -1, -1);
1099 			//ROGUE
1100 
1101 			// RAFAEL
1102 			}
1103 			else if (effects & EF_GREENGIB)
1104 				CG_GibTrail (cent->lerpOrigin, ent.origin, EF_GREENGIB);
1105 			// RAFAEL
1106 
1107 			else if (effects & EF_IONRIPPER) {
1108 				CG_IonripperTrail (cent->lerpOrigin, ent.origin);
1109 				if (cg.currGameMod == GAME_MOD_GLOOM)
1110 					cgi.R_AddLight (ent.origin, 100, 0.3f, 1, 0.3f);
1111 				else
1112 					cgi.R_AddLight (ent.origin, 100, 1, 0.5f, 0.5f);
1113 			}
1114 
1115 			// RAFAEL
1116 			else if (effects & EF_BLUEHYPERBLASTER)
1117 				cgi.R_AddLight (ent.origin, 200, 0, 0, 1);
1118 			// RAFAEL
1119 
1120 			else if (effects & EF_PLASMA) {
1121 				if (effects & EF_ANIM_ALLFAST)
1122 					CG_BlasterGoldTrail (cent->lerpOrigin, ent.origin);
1123 
1124 				cgi.R_AddLight (ent.origin, 130, 1, 0.5, 0.5);
1125 			}
1126 		}
1127 done:
1128 		if (cent->muzzleOn) {
1129 			cent->muzzleOn = qFalse;
1130 			cent->muzzType = -1;
1131 			cent->muzzSilenced = qFalse;
1132 			cent->muzzVWeap = qFalse;
1133 		}
1134 		Vec3Copy (ent.origin, cent->lerpOrigin);
1135 	}
1136 }
1137 
1138 
1139 /*
1140 ================
1141 CG_AddEntities
1142 ================
1143 */
CG_AddEntities(void)1144 void CG_AddEntities (void)
1145 {
1146 	CG_AddViewWeapon ();
1147 	CG_AddPacketEntities ();
1148 	CG_AddTempEnts ();
1149 	CG_AddLocalEnts ();
1150 	CG_AddDLights ();
1151 	CG_AddLightStyles ();
1152 	CG_AddDecals ();
1153 	CG_AddParticles ();
1154 }
1155 
1156 
1157 /*
1158 ==============
1159 CG_ClearEntities
1160 ==============
1161 */
CG_ClearEntities(void)1162 void CG_ClearEntities (void)
1163 {
1164 	memset (cg_entityList, 0, sizeof (cg_entityList));
1165 	memset (cg_parseEntities, 0, sizeof (cg_parseEntities));
1166 
1167 	CG_ClearTempEnts ();
1168 	CG_ClearLocalEnts ();
1169 	CG_ClearDLights ();
1170 	CG_ClearLightStyles ();
1171 	CG_ClearDecals ();
1172 	CG_ClearParticles ();
1173 }
1174 
1175 
1176 /*
1177 ===============
1178 CG_GetEntitySoundOrigin
1179 
1180 Called to get the sound spatialization origin, so that interpolated origins are used.
1181 ===============
1182 */
CG_GetEntitySoundOrigin(int entNum,vec3_t origin,vec3_t velocity)1183 void CG_GetEntitySoundOrigin (int entNum, vec3_t origin, vec3_t velocity)
1184 {
1185 	cgEntity_t	*ent;
1186 
1187 	if (entNum < 0 || entNum >= MAX_CS_EDICTS)
1188 		Com_Error (ERR_DROP, "CG_GetEntitySoundOrigin: bad entNum");
1189 
1190 	ent = &cg_entityList[entNum];
1191 
1192 	if (ent->current.renderFx & (RF_FRAMELERP|RF_BEAM)) {
1193 		origin[0] = ent->current.oldOrigin[0] + (ent->current.origin[0] - ent->current.oldOrigin[0]) * cg.lerpFrac;
1194 		origin[1] = ent->current.oldOrigin[1] + (ent->current.origin[1] - ent->current.oldOrigin[1]) * cg.lerpFrac;
1195 		origin[2] = ent->current.oldOrigin[2] + (ent->current.origin[2] - ent->current.oldOrigin[2]) * cg.lerpFrac;
1196 
1197 		Vec3Subtract (ent->current.origin, ent->current.oldOrigin, velocity);
1198 		Vec3Scale (velocity, 10, velocity);
1199 	}
1200 	else {
1201 		origin[0] = ent->prev.origin[0] + (ent->current.origin[0] - ent->prev.origin[0]) * cg.lerpFrac;
1202 		origin[1] = ent->prev.origin[1] + (ent->current.origin[1] - ent->prev.origin[1]) * cg.lerpFrac;
1203 		origin[2] = ent->prev.origin[2] + (ent->current.origin[2] - ent->prev.origin[2]) * cg.lerpFrac;
1204 
1205 		Vec3Subtract (ent->current.origin, ent->prev.origin, velocity);
1206 		Vec3Scale (velocity, 10, velocity);
1207 	}
1208 
1209 	if (ent->current.solid == 31) {
1210 		struct cBspModel_s	*cModel;
1211 		vec3_t				mins, maxs;
1212 
1213 		cModel = cgi.CM_InlineModel (cg.configStrings[CS_MODELS+ent->current.modelIndex]);
1214 		if (!cModel)
1215 			return;
1216 
1217 		cgi.CM_InlineModelBounds (cModel, mins, maxs);
1218 
1219 		origin[0] += 0.5f * (mins[0] + maxs[0]);
1220 		origin[1] += 0.5f * (mins[1] + maxs[1]);
1221 		origin[2] += 0.5f * (mins[2] + maxs[2]);
1222 	}
1223 }
1224