ui_players.c 34 KB

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