1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (“RTCW MP Source Code”).
8 
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 RTCW MP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 /*
30  * name:		cg_ents.c
31  *
32  * desc:		present snapshot entities, happens every single frame
33  *
34 */
35 
36 
37 #include "cg_local.h"
38 
39 ///////////////////////
40 extern int propellerModel;
41 ///////////////////////
42 
43 /*
44 ======================
45 CG_PositionEntityOnTag
46 
47 Modifies the entities position and axis by the given
48 tag location
49 ======================
50 */
CG_PositionEntityOnTag(refEntity_t * entity,const refEntity_t * parent,char * tagName,int startIndex,vec3_t * offset)51 void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
52 							 char *tagName, int startIndex, vec3_t *offset ) {
53 	int i;
54 	orientation_t lerped;
55 
56 	// lerp the tag
57 	trap_R_LerpTag( &lerped, parent, tagName, startIndex );
58 
59 	// FIXME: allow origin offsets along tag?	//----(SA) Yes! Adding.
60 
61 	VectorCopy( parent->origin, entity->origin );
62 
63 	if ( offset ) {
64 		VectorAdd( lerped.origin, *offset, lerped.origin );
65 	}
66 //----(SA) end
67 
68 	for ( i = 0 ; i < 3 ; i++ ) {
69 		VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
70 	}
71 
72 	// had to cast away the const to avoid compiler problems...
73 	MatrixMultiply( lerped.axis, ( (refEntity_t *)parent )->axis, entity->axis );
74 	// Ridah, not sure why this was here.. causes jittery torso animation, since the torso might have
75 	// different frame/oldFrame
76 	//entity->backlerp = parent->backlerp;
77 }
78 
79 
80 /*
81 ======================
82 CG_PositionRotatedEntityOnTag
83 
84 Modifies the entities position and axis by the given
85 tag location
86 ======================
87 */
CG_PositionRotatedEntityOnTag(refEntity_t * entity,const refEntity_t * parent,char * tagName)88 void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
89 									char *tagName ) {
90 	int i;
91 	orientation_t lerped;
92 	vec3_t tempAxis[3];
93 
94 //AxisClear( entity->axis );
95 	// lerp the tag
96 	trap_R_LerpTag( &lerped, parent, tagName, 0 );
97 
98 	// FIXME: allow origin offsets along tag?
99 	VectorCopy( parent->origin, entity->origin );
100 	for ( i = 0 ; i < 3 ; i++ ) {
101 		VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
102 	}
103 
104 	// had to cast away the const to avoid compiler problems...
105 	MatrixMultiply( entity->axis, lerped.axis, tempAxis );
106 	MatrixMultiply( tempAxis, ( (refEntity_t *)parent )->axis, entity->axis );
107 }
108 
109 
110 //----(SA)	added
111 /*
112 ==============
113 CG_LoseArmor
114 	maybe better in cg_localents.c
115 ==============
116 */
CG_LoseArmor(centity_t * cent,int index)117 void CG_LoseArmor( centity_t *cent, int index ) {
118 }
119 
120 /*
121 ==============
122 CG_AttachedPartChange
123 ==============
124 */
CG_AttachedPartChange(centity_t * cent)125 void CG_AttachedPartChange( centity_t *cent ) {
126 }
127 
128 //----(SA)	end
129 
130 /*
131 ==========================================================================
132 
133 FUNCTIONS CALLED EACH FRAME
134 
135 ==========================================================================
136 */
137 
138 /*
139 ======================
140 CG_SetEntitySoundPosition
141 
142 Also called by event processing code
143 ======================
144 */
CG_SetEntitySoundPosition(centity_t * cent)145 void CG_SetEntitySoundPosition( centity_t *cent ) {
146 	if ( cent->currentState.solid == SOLID_BMODEL ) {
147 		vec3_t origin;
148 		float   *v;
149 
150 		v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
151 		VectorAdd( cent->lerpOrigin, v, origin );
152 		trap_S_UpdateEntityPosition( cent->currentState.number, origin );
153 	} else {
154 		trap_S_UpdateEntityPosition( cent->currentState.number, cent->lerpOrigin );
155 	}
156 }
157 
158 
159 
160 
161 #define LS_FRAMETIME 100 // (ms)  cycle through lightstyle characters at 10fps
162 
163 
164 /*
165 ==============
166 CG_SetDlightIntensity
167 
168 ==============
169 */
CG_AddLightstyle(centity_t * cent)170 void CG_AddLightstyle( centity_t *cent ) {
171 	float lightval;
172 	int cl;
173 	int r, g, b;
174 	int stringlength;
175 	float offset;
176 	int otime;
177 	int lastch, nextch;
178 
179 	if ( cent->dl_stylestring[0] == '\0' ) {
180 		return;
181 	}
182 
183 	otime = cg.time - cent->dl_time;
184 	stringlength = strlen( cent->dl_stylestring );
185 
186 	// it's been a long time since you were updated, lets assume a reset
187 	if ( otime > 2 * LS_FRAMETIME ) {
188 		otime = 0;
189 		cent->dl_frame = cent->dl_oldframe = 0;
190 		cent->dl_backlerp = 0;
191 	}
192 
193 	cent->dl_time = cg.time;
194 
195 	offset = ( (float)otime ) / LS_FRAMETIME;
196 
197 	cent->dl_backlerp += offset;
198 
199 
200 	if ( cent->dl_backlerp > 1 ) {                     // we're moving on to the next frame
201 		cent->dl_oldframe   = cent->dl_oldframe + (int)cent->dl_backlerp;
202 		cent->dl_frame      = cent->dl_oldframe + 1;
203 		if ( cent->dl_oldframe >= stringlength ) {
204 			cent->dl_oldframe = ( cent->dl_oldframe ) % stringlength;
205 			if ( cent->dl_oldframe < 3 && cent->dl_sound ) { // < 3 so if an alarm comes back into the pvs it will only start a sound if it's going to be closely synced with the light, otherwise wait till the next cycle
206 				trap_S_StartSound( NULL, cent->currentState.number, CHAN_AUTO, cgs.gameSounds[cent->dl_sound] );
207 			}
208 		}
209 
210 		if ( cent->dl_frame >= stringlength ) {
211 			cent->dl_frame = ( cent->dl_frame ) % stringlength;
212 		}
213 
214 		cent->dl_backlerp = cent->dl_backlerp - (int)cent->dl_backlerp;
215 	}
216 
217 
218 	lastch = cent->dl_stylestring[cent->dl_oldframe] - 'a';
219 	nextch = cent->dl_stylestring[cent->dl_frame] - 'a';
220 
221 	lightval = ( lastch * ( 1.0 - cent->dl_backlerp ) ) + ( nextch * cent->dl_backlerp );
222 
223 	lightval = ( lightval * ( 1000.0f / 24.0f ) ) - 200.0f;  // they want 'm' as the "middle" value as 300
224 
225 	lightval = max( 0.0f,    lightval );
226 	lightval = min( 1000.0f, lightval );
227 
228 	cl = cent->currentState.constantLight;
229 	r = cl & 255;
230 	g = ( cl >> 8 ) & 255;
231 	b = ( cl >> 16 ) & 255;
232 
233 	trap_R_AddLightToScene( cent->lerpOrigin, lightval, (float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f, 0 ); // overdraw forced to 0 for now
234 }
235 
236 
237 void CG_GetWindVector( vec3_t dir ); // JPW NERVE
238 
239 /*
240 ==================
241 CG_EntityEffects
242 
243 Add continuous entity effects, like local entity emission and lighting
244 ==================
245 */
CG_EntityEffects(centity_t * cent)246 static void CG_EntityEffects( centity_t *cent ) {
247 	static vec3_t dir;
248 	// update sound origins
249 	CG_SetEntitySoundPosition( cent );
250 
251 	// add loop sound
252 	if ( cent->currentState.loopSound ) {
253 		//----(SA)	hmm, the above (CG_SetEntitySoundPosition()) sets s_entityPosition[entityNum] with a valid
254 		//			location, but the looping sound for a bmodel will never get it since that sound is
255 		//			started with the lerpOriging right here. \/ \/	How do looping sounds ever work for bmodels?
256 		//			Or have they always been broken and we just never used them?
257 
258 		if ( cent->currentState.eType == ET_SPEAKER ) {
259 			/*if(cent->currentState.density == 1) {	// NO_PVS
260 				CG_S_AddRealLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.gameSounds[ cent->currentState.loopSound ] );
261 			}
262 			else*/if ( cent->currentState.dmgFlags ) { // range is set
263 				CG_S_AddRangedLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.gameSounds[ cent->currentState.loopSound ], cent->currentState.dmgFlags );
264 			} else {
265 				CG_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.gameSounds[ cent->currentState.loopSound ], 255 );
266 			}
267 		} else if ( cent->currentState.solid == SOLID_BMODEL )   {
268 			vec3_t origin;
269 			float   *v;
270 
271 			v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
272 			VectorAdd( cent->lerpOrigin, v, origin );
273 			CG_S_AddLoopingSound( cent->currentState.number, origin, vec3_origin, cgs.gameSounds[ cent->currentState.loopSound ], 255 );
274 		} else {
275 			CG_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.gameSounds[ cent->currentState.loopSound ], 255 );
276 		}
277 	} /*else {
278 		// stop NO_PVS speakers if they've been turned off
279 		if(cent->currentState.eType == ET_SPEAKER) {
280 			if(cent->currentState.density == 1) {
281 				trap_S_StopLoopingSound(cent->currentState.number);
282 			}
283 		}
284 	}*/
285 
286 
287 	// constant light glow
288 	if ( cent->currentState.constantLight ) {
289 		int cl;
290 		float i, r, g, b;
291 
292 
293 		if ( cent->dl_stylestring[0] != 0 ) {  // it's probably a dlight
294 			CG_AddLightstyle( cent );
295 		} else
296 		{
297 			cl = cent->currentState.constantLight;
298 			r = (float) (cl & 0xFF) / 255.0;
299 			g = (float) ((cl >> 8) & 0xFF) / 255.0;
300 			b = (float) ((cl >> 16) & 0xFF) / 255.0;
301 			i = (float) ((cl >> 24) & 0xFF) * 4.0;
302 			trap_R_AddLightToScene(cent->lerpOrigin, i, r, g, b, 0);
303 		}
304 	}
305 
306 	// Ridah, flaming sounds
307 	if ( CG_EntOnFire( cent ) ) {
308 		// play a flame blow sound when moving
309 		CG_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.flameBlowSound, (int)( 255.0 * ( 1.0 - fabs( cent->fireRiseDir[2] ) ) ) );
310 		// play a burning sound when not moving
311 		CG_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.flameSound, (int)( 0.3 * 255.0 * ( pow( cent->fireRiseDir[2],2 ) ) ) );
312 	}
313 
314 
315 	// DHM - Nerve :: If EF_SMOKING is set, emit smoke
316 	if ( cgs.gametype >= GT_WOLF && cent->currentState.eFlags & EF_SMOKING ) {
317 		float rnd = random();
318 
319 		if ( cent->lastTrailTime < cg.time ) {
320 			cent->lastTrailTime = cg.time + 100;
321 
322 // JPW NERVE -- use wind vector for smoke
323 			CG_GetWindVector( dir );
324 			VectorScale( dir,20,dir ); // was 75, before that 55
325 			if ( dir[2] < 10 ) {
326 				dir[2] += 10;
327 			}
328 //			dir[0] = crandom() * 10;
329 //			dir[1] = crandom() * 10;
330 //			dir[2] = 10 + rnd * 30;
331 // jpw
332 			CG_SmokePuff( cent->lerpOrigin, dir, 15 + ( random() * 10 ),
333 						  0.3 + rnd, 0.3 + rnd, 0.3 + rnd, 0.4, 1500 + ( rand() % 500 ),
334 						  cg.time, cg.time + 500, 0, cgs.media.smokePuffShader );
335 		}
336 	}
337 	// dhm - end
338 // JPW NERVE same thing but for smoking barrels instead of nasty server-side effect from single player
339 	if ( cgs.gametype >= GT_WOLF && cent->currentState.eFlags & EF_SMOKINGBLACK ) {
340 		float rnd = random();
341 
342 		if ( cent->lastTrailTime < cg.time ) {
343 			cent->lastTrailTime = cg.time + 75;
344 
345 			CG_GetWindVector( dir );
346 			VectorScale( dir,50,dir ); // was 75, before that 55
347 			if ( dir[2] < 50 ) {
348 				dir[2] += 50;
349 			}
350 
351 			CG_SmokePuff( cent->lerpOrigin, dir, 40 + random() * 70, //40+(rnd*40),
352 						  rnd * 0.1, rnd * 0.1, rnd * 0.1, 1, 2800 + ( rand() % 4000 ), //2500+(random()*1500),
353 						  cg.time, 0, 0, cgs.media.smokePuffShader );
354 		}
355 	}
356 // jpw
357 
358 
359 
360 }
361 
362 
363 /*
364 ==================
365 CG_General
366 ==================
367 */
CG_General(centity_t * cent)368 static void CG_General( centity_t *cent ) {
369 	refEntity_t ent;
370 	entityState_t       *s1;
371 
372 	s1 = &cent->currentState;
373 
374 	// if set to invisible, skip
375 	if ( !s1->modelindex ) {
376 		return;
377 	}
378 
379 	memset( &ent, 0, sizeof( ent ) );
380 
381 	// set frame
382 
383 	ent.frame = s1->frame;
384 	ent.oldframe = ent.frame;
385 	ent.backlerp = 0;
386 
387 	if ( ent.frame ) {
388 
389 		ent.oldframe -= 1;
390 		ent.backlerp = 1 - cg.frameInterpolation;
391 
392 		if ( cent->currentState.time ) {
393 			ent.fadeStartTime = cent->currentState.time;
394 			ent.fadeEndTime = cent->currentState.time2;
395 		}
396 
397 	}
398 
399 	VectorCopy( cent->lerpOrigin, ent.origin );
400 	VectorCopy( cent->lerpOrigin, ent.oldorigin );
401 
402 	ent.hModel = cgs.gameModels[s1->modelindex];
403 
404 	// player model
405 	if ( s1->number == cg.snap->ps.clientNum ) {
406 		ent.renderfx |= RF_THIRD_PERSON;    // only draw from mirrors
407 	}
408 
409 	if ( cent->currentState.eType == ET_MG42_BARREL ) {
410 		// grab angles from first person user or self if not
411 		// ATVI Wolfenstein Misc #469 - don't track until viewlocked
412 		if ( cent->currentState.otherEntityNum == cg.snap->ps.clientNum && cg.snap->ps.viewlocked ) {
413 			AnglesToAxis( cg.predictedPlayerState.viewangles, ent.axis );
414 		} else {
415 			AnglesToAxis( cent->lerpAngles, ent.axis );
416 		}
417 	} else {
418 		// convert angles to axis
419 		AnglesToAxis( cent->lerpAngles, ent.axis );
420 	}
421 
422 	// scale gamemodels
423 	if ( cent->currentState.eType == ET_GAMEMODEL ) {
424 		VectorScale( ent.axis[0], cent->currentState.angles2[0], ent.axis[0] );
425 		VectorScale( ent.axis[1], cent->currentState.angles2[1], ent.axis[1] );
426 		VectorScale( ent.axis[2], cent->currentState.angles2[2], ent.axis[2] );
427 		ent.nonNormalizedAxes = qtrue;
428 
429 //----(SA)	testing
430 		if ( cent->currentState.apos.trType ) {
431 			ent.reFlags |= REFLAG_ORIENT_LOD;
432 		}
433 //----(SA)	end
434 	}
435 
436 	// add to refresh list
437 	trap_R_AddRefEntityToScene( &ent );
438 
439 	memcpy( &cent->refEnt, &ent, sizeof( refEntity_t ) );
440 }
441 
442 /*
443 ==================
444 CG_Speaker
445 
446 Speaker entities can automatically play sounds
447 ==================
448 */
CG_Speaker(centity_t * cent)449 static void CG_Speaker( centity_t *cent ) {
450 	if ( !cent->currentState.clientNum ) {  // FIXME: use something other than clientNum...
451 		return;     // not auto triggering
452 	}
453 
454 	if ( cg.time < cent->miscTime ) {
455 		return;
456 	}
457 
458 	trap_S_StartSound( NULL, cent->currentState.number, CHAN_ITEM, cgs.gameSounds[cent->currentState.eventParm] );
459 
460 	//	ent->s.frame = ent->wait * 10;
461 	//	ent->s.clientNum = ent->random * 10;
462 	cent->miscTime = cg.time + cent->currentState.frame * 100 + cent->currentState.clientNum * 100 * crandom();
463 }
464 
465 
466 
467 /*
468 ==============
469 CG_DrawHoldableSelect
470 
471   This, of course, will all change when we've got a hud, but for now it makes the holdable items usable and not bad looking
472 ==============
473 */
CG_DrawHoldableSelect(void)474 void CG_DrawHoldableSelect( void ) {
475 /*
476 	int	bits;
477 	int	count;
478 	int	amount;
479 	int	i, x, y, w;
480 	float	*color;
481 	char	*name;
482 	gitem_t	*item;
483 
484 	// don't display if dead
485 	if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
486 		return;
487 	}
488 
489 	color = CG_FadeColor( cg.holdableSelectTime, HOLDABLE_SELECT_TIME );
490 	if ( !color ) {
491 		return;
492 	}
493 	trap_R_SetColor( color );
494 
495 	// showing select clears pickup item display, but not the blend blob
496 	cg.itemPickupTime = 0;
497 
498 	//----(SA)	removed
499 	// count the number of holdables owned
500 	bits = cg.snap->ps.stats[ STAT_HOLDABLE_ITEM ];
501 	count = 0;
502 
503 	for ( i = 1 ; i <= HI_BOOK3; i++ ) {
504 		if ( bits & ( 1 << i ) ) {
505 			if ( cg.predictedPlayerState.holdable[i] ) {	// don't show ones we're out of
506 				count++;
507 			}
508 		}
509 	}
510 
511 	if ( !count ) {
512 		return;
513 	}
514 
515 	x = 320 - count * 20;
516 	y = 380;
517 
518 
519 	for ( i = 1 ; i <= HI_BOOK3 ; i++ ) {
520 		if ( !( bits & ( 1 << i ) ) ) {
521 			continue;
522 		}
523 
524 		amount = cg.predictedPlayerState.holdable[i];
525 
526 		if ( !amount ) {
527 			continue;
528 		}
529 
530 		item = BG_FindItemForHoldable( i );
531 		if ( !item ) {
532 			continue;
533 		}
534 
535 		CG_RegisterItemVisuals( item - bg_itemlist );
536 
537 		// draw icon
538 		if ( i == HI_WINE ) {
539 			// wine icons have three stages since each bottle has three uses (as opposed to others so far where there's only 1 use)
540 			int wine = amount;
541 			if ( wine > 3 ) {
542 				wine = 3;
543 			}
544 			CG_DrawPic( x, y, 32, 32, cg_items[item - bg_itemlist].icons[ 2 - ( wine - 1 ) ] ) ;
545 		} else {
546 			CG_DrawPic( x, y, 32, 32, cg_items[item - bg_itemlist].icons[0] );
547 		}
548 
549 		// draw remaining uses if there's more than one
550 		if ( amount > 1 ) {
551 			CG_DrawBigStringColor( x, y + 34, va( "%d", amount ), color );
552 		}
553 
554 		// draw selection marker
555 		if ( i == cg.holdableSelect ) {
556 			CG_DrawPic( x - 4, y - 4, 40, 40, cgs.media.selectShader );
557 		}
558 
559 		x += 40;
560 	}
561 
562 	// draw the selected name
563 	if ( cg.holdableSelect ) {
564 		item = BG_FindItemForHoldable( cg.holdableSelect );
565 		if ( item ) {
566 			name = item->pickup_name;
567 			if ( name ) {
568 				//----(SA)	trying smaller text
569 //				w = CG_DrawStrlen( name ) * BIGCHAR_WIDTH;
570 				w = CG_DrawStrlen( name ) * 10;
571 				x = ( SCREEN_WIDTH - w ) / 2;
572 //				CG_DrawBigStringColor(x, y - 22, name, color);
573 				CG_DrawStringExt2( x, y + 60, name, color, qfalse, qtrue, 10, 10, 0 );
574 			}
575 		}
576 	}
577 
578 	trap_R_SetColor( NULL );
579 */
580 }
581 
582 
583 /*
584 ==============
585 CG_NextItem_f
586 ==============
587 */
588 
CG_NextItem_f(void)589 void CG_NextItem_f( void ) {
590 /*
591 	int	i;
592 	int	original, next;
593 
594 	if ( !cg.snap ) {
595 		return;
596 	}
597 
598 	if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
599 		return;
600 	}
601 
602 	cg.holdableSelectTime = cg.time;
603 	cg.weaponSelectTime = 0;	// (SA) clear weapon selection drawing
604 
605 	next = original = cg.holdableSelect;
606 
607 	for ( i = 0 ; i < HI_NUM_HOLDABLE ; i++ ) {
608 		next++;
609 
610 		if ( next == HI_NUM_HOLDABLE ) {
611 			next = 0;
612 		}
613 
614 		if ( cg.predictedPlayerState.holdable[next] ) {	//----(SA)
615 			break;
616 		}
617 	}
618 
619 	if ( i == HI_NUM_HOLDABLE ) {
620 		next = original;
621 	}
622 
623 	cg.holdableSelect = next;
624 */
625 }
626 
627 /*
628 ==============
629 CG_PrevItem_f
630 ==============
631 */
CG_PrevItem_f(void)632 void CG_PrevItem_f( void ) {
633 /*
634 	int	i;
635 	int	original, next;
636 
637 	if ( !cg.snap ) {
638 		return;
639 	}
640 
641 	if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
642 		return;
643 	}
644 
645 	cg.weaponSelectTime = 0;	// (SA) clear weapon selection drawing
646 	cg.holdableSelectTime = cg.time;
647 
648 	next = original = cg.holdableSelect;
649 
650 	for ( i = 0 ; i < HI_NUM_HOLDABLE ; i++ ) {
651 		next--;
652 
653 		if ( next == -1 ) {
654 			next = HI_NUM_HOLDABLE - 1;
655 		}
656 
657 		if ( cg.predictedPlayerState.holdable[next] ) {	//----(SA)
658 			break;
659 		}
660 	}
661 
662 	if ( i == HI_NUM_HOLDABLE ) {
663 		next = original;
664 	}
665 
666 	cg.holdableSelect = next;
667 */
668 }
669 
670 /*
671 ==============
672 CG_Item_f
673 ==============
674 */
CG_Item_f(void)675 void CG_Item_f( void ) {
676 	//int	num;
677 	//num = atoi( CG_Argv( 1 ) );
678 
679 	//cg.holdableSelectTime = cg.time;
680 
681 	//CG_Printf( "Item set to: %d\n", num );
682 }
683 
684 
685 
686 //----(SA)	added
687 /*
688 ==============
689 CG_HoldableUsedupChange
690 ==============
691 */
CG_HoldableUsedupChange(void)692 void CG_HoldableUsedupChange( void ) {
693 	int holding;
694 
695 	holding = cg.holdableSelect;
696 
697 	CG_NextItem_f();
698 
699 	if ( cg.holdableSelect == holding ) {  // nothing else to go to
700 		cg.holdableSelect = 0;
701 		cg.weaponSelectTime = 0;
702 		return;
703 	}
704 }
705 //----(SA)	end
706 
707 
708 
CG_PlayerSeesItem(playerState_t * ps,entityState_t * item,int atTime,int itemType)709 qboolean CG_PlayerSeesItem( playerState_t *ps, entityState_t *item, int atTime, int itemType ) {
710 	vec3_t vorigin, eorigin, viewa, dir;
711 	float dot, dist, foo;
712 	trace_t tr;
713 
714 	BG_EvaluateTrajectory( &item->pos, atTime, eorigin );
715 
716 	VectorCopy( ps->origin, vorigin );
717 	vorigin[2] += ps->viewheight;           // get the view loc up to the viewheight
718 //	eorigin[2] += 8;						// and subtract the item's offset (that is used to place it on the ground)
719 
720 
721 	VectorSubtract( vorigin, eorigin, dir );
722 
723 	dist = VectorNormalize( dir );            // dir is now the direction from the item to the player
724 
725 	if ( dist > 255 ) {
726 		return qfalse;                      // only run the remaining stuff on items that are close enough
727 
728 	}
729 	// (SA) FIXME: do this without AngleVectors.
730 	//		It'd be nice if the angle vectors for the player
731 	//		have already been figured at this point and I can
732 	//		just pick them up.  (if anybody is storing this somewhere,
733 	//		for the current frame please let me know so I don't
734 	//		have to do redundant calcs)
735 	AngleVectors( ps->viewangles, viewa, 0, 0 );
736 	dot = DotProduct( viewa, dir );
737 
738 	// give more range based on distance (the hit area is wider when closer)
739 
740 //	foo = -0.94f - (dist/255.0f) * 0.057f;	// (ranging from -0.94 to -0.997) (it happened to be a pretty good range)
741 	foo = -0.94f - ( dist * ( 1.0f / 255.0f ) ) * 0.057f;   // (ranging from -0.94 to -0.997) (it happened to be a pretty good range)
742 
743 ///	Com_Printf("test: if(%f > %f) return qfalse (dot > foo)\n", dot, foo);
744 	if ( dot > foo ) {
745 		return qfalse;
746 	}
747 
748 	// (SA) okay, everything else is okay, so do a bloody trace. (so coronas on treasure doesn't show through walls) <sigh>
749 	if ( itemType == IT_TREASURE ) {
750 		CG_Trace( &tr, vorigin, NULL, NULL, eorigin, -1, MASK_SOLID );
751 
752 		if ( tr.fraction != 1 ) {
753 			return qfalse;
754 		}
755 	}
756 
757 	return qtrue;
758 
759 }
760 
761 
762 /*
763 ==================
764 CG_Item
765 ==================
766 */
CG_Item(centity_t * cent)767 static void CG_Item( centity_t *cent ) {
768 	refEntity_t ent;
769 	entityState_t       *es;
770 	gitem_t             *item;
771 	qboolean hasStand, highlight;
772 	float highlightFadeScale = 1.0f;
773 
774 	es = &cent->currentState;
775 
776 	hasStand = qfalse;
777 	highlight = qfalse;
778 
779 	// (item index is stored in es->modelindex for item)
780 
781 	if ( es->modelindex >= bg_numItems ) {
782 		CG_Error( "Bad item index %i on entity", es->modelindex );
783 	}
784 
785 	// if set to invisible, skip
786 	if ( !es->modelindex || ( es->eFlags & EF_NODRAW ) ) {
787 		return;
788 	}
789 
790 	item = &bg_itemlist[ es->modelindex ];
791 
792 	if ( cg_simpleItems.integer && item->giType != IT_TEAM ) {
793 		memset( &ent, 0, sizeof( ent ) );
794 		ent.reType = RT_SPRITE;
795 		VectorCopy( cent->lerpOrigin, ent.origin );
796 		ent.radius = 14;
797 		ent.customShader = cg_items[es->modelindex].icons[0];
798 		ent.shaderRGBA[0] = 255;
799 		ent.shaderRGBA[1] = 255;
800 		ent.shaderRGBA[2] = 255;
801 		ent.shaderRGBA[3] = 255;
802 		trap_R_AddRefEntityToScene( &ent );
803 		return;
804 	}
805 
806 	memset( &ent, 0, sizeof( ent ) );
807 
808 	ent.nonNormalizedAxes = qfalse;
809 
810 	if ( item->giType == IT_WEAPON ) {
811 		weaponInfo_t    *weaponInfo = &cg_weapons[item->giTag];
812 
813 		if ( weaponInfo->standModel ) {
814 			hasStand = qtrue;
815 		}
816 
817 		if ( hasStand ) {                          // first try to put the weapon on it's 'stand'
818 			refEntity_t stand;
819 
820 			memset( &stand, 0, sizeof( stand ) );
821 			stand.hModel = weaponInfo->standModel;
822 
823 			if ( es->eFlags & EF_SPINNING ) {
824 				if ( es->groundEntityNum == -1 || !es->groundEntityNum ) { // (SA) spinning with a stand will spin the stand and the attached weap (only when in the air)
825 					VectorCopy( cg.autoAnglesSlow, cent->lerpAngles );
826 					VectorCopy( cg.autoAnglesSlow, cent->lastLerpAngles );
827 				} else {
828 					VectorCopy( cent->lastLerpAngles, cent->lerpAngles );   // make a tossed weapon sit on the ground in a position that matches how it was yawed
829 				}
830 			}
831 
832 			AnglesToAxis( cent->lerpAngles, stand.axis );
833 			VectorCopy( cent->lerpOrigin, stand.origin );
834 
835 			// scale the stand to match the weapon scale ( the weapon will also be scaled inside CG_PositionEntityOnTag() )
836 			VectorScale( stand.axis[0], 1.5, stand.axis[0] );
837 			VectorScale( stand.axis[1], 1.5, stand.axis[1] );
838 			VectorScale( stand.axis[2], 1.5, stand.axis[2] );
839 
840 //----(SA)	modified
841 			if ( cent->currentState.frame ) {
842 				CG_PositionEntityOnTag( &ent, &stand, va( "tag_stand%d", cent->currentState.frame ), 0, NULL );
843 			} else {
844 				CG_PositionEntityOnTag( &ent, &stand, "tag_stand", 0, NULL );
845 			}
846 //----(SA)	end
847 
848 			VectorCopy( ent.origin, ent.oldorigin );
849 			ent.nonNormalizedAxes = qtrue;
850 
851 		} else {                                // then default to laying it on it's side
852 			if ( !cg_items[es->modelindex].models[2] ) {
853 				cent->lerpAngles[2] += 90;
854 			}
855 
856 			AnglesToAxis( cent->lerpAngles, ent.axis );
857 
858 			// increase the size of the weapons when they are presented as items
859 			VectorScale( ent.axis[0], 1.5, ent.axis[0] );
860 			VectorScale( ent.axis[1], 1.5, ent.axis[1] );
861 			VectorScale( ent.axis[2], 1.5, ent.axis[2] );
862 			ent.nonNormalizedAxes = qtrue;
863 
864 			VectorCopy( cent->lerpOrigin, ent.origin );
865 			VectorCopy( cent->lerpOrigin, ent.oldorigin );
866 
867 			if ( es->eFlags & EF_SPINNING ) {  // spinning will override the angles set by a stand
868 				if ( es->groundEntityNum == -1 || !es->groundEntityNum ) { // (SA) spinning with a stand will spin the stand and the attached weap (only when in the air)
869 					VectorCopy( cg.autoAnglesSlow, cent->lerpAngles );
870 					VectorCopy( cg.autoAnglesSlow, cent->lastLerpAngles );
871 				} else {
872 					VectorCopy( cent->lastLerpAngles, cent->lerpAngles );   // make a tossed weapon sit on the ground in a position that matches how it was yawed
873 				}
874 			}
875 		}
876 
877 	} else {
878 		AnglesToAxis( cent->lerpAngles, ent.axis );
879 		VectorCopy( cent->lerpOrigin, ent.origin );
880 		VectorCopy( cent->lerpOrigin, ent.oldorigin );
881 
882 		if ( es->eFlags & EF_SPINNING ) {  // spinning will override the angles set by a stand
883 			VectorCopy( cg.autoAnglesSlow, cent->lerpAngles );
884 			AxisCopy( cg.autoAxisSlow, ent.axis );
885 		}
886 	}
887 
888 
889 	if ( es->modelindex2 ) {   // modelindex2 was specified for the ent, meaning it probably has an alternate model (as opposed to the one in the itemlist)
890 							   // try to load it first, and if it fails, default to the itemlist model
891 		ent.hModel = cgs.gameModels[ es->modelindex2 ];
892 	} else {
893 		if ( item->giType == IT_WEAPON && cg_items[es->modelindex].models[2] ) {	// check if there's a specific model for weapon pickup placement
894 			ent.hModel = cg_items[es->modelindex].models[2];
895 		} else if ( item->giType == IT_HEALTH || item->giType == IT_AMMO || item->giType == IT_POWERUP ) {
896 			if ( es->density < ( 1 << 9 ) ) {  // (10 bits of data transmission for density)
897 				ent.hModel = cg_items[es->modelindex].models[es->density];  // multi-state powerups store their state in 'density'
898 			} else {
899 				ent.hModel = cg_items[es->modelindex].models[0];
900 			}
901 		} else {
902 			ent.hModel = cg_items[es->modelindex].models[0];
903 		}
904 	}
905 
906 	//----(SA)	find midpoint for highlight corona.
907 	//			Can't do it when item is registered since it wouldn't know about replacement model
908 	if ( !( cent->usehighlightOrigin ) ) {
909 		vec3_t mins, maxs, offset;
910 		int i;
911 
912 		trap_R_ModelBounds( ent.hModel, mins, maxs );           // get bounds
913 
914 		for ( i = 0 ; i < 3 ; i++ ) {
915 			offset[i] = mins[i] + 0.5 * ( maxs[i] - mins[i] );  // find object-space center
916 		}
917 
918 		VectorCopy( cent->lerpOrigin, cent->highlightOrigin );        // set 'midpoint' to origin
919 
920 		for ( i = 0 ; i < 3 ; i++ ) {                           // adjust midpoint by offset and orientation
921 			cent->highlightOrigin[i] += offset[0] * ent.axis[0][i] +
922 										offset[1] * ent.axis[1][i] +
923 										offset[2] * ent.axis[2][i];
924 		}
925 
926 		cent->usehighlightOrigin = qtrue;
927 	}
928 
929 	// items without glow textures need to keep a minimum light value so they are always visible
930 //	if ( ( item->giType == IT_WEAPON ) || ( item->giType == IT_ARMOR ) ) {
931 	ent.renderfx |= RF_MINLIGHT;
932 //	}
933 
934 	// highlighting items the player looks at
935 	if ( cg_drawCrosshairPickups.integer ) {
936 
937 
938 		if ( cg_drawCrosshairPickups.integer == 2 ) {  // '2' is 'force highlights'
939 			highlight = qtrue;
940 		}
941 
942 		if ( CG_PlayerSeesItem( &cg.predictedPlayerState, es, cg.time, item->giType ) ) {
943 			highlight = qtrue;
944 
945 			if ( item->giType == IT_TREASURE ) {
946 				trap_R_AddCoronaToScene( cent->highlightOrigin, 1, 0.85, 0.5, 2, cent->currentState.number, qtrue ); //----(SA)	add corona to treasure
947 			}
948 		} else {
949 			if ( item->giType == IT_TREASURE ) {
950 				trap_R_AddCoronaToScene( cent->highlightOrigin, 1, 0.85, 0.5, 2, cent->currentState.number, qfalse );    //----(SA)	"empty corona" for proper fades
951 			}
952 		}
953 
954 //----(SA)	added fixed item highlight fading
955 
956 		if ( highlight ) {
957 			if ( !cent->highlighted ) {
958 				cent->highlighted = qtrue;
959 				cent->highlightTime = cg.time;
960 			}
961 			ent.hilightIntensity = ( ( cg.time - cent->highlightTime ) / 250.0f ) * highlightFadeScale;  // .25 sec to brighten up
962 		} else {
963 			if ( cent->highlighted ) {
964 				cent->highlighted = qfalse;
965 				cent->highlightTime = cg.time;
966 			}
967 			ent.hilightIntensity = 1.0f - ( ( cg.time - cent->highlightTime ) / 1000.0f ) * highlightFadeScale; // 1 sec to dim down (diff in time causes problems if you quickly flip to/away from looking at the item)
968 		}
969 
970 		if ( ent.hilightIntensity < 0.25f ) {   // leave a minlight
971 			ent.hilightIntensity = 0.25f;
972 		}
973 		if ( ent.hilightIntensity > 1 ) {
974 			ent.hilightIntensity = 1.0;
975 		}
976 	}
977 //----(SA)	end
978 
979 
980 	// add to refresh list
981 	trap_R_AddRefEntityToScene( &ent );
982 }
983 
984 //============================================================================
985 
986 /*
987 ===============
988 CG_Smoker
989 ===============
990 */
CG_Smoker(centity_t * cent)991 static void CG_Smoker( centity_t *cent ) {
992 	// this ent has some special setting up
993 	// time = speed
994 	// time2 = duration
995 	// angles2[0] = start_size
996 	// angles2[1] = end_size
997 	// angles2[2] = wait
998 	// dl_intensity = health
999 	// constantLight = delay
1000 	// origin2 = normal to emit particles along
1001 
1002 	if ( cg.time - cent->highlightTime > cent->currentState.constantLight ) {
1003 		// FIXME: make this framerate independant?
1004 		cent->highlightTime = cg.time;  // fire a particle this frame
1005 
1006 		if ( cent->currentState.density == 3 ) { // cannon
1007 			CG_ParticleSmoke( cgs.media.smokePuffShaderdirty, cent );
1008 		} else if ( !( cent->currentState.density ) ) {
1009 			CG_ParticleSmoke( cgs.media.smokePuffShader, cent );
1010 		} else {
1011 			CG_ParticleSmoke( cgs.media.smokePuffShader, cent );
1012 		}
1013 	}
1014 
1015 	cent->lastTrailTime = cg.time;  // time we were last received at the client
1016 }
1017 
1018 /*
1019 ===============
1020 CG_Missile
1021 ===============
1022 */
1023 
1024 extern void CG_RocketTrail( centity_t *ent, const weaponInfo_t *wi );
1025 
CG_Missile(centity_t * cent)1026 static void CG_Missile( centity_t *cent ) {
1027 	refEntity_t ent;
1028 	entityState_t       *s1;
1029 	const weaponInfo_t      *weapon;
1030 
1031 	s1 = &cent->currentState;
1032 	if ( s1->weapon >= WP_NUM_WEAPONS ) {
1033 		s1->weapon = 0;
1034 	}
1035 	weapon = &cg_weapons[s1->weapon];
1036 
1037 	// calculate the axis
1038 	VectorCopy( s1->angles, cent->lerpAngles );
1039 
1040 	// add trails
1041 	if ( cent->currentState.eType == ET_FP_PARTS
1042 		 || cent->currentState.eType == ET_FIRE_COLUMN
1043 		 || cent->currentState.eType == ET_FIRE_COLUMN_SMOKE
1044 		 || cent->currentState.eType == ET_RAMJET ) {
1045 		CG_RocketTrail( cent, NULL );
1046 	} else if ( weapon->missileTrailFunc ) {
1047 		weapon->missileTrailFunc( cent, weapon );
1048 	}
1049 
1050 	// add dynamic light
1051 	if ( weapon->missileDlight ) {
1052 		trap_R_AddLightToScene( cent->lerpOrigin, weapon->missileDlight,
1053 								weapon->missileDlightColor[0], weapon->missileDlightColor[1], weapon->missileDlightColor[2], 0 );
1054 	}
1055 
1056 //----(SA)	whoops, didn't mean to check it in with the missile flare
1057 
1058 	// add missile sound
1059 	if ( weapon->missileSound ) {
1060 		vec3_t velocity;
1061 
1062 		BG_EvaluateTrajectoryDelta( &cent->currentState.pos, cg.time, velocity );
1063 		CG_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->missileSound, 255 );
1064 	}
1065 
1066 	// DHM - Nerve :: Don't tick until armed
1067 	if ( cgs.gametype >= GT_WOLF && cent->currentState.weapon == WP_DYNAMITE ) {
1068 		if ( cent->currentState.teamNum < 4 ) {
1069 			vec3_t velocity;
1070 
1071 			BG_EvaluateTrajectoryDelta( &cent->currentState.pos, cg.time, velocity );
1072 			CG_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->spindownSound, 255 );
1073 		}
1074 	}
1075 
1076 	// create the render entity
1077 	memset( &ent, 0, sizeof( ent ) );
1078 	VectorCopy( cent->lerpOrigin, ent.origin );
1079 	VectorCopy( cent->lerpOrigin, ent.oldorigin );
1080 
1081 //----(SA) removed plasma gun code as sp5 is taking that spot
1082 
1083 	// flicker between two skins
1084 	ent.skinNum = cg.clientFrame & 1;
1085 
1086 	if ( cent->currentState.eType == ET_FP_PARTS ) {
1087 		ent.hModel = cgs.gameModels[cent->currentState.modelindex];
1088 	} else if ( cent->currentState.eType == ET_EXPLO_PART )     {
1089 		ent.hModel = cgs.gameModels[cent->currentState.modelindex];
1090 	} else if ( cent->currentState.eType == ET_FLAMEBARREL ) {
1091 		ent.hModel = cgs.media.flamebarrel;
1092 	} else if ( cent->currentState.eType == ET_FIRE_COLUMN || cent->currentState.eType == ET_FIRE_COLUMN_SMOKE ) {
1093 		// it may have a model sometime in the future
1094 		ent.hModel = 0;
1095 	} else if ( cent->currentState.eType == ET_RAMJET ) {
1096 		ent.hModel = 0;
1097 	}
1098 	// ent.hModel = cgs.gameModels[cent->currentState.modelindex];
1099 	else {
1100 		ent.hModel = weapon->missileModel;
1101 	}
1102 	ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;
1103 
1104 	// convert direction of travel into axis
1105 	if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
1106 		ent.axis[0][2] = 1;
1107 	}
1108 
1109 	// spin as it moves
1110 	if ( s1->pos.trType != TR_STATIONARY ) {
1111 		RotateAroundDirection( ent.axis, cg.time / 4 );
1112 	} else {
1113 		RotateAroundDirection( ent.axis, s1->time );
1114 	}
1115 
1116 	// Rafael
1117 	// Added this since it may be a propExlosion
1118 	if ( ent.hModel ) {
1119 		// add to refresh list, possibly with quad glow
1120 		CG_AddRefEntityWithPowerups( &ent, s1->powerups, TEAM_FREE, s1, vec3_origin );
1121 	}
1122 
1123 }
1124 
1125 /*
1126 ===============
1127 CG_Bat
1128 
1129   RF, a bat now is actually a spirit
1130 ===============
1131 */
CG_Bat(centity_t * cent)1132 static void CG_Bat( centity_t *cent ) {
1133 	CG_ParticleBat( cent );
1134 	CG_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.batsFlyingLoopSound, 5 );
1135 }
1136 
1137 static animation_t grabberAnims[6];
1138 static animation_t footlockerAnims[3];
1139 static animation_t multi_flagpoleAnims[9];
1140 
1141 typedef struct {
1142 	char *name;                 // for QVM this cannot be char name[MAX_QPATH];
1143 	int firstFrame;
1144 	int numFrames;
1145 	int loopFrames;             // 0 to numFrames
1146 	int frameLerp;              // msec between frames
1147 	int initialLerp;            // msec to get to first frame
1148 	int moveSpeed;
1149 	int animBlend;              // take this long to blend to next anim
1150 	//
1151 	// derived
1152 	//
1153 	int duration;
1154 	int nameHash;
1155 	int flags;
1156 	int movetype;
1157 } animationInline_t;
1158 
CG_InlineAnimToFullAnim(const animationInline_t * inl,animation_t * anim)1159 static void CG_InlineAnimToFullAnim( const animationInline_t *inl, animation_t *anim ) {
1160 	Com_Memset( anim, 0, sizeof ( animation_t ) );
1161 	anim->firstFrame = inl->firstFrame;
1162 	anim->numFrames = inl->numFrames;
1163 	anim->loopFrames = inl->loopFrames;
1164 	anim->frameLerp = inl->frameLerp;
1165 	anim->initialLerp = inl->initialLerp;
1166 }
1167 
1168 //----(SA)	animation_t struct changed, so changes are to keep this working
1169 static animationInline_t grabberAnimsInline[6] = {
1170 	{"", 0,  6,  6,  1000 / 5,     1000 / 5  },  // (main idle)
1171 	{"", 5,  21, 21, 1000 / 7,     1000 / 7  },  // (random idle)
1172 	{"", 25, 11, 0,  1000 / 15,    1000 / 15 },  // (attack big swipe)
1173 	{"", 35, 16, 0,  1000 / 15,    1000 / 15 },  // (attack small swipe)
1174 	{"", 50, 16, 0,  1000 / 15,    1000 / 15 },  // (attack grab)
1175 	{"", 66, 1,  0,  1000 / 15,    1000 / 15 }   // (starting position)
1176 };
1177 
1178 //----(SA)	added
1179 static animationInline_t footlockerAnimsInline[3] = {
1180 	{"", 0,  1,  1,  1000 / 5,     1000 / 5  },  // (main idle)
1181 	{"", 0,  5,  5,  1000 / 5,     1000 / 5  },  // (lock rattle)
1182 	{"", 5,  6,  0,  1000 / 5,     1000 / 5  }   // (break open)
1183 };
1184 
1185 //----(SA)	end
1186 
1187 // DHM - Nerve :: capture and hold flag
1188 
1189 static animationInline_t multi_flagpoleAnimsInline[9] = {
1190 	{"", 0,      1,      0,      1000 / 15,    1000 / 15 },  // (no flags, idle)
1191 	{"", 0,      15,     0,      1000 / 15,    1000 / 15 },  // (axis flag rising)
1192 	{"", 490,    15,     0,      1000 / 15,    1000 / 15 },  // (american flag rising)
1193 	{"", 20,     211,    211,    1000 / 15,    1000 / 15 },  // (axis flag raised)
1194 	{"", 255,    211,    211,    1000 / 15,    1000 / 15 },  // (american flag raised)
1195 	{"", 235,    15,     0,      1000 / 15,    1000 / 15 },  // (axis switching to american)
1196 	{"", 470,    15,     0,      1000 / 15,    1000 / 15 },  // (american switching to axis)
1197 	{"", 510,    15,     0,      1000 / 15,    1000 / 15 },  // (axis flag falling)
1198 	{"", 530,    15,     0,      1000 / 15,    1000 / 15 }   // (american flag falling)
1199 };
1200 
1201 // dhm - end
1202 
1203 /*
1204 ===============
1205 CG_InitTrapAnimations
1206 ===============
1207 */
CG_InitTrapAnimations(void)1208 void CG_InitTrapAnimations( void ) {
1209 	int i;
1210 
1211 	for ( i = 0; i < ARRAY_LEN(grabberAnims); i++ ) {
1212 		CG_InlineAnimToFullAnim( &grabberAnimsInline[i], &grabberAnims[i] );
1213 	}
1214 	for ( i = 0; i < ARRAY_LEN(footlockerAnims); i++ ) {
1215 		CG_InlineAnimToFullAnim( &footlockerAnimsInline[i], &footlockerAnims[i] );
1216 	}
1217 	for ( i = 0; i < ARRAY_LEN(multi_flagpoleAnims); i++ ) {
1218 		CG_InlineAnimToFullAnim( &multi_flagpoleAnimsInline[i], &multi_flagpoleAnims[i] );
1219 	}
1220 }
1221 
1222 extern void CG_RunLerpFrame( clientInfo_t *ci, lerpFrame_t *lf, int newAnimation, float speedScale );
1223 
1224 
1225 /*
1226 ==============
1227 CG_TrapSetAnim
1228 ==============
1229 */
CG_TrapSetAnim(centity_t * cent,lerpFrame_t * lf,int newAnim)1230 static void CG_TrapSetAnim( centity_t *cent, lerpFrame_t *lf, int newAnim ) {
1231 	// transition animation
1232 	lf->animationNumber = cent->currentState.frame;
1233 
1234 	if ( 0 ) {
1235 		lf->animation = &grabberAnims[cent->currentState.frame];
1236 	} else {
1237 		lf->animation = &footlockerAnims[cent->currentState.frame];
1238 	}
1239 
1240 	// DHM - Nerve :: teamNum specifies which set of animations to use (only 1 exists right now)
1241 	if ( cgs.gametype >= GT_WOLF ) {
1242 		switch ( cent->currentState.teamNum ) {
1243 
1244 		case 1:
1245 			lf->animation = &multi_flagpoleAnims[ cent->currentState.frame ];
1246 			break;
1247 		default:
1248 			// Keep what was set above
1249 			break;
1250 		}
1251 	}
1252 	// dhm - end
1253 
1254 	lf->animationTime = lf->frameTime + lf->animation->initialLerp;
1255 }
1256 
1257 /*
1258 ==============
1259 CG_Trap
1260 	// TODO: change from 'trap' to something else.  'trap' is a misnomer.  it's actually used for other stuff too
1261 ==============
1262 */
CG_Trap(centity_t * cent)1263 static void CG_Trap( centity_t *cent ) {
1264 	refEntity_t ent;
1265 	entityState_t       *cs;
1266 	lerpFrame_t         *traplf;
1267 
1268 	memset( &ent, 0, sizeof( ent ) );
1269 
1270 	cs = &cent->currentState;
1271 
1272 	traplf = &cent->lerpFrame;
1273 
1274 	// initial setup
1275 	if ( !traplf->oldFrameTime ) {
1276 		traplf->frameTime       =
1277 			traplf->oldFrameTime    = cg.time;
1278 
1279 		CG_TrapSetAnim( cent, traplf, cs->frame );
1280 
1281 		traplf->frame           =
1282 			traplf->oldFrame        = traplf->animation->firstFrame;
1283 	}
1284 
1285 	// transition to new anim if requested
1286 	if ( ( traplf->animationNumber != cs->frame ) || !traplf->animation ) {
1287 		CG_TrapSetAnim( cent, traplf, cs->frame );
1288 	}
1289 
1290 	CG_RunLerpFrame( NULL, traplf, 0, 1 );    // use existing lerp code rather than re-writing
1291 
1292 	ent.frame       = traplf->frame;
1293 	ent.oldframe    = traplf->oldFrame;
1294 	ent.backlerp    = traplf->backlerp;
1295 
1296 	VectorCopy( cent->lerpOrigin, ent.origin );
1297 	VectorCopy( cent->lerpOrigin, ent.oldorigin );
1298 
1299 	ent.hModel = cgs.gameModels[cs->modelindex];
1300 
1301 	AnglesToAxis( cent->lerpAngles, ent.axis );
1302 
1303 	trap_R_AddRefEntityToScene( &ent );
1304 
1305 	memcpy( &cent->refEnt, &ent, sizeof( refEntity_t ) );
1306 }
1307 //----(SA)	end
1308 
1309 
1310 /*
1311 ==============
1312 CG_Corona
1313 ==============
1314 */
CG_Corona(centity_t * cent)1315 static void CG_Corona( centity_t *cent ) {
1316 	trace_t tr;
1317 	int r, g, b;
1318 	int dli;
1319 	qboolean visible = qfalse,
1320 			 behind = qfalse,
1321 			 toofar = qfalse;
1322 
1323 	float dot, dist;
1324 	vec3_t dir;
1325 
1326 	if ( cg_coronas.integer == 0 ) {   // if set to '0' no coronas
1327 		return;
1328 	}
1329 
1330 	dli = cent->currentState.dl_intensity;
1331 	r = dli & 255;
1332 	g = ( dli >> 8 ) & 255;
1333 	b = ( dli >> 16 ) & 255;
1334 
1335 	// only coronas that are in your PVS are being added
1336 
1337 	VectorSubtract( cg.refdef.vieworg, cent->lerpOrigin, dir );
1338 
1339 	dist = VectorNormalize2( dir, dir );
1340 	if ( dist > cg_coronafardist.integer ) {   // performance variable cg_coronafardist will keep down super long traces
1341 		toofar = qtrue;
1342 	}
1343 
1344 	dot = DotProduct( dir, cg.refdef.viewaxis[0] );
1345 	if ( dot >= -0.6 ) {     // assumes ~90 deg fov	(SA) changed value to 0.6 (screen corner at 90 fov)
1346 		behind = qtrue;     // use the dot to at least do trivial removal of those behind you.
1347 	}
1348 	// yeah, I could calc side planes to clip against, but would that be worth it? (much better than dumb dot>= thing?)
1349 
1350 //	CG_Printf("dot: %f\n", dot);
1351 
1352 	if ( cg_coronas.integer == 2 ) {   // if set to '2' trace everything
1353 		behind = qfalse;
1354 		toofar = qfalse;
1355 	}
1356 
1357 
1358 	if ( !behind && !toofar ) {
1359 		CG_Trace( &tr, cg.refdef.vieworg, NULL, NULL, cent->lerpOrigin, -1, MASK_SOLID | CONTENTS_BODY ); // added blockage by players.  not sure how this is going to be since this is their bb, not their model (too much blockage)
1360 
1361 		if ( tr.fraction == 1 ) {
1362 			visible = qtrue;
1363 		}
1364 
1365 		trap_R_AddCoronaToScene( cent->lerpOrigin, (float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f, (float)cent->currentState.density / 255.0f, cent->currentState.number, visible );
1366 	}
1367 }
1368 
1369 
1370 /*
1371 ==============
1372 CG_Efx
1373 ==============
1374 */
CG_Efx(centity_t * cent)1375 static void CG_Efx( centity_t *cent ) {
1376 	int i;
1377 	float rnd;
1378 	trace_t trace;
1379 	vec3_t perpvec;
1380 	vec3_t stickPoint;
1381 	float movePerUpdate;
1382 
1383 	if ( cent->currentState.eType == ET_EF_TESLA ) {
1384 		rnd = cent->currentState.angles2[0];
1385 
1386 		for ( i = 0; i < MAX_TESLA_BOLTS; i++ ) {
1387 			if ( cent->boltTimes[i] < cg.time ) {
1388 				VectorSet( cent->boltLocs[i], crandom(), crandom(), crandom() );
1389 				VectorNormalize2( cent->boltLocs[i], cent->boltLocs[i] );
1390 				VectorMA( cent->currentState.origin2, rnd, cent->boltLocs[i], cent->boltLocs[i] );
1391 
1392 				cent->boltTimes[i] = cg.time + rand() % cent->currentState.time2;     // hold this position for ~1 second ('stickytime' value is stored in .time2)
1393 
1394 				// cut the bolt short if it collides w/ something
1395 				CG_Trace( &trace, cent->currentState.origin, NULL, NULL, cent->boltLocs[i], -1, MASK_SOLID | CONTENTS_BODY );
1396 
1397 				if ( trace.fraction < 1 ) {
1398 					// take damage
1399 					if ( trace.entityNum != ENTITYNUM_WORLD ) {
1400 //						CG_ClientDamage(trace.entityNum, cent->currentState.number, CLDMG_TESLA);
1401 //						cg_entities[trace.entityNum].pe.teslaDamagedTime = cg.time;
1402 					}
1403 
1404 					VectorCopy( trace.endpos, cent->boltLocs[i] );
1405 
1406 					// store perpendicular vector so end can 'crawl'
1407 					PerpendicularVector( perpvec, trace.plane.normal );
1408 
1409 					RotatePointAroundVector( stickPoint, trace.plane.normal, perpvec, crandom() * 360 );
1410 
1411 					// scale it so it won't move too far with bolts that have long boltTimer's
1412 					movePerUpdate = 1.0f / (float)( cent->boltTimes[i] - cg.time );
1413 
1414 					// move a max of 64 away from the 'original' target location
1415 					VectorScale( stickPoint, movePerUpdate * trace.fraction * 64.0f, cent->boltCrawlDirs[i] );
1416 
1417 				} else {
1418 					VectorSet( cent->boltCrawlDirs[i], 0, 0, 0 );
1419 				}
1420 
1421 			}
1422 		}
1423 
1424 		for ( i = 0; i < MAX_TESLA_BOLTS; i++ ) {
1425 
1426 			if ( cent->boltCrawlDirs[0] ) {
1427 				VectorMA( cent->boltLocs[i], cent->boltTimes[i] - cg.time, cent->boltCrawlDirs[i], perpvec );
1428 			} else {
1429 				VectorCopy( cent->boltLocs[i], perpvec );
1430 			}
1431 
1432 			CG_DynamicLightningBolt(    cgs.media.lightningBoltShader,  // shader
1433 										cent->currentState.origin,      // start
1434 										perpvec,                // end
1435 										cent->currentState.density,     // numBolts
1436 										cent->currentState.frame,       // maxWidth
1437 										qtrue,      // fade
1438 										1.0,        // startAlpha
1439 										0,          // recursion
1440 										i * i * 2 );     // randseed
1441 		}
1442 
1443 		// add dlight
1444 		if ( cent->currentState.dl_intensity ) {
1445 			int r, g, b;
1446 			int dli;
1447 			int randomness = cent->currentState.time2;
1448 
1449 			if ( ( cg.time / 50 ) % ( randomness + ( cg.time % randomness ) ) == 0 ) {
1450 				dli = cent->currentState.dl_intensity;
1451 				r = dli & 255;
1452 				g = ( dli >> 8 ) & 255;
1453 				b = ( dli >> 16 ) & 255;
1454 				trap_R_AddLightToScene( cent->currentState.origin, cent->currentState.time, (float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f, 0 );
1455 			}
1456 		}
1457 	} else if ( cent->currentState.eType == ET_EF_SPOTLIGHT )     {
1458 
1459 		vec3_t targetpos, normalized_direction, direction;
1460 		float dist, fov = 90;
1461 		vec4_t color = {1, 1, 1, .1};
1462 		int splinetarget = 0;
1463 		char    *cs;
1464 
1465 		VectorCopy( cent->currentState.origin2, targetpos );
1466 
1467 		splinetarget = cent->overheatTime;
1468 
1469 		if ( !splinetarget ) {
1470 			cs = (char *)CG_ConfigString( CS_SPLINES + cent->currentState.density );
1471 			cent->overheatTime = splinetarget = CG_LoadCamera( va( "cameras/%s.camera", cs ) );
1472 			if ( splinetarget != -1 ) {
1473 				trap_startCamera( splinetarget, cg.time );
1474 			}
1475 		} else {
1476 			vec3_t angles;
1477 			if ( splinetarget != -1 ) {
1478 				if ( trap_getCameraInfo( splinetarget, cg.time, &targetpos, &angles, &fov ) ) {
1479 
1480 				} else {    // loop
1481 					trap_startCamera( splinetarget, cg.time );
1482 					trap_getCameraInfo( splinetarget, cg.time, &targetpos, &angles, &fov );
1483 				}
1484 			}
1485 		}
1486 
1487 
1488 		normalized_direction[0] = direction[0] = targetpos[0] - cent->currentState.origin[0];
1489 		normalized_direction[1] = direction[1] = targetpos[1] - cent->currentState.origin[1];
1490 		normalized_direction[2] = direction[2] = targetpos[2] - cent->currentState.origin[2];
1491 
1492 		dist = VectorNormalize( normalized_direction );
1493 
1494 		if ( dist == 0 ) {
1495 			return;
1496 		}
1497 
1498 		CG_Spotlight( cent, color, cent->currentState.origin, normalized_direction, 999, 2048, 10, fov, 0 );
1499 	}
1500 }
1501 
1502 
1503 //----(SA) adding func_explosive
1504 
1505 /*
1506 ===============
1507 CG_Explosive
1508 	This is currently almost exactly the same as CG_Mover
1509 	It's split out so that any changes or experiments are
1510 	unattached to anything else.
1511 ===============
1512 */
CG_Explosive(centity_t * cent)1513 static void CG_Explosive( centity_t *cent ) {
1514 	refEntity_t ent;
1515 	entityState_t       *s1;
1516 
1517 	s1 = &cent->currentState;
1518 
1519 
1520 	// create the render entity
1521 	memset( &ent, 0, sizeof( ent ) );
1522 //	VectorCopy( cent->lerpOrigin, ent.origin);
1523 	VectorCopy( cent->lerpOrigin, ent.oldorigin );
1524 //	VectorCopy( ent.origin, cent->lerpOrigin);
1525 	AnglesToAxis( cent->lerpAngles, ent.axis );
1526 
1527 	ent.renderfx = RF_NOSHADOW;
1528 
1529 	// get the model, either as a bmodel or a modelindex
1530 	if ( s1->solid == SOLID_BMODEL ) {
1531 		ent.hModel = cgs.inlineDrawModel[s1->modelindex];
1532 	} else {
1533 		ent.hModel = cgs.gameModels[s1->modelindex];
1534 	}
1535 
1536 	// add to refresh list
1537 	// trap_R_AddRefEntityToScene(&ent);
1538 
1539 	// add the secondary model
1540 	if ( s1->modelindex2 ) {
1541 		ent.skinNum = 0;
1542 		ent.hModel = cgs.gameModels[s1->modelindex2];
1543 		trap_R_AddRefEntityToScene( &ent );
1544 	} else {
1545 		trap_R_AddRefEntityToScene( &ent );
1546 	}
1547 
1548 }
1549 
1550 //----(SA) done
1551 
1552 // declaration for add bullet particles (might as well stick this one in a .h file I think)
1553 extern void CG_AddBulletParticles( vec3_t origin, vec3_t dir, int speed, int duration, int count, float randScale );
1554 
1555 /*
1556 ===============
1557 CG_Mover
1558 ===============
1559 */
CG_Mover(centity_t * cent)1560 static void CG_Mover( centity_t *cent ) {
1561 	refEntity_t ent;
1562 	entityState_t       *s1;
1563 
1564 	s1 = &cent->currentState;
1565 
1566 	// create the render entity
1567 	memset( &ent, 0, sizeof( ent ) );
1568 
1569 	VectorCopy( cent->lerpOrigin, ent.origin );
1570 	VectorCopy( cent->lerpOrigin, ent.oldorigin );
1571 
1572 	AnglesToAxis( cent->lerpAngles, ent.axis );
1573 
1574 	ent.renderfx = RF_NOSHADOW;
1575 
1576 	// flicker between two skins (FIXME?)
1577 	ent.skinNum = 0;
1578 
1579 	// get the model, either as a bmodel or a modelindex
1580 	if ( s1->solid == SOLID_BMODEL ) {
1581 		ent.hModel = cgs.inlineDrawModel[s1->modelindex];
1582 	} else {
1583 		ent.hModel = cgs.gameModels[s1->modelindex];
1584 	}
1585 
1586 	// Rafael
1587 	//  testing for mike to get movers to scale
1588 	if ( cent->currentState.density == ET_MOVERSCALED ) {
1589 		VectorScale( ent.axis[0], cent->currentState.angles2[0], ent.axis[0] );
1590 		VectorScale( ent.axis[1], cent->currentState.angles2[1], ent.axis[1] );
1591 		VectorScale( ent.axis[2], cent->currentState.angles2[2], ent.axis[2] );
1592 		ent.nonNormalizedAxes = qtrue;
1593 	}
1594 
1595 
1596 //----(SA)	added
1597 	if ( cent->currentState.eType == ET_ALARMBOX ) {
1598 		ent.renderfx |= RF_MINLIGHT;
1599 	}
1600 //----(SA)	end
1601 
1602 
1603 	// add the secondary model
1604 	if ( s1->modelindex2 ) {
1605 		ent.skinNum = 0;
1606 		ent.hModel = cgs.gameModels[s1->modelindex2];
1607 		ent.frame = s1->frame;
1608 		trap_R_AddRefEntityToScene( &ent );
1609 		memcpy( &cent->refEnt, &ent, sizeof( refEntity_t ) );
1610 	} else {
1611 		trap_R_AddRefEntityToScene( &ent );
1612 	}
1613 
1614 	// add propeller and sfx to me109
1615 	if ( cent->currentState.density == 7 || cent->currentState.density == 8 ) {
1616 		refEntity_t propeller;
1617 		vec3_t angles;
1618 
1619 		memset( &propeller, 0, sizeof( propeller ) );
1620 		VectorCopy( ent.lightingOrigin, propeller.lightingOrigin );
1621 		propeller.shadowPlane = ent.shadowPlane;
1622 		propeller.renderfx = ent.renderfx;
1623 
1624 		propeller.hModel = propellerModel;
1625 
1626 		angles[PITCH] = cg.time % 16;
1627 
1628 		AnglesToAxis( angles, propeller.axis );
1629 
1630 		CG_PositionRotatedEntityOnTag( &propeller, &ent, "tag_prop" );
1631 
1632 		trap_R_AddRefEntityToScene( &propeller );
1633 
1634 		if ( cent->currentState.density == 8 ) {
1635 			refEntity_t flash;
1636 			vec3_t angles;
1637 
1638 			angles[YAW] = 90;
1639 			angles[ROLL] = random() * 90;
1640 
1641 			memset( &flash, 0, sizeof( flash ) );
1642 			flash.renderfx = ent.shadowPlane;
1643 			//flash.hModel = cgs.media.mg42muzzleflash;
1644 			flash.hModel = cgs.media.planemuzzleflash;
1645 
1646 			AnglesToAxis( angles, flash.axis );
1647 			CG_PositionRotatedEntityOnTag( &flash, &ent, "tag_gun1" );
1648 
1649 			trap_R_AddRefEntityToScene( &flash );
1650 			trap_R_AddLightToScene( flash.origin, 200 + ( rand() & 31 ),1.0, 0.6, 0.23, 0 );
1651 
1652 			memset( &flash, 0, sizeof( flash ) );
1653 			flash.renderfx = ent.shadowPlane;
1654 			//flash.hModel = cgs.media.mg42muzzleflash;
1655 			flash.hModel = cgs.media.planemuzzleflash;
1656 
1657 			AnglesToAxis( angles, flash.axis );
1658 			CG_PositionRotatedEntityOnTag( &flash, &ent, "tag_gun02" );
1659 
1660 			trap_R_AddRefEntityToScene( &flash );
1661 			trap_R_AddLightToScene( flash.origin, 200 + ( rand() & 31 ),1.0, 0.6, 0.23, 0 );
1662 		}
1663 	}
1664 
1665 	// alarm box spark effects
1666 
1667 	if ( cent->currentState.eType == ET_ALARMBOX ) {
1668 		if ( cent->currentState.frame == 2 ) {    // i'm dead
1669 			if ( rand() % 50 == 1 ) {
1670 				vec3_t angNorm;                 // normalized angles
1671 				VectorNormalize2( cent->lerpAngles, angNorm );
1672 				//		(origin, dir, speed, duration, count, 'randscale')
1673 				CG_AddBulletParticles( cent->lerpOrigin, angNorm, 2, 800, 4, 16.0f );
1674 				trap_S_StartSound( NULL, cent->currentState.number, CHAN_AUTO, cgs.media.sparkSounds[0] );
1675 			}
1676 		}
1677 	}
1678 }
1679 
1680 /*
1681 ===============
1682 CG_Beam
1683 
1684 Also called as an event
1685 ===============
1686 */
CG_Beam(centity_t * cent)1687 void CG_Beam( centity_t *cent ) {
1688 	refEntity_t ent;
1689 	entityState_t       *s1;
1690 
1691 	s1 = &cent->currentState;
1692 
1693 	// create the render entity
1694 	memset( &ent, 0, sizeof( ent ) );
1695 	VectorCopy( s1->pos.trBase, ent.origin );
1696 	VectorCopy( s1->origin2, ent.oldorigin );
1697 
1698 	AxisClear( ent.axis );
1699 	ent.reType = RT_BEAM;
1700 
1701 	ent.renderfx = RF_NOSHADOW;
1702 
1703 	// add to refresh list
1704 	trap_R_AddRefEntityToScene( &ent );
1705 }
1706 
1707 
1708 /*
1709 ===============
1710 CG_Portal
1711 ===============
1712 */
CG_Portal(centity_t * cent)1713 static void CG_Portal( centity_t *cent ) {
1714 	refEntity_t ent;
1715 	entityState_t       *s1;
1716 
1717 	s1 = &cent->currentState;
1718 
1719 	// create the render entity
1720 	memset( &ent, 0, sizeof( ent ) );
1721 	VectorCopy( cent->lerpOrigin, ent.origin );
1722 	VectorCopy( s1->origin2, ent.oldorigin );
1723 	ByteToDir( s1->eventParm, ent.axis[0] );
1724 	PerpendicularVector( ent.axis[1], ent.axis[0] );
1725 
1726 	// negating this tends to get the directions like they want
1727 	// we really should have a camera roll value
1728 	VectorSubtract( vec3_origin, ent.axis[1], ent.axis[1] );
1729 
1730 	CrossProduct( ent.axis[0], ent.axis[1], ent.axis[2] );
1731 	ent.reType = RT_PORTALSURFACE;
1732 	ent.frame = s1->frame;      // rotation speed
1733 	ent.skinNum = s1->clientNum / 256.0 * 360;    // roll offset
1734 
1735 	// add to refresh list
1736 	trap_R_AddRefEntityToScene( &ent );
1737 }
1738 
1739 /*
1740 ===============
1741 CG_Prop
1742 ===============
1743 */
CG_Prop(centity_t * cent)1744 static void CG_Prop( centity_t *cent ) {
1745 	refEntity_t ent;
1746 	entityState_t       *s1;
1747 	vec3_t angles;
1748 	float scale;
1749 
1750 	s1 = &cent->currentState;
1751 
1752 	// create the render entity
1753 	memset( &ent, 0, sizeof( ent ) );
1754 
1755 	if ( cg.renderingThirdPerson ) {
1756 		VectorCopy( cent->lerpOrigin, ent.origin );
1757 		VectorCopy( cent->lerpOrigin, ent.oldorigin );
1758 
1759 		ent.frame = s1->frame;
1760 		ent.oldframe = ent.frame;
1761 		ent.backlerp = 0;
1762 	} else
1763 	{
1764 		VectorCopy( cg.refdef.vieworg, ent.origin );
1765 		VectorCopy( cg.refdefViewAngles, angles );
1766 
1767 		if ( cg.bobcycle & 1 ) {
1768 			scale = -cg.xyspeed;
1769 		} else {
1770 			scale = cg.xyspeed;
1771 		}
1772 
1773 		// modify angles from bobbing
1774 		angles[ROLL] += scale * cg.bobfracsin * 0.005;
1775 		angles[YAW] += scale * cg.bobfracsin * 0.01;
1776 		angles[PITCH] += cg.xyspeed * cg.bobfracsin * 0.005;
1777 
1778 		VectorCopy( angles, cent->lerpAngles );
1779 
1780 		ent.frame = s1->frame;
1781 		ent.oldframe = ent.frame;
1782 		ent.backlerp = 0;
1783 
1784 		if ( cent->currentState.density ) {
1785 			ent.frame = s1->frame + cent->currentState.density;
1786 			ent.oldframe = ent.frame - 1;
1787 			ent.backlerp = 1 - cg.frameInterpolation;
1788 			ent.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON;
1789 
1790 			//CG_Printf ("frame %d oldframe %d\n", ent.frame, ent.oldframe);
1791 		} else if ( ent.frame )     {
1792 			ent.oldframe -= 1;
1793 			ent.backlerp = 1 - cg.frameInterpolation;
1794 		} else
1795 		{
1796 			ent.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON;
1797 		}
1798 	}
1799 
1800 	AnglesToAxis( cent->lerpAngles, ent.axis );
1801 
1802 	ent.renderfx |= RF_NOSHADOW;
1803 
1804 	// flicker between two skins (FIXME?)
1805 	ent.skinNum = ( cg.time >> 6 ) & 1;
1806 
1807 	// get the model, either as a bmodel or a modelindex
1808 	if ( s1->solid == SOLID_BMODEL ) {
1809 		ent.hModel = cgs.inlineDrawModel[s1->modelindex];
1810 	} else {
1811 		ent.hModel = cgs.gameModels[s1->modelindex];
1812 	}
1813 
1814 	// add the secondary model
1815 	if ( s1->modelindex2 ) {
1816 		ent.skinNum = 0;
1817 		ent.hModel = cgs.gameModels[s1->modelindex2];
1818 		ent.frame = s1->frame;
1819 		trap_R_AddRefEntityToScene( &ent );
1820 		memcpy( &cent->refEnt, &ent, sizeof( refEntity_t ) );
1821 	} else {
1822 		trap_R_AddRefEntityToScene( &ent );
1823 	}
1824 
1825 }
1826 
1827 /*
1828 ================
1829 CG_CreateRotationMatrix
1830 ================
1831 */
CG_CreateRotationMatrix(vec3_t angles,vec3_t matrix[3])1832 void CG_CreateRotationMatrix(vec3_t angles, vec3_t matrix[3]) {
1833 	AngleVectors(angles, matrix[0], matrix[1], matrix[2]);
1834 	VectorInverse(matrix[1]);
1835 }
1836 
1837 /*
1838 ================
1839 CG_TransposeMatrix
1840 ================
1841 */
CG_TransposeMatrix(vec3_t matrix[3],vec3_t transpose[3])1842 void CG_TransposeMatrix(vec3_t matrix[3], vec3_t transpose[3]) {
1843 	int i, j;
1844 	for (i = 0; i < 3; i++) {
1845 		for (j = 0; j < 3; j++) {
1846 			transpose[i][j] = matrix[j][i];
1847 		}
1848 	}
1849 }
1850 
1851 /*
1852 ================
1853 CG_RotatePoint
1854 ================
1855 */
CG_RotatePoint(vec3_t point,vec3_t matrix[3])1856 void CG_RotatePoint(vec3_t point, vec3_t matrix[3]) {
1857 	vec3_t tvec;
1858 	VectorCopy(point, tvec);
1859 	point[0] = DotProduct(matrix[0], tvec);
1860 	point[1] = DotProduct(matrix[1], tvec);
1861 	point[2] = DotProduct(matrix[2], tvec);
1862 }
1863 
1864 /*
1865 =========================
1866 CG_AdjustPositionForMover
1867 
1868 Also called by client movement prediction code
1869 =========================
1870 */
CG_AdjustPositionForMover(const vec3_t in,int moverNum,int fromTime,int toTime,vec3_t out,vec3_t angles_in,vec3_t angles_out)1871 void CG_AdjustPositionForMover(const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out, vec3_t angles_in, vec3_t angles_out) {
1872 	centity_t   *cent;
1873 	vec3_t oldOrigin, origin, deltaOrigin;
1874 	vec3_t oldAngles, angles, deltaAngles;
1875 	vec3_t	matrix[3], transpose[3];
1876 	vec3_t	org, org2, move2;
1877 
1878 //	if ( outDeltaAngles ) {
1879 //		VectorClear( outDeltaAngles );
1880 //	}
1881 
1882 	if ( moverNum <= 0 || moverNum >= ENTITYNUM_MAX_NORMAL ) {
1883 		VectorCopy( in, out );
1884 		VectorCopy(angles_in, angles_out);
1885 		return;
1886 	}
1887 
1888 	cent = &cg_entities[ moverNum ];
1889 
1890 	if ( cent->currentState.eType != ET_MOVER ) {
1891 		VectorCopy( in, out );
1892 		VectorCopy(angles_in, angles_out);
1893 		return;
1894 	}
1895 
1896 	BG_EvaluateTrajectory( &cent->currentState.pos, fromTime, oldOrigin );
1897 	BG_EvaluateTrajectory( &cent->currentState.apos, fromTime, oldAngles );
1898 
1899 	BG_EvaluateTrajectory( &cent->currentState.pos, toTime, origin );
1900 	BG_EvaluateTrajectory( &cent->currentState.apos, toTime, angles );
1901 
1902 	VectorSubtract( origin, oldOrigin, deltaOrigin );
1903 	VectorSubtract( angles, oldAngles, deltaAngles );
1904 
1905 	// origin change when on a rotating object
1906 	CG_CreateRotationMatrix( deltaAngles, transpose );
1907 	CG_TransposeMatrix( transpose, matrix );
1908 	VectorSubtract( in, oldOrigin, org );
1909 	VectorCopy( org, org2 );
1910 	CG_RotatePoint( org2, matrix );
1911 	VectorSubtract( org2, org, move2 );
1912 	VectorAdd( deltaOrigin, move2, deltaOrigin );
1913 
1914 	VectorAdd( in, deltaOrigin, out );
1915 //	if ( outDeltaAngles ) {
1916 //		VectorCopy( deltaAngles, outDeltaAngles );
1917 //	}
1918 
1919 	VectorAdd( angles_in, deltaAngles, angles_out );
1920 }
1921 
1922 
1923 /*
1924 =============================
1925 CG_InterpolateEntityPosition
1926 =============================
1927 */
CG_InterpolateEntityPosition(centity_t * cent)1928 static void CG_InterpolateEntityPosition( centity_t *cent ) {
1929 	vec3_t current, next;
1930 	float f;
1931 
1932 	// it would be an internal error to find an entity that interpolates without
1933 	// a snapshot ahead of the current one
1934 	if ( cg.nextSnap == NULL ) {
1935 		// DHM - Nerve :: FIXME? There are some cases when in Limbo mode during a map restart
1936 		//					that were tripping this error.
1937 		//CG_Error( "CG_InterpolateEntityPosition: cg.nextSnap == NULL" );
1938 		//CG_Printf("CG_InterpolateEntityPosition: cg.nextSnap == NULL");
1939 		return;
1940 	}
1941 
1942 	f = cg.frameInterpolation;
1943 
1944 	// this will linearize a sine or parabolic curve, but it is important
1945 	// to not extrapolate player positions if more recent data is available
1946 	BG_EvaluateTrajectory( &cent->currentState.pos, cg.snap->serverTime, current );
1947 	BG_EvaluateTrajectory( &cent->nextState.pos, cg.nextSnap->serverTime, next );
1948 
1949 	cent->lerpOrigin[0] = current[0] + f * ( next[0] - current[0] );
1950 	cent->lerpOrigin[1] = current[1] + f * ( next[1] - current[1] );
1951 	cent->lerpOrigin[2] = current[2] + f * ( next[2] - current[2] );
1952 
1953 	BG_EvaluateTrajectory( &cent->currentState.apos, cg.snap->serverTime, current );
1954 	BG_EvaluateTrajectory( &cent->nextState.apos, cg.nextSnap->serverTime, next );
1955 
1956 	cent->lerpAngles[0] = LerpAngle( current[0], next[0], f );
1957 	cent->lerpAngles[1] = LerpAngle( current[1], next[1], f );
1958 	cent->lerpAngles[2] = LerpAngle( current[2], next[2], f );
1959 
1960 }
1961 
1962 /*
1963 ===============
1964 CG_CalcEntityLerpPositions
1965 
1966 ===============
1967 */
CG_CalcEntityLerpPositions(centity_t * cent)1968 static void CG_CalcEntityLerpPositions( centity_t *cent ) {
1969 	if ( cent->interpolate && cent->currentState.pos.trType == TR_INTERPOLATE ) {
1970 		CG_InterpolateEntityPosition( cent );
1971 		return;
1972 	}
1973 
1974 	// NERVE - SMF - fix for jittery clients in multiplayer
1975 	if ( cgs.gametype != GT_SINGLE_PLAYER ) {
1976 		// first see if we can interpolate between two snaps for
1977 		// linear extrapolated clients
1978 		if ( cent->interpolate && cent->currentState.pos.trType == TR_LINEAR_STOP &&
1979 			 cent->currentState.number < MAX_CLIENTS ) {
1980 			CG_InterpolateEntityPosition( cent );
1981 			return;
1982 		}
1983 	}
1984 	// -NERVE - SMF
1985 
1986 	// just use the current frame and evaluate as best we can
1987 	BG_EvaluateTrajectory( &cent->currentState.pos, cg.time, cent->lerpOrigin );
1988 	BG_EvaluateTrajectory( &cent->currentState.apos, cg.time, cent->lerpAngles );
1989 
1990 	// adjust for riding a mover if it wasn't rolled into the predicted
1991 	// player state
1992 	if ( cent != &cg.predictedPlayerEntity ) {
1993 		CG_AdjustPositionForMover( cent->lerpOrigin, cent->currentState.groundEntityNum,
1994 									cg.snap->serverTime, cg.time, cent->lerpOrigin, cent->lerpAngles, cent->lerpAngles);
1995 	}
1996 }
1997 
1998 /*
1999 ===============
2000 CG_ProcessEntity
2001 ===============
2002 */
CG_ProcessEntity(centity_t * cent)2003 static void CG_ProcessEntity( centity_t *cent ) {
2004 	switch ( cent->currentState.eType ) {
2005 	default:
2006 		CG_Error( "Bad entity type: %i", cent->currentState.eType );
2007 		break;
2008 	case ET_CONCUSSIVE_TRIGGER:
2009 	case ET_CAMERA:
2010 	case ET_INVISIBLE:
2011 	case ET_PUSH_TRIGGER:
2012 	case ET_TELEPORT_TRIGGER:
2013 	case ET_OID_TRIGGER:
2014 	case ET_AI_EFFECT:
2015 	case ET_EXPLOSIVE_INDICATOR:        // NERVE - SMF
2016 		break;
2017 	case ET_SPEAKER:
2018 		CG_Speaker( cent );
2019 		break;
2020 	case ET_GAMEMODEL:
2021 		if ( !cg_drawGamemodels.integer ) {
2022 			break;
2023 		}
2024 	case ET_MG42_BARREL:
2025 	case ET_FOOTLOCKER:
2026 	case ET_GENERAL:
2027 		CG_General( cent );
2028 		break;
2029 	case ET_CORPSE:
2030 	case ET_PLAYER:
2031 		CG_Player( cent );
2032 		break;
2033 	case ET_ITEM:
2034 		CG_Item( cent );
2035 		break;
2036 	case ET_MISSILE:
2037 	case ET_FLAMEBARREL:
2038 	case ET_FP_PARTS:
2039 	case ET_FIRE_COLUMN:
2040 	case ET_FIRE_COLUMN_SMOKE:
2041 	case ET_EXPLO_PART:
2042 	case ET_RAMJET:
2043 		CG_Missile( cent );
2044 		break;
2045 	case ET_EF_TESLA:
2046 	case ET_EF_SPOTLIGHT:
2047 	case ET_EFFECT3:
2048 		CG_Efx( cent );
2049 		break;
2050 	case ET_EXPLOSIVE:
2051 		CG_Explosive( cent );
2052 		break;
2053 	case ET_TRAP:
2054 		CG_Trap( cent );
2055 		break;
2056 	case ET_ALARMBOX:
2057 	case ET_MOVER:
2058 		CG_Mover( cent );
2059 		break;
2060 	case ET_PROP:
2061 		CG_Prop( cent );
2062 		break;
2063 	case ET_BEAM:
2064 		CG_Beam( cent );
2065 		break;
2066 	case ET_PORTAL:
2067 		CG_Portal( cent );
2068 		break;
2069 	case ET_CORONA:
2070 		CG_Corona( cent );
2071 		break;
2072 	case ET_BAT:
2073 		CG_Bat( cent );
2074 		break;
2075 	case ET_SMOKER:
2076 		CG_Smoker( cent );
2077 		break;
2078 	}
2079 }
2080 
2081 /*
2082 ===============
2083 CG_AddCEntity
2084 
2085 ===============
2086 */
CG_AddCEntity(centity_t * cent)2087 static void CG_AddCEntity( centity_t *cent ) {
2088 	// event-only entities will have been dealt with already
2089 	if ( cent->currentState.eType >= ET_EVENTS ) {
2090 		return;
2091 	}
2092 
2093 	cent->processedFrame = cg.clientFrame;
2094 
2095 	// calculate the current origin
2096 	CG_CalcEntityLerpPositions( cent );
2097 
2098 	// add automatic effects
2099 	CG_EntityEffects( cent );
2100 
2101 	// call the appropriate function which will add this entity to the view accordingly
2102 	CG_ProcessEntity( cent );
2103 }
2104 
2105 /*
2106 ==================
2107 CG_AddEntityToTag
2108 ==================
2109 */
CG_AddEntityToTag(centity_t * cent)2110 static void CG_AddEntityToTag( centity_t *cent ) {
2111 	entityState_t       *s1;
2112 	centity_t           *centParent;
2113 	entityState_t       *sParent;
2114 	refEntity_t ent;
2115 	char *cs, *token = NULL;
2116 	int i, pi;
2117 	vec3_t ang;
2118 
2119 	memset( &ent, 0, sizeof( ent ) );
2120 
2121 	// event-only entities will have been dealt with already
2122 	if ( cent->currentState.eType >= ET_EVENTS ) {
2123 		return;
2124 	}
2125 
2126 	if ( cent->processedFrame == cg.clientFrame ) {
2127 		// already processed this frame
2128 		return;
2129 	}
2130 
2131 	// calculate the current origin
2132 	CG_CalcEntityLerpPositions( cent );
2133 
2134 	s1 = &cent->currentState;
2135 
2136 	// find us in the list of tagged entities
2137 	sParent = NULL;
2138 	centParent = NULL;
2139 	for ( i = CS_TAGCONNECTS + 1; i < CS_TAGCONNECTS + MAX_TAGCONNECTS; i++ ) {   // NOTE: +1 since G_FindConfigStringIndex() starts at index 1 rather than 0 (not sure why)
2140 		cs = (char *)CG_ConfigString( i );
2141 		token = COM_Parse( &cs );
2142 		if ( !token[0] ) {
2143 			break;
2144 		}
2145 		if ( atoi( token ) == s1->number ) {
2146 			token = COM_Parse( &cs );
2147 			if ( !token[0] ) {
2148 				CG_Error( "CG_EntityTagConnected: missing parameter in configstring" );
2149 			}
2150 			pi = atoi( token );
2151 			if ( pi < 0 || pi >= MAX_GENTITIES ) {
2152 				CG_Error( "CG_EntityTagConnected: parent out of range" );
2153 			}
2154 			centParent = &cg_entities[pi];
2155 			sParent = &( cg_entities[pi].currentState );
2156 			token = COM_Parse( &cs );
2157 			if ( !token[0] ) {
2158 				CG_Error( "CG_EntityTagConnected: missing parameter in configstring" );
2159 			}
2160 
2161 			// NOTE: token is now the tag name to attach to
2162 
2163 			break;
2164 		}
2165 	}
2166 
2167 	if ( !sParent ) {
2168 		CG_Error( "CG_EntityTagConnected: unable to find configstring to perform connection" );
2169 	}
2170 
2171 	// if parent isn't visible, then don't draw us
2172 	if ( !centParent->currentValid ) {
2173 		return;
2174 	}
2175 
2176 	// make sure all parents are added first
2177 	if ( centParent->processedFrame != cg.clientFrame ) {
2178 		if ( sParent->eFlags & EF_TAGCONNECT ) {
2179 			CG_AddEntityToTag( centParent );
2180 		}
2181 	}
2182 
2183 	// if there was a higher ranking parent not added to the scene, then don't add us
2184 	if ( centParent->processedFrame != cg.clientFrame ) {
2185 		return;
2186 	}
2187 
2188 	cent->processedFrame = cg.clientFrame;
2189 
2190 	// start with default axis
2191 	AnglesToAxis( vec3_origin, ent.axis );
2192 
2193 	// get the tag position from parent
2194 	CG_PositionEntityOnTag( &ent, &centParent->refEnt, token, 0, NULL );
2195 
2196 	VectorCopy( ent.origin, cent->lerpOrigin );
2197 	// we need to add the child's angles to the tag angles
2198 	if ( !cent->currentState.density ) {	// this entity should rotate with it's parent, but can turn around using it's own angles
2199 		AxisToAngles( ent.axis, ang );
2200 		VectorAdd( cent->lerpAngles, ang, cent->lerpAngles );
2201 	} else {	// face our angles exactly
2202 		BG_EvaluateTrajectory( &cent->currentState.apos, cg.time, cent->lerpAngles );
2203 	}
2204 
2205 	// add automatic effects
2206 	CG_EntityEffects( cent );
2207 
2208 	// call the appropriate function which will add this entity to the view accordingly
2209 	CG_ProcessEntity( cent );
2210 }
2211 
2212 /*
2213 ===============
2214 CG_AddPacketEntities
2215 
2216 ===============
2217 */
CG_AddPacketEntities(void)2218 void CG_AddPacketEntities( void ) {
2219 	int num;
2220 	centity_t           *cent;
2221 	playerState_t       *ps;
2222 	//int	clcount;
2223 
2224 	// set cg.frameInterpolation
2225 	if ( cg.nextSnap ) {
2226 		int delta;
2227 
2228 		delta = ( cg.nextSnap->serverTime - cg.snap->serverTime );
2229 		if ( delta == 0 ) {
2230 			cg.frameInterpolation = 0;
2231 		} else {
2232 			cg.frameInterpolation = (float)( cg.time - cg.snap->serverTime ) / delta;
2233 		}
2234 	} else {
2235 		cg.frameInterpolation = 0;  // actually, it should never be used, because
2236 									// no entities should be marked as interpolating
2237 	}
2238 
2239 	// the auto-rotating items will all have the same axis
2240 	cg.autoAnglesSlow[0] = 0;
2241 	cg.autoAnglesSlow[1] = ( cg.time & 4095 ) * 360 / 4095.0;
2242 	cg.autoAnglesSlow[2] = 0;
2243 
2244 	cg.autoAngles[0] = 0;
2245 	cg.autoAngles[1] = ( cg.time & 2047 ) * 360 / 2048.0;
2246 	cg.autoAngles[2] = 0;
2247 
2248 	cg.autoAnglesFast[0] = 0;
2249 	cg.autoAnglesFast[1] = ( cg.time & 1023 ) * 360 / 1024.0f;
2250 	cg.autoAnglesFast[2] = 0;
2251 
2252 	AnglesToAxis( cg.autoAnglesSlow, cg.autoAxisSlow );
2253 	AnglesToAxis( cg.autoAngles, cg.autoAxis );
2254 	AnglesToAxis( cg.autoAnglesFast, cg.autoAxisFast );
2255 
2256 	// generate and add the entity from the playerstate
2257 	ps = &cg.predictedPlayerState;
2258 	BG_PlayerStateToEntityState( ps, &cg.predictedPlayerEntity.currentState, qfalse );
2259 	CG_AddCEntity( &cg.predictedPlayerEntity );
2260 
2261 	// lerp the non-predicted value for lightning gun origins
2262 	CG_CalcEntityLerpPositions( &cg_entities[ cg.snap->ps.clientNum ] );
2263 
2264 	// add each entity sent over by the server
2265 
2266 	// NON TAG-CONNECTED ENTITIES
2267 	for ( num = 0 ; num < cg.snap->numEntities ; num++ ) {
2268 		cent = &cg_entities[ cg.snap->entities[ num ].number ];
2269 		if ( !( cent->currentState.eFlags & EF_TAGCONNECT ) ) {
2270 			CG_AddCEntity( cent );
2271 		}
2272 	}
2273 
2274 	// TAG-CONNECTED ENTITIES (connected to NON TAG-CONNECTED ENTITIES)
2275 	for ( num = 0 ; num < cg.snap->numEntities ; num++ ) {
2276 		cent = &cg_entities[ cg.snap->entities[ num ].number ];
2277 		if ( cent->currentState.eFlags & EF_TAGCONNECT ) {
2278 			CG_AddEntityToTag( cent );
2279 		}
2280 	}
2281 
2282 	// Ridah, add the flamethrower sounds
2283 	CG_UpdateFlamethrowerSounds();
2284 }
2285