weevil.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702
  1. #include "../idlib/precompiled.h"
  2. #pragma hdrstop
  3. #include "Game_local.h"
  4. #define WALKSPEED 9 //was 9
  5. #define WALKBOUNCE 6 //if hit an obstacle then bounce a bit to dislodge itself.
  6. #define DIST_MOD 0.11f
  7. #define MAXPITCH 50
  8. #define MAXROLL 50
  9. #define OFFROADTIME 200
  10. #define JUMPDISTANCE 192
  11. #define JUMPHEIGHT 256
  12. #define PLUGMOVETIME 0.2f
  13. #define JUMP_STARTTIME 400
  14. const idEventDef EV_weevil_forward( "weevilforward", "d" );
  15. const idEventDef EV_weevil_turn( "weevilturn", "d" );
  16. const idEventDef EV_weevil_stop( "weevilstop" );
  17. const idEventDef EV_weevil_stand( "weevilstand" );
  18. const idEventDef EV_weevil_light( "weevillight", "d" );
  19. const idEventDef EV_weevil_jump( "weeviljump" );
  20. const idEventDef EV_weevil_gravity( "weevilgravity", "d" );
  21. const idEventDef EV_weevil_getgravity( "weevilgetgravity", NULL, 'd' );
  22. const idEventDef EV_weevil_doplug( "weevildoplug", "de" );
  23. const idEventDef EV_weevil_plugConnected( "<weevilPlugConnected>", "v" );
  24. const idEventDef EV_weevil_isPlugConnected( "isweevilplugconnected", NULL, 'd' );
  25. const idEventDef EV_weevil_eyemove("weevileyemove", "ffd", 'd');
  26. CLASS_DECLARATION( idMoveableItem, idWeevil )
  27. EVENT( EV_weevil_forward, idWeevil::Event_forward )
  28. EVENT( EV_weevil_turn, idWeevil::Event_turn )
  29. EVENT( EV_weevil_stop, idWeevil::Event_stop )
  30. EVENT( EV_weevil_stand, idWeevil::Event_stand )
  31. EVENT( EV_weevil_light, idWeevil::Event_light )
  32. EVENT( EV_weevil_jump, idWeevil::Event_jump )
  33. EVENT( EV_weevil_gravity, idWeevil::Event_gravity )
  34. EVENT( EV_weevil_getgravity, idWeevil::Event_getgravity )
  35. EVENT( EV_weevil_doplug, idWeevil::Event_doPlug )
  36. EVENT( EV_weevil_plugConnected, idWeevil::Event_plugdone)
  37. EVENT( EV_weevil_isPlugConnected, idWeevil::Event_isweevilplugconnected)
  38. EVENT( EV_weevil_eyemove, idWeevil::Event_weevilEyeMove)
  39. END_CLASS
  40. void idWeevil::Save( idSaveGame *savefile ) const
  41. {
  42. savefile->WriteBool(isGravity);
  43. savefile->WriteInt(state);
  44. savefile->WriteInt(walkDirection);
  45. savefile->WriteInt(turnCurrent);
  46. savefile->WriteInt(turnTarget);
  47. savefile->WriteInt(turnDirection);
  48. savefile->WriteInt(offroadTimer);
  49. savefile->WriteInt(jumpstartTimer);
  50. savefile->WriteVec3(lastPosition);
  51. savefile->WriteObject(model);
  52. savefile->WriteObject(nosecam);
  53. savefile->WriteObject(light);
  54. savefile->WriteObject(plugModel);
  55. savefile->WriteObject(beamStart);
  56. savefile->WriteObject(beamEnd);
  57. savefile->WriteJoint(headJoint);
  58. }
  59. void idWeevil::Restore( idRestoreGame *savefile )
  60. {
  61. savefile->ReadBool(isGravity);
  62. savefile->ReadInt(state);
  63. savefile->ReadInt(walkDirection);
  64. savefile->ReadInt(turnCurrent);
  65. savefile->ReadInt(turnTarget);
  66. savefile->ReadInt(turnDirection);
  67. savefile->ReadInt(offroadTimer);
  68. savefile->ReadInt(jumpstartTimer);
  69. savefile->ReadVec3(lastPosition);
  70. savefile->ReadObject(reinterpret_cast<idClass *&>(model));
  71. savefile->ReadObject(reinterpret_cast<idClass *&>(nosecam));
  72. savefile->ReadObject(reinterpret_cast<idClass *&>(light));
  73. savefile->ReadObject(reinterpret_cast<idClass *&>(plugModel));
  74. savefile->ReadObject(reinterpret_cast<idClass *&>(beamStart));
  75. savefile->ReadObject(reinterpret_cast<idClass *&>(beamEnd));
  76. savefile->ReadJoint(headJoint);
  77. }
  78. void idWeevil::Spawn( void )
  79. {
  80. idDict args;
  81. jointHandle_t noseJoint;
  82. idVec3 nosePos;
  83. idMat3 noseAxis;
  84. idVec3 forward;
  85. state = 0;
  86. walkDirection = 1;
  87. turnCurrent = 0;
  88. turnTarget = 0;
  89. turnDirection = 1;
  90. offroadTimer = 0;
  91. jumpstartTimer = 0;
  92. lastPosition = vec3_zero;
  93. isGravity = true;
  94. BecomeActive( TH_THINK );
  95. //MODEL
  96. this->GetPhysics()->GetAxis().ToAngles().ToVectors(&forward);
  97. args.Clear();
  98. args.SetVector( "origin", this->GetPhysics()->GetOrigin() );
  99. args.Set( "model", "monster_weevil" );
  100. args.Set( "cycle", "-1" );
  101. //args.Set( "notriggeronanim", "1" );
  102. model = ( idAnimatedEntity * )gameLocal.SpawnEntityType( idAnimatedEntity::Type, &args );
  103. model->SetAxis( this->GetPhysics()->GetAxis() );
  104. // BEAMS / PLUGS
  105. args.Clear();
  106. args.Set( "model", "models/datajack_plug/tris.ase" );
  107. args.SetBool( "solid", 0);
  108. plugModel = ( idMover * )gameLocal.SpawnEntityType( idMover::Type, &args );
  109. plugModel->Hide();
  110. args.Clear();
  111. args.SetInt( "start_off", 1);
  112. beamEnd = ( idBeam * )gameLocal.SpawnEntityType( idBeam::Type );
  113. args.Clear();
  114. args.Set( "target", beamEnd->name.c_str() );
  115. args.Set( "skin", "skins/beam_black");
  116. args.SetInt( "start_off", 1);
  117. beamStart = ( idBeam * )gameLocal.SpawnEntityType( idBeam::Type, &args );
  118. beamStart->Hide();
  119. beamStart->Event_SetWidth(0.5f);
  120. idVec3 selfForward, selfUp;
  121. idAngles selfAng = this->GetPhysics()->GetAxis().ToAngles();
  122. selfAng.ToVectors(&selfForward, NULL, &selfUp);
  123. idVec3 attachPoint = this->GetPhysics()->GetOrigin() + (selfForward * -3) + (selfUp * 7);
  124. beamStart->SetOrigin(attachPoint );
  125. beamStart->Bind( this, false );
  126. idVec3 plugForward;
  127. idAngles plugAng = plugModel->GetPhysics()->GetAxis().ToAngles();
  128. selfAng.ToVectors(&plugForward, NULL, NULL);
  129. idVec3 plugEndPoint = plugModel->GetPhysics()->GetOrigin() + (plugForward * 0.9f);
  130. beamEnd->SetOrigin(plugEndPoint);
  131. beamEnd->Bind(plugModel, false);
  132. beamEnd->Hide();
  133. model->Bind(this, true);
  134. //nose camera.
  135. noseJoint = model->GetAnimator()->GetJointHandle( "head" );
  136. model->GetAnimator()->GetJointTransform( noseJoint, gameLocal.time, nosePos, noseAxis );
  137. args.Clear();
  138. args.SetVector( "origin", nosePos + this->GetPhysics()->GetOrigin() + (forward * -2));
  139. args.Set( "classname", "func_cameraview" );
  140. gameLocal.SpawnEntityDef( args, &nosecam );
  141. nosecam->BindToJoint(model, noseJoint, true);
  142. nosecam->BecomeActive(TH_PHYSICS);
  143. this->spawnArgs.Set("nosecamera_name", nosecam->GetName());
  144. //spawn light.
  145. args.Clear();
  146. args.SetVector( "origin", nosePos + this->GetPhysics()->GetOrigin() + (forward * 0.9f) );
  147. args.Set( "light_target", "1 0 -0.3" );
  148. args.Set( "light_up", "0 0 1" );
  149. args.Set( "light_right", "0 -1.5 0" );
  150. args.Set( "light_end", "128 0 0" );
  151. args.Set( "texture", "lights/monitorglow" );
  152. args.Set("start_off", "1");
  153. args.SetInt("alarmlight", 0);
  154. light = ( idLight * )gameLocal.SpawnEntityType( idLight::Type, &args );
  155. light->Bind(this, true);
  156. headJoint = model->GetAnimator()->GetJointHandle( "head" );
  157. }
  158. bool idWeevil::isJumping( void )
  159. {
  160. if (jumpstartTimer > gameLocal.time)
  161. return true;
  162. return false;
  163. }
  164. void idWeevil::OnGet( void )
  165. {
  166. gameLocal.KillCCTVs();
  167. beamStart->PostEventMS( &EV_Remove, 0 );
  168. beamEnd->PostEventMS( &EV_Remove, 0 );
  169. plugModel->PostEventMS( &EV_Remove, 0 );
  170. }
  171. void idWeevil::Event_isweevilplugconnected( void )
  172. {
  173. if (plugModel->IsHidden())
  174. idThread::ReturnInt(0);
  175. else
  176. idThread::ReturnInt(1);
  177. }
  178. void idWeevil::Event_plugdone( idVec3 const &position )
  179. {
  180. idDict args;
  181. args.Clear();
  182. args.SetVector( "origin", position );
  183. args.Set( "model", spawnArgs.GetString("smoke_plug", "plugconnect.prt") );
  184. args.SetBool( "start_off", false );
  185. gameLocal.SpawnEntityType( idExplodable::Type, &args );
  186. StartSound( "snd_plugin", SND_CHANNEL_ANY, 0, false, NULL );
  187. }
  188. void idWeevil::Event_doPlug( int active, idEntity *jackEnt )
  189. {
  190. if (active <= 0)
  191. {
  192. //unplug.
  193. beamStart->Hide();
  194. plugModel->Hide();
  195. return;
  196. }
  197. //plug in.
  198. plugModel->GetPhysics()->SetAxis( jackEnt->GetPhysics()->GetAxis() );
  199. idVec3 jackForward, jackUp;
  200. idAngles selfAng = jackEnt->GetPhysics()->GetAxis().ToAngles();
  201. selfAng.ToVectors(&jackForward, NULL, &jackUp);
  202. idVec3 jackEndPos = jackEnt->GetPhysics()->GetOrigin() + (jackForward * 0.4) + (jackUp * 1.1);
  203. //gameRenderWorld->DebugLine(idVec4(0,1,0,1), plugModel->GetPhysics()->GetOrigin(), plugModel->GetPhysics()->GetOrigin() + idVec3(0,0,128), 10000);
  204. //gameRenderWorld->DebugLine(idVec4(1,1,0,1), jackEndPos, jackEndPos + idVec3(0,0,128), 10000);
  205. idVec3 weevilUp;
  206. idAngles weevilAng = this->GetPhysics()->GetAxis().ToAngles();
  207. weevilAng.ToVectors(NULL, NULL, &weevilUp);
  208. plugModel->SetOrigin( this->GetPhysics()->GetOrigin() + (weevilUp * 6) );
  209. plugModel->Event_SetMoveTime(PLUGMOVETIME);
  210. plugModel->Event_MoveToPos(jackEndPos);
  211. PostEventSec( &EV_weevil_plugConnected, PLUGMOVETIME, jackEndPos);
  212. beamEnd->Show();
  213. beamStart->Show();
  214. plugModel->Show();
  215. }
  216. void idWeevil::Event_jump( void )
  217. {
  218. Event_stand();
  219. state = JUMPSTART;
  220. this->Event_PlayAnim("jump_start", false);
  221. jumpstartTimer = gameLocal.time + JUMP_STARTTIME;
  222. StartSound( "snd_jump", SND_CHANNEL_ANY, 0, false, NULL );
  223. }
  224. void idWeevil::Event_getgravity( )
  225. {
  226. if (this->isGravity <= 0)
  227. {
  228. idThread::ReturnInt( 0 );
  229. }
  230. else
  231. {
  232. idThread::ReturnInt( 1 );
  233. }
  234. }
  235. void idWeevil::Event_gravity( int value )
  236. {
  237. if (value <= 0)
  238. {
  239. //gravity is now OFF.
  240. this->isGravity = 0;
  241. lastGravityState = false;
  242. }
  243. else
  244. {
  245. //turn on gravity.
  246. this->isGravity = 1;
  247. }
  248. if (value >= 1)
  249. {
  250. //turn on gravity.
  251. physicsObj.SetGravity( gameLocal.GetGravity() );
  252. }
  253. }
  254. void idWeevil::Event_light( int value )
  255. {
  256. if (value)
  257. {
  258. if ( !light->isOn )
  259. {
  260. StartSound( "snd_light", SND_CHANNEL_ANY, 0, false, NULL );
  261. }
  262. light->On();
  263. }
  264. else
  265. {
  266. if ( light->isOn )
  267. {
  268. StartSound( "snd_light", SND_CHANNEL_ANY, 0, false, NULL );
  269. }
  270. light->Off();
  271. }
  272. }
  273. void idWeevil::Event_stand( void )
  274. {
  275. CancelEvents( &EV_weevil_stop );
  276. state = IDLE;
  277. if (!isGravity)
  278. {
  279. return;
  280. }
  281. idAngles ang = this->GetPhysics()->GetAxis().ToAngles();
  282. ang.pitch = 0;
  283. ang.roll = 0;
  284. this->GetPhysics()->SetAxis( ang.ToMat3() );
  285. this->SetOrigin(this->GetPhysics()->GetOrigin() + idVec3(0,0,3.0f));
  286. StartSound( "snd_stand", SND_CHANNEL_ANY, 0, false, NULL );
  287. }
  288. void idWeevil::Event_turn( int degrees )
  289. {
  290. if (degrees == 0)
  291. {
  292. return;
  293. }
  294. CancelEvents( &EV_weevil_stop );
  295. state = TURNING;
  296. turnCurrent = 0;
  297. turnTarget = idMath::Abs(degrees);
  298. if (degrees > 0)
  299. {
  300. turnDirection = -1;
  301. }
  302. else
  303. {
  304. turnDirection = 1;
  305. }
  306. Event_PlayAnim("turning", true);
  307. //this->Event_PlayAnim( "idle", false );
  308. }
  309. void idWeevil::Event_forward( int distance )
  310. {
  311. if (distance == 0)
  312. {
  313. return;
  314. }
  315. CancelEvents( &EV_weevil_stop );
  316. if (distance > 0)
  317. {
  318. walkDirection = 1;
  319. Event_PlayAnim("walk", true);
  320. }
  321. else
  322. {
  323. walkDirection = -1;
  324. Event_PlayAnim("walkbackwards", true);
  325. }
  326. offroadTimer = gameLocal.time + OFFROADTIME;
  327. state = WALKING;
  328. PostEventSec( &EV_weevil_stop, idMath::Abs(distance) * DIST_MOD );
  329. }
  330. void idWeevil::Event_stop( void )
  331. {
  332. CancelEvents( &EV_weevil_stop );
  333. state = IDLE;
  334. Event_PlayAnim("idle", false);
  335. }
  336. void idWeevil::UpdateStates( void )
  337. {
  338. if (state == WALKING)
  339. {
  340. if (CanMove())
  341. {
  342. idVec3 curPos;
  343. float bounceAmount;
  344. idVec3 forward;
  345. this->GetPhysics()->GetAxis().ToAngles().ToVectors(&forward);
  346. forward.Normalize();
  347. curPos = this->GetPhysics()->GetOrigin();
  348. if (HasObstacle() ||
  349. (idMath::Fabs(lastPosition.x - curPos.x) < 0.0001f
  350. && idMath::Fabs(lastPosition.y - curPos.y) < 0.0001f
  351. && idMath::Fabs(lastPosition.z - curPos.z) < 0.0001f))
  352. {
  353. //"off-road" mode.
  354. bounceAmount = WALKBOUNCE;
  355. }
  356. else
  357. {
  358. //normal walk.
  359. bounceAmount = 0;
  360. }
  361. this->GetPhysics()->SetLinearVelocity(forward * (WALKSPEED * walkDirection) + (idVec3(0,0,1) * bounceAmount));
  362. lastPosition.x = curPos.x;
  363. lastPosition.y = curPos.y;
  364. lastPosition.z = curPos.z;
  365. }
  366. }
  367. else if (state == TURNING)
  368. {
  369. idAngles ang = this->GetPhysics()->GetAxis().ToAngles();
  370. ang.yaw += turnDirection;
  371. turnCurrent++;
  372. if (turnCurrent >= turnTarget)
  373. {
  374. Event_stop();
  375. return;
  376. }
  377. if (CanMove())
  378. {
  379. this->GetPhysics()->SetAxis( ang.ToMat3() );
  380. }
  381. }
  382. else if (state == JUMPSTART)
  383. {
  384. if (gameLocal.time > jumpstartTimer)
  385. {
  386. idVec3 forward;
  387. idAngles ang = this->GetPhysics()->GetAxis().ToAngles();
  388. ang.pitch = 0;
  389. ang.roll = 0;
  390. ang.ToVectors(&forward);
  391. if (this->GetPhysics()->HasGroundContacts())
  392. {
  393. Event_stand();
  394. this->GetPhysics()->SetLinearVelocity((forward * JUMPDISTANCE) + (idVec3(0,0,1) * JUMPHEIGHT));
  395. }
  396. this->Event_PlayAnim( "jump_loop", true );
  397. state = JUMPING;
  398. jumpstartTimer = gameLocal.time + 200; //guarantee some jump time before it is allowed to enter landing state.
  399. }
  400. }
  401. else if (state == JUMPING)
  402. {
  403. if (this->IsGrabbed())
  404. {
  405. state = IDLE;
  406. return;
  407. }
  408. if (this->GetPhysics()->HasGroundContacts() && gameLocal.time > jumpstartTimer)
  409. {
  410. state = IDLE;
  411. Event_stand();
  412. this->GetPhysics()->PutToRest();
  413. this->Event_PlayAnim( "jump_end", false );
  414. return;
  415. }
  416. }
  417. }
  418. bool idWeevil::HasObstacle( void )
  419. {
  420. trace_t forwardTr;
  421. idVec3 forward, up, right;
  422. idVec3 traceStart;
  423. trace_t downTr1, downTr2, downTr3;
  424. this->GetPhysics()->GetAxis().ToAngles().ToVectors(&forward, &right, &up);
  425. traceStart = this->GetPhysics()->GetOrigin() + (up * 1);
  426. gameLocal.clip.TracePoint( forwardTr, traceStart, traceStart + (forward * (16 * walkDirection)), MASK_SOLID, this );
  427. if (forwardTr.fraction < 1)
  428. {
  429. return true;
  430. }
  431. //do the down trace to see if weevil is on a pit.
  432. gameLocal.clip.TracePoint( downTr1, traceStart + (forward * 5), traceStart + (forward * 5) + (up * -3), MASK_SOLID, this );
  433. if (downTr1.fraction >= 1)
  434. {
  435. return true;
  436. }
  437. gameLocal.clip.TracePoint( downTr2, traceStart + (forward * -5), traceStart + (forward * -5) + (up * -3), MASK_SOLID, this );
  438. if (downTr2.fraction >= 1)
  439. {
  440. return true;
  441. }
  442. gameLocal.clip.TracePoint( downTr3, traceStart, traceStart + (up * -3), MASK_SOLID, this );
  443. if (downTr3.fraction >= 1)
  444. {
  445. return true;
  446. }
  447. return false;
  448. }
  449. bool idWeevil::CanMove( void )
  450. {
  451. idAngles angles = this->GetPhysics()->GetAxis().ToAngles();
  452. //return false if: being held, not touching ground, pitch is tilted far, roll is tilted far.
  453. if (this->IsGrabbed() || !this->GetPhysics()->HasGroundContacts() || idMath::Abs( angles.pitch) >= MAXPITCH || idMath::Abs(angles.roll) >= MAXROLL)
  454. {
  455. return false;
  456. }
  457. return true;
  458. }
  459. void idWeevil::Think( void )
  460. {
  461. //RunPhysics();
  462. if (touchTriggers)
  463. {
  464. TouchTriggers();
  465. }
  466. //for the vacuum check.
  467. //if (!isGravity)
  468. {
  469. idMoveableItem::Think();
  470. }
  471. UpdateStates();
  472. Present();
  473. }
  474. void idWeevil::Present( void )
  475. {
  476. // don't present to the renderer if the entity hasn't changed
  477. if ( !( thinkFlags & TH_UPDATEVISUALS ) )
  478. {
  479. return;
  480. }
  481. BecomeInactive( TH_UPDATEVISUALS );
  482. // if set to invisible, skip
  483. if ( !renderEntity.hModel || IsHidden() )
  484. {
  485. return;
  486. }
  487. // add to refresh list
  488. if ( modelDefHandle == -1 )
  489. {
  490. modelDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
  491. }
  492. else
  493. {
  494. gameRenderWorld->UpdateEntityDef( modelDefHandle, &renderEntity );
  495. }
  496. }
  497. void idWeevil::Event_PlayAnim( const char* animname, bool cycle)
  498. {
  499. int anim;
  500. int channel = 0;
  501. int animBlendFrames = 4;
  502. int animDoneTime = 0;
  503. anim = model->GetAnimator()->GetAnim( animname );
  504. if ( !anim )
  505. {
  506. gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() );
  507. model->GetAnimator()->Clear( channel, gameLocal.time, FRAME2MS( animBlendFrames ) );
  508. animDoneTime = 0;
  509. }
  510. else
  511. {
  512. if (cycle)
  513. {
  514. model->GetAnimator()->CycleAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
  515. }
  516. else
  517. {
  518. model->GetAnimator()->PlayAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
  519. }
  520. animDoneTime = model->GetAnimator()->CurrentAnim( channel )->GetEndTime();
  521. }
  522. animBlendFrames = 0;
  523. }
  524. void idWeevil::Event_weevilEyeMove(float pitch, float yaw, bool reset)
  525. {
  526. if (reset)
  527. {
  528. headAngle = idAngles(0,0,0);
  529. model->GetAnimator()->SetJointAxis(headJoint, JOINTMOD_WORLD, headAngle.ToMat3());
  530. idThread::ReturnInt(1);
  531. return;
  532. }
  533. float newYaw = idMath::ClampFloat(-90, 90, headAngle.yaw - yaw);
  534. float newPitch = idMath::ClampFloat(-90, 90, headAngle.roll + pitch);
  535. if (idMath::Fabs(headAngle.roll) + idMath::Fabs(pitch) > 90 || idMath::Fabs(headAngle.yaw) + idMath::Fabs(yaw) > 90)
  536. {
  537. idThread::ReturnInt(0);
  538. }
  539. else
  540. {
  541. idThread::ReturnInt(1);
  542. }
  543. headAngle.roll = newPitch;
  544. headAngle.yaw = newYaw;
  545. model->GetAnimator()->SetJointAxis(headJoint, JOINTMOD_LOCAL, headAngle.ToMat3());
  546. }