1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 //
23 // ui_players.c
24 
25 #include "ui_local.h"
26 
27 
28 #define UI_TIMER_GESTURE		2300
29 #define UI_TIMER_JUMP			1000
30 #define UI_TIMER_LAND			130
31 #define UI_TIMER_WEAPON_SWITCH	300
32 #define UI_TIMER_ATTACK			500
33 #define	UI_TIMER_MUZZLE_FLASH	20
34 #define	UI_TIMER_WEAPON_DELAY	250
35 
36 #define JUMP_HEIGHT				56
37 
38 #define SWINGSPEED				0.3f
39 
40 #define SPIN_SPEED				0.9f
41 #define COAST_TIME				1000
42 
43 
44 static int			dp_realtime;
45 static float		jumpHeight;
46 sfxHandle_t weaponChangeSound;
47 
48 
49 /*
50 ===============
51 UI_PlayerInfo_SetWeapon
52 ===============
53 */
UI_PlayerInfo_SetWeapon(playerInfo_t * pi,weapon_t weaponNum)54 static void UI_PlayerInfo_SetWeapon( playerInfo_t *pi, weapon_t weaponNum ) {
55 	gitem_t *	item;
56 	char		path[MAX_QPATH];
57 
58 	pi->currentWeapon = weaponNum;
59 tryagain:
60 	pi->realWeapon = weaponNum;
61 	pi->weaponModel = 0;
62 	pi->barrelModel = 0;
63 	pi->flashModel = 0;
64 
65 	if ( weaponNum == WP_NONE ) {
66 		return;
67 	}
68 
69 	for ( item = bg_itemlist + 1; item->classname ; item++ ) {
70 		if ( item->giType != IT_WEAPON ) {
71 			continue;
72 		}
73 		if ( item->giTag == weaponNum ) {
74 			break;
75 		}
76 	}
77 
78 	if ( item->classname ) {
79 		pi->weaponModel = trap_R_RegisterModel( item->world_model[0] );
80 	}
81 
82 	if( pi->weaponModel == 0 ) {
83 		if( weaponNum == WP_MACHINEGUN ) {
84 			weaponNum = WP_NONE;
85 			goto tryagain;
86 		}
87 		weaponNum = WP_MACHINEGUN;
88 		goto tryagain;
89 	}
90 
91 	if ( weaponNum == WP_MACHINEGUN || weaponNum == WP_GAUNTLET || weaponNum == WP_BFG ) {
92 		strcpy( path, item->world_model[0] );
93 		COM_StripExtension(path, path, sizeof(path));
94 		strcat( path, "_barrel.md3" );
95 		pi->barrelModel = trap_R_RegisterModel( path );
96 	}
97 
98 	strcpy( path, item->world_model[0] );
99 	COM_StripExtension(path, path, sizeof(path));
100 	strcat( path, "_flash.md3" );
101 	pi->flashModel = trap_R_RegisterModel( path );
102 
103 	switch( weaponNum ) {
104 	case WP_GAUNTLET:
105 		MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 );
106 		break;
107 
108 	case WP_MACHINEGUN:
109 		MAKERGB( pi->flashDlightColor, 1, 1, 0 );
110 		break;
111 
112 	case WP_SHOTGUN:
113 		MAKERGB( pi->flashDlightColor, 1, 1, 0 );
114 		break;
115 
116 	case WP_GRENADE_LAUNCHER:
117 		MAKERGB( pi->flashDlightColor, 1, 0.7f, 0.5f );
118 		break;
119 
120 	case WP_ROCKET_LAUNCHER:
121 		MAKERGB( pi->flashDlightColor, 1, 0.75f, 0 );
122 		break;
123 
124 	case WP_LIGHTNING:
125 		MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 );
126 		break;
127 
128 	case WP_RAILGUN:
129 		MAKERGB( pi->flashDlightColor, 1, 0.5f, 0 );
130 		break;
131 
132 	case WP_PLASMAGUN:
133 		MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 );
134 		break;
135 
136 	case WP_BFG:
137 		MAKERGB( pi->flashDlightColor, 1, 0.7f, 1 );
138 		break;
139 
140 	case WP_GRAPPLING_HOOK:
141 		MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 );
142 		break;
143 
144 	default:
145 		MAKERGB( pi->flashDlightColor, 1, 1, 1 );
146 		break;
147 	}
148 }
149 
150 
151 /*
152 ===============
153 UI_ForceLegsAnim
154 ===============
155 */
UI_ForceLegsAnim(playerInfo_t * pi,int anim)156 static void UI_ForceLegsAnim( playerInfo_t *pi, int anim ) {
157 	pi->legsAnim = ( ( pi->legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
158 
159 	if ( anim == LEGS_JUMP ) {
160 		pi->legsAnimationTimer = UI_TIMER_JUMP;
161 	}
162 }
163 
164 
165 /*
166 ===============
167 UI_SetLegsAnim
168 ===============
169 */
UI_SetLegsAnim(playerInfo_t * pi,int anim)170 static void UI_SetLegsAnim( playerInfo_t *pi, int anim ) {
171 	if ( pi->pendingLegsAnim ) {
172 		anim = pi->pendingLegsAnim;
173 		pi->pendingLegsAnim = 0;
174 	}
175 	UI_ForceLegsAnim( pi, anim );
176 }
177 
178 
179 /*
180 ===============
181 UI_ForceTorsoAnim
182 ===============
183 */
UI_ForceTorsoAnim(playerInfo_t * pi,int anim)184 static void UI_ForceTorsoAnim( playerInfo_t *pi, int anim ) {
185 	pi->torsoAnim = ( ( pi->torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
186 
187 	if ( anim == TORSO_GESTURE ) {
188 		pi->torsoAnimationTimer = UI_TIMER_GESTURE;
189 	}
190 
191 	if ( anim == TORSO_ATTACK || anim == TORSO_ATTACK2 ) {
192 		pi->torsoAnimationTimer = UI_TIMER_ATTACK;
193 	}
194 }
195 
196 
197 /*
198 ===============
199 UI_SetTorsoAnim
200 ===============
201 */
UI_SetTorsoAnim(playerInfo_t * pi,int anim)202 static void UI_SetTorsoAnim( playerInfo_t *pi, int anim ) {
203 	if ( pi->pendingTorsoAnim ) {
204 		anim = pi->pendingTorsoAnim;
205 		pi->pendingTorsoAnim = 0;
206 	}
207 
208 	UI_ForceTorsoAnim( pi, anim );
209 }
210 
211 
212 /*
213 ===============
214 UI_TorsoSequencing
215 ===============
216 */
UI_TorsoSequencing(playerInfo_t * pi)217 static void UI_TorsoSequencing( playerInfo_t *pi ) {
218 	int		currentAnim;
219 
220 	currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT;
221 
222 	if ( pi->weapon != pi->currentWeapon ) {
223 		if ( currentAnim != TORSO_DROP ) {
224 			pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH;
225 			UI_ForceTorsoAnim( pi, TORSO_DROP );
226 		}
227 	}
228 
229 	if ( pi->torsoAnimationTimer > 0 ) {
230 		return;
231 	}
232 
233 	if( currentAnim == TORSO_GESTURE ) {
234 		UI_SetTorsoAnim( pi, TORSO_STAND );
235 		return;
236 	}
237 
238 	if( currentAnim == TORSO_ATTACK || currentAnim == TORSO_ATTACK2 ) {
239 		UI_SetTorsoAnim( pi, TORSO_STAND );
240 		return;
241 	}
242 
243 	if ( currentAnim == TORSO_DROP ) {
244 		UI_PlayerInfo_SetWeapon( pi, pi->weapon );
245 		pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH;
246 		UI_ForceTorsoAnim( pi, TORSO_RAISE );
247 		return;
248 	}
249 
250 	if ( currentAnim == TORSO_RAISE ) {
251 		UI_SetTorsoAnim( pi, TORSO_STAND );
252 		return;
253 	}
254 }
255 
256 
257 /*
258 ===============
259 UI_LegsSequencing
260 ===============
261 */
UI_LegsSequencing(playerInfo_t * pi)262 static void UI_LegsSequencing( playerInfo_t *pi ) {
263 	int		currentAnim;
264 
265 	currentAnim = pi->legsAnim & ~ANIM_TOGGLEBIT;
266 
267 	if ( pi->legsAnimationTimer > 0 ) {
268 		if ( currentAnim == LEGS_JUMP ) {
269 			jumpHeight = JUMP_HEIGHT * sin( M_PI * ( UI_TIMER_JUMP - pi->legsAnimationTimer ) / UI_TIMER_JUMP );
270 		}
271 		return;
272 	}
273 
274 	if ( currentAnim == LEGS_JUMP ) {
275 		UI_ForceLegsAnim( pi, LEGS_LAND );
276 		pi->legsAnimationTimer = UI_TIMER_LAND;
277 		jumpHeight = 0;
278 		return;
279 	}
280 
281 	if ( currentAnim == LEGS_LAND ) {
282 		UI_SetLegsAnim( pi, LEGS_IDLE );
283 		return;
284 	}
285 }
286 
287 
288 /*
289 ======================
290 UI_PositionEntityOnTag
291 ======================
292 */
UI_PositionEntityOnTag(refEntity_t * entity,const refEntity_t * parent,clipHandle_t parentModel,char * tagName)293 static void UI_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
294 							clipHandle_t parentModel, char *tagName ) {
295 	int				i;
296 	orientation_t	lerped;
297 
298 	// lerp the tag
299 	trap_CM_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
300 		1.0 - parent->backlerp, tagName );
301 
302 	// FIXME: allow origin offsets along tag?
303 	VectorCopy( parent->origin, entity->origin );
304 	for ( i = 0 ; i < 3 ; i++ ) {
305 		VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
306 	}
307 
308 	// cast away const because of compiler problems
309 	MatrixMultiply( lerped.axis, ((refEntity_t*)parent)->axis, entity->axis );
310 	entity->backlerp = parent->backlerp;
311 }
312 
313 
314 /*
315 ======================
316 UI_PositionRotatedEntityOnTag
317 ======================
318 */
UI_PositionRotatedEntityOnTag(refEntity_t * entity,const refEntity_t * parent,clipHandle_t parentModel,char * tagName)319 static void UI_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
320 							clipHandle_t parentModel, char *tagName ) {
321 	int				i;
322 	orientation_t	lerped;
323 	vec3_t			tempAxis[3];
324 
325 	// lerp the tag
326 	trap_CM_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
327 		1.0 - parent->backlerp, tagName );
328 
329 	// FIXME: allow origin offsets along tag?
330 	VectorCopy( parent->origin, entity->origin );
331 	for ( i = 0 ; i < 3 ; i++ ) {
332 		VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
333 	}
334 
335 	// cast away const because of compiler problems
336 	MatrixMultiply( entity->axis, ((refEntity_t *)parent)->axis, tempAxis );
337 	MatrixMultiply( lerped.axis, tempAxis, entity->axis );
338 }
339 
340 
341 /*
342 ===============
343 UI_SetLerpFrameAnimation
344 ===============
345 */
UI_SetLerpFrameAnimation(playerInfo_t * ci,lerpFrame_t * lf,int newAnimation)346 static void UI_SetLerpFrameAnimation( playerInfo_t *ci, lerpFrame_t *lf, int newAnimation ) {
347 	animation_t	*anim;
348 
349 	lf->animationNumber = newAnimation;
350 	newAnimation &= ~ANIM_TOGGLEBIT;
351 
352 	if ( newAnimation < 0 || newAnimation >= MAX_ANIMATIONS ) {
353 		trap_Error( va("Bad animation number: %i", newAnimation) );
354 	}
355 
356 	anim = &ci->animations[ newAnimation ];
357 
358 	lf->animation = anim;
359 	lf->animationTime = lf->frameTime + anim->initialLerp;
360 }
361 
362 
363 /*
364 ===============
365 UI_RunLerpFrame
366 ===============
367 */
UI_RunLerpFrame(playerInfo_t * ci,lerpFrame_t * lf,int newAnimation)368 static void UI_RunLerpFrame( playerInfo_t *ci, lerpFrame_t *lf, int newAnimation ) {
369 	int			f;
370 	animation_t	*anim;
371 
372 	// see if the animation sequence is switching
373 	if ( newAnimation != lf->animationNumber || !lf->animation ) {
374 		UI_SetLerpFrameAnimation( ci, lf, newAnimation );
375 	}
376 
377 	// if we have passed the current frame, move it to
378 	// oldFrame and calculate a new frame
379 	if ( dp_realtime >= lf->frameTime ) {
380 		lf->oldFrame = lf->frame;
381 		lf->oldFrameTime = lf->frameTime;
382 
383 		// get the next frame based on the animation
384 		anim = lf->animation;
385 		if ( dp_realtime < lf->animationTime ) {
386 			lf->frameTime = lf->animationTime;		// initial lerp
387 		} else {
388 			lf->frameTime = lf->oldFrameTime + anim->frameLerp;
389 		}
390 		f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp;
391 		if ( f >= anim->numFrames ) {
392 			f -= anim->numFrames;
393 			if ( anim->loopFrames ) {
394 				f %= anim->loopFrames;
395 				f += anim->numFrames - anim->loopFrames;
396 			} else {
397 				f = anim->numFrames - 1;
398 				// the animation is stuck at the end, so it
399 				// can immediately transition to another sequence
400 				lf->frameTime = dp_realtime;
401 			}
402 		}
403 		lf->frame = anim->firstFrame + f;
404 		if ( dp_realtime > lf->frameTime ) {
405 			lf->frameTime = dp_realtime;
406 		}
407 	}
408 
409 	if ( lf->frameTime > dp_realtime + 200 ) {
410 		lf->frameTime = dp_realtime;
411 	}
412 
413 	if ( lf->oldFrameTime > dp_realtime ) {
414 		lf->oldFrameTime = dp_realtime;
415 	}
416 	// calculate current lerp value
417 	if ( lf->frameTime == lf->oldFrameTime ) {
418 		lf->backlerp = 0;
419 	} else {
420 		lf->backlerp = 1.0 - (float)( dp_realtime - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime );
421 	}
422 }
423 
424 
425 /*
426 ===============
427 UI_PlayerAnimation
428 ===============
429 */
UI_PlayerAnimation(playerInfo_t * pi,int * legsOld,int * legs,float * legsBackLerp,int * torsoOld,int * torso,float * torsoBackLerp)430 static void UI_PlayerAnimation( playerInfo_t *pi, int *legsOld, int *legs, float *legsBackLerp,
431 						int *torsoOld, int *torso, float *torsoBackLerp ) {
432 
433 	// legs animation
434 	pi->legsAnimationTimer -= uiInfo.uiDC.frameTime;
435 	if ( pi->legsAnimationTimer < 0 ) {
436 		pi->legsAnimationTimer = 0;
437 	}
438 
439 	UI_LegsSequencing( pi );
440 
441 	if ( pi->legs.yawing && ( pi->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_IDLE ) {
442 		UI_RunLerpFrame( pi, &pi->legs, LEGS_TURN );
443 	} else {
444 		UI_RunLerpFrame( pi, &pi->legs, pi->legsAnim );
445 	}
446 	*legsOld = pi->legs.oldFrame;
447 	*legs = pi->legs.frame;
448 	*legsBackLerp = pi->legs.backlerp;
449 
450 	// torso animation
451 	pi->torsoAnimationTimer -= uiInfo.uiDC.frameTime;
452 	if ( pi->torsoAnimationTimer < 0 ) {
453 		pi->torsoAnimationTimer = 0;
454 	}
455 
456 	UI_TorsoSequencing( pi );
457 
458 	UI_RunLerpFrame( pi, &pi->torso, pi->torsoAnim );
459 	*torsoOld = pi->torso.oldFrame;
460 	*torso = pi->torso.frame;
461 	*torsoBackLerp = pi->torso.backlerp;
462 }
463 
464 
465 /*
466 ==================
467 UI_SwingAngles
468 ==================
469 */
UI_SwingAngles(float destination,float swingTolerance,float clampTolerance,float speed,float * angle,qboolean * swinging)470 static void UI_SwingAngles( float destination, float swingTolerance, float clampTolerance,
471 					float speed, float *angle, qboolean *swinging ) {
472 	float	swing;
473 	float	move;
474 	float	scale;
475 
476 	if ( !*swinging ) {
477 		// see if a swing should be started
478 		swing = AngleSubtract( *angle, destination );
479 		if ( swing > swingTolerance || swing < -swingTolerance ) {
480 			*swinging = qtrue;
481 		}
482 	}
483 
484 	if ( !*swinging ) {
485 		return;
486 	}
487 
488 	// modify the speed depending on the delta
489 	// so it doesn't seem so linear
490 	swing = AngleSubtract( destination, *angle );
491 	scale = fabs( swing );
492 	if ( scale < swingTolerance * 0.5 ) {
493 		scale = 0.5;
494 	} else if ( scale < swingTolerance ) {
495 		scale = 1.0;
496 	} else {
497 		scale = 2.0;
498 	}
499 
500 	// swing towards the destination angle
501 	if ( swing >= 0 ) {
502 		move = uiInfo.uiDC.frameTime * scale * speed;
503 		if ( move >= swing ) {
504 			move = swing;
505 			*swinging = qfalse;
506 		}
507 		*angle = AngleMod( *angle + move );
508 	} else if ( swing < 0 ) {
509 		move = uiInfo.uiDC.frameTime * scale * -speed;
510 		if ( move <= swing ) {
511 			move = swing;
512 			*swinging = qfalse;
513 		}
514 		*angle = AngleMod( *angle + move );
515 	}
516 
517 	// clamp to no more than tolerance
518 	swing = AngleSubtract( destination, *angle );
519 	if ( swing > clampTolerance ) {
520 		*angle = AngleMod( destination - (clampTolerance - 1) );
521 	} else if ( swing < -clampTolerance ) {
522 		*angle = AngleMod( destination + (clampTolerance - 1) );
523 	}
524 }
525 
526 
527 /*
528 ======================
529 UI_MovedirAdjustment
530 ======================
531 */
UI_MovedirAdjustment(playerInfo_t * pi)532 static float UI_MovedirAdjustment( playerInfo_t *pi ) {
533 	vec3_t		relativeAngles;
534 	vec3_t		moveVector;
535 
536 	VectorSubtract( pi->viewAngles, pi->moveAngles, relativeAngles );
537 	AngleVectors( relativeAngles, moveVector, NULL, NULL );
538 	if ( Q_fabs( moveVector[0] ) < 0.01 ) {
539 		moveVector[0] = 0.0;
540 	}
541 	if ( Q_fabs( moveVector[1] ) < 0.01 ) {
542 		moveVector[1] = 0.0;
543 	}
544 
545 	if ( moveVector[1] == 0 && moveVector[0] > 0 ) {
546 		return 0;
547 	}
548 	if ( moveVector[1] < 0 && moveVector[0] > 0 ) {
549 		return 22;
550 	}
551 	if ( moveVector[1] < 0 && moveVector[0] == 0 ) {
552 		return 45;
553 	}
554 	if ( moveVector[1] < 0 && moveVector[0] < 0 ) {
555 		return -22;
556 	}
557 	if ( moveVector[1] == 0 && moveVector[0] < 0 ) {
558 		return 0;
559 	}
560 	if ( moveVector[1] > 0 && moveVector[0] < 0 ) {
561 		return 22;
562 	}
563 	if ( moveVector[1] > 0 && moveVector[0] == 0 ) {
564 		return  -45;
565 	}
566 
567 	return -22;
568 }
569 
570 
571 /*
572 ===============
573 UI_PlayerAngles
574 ===============
575 */
UI_PlayerAngles(playerInfo_t * pi,vec3_t legs[3],vec3_t torso[3],vec3_t head[3])576 static void UI_PlayerAngles( playerInfo_t *pi, vec3_t legs[3], vec3_t torso[3], vec3_t head[3] ) {
577 	vec3_t		legsAngles, torsoAngles, headAngles;
578 	float		dest;
579 	float		adjust;
580 
581 	VectorCopy( pi->viewAngles, headAngles );
582 	headAngles[YAW] = AngleMod( headAngles[YAW] );
583 	VectorClear( legsAngles );
584 	VectorClear( torsoAngles );
585 
586 	// --------- yaw -------------
587 
588 	// allow yaw to drift a bit
589 	if ( ( pi->legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLE
590 		|| ( pi->torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STAND  ) {
591 		// if not standing still, always point all in the same direction
592 		pi->torso.yawing = qtrue;	// always center
593 		pi->torso.pitching = qtrue;	// always center
594 		pi->legs.yawing = qtrue;	// always center
595 	}
596 
597 	// adjust legs for movement dir
598 	adjust = UI_MovedirAdjustment( pi );
599 	legsAngles[YAW] = headAngles[YAW] + adjust;
600 	torsoAngles[YAW] = headAngles[YAW] + 0.25 * adjust;
601 
602 
603 	// torso
604 	UI_SwingAngles( torsoAngles[YAW], 25, 90, SWINGSPEED, &pi->torso.yawAngle, &pi->torso.yawing );
605 	UI_SwingAngles( legsAngles[YAW], 40, 90, SWINGSPEED, &pi->legs.yawAngle, &pi->legs.yawing );
606 
607 	torsoAngles[YAW] = pi->torso.yawAngle;
608 	legsAngles[YAW] = pi->legs.yawAngle;
609 
610 	// --------- pitch -------------
611 
612 	// only show a fraction of the pitch angle in the torso
613 	if ( headAngles[PITCH] > 180 ) {
614 		dest = (-360 + headAngles[PITCH]) * 0.75;
615 	} else {
616 		dest = headAngles[PITCH] * 0.75;
617 	}
618 	UI_SwingAngles( dest, 15, 30, 0.1f, &pi->torso.pitchAngle, &pi->torso.pitching );
619 	torsoAngles[PITCH] = pi->torso.pitchAngle;
620 
621 	// pull the angles back out of the hierarchial chain
622 	AnglesSubtract( headAngles, torsoAngles, headAngles );
623 	AnglesSubtract( torsoAngles, legsAngles, torsoAngles );
624 	AnglesToAxis( legsAngles, legs );
625 	AnglesToAxis( torsoAngles, torso );
626 	AnglesToAxis( headAngles, head );
627 }
628 
629 
630 /*
631 ===============
632 UI_PlayerFloatSprite
633 ===============
634 */
UI_PlayerFloatSprite(playerInfo_t * pi,vec3_t origin,qhandle_t shader)635 static void UI_PlayerFloatSprite( playerInfo_t *pi, vec3_t origin, qhandle_t shader ) {
636 	refEntity_t		ent;
637 
638 	memset( &ent, 0, sizeof( ent ) );
639 	VectorCopy( origin, ent.origin );
640 	ent.origin[2] += 48;
641 	ent.reType = RT_SPRITE;
642 	ent.customShader = shader;
643 	ent.radius = 10;
644 	ent.renderfx = 0;
645 	trap_R_AddRefEntityToScene( &ent );
646 }
647 
648 
649 /*
650 ======================
651 UI_MachinegunSpinAngle
652 ======================
653 */
UI_MachinegunSpinAngle(playerInfo_t * pi)654 float	UI_MachinegunSpinAngle( playerInfo_t *pi ) {
655 	int		delta;
656 	float	angle;
657 	float	speed;
658 	int		torsoAnim;
659 
660 	delta = dp_realtime - pi->barrelTime;
661 	if ( pi->barrelSpinning ) {
662 		angle = pi->barrelAngle + delta * SPIN_SPEED;
663 	} else {
664 		if ( delta > COAST_TIME ) {
665 			delta = COAST_TIME;
666 		}
667 
668 		speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME );
669 		angle = pi->barrelAngle + delta * speed;
670 	}
671 
672 	torsoAnim = pi->torsoAnim  & ~ANIM_TOGGLEBIT;
673 	if( torsoAnim == TORSO_ATTACK2 ) {
674 		torsoAnim = TORSO_ATTACK;
675 	}
676 	if ( pi->barrelSpinning == !(torsoAnim == TORSO_ATTACK) ) {
677 		pi->barrelTime = dp_realtime;
678 		pi->barrelAngle = AngleMod( angle );
679 		pi->barrelSpinning = !!(torsoAnim == TORSO_ATTACK);
680 	}
681 
682 	return angle;
683 }
684 
685 
686 /*
687 ===============
688 UI_DrawPlayer
689 ===============
690 */
UI_DrawPlayer(float x,float y,float w,float h,playerInfo_t * pi,int time)691 void UI_DrawPlayer( float x, float y, float w, float h, playerInfo_t *pi, int time ) {
692 	refdef_t		refdef;
693 	refEntity_t		legs;
694 	refEntity_t		torso;
695 	refEntity_t		head;
696 	refEntity_t		gun;
697 	refEntity_t		barrel;
698 	refEntity_t		flash;
699 	vec3_t			origin;
700 	int				renderfx;
701 	vec3_t			mins = {-16, -16, -24};
702 	vec3_t			maxs = {16, 16, 32};
703 	float			len;
704 	float			xx;
705 
706 	if ( !pi->legsModel || !pi->torsoModel || !pi->headModel || !pi->animations[0].numFrames ) {
707 		return;
708 	}
709 
710 	// this allows the ui to cache the player model on the main menu
711 	if (w == 0 || h == 0) {
712 		return;
713 	}
714 
715 	dp_realtime = time;
716 
717 	if ( pi->pendingWeapon != -1 && dp_realtime > pi->weaponTimer ) {
718 		pi->weapon = pi->pendingWeapon;
719 		pi->lastWeapon = pi->pendingWeapon;
720 		pi->pendingWeapon = -1;
721 		pi->weaponTimer = 0;
722 		if( pi->currentWeapon != pi->weapon ) {
723 			trap_S_StartLocalSound( weaponChangeSound, CHAN_LOCAL );
724 		}
725 	}
726 
727 	UI_AdjustFrom640( &x, &y, &w, &h );
728 
729 	y -= jumpHeight;
730 
731 	memset( &refdef, 0, sizeof( refdef ) );
732 	memset( &legs, 0, sizeof(legs) );
733 	memset( &torso, 0, sizeof(torso) );
734 	memset( &head, 0, sizeof(head) );
735 
736 	refdef.rdflags = RDF_NOWORLDMODEL;
737 
738 	AxisClear( refdef.viewaxis );
739 
740 	refdef.x = x;
741 	refdef.y = y;
742 	refdef.width = w;
743 	refdef.height = h;
744 
745 	refdef.fov_x = (int)((float)refdef.width / 640.0f * 90.0f);
746 	xx = refdef.width / tan( refdef.fov_x / 360 * M_PI );
747 	refdef.fov_y = atan2( refdef.height, xx );
748 	refdef.fov_y *= ( 360 / (float)M_PI );
749 
750 	// calculate distance so the player nearly fills the box
751 	len = 0.7 * ( maxs[2] - mins[2] );
752 	origin[0] = len / tan( DEG2RAD(refdef.fov_x) * 0.5 );
753 	origin[1] = 0.5 * ( mins[1] + maxs[1] );
754 	origin[2] = -0.5 * ( mins[2] + maxs[2] );
755 
756 	refdef.time = dp_realtime;
757 
758 	trap_R_ClearScene();
759 
760 	// get the rotation information
761 	UI_PlayerAngles( pi, legs.axis, torso.axis, head.axis );
762 
763 	// get the animation state (after rotation, to allow feet shuffle)
764 	UI_PlayerAnimation( pi, &legs.oldframe, &legs.frame, &legs.backlerp,
765 		 &torso.oldframe, &torso.frame, &torso.backlerp );
766 
767 	renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW;
768 
769 	//
770 	// add the legs
771 	//
772 	legs.hModel = pi->legsModel;
773 	legs.customSkin = pi->legsSkin;
774 
775 	VectorCopy( origin, legs.origin );
776 
777 	VectorCopy( origin, legs.lightingOrigin );
778 	legs.renderfx = renderfx;
779 	VectorCopy (legs.origin, legs.oldorigin);
780 
781 	trap_R_AddRefEntityToScene( &legs );
782 
783 	if (!legs.hModel) {
784 		return;
785 	}
786 
787 	//
788 	// add the torso
789 	//
790 	torso.hModel = pi->torsoModel;
791 	if (!torso.hModel) {
792 		return;
793 	}
794 
795 	torso.customSkin = pi->torsoSkin;
796 
797 	VectorCopy( origin, torso.lightingOrigin );
798 
799 	UI_PositionRotatedEntityOnTag( &torso, &legs, pi->legsModel, "tag_torso");
800 
801 	torso.renderfx = renderfx;
802 
803 	trap_R_AddRefEntityToScene( &torso );
804 
805 	//
806 	// add the head
807 	//
808 	head.hModel = pi->headModel;
809 	if (!head.hModel) {
810 		return;
811 	}
812 	head.customSkin = pi->headSkin;
813 
814 	VectorCopy( origin, head.lightingOrigin );
815 
816 	UI_PositionRotatedEntityOnTag( &head, &torso, pi->torsoModel, "tag_head");
817 
818 	head.renderfx = renderfx;
819 
820 	trap_R_AddRefEntityToScene( &head );
821 
822 	//
823 	// add the gun
824 	//
825 	if ( pi->currentWeapon != WP_NONE ) {
826 		memset( &gun, 0, sizeof(gun) );
827 		gun.hModel = pi->weaponModel;
828 		VectorCopy( origin, gun.lightingOrigin );
829 		UI_PositionEntityOnTag( &gun, &torso, pi->torsoModel, "tag_weapon");
830 		gun.renderfx = renderfx;
831 		trap_R_AddRefEntityToScene( &gun );
832 	}
833 
834 	//
835 	// add the spinning barrel
836 	//
837 	if ( pi->realWeapon == WP_MACHINEGUN || pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) {
838 		vec3_t	angles;
839 
840 		memset( &barrel, 0, sizeof(barrel) );
841 		VectorCopy( origin, barrel.lightingOrigin );
842 		barrel.renderfx = renderfx;
843 
844 		barrel.hModel = pi->barrelModel;
845 		angles[YAW] = 0;
846 		angles[PITCH] = 0;
847 		angles[ROLL] = UI_MachinegunSpinAngle( pi );
848 		if( pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) {
849 			angles[PITCH] = angles[ROLL];
850 			angles[ROLL] = 0;
851 		}
852 		AnglesToAxis( angles, barrel.axis );
853 
854 		UI_PositionRotatedEntityOnTag( &barrel, &gun, pi->weaponModel, "tag_barrel");
855 
856 		trap_R_AddRefEntityToScene( &barrel );
857 	}
858 
859 	//
860 	// add muzzle flash
861 	//
862 	if ( dp_realtime <= pi->muzzleFlashTime ) {
863 		if ( pi->flashModel ) {
864 			memset( &flash, 0, sizeof(flash) );
865 			flash.hModel = pi->flashModel;
866 			VectorCopy( origin, flash.lightingOrigin );
867 			UI_PositionEntityOnTag( &flash, &gun, pi->weaponModel, "tag_flash");
868 			flash.renderfx = renderfx;
869 			trap_R_AddRefEntityToScene( &flash );
870 		}
871 
872 		// make a dlight for the flash
873 		if ( pi->flashDlightColor[0] || pi->flashDlightColor[1] || pi->flashDlightColor[2] ) {
874 			trap_R_AddLightToScene( flash.origin, 200 + (rand()&31), pi->flashDlightColor[0],
875 				pi->flashDlightColor[1], pi->flashDlightColor[2] );
876 		}
877 	}
878 
879 	//
880 	// add the chat icon
881 	//
882 	if ( pi->chat ) {
883 		UI_PlayerFloatSprite( pi, origin, trap_R_RegisterShaderNoMip( "sprites/balloon3" ) );
884 	}
885 
886 	//
887 	// add an accent light
888 	//
889 	origin[0] -= 100;	// + = behind, - = in front
890 	origin[1] += 100;	// + = left, - = right
891 	origin[2] += 100;	// + = above, - = below
892 	trap_R_AddLightToScene( origin, 500, 1.0, 1.0, 1.0 );
893 
894 	origin[0] -= 100;
895 	origin[1] -= 100;
896 	origin[2] -= 100;
897 	trap_R_AddLightToScene( origin, 500, 1.0, 0.0, 0.0 );
898 
899 	trap_R_RenderScene( &refdef );
900 }
901 
902 /*
903 ==========================
904 UI_FileExists
905 ==========================
906 */
UI_FileExists(const char * filename)907 static qboolean	UI_FileExists(const char *filename) {
908 	int len;
909 
910 	len = trap_FS_FOpenFile( filename, NULL, FS_READ );
911 	if (len>0) {
912 		return qtrue;
913 	}
914 	return qfalse;
915 }
916 
917 /*
918 ==========================
919 UI_FindClientHeadFile
920 ==========================
921 */
UI_FindClientHeadFile(char * filename,int length,const char * teamName,const char * headModelName,const char * headSkinName,const char * base,const char * ext)922 static qboolean	UI_FindClientHeadFile( char *filename, int length, const char *teamName, const char *headModelName, const char *headSkinName, const char *base, const char *ext ) {
923 	char *team, *headsFolder;
924 	int i;
925 
926 	team = "default";
927 
928 	if ( headModelName[0] == '*' ) {
929 		headsFolder = "heads/";
930 		headModelName++;
931 	}
932 	else {
933 		headsFolder = "";
934 	}
935 	while(1) {
936 		for ( i = 0; i < 2; i++ ) {
937 			if ( i == 0 && teamName && *teamName ) {
938 				Com_sprintf( filename, length, "models/players/%s%s/%s/%s%s_%s.%s", headsFolder, headModelName, headSkinName, teamName, base, team, ext );
939 			}
940 			else {
941 				Com_sprintf( filename, length, "models/players/%s%s/%s/%s_%s.%s", headsFolder, headModelName, headSkinName, base, team, ext );
942 			}
943 			if ( UI_FileExists( filename ) ) {
944 				return qtrue;
945 			}
946 			if ( i == 0 && teamName && *teamName ) {
947 				Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", headsFolder, headModelName, teamName, base, headSkinName, ext );
948 			}
949 			else {
950 				Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", headsFolder, headModelName, base, headSkinName, ext );
951 			}
952 			if ( UI_FileExists( filename ) ) {
953 				return qtrue;
954 			}
955 			if ( !teamName || !*teamName ) {
956 				break;
957 			}
958 		}
959 		// if tried the heads folder first
960 		if ( headsFolder[0] ) {
961 			break;
962 		}
963 		headsFolder = "heads/";
964 	}
965 
966 	return qfalse;
967 }
968 
969 /*
970 ==========================
971 UI_RegisterClientSkin
972 ==========================
973 */
UI_RegisterClientSkin(playerInfo_t * pi,const char * modelName,const char * skinName,const char * headModelName,const char * headSkinName,const char * teamName)974 static qboolean	UI_RegisterClientSkin( playerInfo_t *pi, const char *modelName, const char *skinName, const char *headModelName, const char *headSkinName , const char *teamName) {
975 	char		filename[MAX_QPATH*2];
976 
977 	if (teamName && *teamName) {
978 		Com_sprintf( filename, sizeof( filename ), "models/players/%s/%s/lower_%s.skin", modelName, teamName, skinName );
979 	} else {
980 		Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower_%s.skin", modelName, skinName );
981 	}
982 	pi->legsSkin = trap_R_RegisterSkin( filename );
983 	if (!pi->legsSkin) {
984 		if (teamName && *teamName) {
985 			Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%s/lower_%s.skin", modelName, teamName, skinName );
986 		} else {
987 			Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/lower_%s.skin", modelName, skinName );
988 		}
989 		pi->legsSkin = trap_R_RegisterSkin( filename );
990 	}
991 
992 	if (teamName && *teamName) {
993 		Com_sprintf( filename, sizeof( filename ), "models/players/%s/%s/upper_%s.skin", modelName, teamName, skinName );
994 	} else {
995 		Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper_%s.skin", modelName, skinName );
996 	}
997 	pi->torsoSkin = trap_R_RegisterSkin( filename );
998 	if (!pi->torsoSkin) {
999 		if (teamName && *teamName) {
1000 			Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%s/upper_%s.skin", modelName, teamName, skinName );
1001 		} else {
1002 			Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/upper_%s.skin", modelName, skinName );
1003 		}
1004 		pi->torsoSkin = trap_R_RegisterSkin( filename );
1005 	}
1006 
1007 	if ( UI_FindClientHeadFile( filename, sizeof(filename), teamName, headModelName, headSkinName, "head", "skin" ) ) {
1008 		pi->headSkin = trap_R_RegisterSkin( filename );
1009 	}
1010 
1011 	if ( !pi->legsSkin || !pi->torsoSkin || !pi->headSkin ) {
1012 		return qfalse;
1013 	}
1014 
1015 	return qtrue;
1016 }
1017 
1018 
1019 /*
1020 ======================
1021 UI_ParseAnimationFile
1022 ======================
1023 */
UI_ParseAnimationFile(const char * filename,animation_t * animations)1024 static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animations ) {
1025 	char		*text_p, *prev;
1026 	int			len;
1027 	int			i;
1028 	char		*token;
1029 	float		fps;
1030 	int			skip;
1031 	char		text[20000];
1032 	fileHandle_t	f;
1033 
1034 	memset( animations, 0, sizeof( animation_t ) * MAX_ANIMATIONS );
1035 
1036 	// load the file
1037 	len = trap_FS_FOpenFile( filename, &f, FS_READ );
1038 	if ( len <= 0 ) {
1039 		return qfalse;
1040 	}
1041 	if ( len >= ( sizeof( text ) - 1 ) ) {
1042 		Com_Printf( "File %s too long\n", filename );
1043 		trap_FS_FCloseFile( f );
1044 		return qfalse;
1045 	}
1046 	trap_FS_Read( text, len, f );
1047 	text[len] = 0;
1048 	trap_FS_FCloseFile( f );
1049 
1050 	COM_Compress(text);
1051 
1052 	// parse the text
1053 	text_p = text;
1054 	skip = 0;	// quite the compiler warning
1055 
1056 	// read optional parameters
1057 	while ( 1 ) {
1058 		prev = text_p;	// so we can unget
1059 		token = COM_Parse( &text_p );
1060 		if ( !token ) {
1061 			break;
1062 		}
1063 		if ( !Q_stricmp( token, "footsteps" ) ) {
1064 			token = COM_Parse( &text_p );
1065 			if ( !token ) {
1066 				break;
1067 			}
1068 			continue;
1069 		} else if ( !Q_stricmp( token, "headoffset" ) ) {
1070 			for ( i = 0 ; i < 3 ; i++ ) {
1071 				token = COM_Parse( &text_p );
1072 				if ( !token ) {
1073 					break;
1074 				}
1075 			}
1076 			continue;
1077 		} else if ( !Q_stricmp( token, "sex" ) ) {
1078 			token = COM_Parse( &text_p );
1079 			if ( !token ) {
1080 				break;
1081 			}
1082 			continue;
1083 		}
1084 
1085 		// if it is a number, start parsing animations
1086 		if ( token[0] >= '0' && token[0] <= '9' ) {
1087 			text_p = prev;	// unget the token
1088 			break;
1089 		}
1090 
1091 		Com_Printf( "unknown token '%s' is %s\n", token, filename );
1092 	}
1093 
1094 	// read information for each frame
1095 	for ( i = 0 ; i < MAX_ANIMATIONS ; i++ ) {
1096 
1097 		token = COM_Parse( &text_p );
1098 		if ( !token ) {
1099 			break;
1100 		}
1101 		animations[i].firstFrame = atoi( token );
1102 		// leg only frames are adjusted to not count the upper body only frames
1103 		if ( i == LEGS_WALKCR ) {
1104 			skip = animations[LEGS_WALKCR].firstFrame - animations[TORSO_GESTURE].firstFrame;
1105 		}
1106 		if ( i >= LEGS_WALKCR ) {
1107 			animations[i].firstFrame -= skip;
1108 		}
1109 
1110 		token = COM_Parse( &text_p );
1111 		if ( !token ) {
1112 			break;
1113 		}
1114 		animations[i].numFrames = atoi( token );
1115 
1116 		token = COM_Parse( &text_p );
1117 		if ( !token ) {
1118 			break;
1119 		}
1120 		animations[i].loopFrames = atoi( token );
1121 
1122 		token = COM_Parse( &text_p );
1123 		if ( !token ) {
1124 			break;
1125 		}
1126 		fps = atof( token );
1127 		if ( fps == 0 ) {
1128 			fps = 1;
1129 		}
1130 		animations[i].frameLerp = 1000 / fps;
1131 		animations[i].initialLerp = 1000 / fps;
1132 	}
1133 
1134 	if ( i != MAX_ANIMATIONS ) {
1135 		Com_Printf( "Error parsing animation file: %s", filename );
1136 		return qfalse;
1137 	}
1138 
1139 	return qtrue;
1140 }
1141 
1142 /*
1143 ==========================
1144 UI_RegisterClientModelname
1145 ==========================
1146 */
UI_RegisterClientModelname(playerInfo_t * pi,const char * modelSkinName,const char * headModelSkinName,const char * teamName)1147 qboolean UI_RegisterClientModelname( playerInfo_t *pi, const char *modelSkinName, const char *headModelSkinName, const char *teamName ) {
1148 	char		modelName[MAX_QPATH];
1149 	char		skinName[MAX_QPATH];
1150 	char		headModelName[MAX_QPATH];
1151 	char		headSkinName[MAX_QPATH];
1152 	char		filename[MAX_QPATH];
1153 	char		*slash;
1154 
1155 	pi->torsoModel = 0;
1156 	pi->headModel = 0;
1157 
1158 	if ( !modelSkinName[0] ) {
1159 		return qfalse;
1160 	}
1161 
1162 	Q_strncpyz( modelName, modelSkinName, sizeof( modelName ) );
1163 
1164 	slash = strchr( modelName, '/' );
1165 	if ( !slash ) {
1166 		// modelName did not include a skin name
1167 		Q_strncpyz( skinName, "default", sizeof( skinName ) );
1168 	} else {
1169 		Q_strncpyz( skinName, slash + 1, sizeof( skinName ) );
1170 		*slash = '\0';
1171 	}
1172 
1173 	Q_strncpyz( headModelName, headModelSkinName, sizeof( headModelName ) );
1174 	slash = strchr( headModelName, '/' );
1175 	if ( !slash ) {
1176 		// modelName did not include a skin name
1177 		Q_strncpyz( headSkinName, "default", sizeof( skinName ) );
1178 	} else {
1179 		Q_strncpyz( headSkinName, slash + 1, sizeof( skinName ) );
1180 		*slash = '\0';
1181 	}
1182 
1183 	// load cmodels before models so filecache works
1184 
1185 	Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower.md3", modelName );
1186 	pi->legsModel = trap_R_RegisterModel( filename );
1187 	if ( !pi->legsModel ) {
1188 		Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/lower.md3", modelName );
1189 		pi->legsModel = trap_R_RegisterModel( filename );
1190 		if ( !pi->legsModel ) {
1191 			Com_Printf( "Failed to load model file %s\n", filename );
1192 			return qfalse;
1193 		}
1194 	}
1195 
1196 	Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper.md3", modelName );
1197 	pi->torsoModel = trap_R_RegisterModel( filename );
1198 	if ( !pi->torsoModel ) {
1199 		Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/upper.md3", modelName );
1200 		pi->torsoModel = trap_R_RegisterModel( filename );
1201 		if ( !pi->torsoModel ) {
1202 			Com_Printf( "Failed to load model file %s\n", filename );
1203 			return qfalse;
1204 		}
1205 	}
1206 
1207 	if (headModelName[0] == '*' ) {
1208 		Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", &headModelName[1], &headModelName[1] );
1209 	}
1210 	else {
1211 		Com_sprintf( filename, sizeof( filename ), "models/players/%s/head.md3", headModelName );
1212 	}
1213 	pi->headModel = trap_R_RegisterModel( filename );
1214 	if ( !pi->headModel && headModelName[0] != '*') {
1215 		Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", headModelName, headModelName );
1216 		pi->headModel = trap_R_RegisterModel( filename );
1217 	}
1218 
1219 	if (!pi->headModel) {
1220 		Com_Printf( "Failed to load model file %s\n", filename );
1221 		return qfalse;
1222 	}
1223 
1224 	// if any skins failed to load, fall back to default
1225 	if ( !UI_RegisterClientSkin( pi, modelName, skinName, headModelName, headSkinName, teamName) ) {
1226 		if ( !UI_RegisterClientSkin( pi, modelName, "default", headModelName, "default", teamName ) ) {
1227 			Com_Printf( "Failed to load skin file: %s : %s\n", modelName, skinName );
1228 			return qfalse;
1229 		}
1230 	}
1231 
1232 	// load the animations
1233 	Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", modelName );
1234 	if ( !UI_ParseAnimationFile( filename, pi->animations ) ) {
1235 		Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/animation.cfg", modelName );
1236 		if ( !UI_ParseAnimationFile( filename, pi->animations ) ) {
1237 			Com_Printf( "Failed to load animation file %s\n", filename );
1238 			return qfalse;
1239 		}
1240 	}
1241 
1242 	return qtrue;
1243 }
1244 
1245 
1246 /*
1247 ===============
1248 UI_PlayerInfo_SetModel
1249 ===============
1250 */
UI_PlayerInfo_SetModel(playerInfo_t * pi,const char * model,const char * headmodel,char * teamName)1251 void UI_PlayerInfo_SetModel( playerInfo_t *pi, const char *model, const char *headmodel, char *teamName ) {
1252 	memset( pi, 0, sizeof(*pi) );
1253 	UI_RegisterClientModelname( pi, model, headmodel, teamName );
1254 	pi->weapon = WP_MACHINEGUN;
1255 	pi->currentWeapon = pi->weapon;
1256 	pi->lastWeapon = pi->weapon;
1257 	pi->pendingWeapon = -1;
1258 	pi->weaponTimer = 0;
1259 	pi->chat = qfalse;
1260 	pi->newModel = qtrue;
1261 	UI_PlayerInfo_SetWeapon( pi, pi->weapon );
1262 }
1263 
1264 
1265 /*
1266 ===============
1267 UI_PlayerInfo_SetInfo
1268 ===============
1269 */
UI_PlayerInfo_SetInfo(playerInfo_t * pi,int legsAnim,int torsoAnim,vec3_t viewAngles,vec3_t moveAngles,weapon_t weaponNumber,qboolean chat)1270 void UI_PlayerInfo_SetInfo( playerInfo_t *pi, int legsAnim, int torsoAnim, vec3_t viewAngles, vec3_t moveAngles, weapon_t weaponNumber, qboolean chat ) {
1271 	int			currentAnim;
1272 	weapon_t	weaponNum;
1273 
1274 	pi->chat = chat;
1275 
1276 	// view angles
1277 	VectorCopy( viewAngles, pi->viewAngles );
1278 
1279 	// move angles
1280 	VectorCopy( moveAngles, pi->moveAngles );
1281 
1282 	if ( pi->newModel ) {
1283 		pi->newModel = qfalse;
1284 
1285 		jumpHeight = 0;
1286 		pi->pendingLegsAnim = 0;
1287 		UI_ForceLegsAnim( pi, legsAnim );
1288 		pi->legs.yawAngle = viewAngles[YAW];
1289 		pi->legs.yawing = qfalse;
1290 
1291 		pi->pendingTorsoAnim = 0;
1292 		UI_ForceTorsoAnim( pi, torsoAnim );
1293 		pi->torso.yawAngle = viewAngles[YAW];
1294 		pi->torso.yawing = qfalse;
1295 
1296 		if ( weaponNumber != -1 ) {
1297 			pi->weapon = weaponNumber;
1298 			pi->currentWeapon = weaponNumber;
1299 			pi->lastWeapon = weaponNumber;
1300 			pi->pendingWeapon = -1;
1301 			pi->weaponTimer = 0;
1302 			UI_PlayerInfo_SetWeapon( pi, pi->weapon );
1303 		}
1304 
1305 		return;
1306 	}
1307 
1308 	// weapon
1309 	if ( weaponNumber == -1 ) {
1310 		pi->pendingWeapon = -1;
1311 		pi->weaponTimer = 0;
1312 	}
1313 	else if ( weaponNumber != WP_NONE ) {
1314 		pi->pendingWeapon = weaponNumber;
1315 		pi->weaponTimer = dp_realtime + UI_TIMER_WEAPON_DELAY;
1316 	}
1317 	weaponNum = pi->lastWeapon;
1318 	pi->weapon = weaponNum;
1319 
1320 	if ( torsoAnim == BOTH_DEATH1 || legsAnim == BOTH_DEATH1 ) {
1321 		torsoAnim = legsAnim = BOTH_DEATH1;
1322 		pi->weapon = pi->currentWeapon = WP_NONE;
1323 		UI_PlayerInfo_SetWeapon( pi, pi->weapon );
1324 
1325 		jumpHeight = 0;
1326 		pi->pendingLegsAnim = 0;
1327 		UI_ForceLegsAnim( pi, legsAnim );
1328 
1329 		pi->pendingTorsoAnim = 0;
1330 		UI_ForceTorsoAnim( pi, torsoAnim );
1331 
1332 		return;
1333 	}
1334 
1335 	// leg animation
1336 	currentAnim = pi->legsAnim & ~ANIM_TOGGLEBIT;
1337 	if ( legsAnim != LEGS_JUMP && ( currentAnim == LEGS_JUMP || currentAnim == LEGS_LAND ) ) {
1338 		pi->pendingLegsAnim = legsAnim;
1339 	}
1340 	else if ( legsAnim != currentAnim ) {
1341 		jumpHeight = 0;
1342 		pi->pendingLegsAnim = 0;
1343 		UI_ForceLegsAnim( pi, legsAnim );
1344 	}
1345 
1346 	// torso animation
1347 	if ( torsoAnim == TORSO_STAND || torsoAnim == TORSO_STAND2 ) {
1348 		if ( weaponNum == WP_NONE || weaponNum == WP_GAUNTLET ) {
1349 			torsoAnim = TORSO_STAND2;
1350 		}
1351 		else {
1352 			torsoAnim = TORSO_STAND;
1353 		}
1354 	}
1355 
1356 	if ( torsoAnim == TORSO_ATTACK || torsoAnim == TORSO_ATTACK2 ) {
1357 		if ( weaponNum == WP_NONE || weaponNum == WP_GAUNTLET ) {
1358 			torsoAnim = TORSO_ATTACK2;
1359 		}
1360 		else {
1361 			torsoAnim = TORSO_ATTACK;
1362 		}
1363 		pi->muzzleFlashTime = dp_realtime + UI_TIMER_MUZZLE_FLASH;
1364 		//FIXME play firing sound here
1365 	}
1366 
1367 	currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT;
1368 
1369 	if ( weaponNum != pi->currentWeapon || currentAnim == TORSO_RAISE || currentAnim == TORSO_DROP ) {
1370 		pi->pendingTorsoAnim = torsoAnim;
1371 	}
1372 	else if ( ( currentAnim == TORSO_GESTURE || currentAnim == TORSO_ATTACK ) && ( torsoAnim != currentAnim ) ) {
1373 		pi->pendingTorsoAnim = torsoAnim;
1374 	}
1375 	else if ( torsoAnim != currentAnim ) {
1376 		pi->pendingTorsoAnim = 0;
1377 		UI_ForceTorsoAnim( pi, torsoAnim );
1378 	}
1379 }
1380